diff -Nru libpod-3.2.1+ds1/build_osx.md libpod-3.4.4+ds1/build_osx.md --- libpod-3.2.1+ds1/build_osx.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/build_osx.md 2021-12-26 00:45:51.000000000 +0000 @@ -18,7 +18,7 @@ You can obtain the latest source code for Podman from its github repository. ``` -$ git clone http://github.com/containers/podman go/src/github.com/containers/podman +$ git clone https://github.com/containers/podman go/src/github.com/containers/podman ``` ## Build client diff -Nru libpod-3.2.1+ds1/changelog.txt libpod-3.4.4+ds1/changelog.txt --- libpod-3.2.1+ds1/changelog.txt 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/changelog.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,6955 +0,0 @@ -- Changelog for v3.2.1 (2021-06-11): - * Updated release notes for v3.2.1 - * remote events: fix --stream=false - * [CI:DOCS] fix incorrect network remove api doc - * remote: always send resize before the container starts - * remote events: support labels - * remote pull: cancel pull when connection is closed - * Fix network prune api docs - * Improve systemd-resolved detection - * logs: k8s-file: fix race - * Fix image prune --filter cmd behavior - * podman-remote build should handle -f option properly - * System tests: deal with crun 0.20.1 - * Fix build tags for pkg/machine... - * Fix pre-checkpointing - * container: ignore named hierarchies - * [v3.2] vendor containers/common@v0.38.9 - * rootless: fix fast join userns path - * [v3.2] vendor containers/common@v0.38.7 - * [v3.2] vendor containers/common@v0.38.6 - * Correct qemu options for Intel macs - -- Changelog for v3.2.0 (2021-06-03): - * Final release notes updates for v3.2.0 - * add ipv6 nameservers only when the container has ipv6 enabled - * Use request context instead of background - * [v.3.2] events: support disjunctive filters - * System tests: add :Z to volume mounts - * generate systemd: make mounts portable - * vendor containers/storage@v1.31.3 - * vendor containers/common@v0.38.5 - -- Changelog for v3.2.0-RC3 (2021-05-26): - * Update release notes for v3.2.0-RC3 - * Fix race on podman start --all - * Fix race condition in running ls container in a pod - * docs: --cert-dir: point to containers-certs.d(5) - * Handle hard links in different directories - * Improve OCI Runtime error - * Handle hard links in remote builds - * Podman info add support for status of cgroup controllers - * Drop container does not exist on removal to debugf - * Downgrade API service routing table logging - * add libimage events - * docs: generate systemd: XDG_RUNTIME_DIR - * Fix problem copying files when container is in host pid namespace - -- Changelog for v3.2.0-RC2 (2021-05-20): - * update c/common - * Update Cirrus DEST_BRANCH to v3.2 - * Updated vendors of c/image, c/storage, Buildah - * Initial release notes for v3.2.0-RC2 - * Add script for identifying commits in release branches - * Add host.containers.internal entry into container's etc/hosts - * image prune: remove unused images only with `--all` - * podman network reload add rootless support - * Use more recent `stale` release... - * network tutorial: update with rootless cni changes - * [CI:DOCS] Update first line in intro page - * Use updated VM images + updated automation tooling - * auto-update service: prune images - * make vendor - * fix system upgrade tests - * Print "extracting" only on compressed file - * podman image tree: restore previous behavior - * fix network restart always test - * fix incorrect log driver in podman container image - * Add support for cli network prune --filter flag - * Move filter parsing to common utils - * Bump github.com/containers/storage from 1.30.2 to 1.30.3 - * Update nix pin with `make nixpkgs` - * [CI:DOCS] hack/bats - new helper for running system tests - * fix restart always with slirp4netns - * Bump github.com/opencontainers/runc from 1.0.0-rc93 to 1.0.0-rc94 - * Bump github.com/coreos/go-systemd/v22 from 22.3.1 to 22.3.2 - * Add host.serviceIsRemote to podman info results - * Add client disconnect to build handler loop - * Remove obsolete skips - * Fix podman-remote build --rm=false ... - * fix: improved "containers/{name}/wait" endpoint - * Bump github.com/containers/storage from 1.30.1 to 1.30.2 - * Add envars to the generated systemd unit - * fix: use UTC Time Stamps in response JSON - * fix container startup for empty pidfile - * Kube like pods should share ipc,net,uts by default - * fix: compat API "images/get" for multiple images - * Revert escaped double dash man page flag syntax - * Report Download complete in Compatibility mode - * Add documentation on short-names - * Bump github.com/docker/docker - * Adds support to preserve auto update labels in generate and play kube - * [CI:DOCS] Stop conversion of `--` into en dash - * Revert Patch to relabel if selinux not enabled - * fix per review request - * Add support for environment variable secrets - * fix pre review request - * Fix infinite loop in isPathOnVolume - * Add containers.conf information for changing defaults - * CI: run rootless tests under ubuntu - * Fix wrong macvlan PNG in networking doc. - * Add restart-policy to container filters & --filter to podman start - * Fixes docker-compose cannot set static ip when use ipam - * channel: simplify implementation - * build: improve regex for iidfile - * Bump github.com/onsi/gomega from 1.11.0 to 1.12.0 - * cgroup: fix rootless --cgroup-parent with pods - * fix: docker APIv2 `images/get` - * codespell cleanup - * Minor podmanimage docs updates. - * Fix handling of runlabel IMAGE and NAME - * Bump to v3.2.0-dev - * Bump to v3.2.0-rc1 - * podman: set volatile storage flag for --rm containers - * Bump github.com/onsi/ginkgo from 1.16.1 to 1.16.2 - * Bump github.com/containers/image/v5 from 5.11.1 to 5.12.0 - * Add filepath glob support to --security-opt unmask - * Force log_driver to k8s-file for containers in containers - -- Changelog for v3.2.0-rc1 (2021-05-05) - * migrate Podman to containers/common/libimage - * add --mac-address to podman play kube - * compat api: Networks must be empty instead of null - * System tests: honor $OCI_RUNTIME (for CI) - * is this a bug? - * system test image: add arm64v8 image - * Fix troubleshooting documentation on handling sublemental groups. - * Add --all to podman start - * Fix variable reference typo. in multi-arch image action - * cgroup: always honor --cgroup-parent with cgroupfs - * Bump github.com/uber/jaeger-client-go - * Don't require tests for github-actions & metadata - * Detect if in podman machine virtual vm - * Fix multi-arch image workflow typo - * [CI:DOCS] Add titles to remote docs (windows) - * Remove unused VolumeList* structs - * Cirrus: Update F34beta -> F34 - * Update container image docs + fix unstable execution - * Bump github.com/containers/storage from 1.30.0 to 1.30.1 - * TODO complete - * Docker returns 'die' status rather then 'died' status - * Check if another VM is running on machine start - * [CI:DOCS] Improve titles of command HTML pages - * system tests: networking: fix another race condition - * Use seccomp_profile as default profile if defined in containers.conf - * Bump github.com/json-iterator/go from 1.1.10 to 1.1.11 - * Vendored - * Autoupdate local label functional - * System tests: fix two race conditions - * Add more documentation on conmon - * Allow docker volume create API to pass without name - * Cirrus: Update Ubuntu images to 21.04 - * Skip blkio-weight test when no kernel BFQ support - * rootless: Tell the user what was led to the error, not just what it is - * Add troubleshooting advice about the --userns option. - * Fix images prune filter until - * Fix logic for pushing stable multi-arch images - * Fixes generate kube incorrect when bind-mounting "/" and "/root" - * libpod/image: unit tests: don't use system's registries.conf.d - * runtime: create userns when CAP_SYS_ADMIN is not present - * rootless: attempt to copy current mappings first - * [CI:DOCS] Restore missing content to manpages - * [CI:DOCS] Fix Markdown layout bugs - * Fix podman ps --filter ancestor to match exact ImageName/ImageID - * Add machine-enabled to containers.conf for machine - * Several multi-arch image build/push fixes - * Add podman run --timeout option - * Parse slirp4netns net options with compat api - * Fix rootlesskit port forwarder with custom slirp cidr - * Fix removal race condition in ListContainers - * Add github-action workflow to build/push multi-arch - * rootless: if root is not sub?id raise a debug message - * Bump github.com/containers/common from 0.36.0 to 0.37.0 - * Add go template shell completion for --format - * Add --group-add keep-groups: suplimentary groups into container - * Fixes from make codespell - * Typo fix to usage text of --compress option - * corrupt-image test: fix an oops - * Add --noheading flag to all list commands - * Bump github.com/containers/storage from 1.29.0 to 1.30.0 - * Bump github.com/containers/image/v5 from 5.11.0 to 5.11.1 - * [CI:DOCS] Fix Markdown table layout bugs - * podman-remote should show podman.sock info - * rmi: don't break when the image is missing a manifest - * [CI:DOCS] Rewrite --uidmap doc in podman-create.1.md and podman-run.1.md - * Add support for CDI device configuration - * [CI:DOCS] Add missing dash to verbose option - * Bump github.com/uber/jaeger-client-go - * Remove an advanced layer diff function - * Ensure mount destination is clean, no trailing slash - * add it for inspect pidfile - * [CI:DOCS] Fix introduction page typo - * support pidfile on container restore - * fix start it - * skip pidfile test on remote - * improve document - * set pidfile default value int containerconfig - * add pidfile in inspection - * add pidfile it for container start - * skip pidfile it on remote - * Modify according to comments - * WIP: drop test requirement - * runtime: bump required conmon version - * runtime: return findConmon to libpod - * oci: drop ExecContainerCleanup - * oci: use `--full-path` option for conmon - * use AttachSocketPath when removing conmon files - * hide conmon-pidfile flag on remote mode - * Fix possible panic in libpod/image/prune.go - * add --ip to podman play kube - * add flag autocomplete - * add ut - * add flag "--pidfile" for podman create/run - * Add network bindings tests: remove and list - * Fix build with GO111MODULE=off - * system tests: build --pull-never: deal with flakes - * compose test: diagnose flakes v3 - * podman play kube apply correct log driver - * Fixes podman-remote save to directories does not work - * Bump github.com/rootless-containers/rootlesskit from 0.14.1 to 0.14.2 - * Update documentation of podman-run to reflect volume "U" option - * Fix flake on failed podman-remote build : try 2 - * compose test: ongoing efforts to diagnose flakes - * Test that we don't error out on advertised --log-level values - * At trace log level, print error text using %+v instead of %v - * pkg/errorhandling.JoinErrors: don't throw away context for lone errors - * Recognize --log-level=trace - * Fix flake on failed podman-remote build - * System tests: fix racy podman-inspect - * Fixes invalid expression in save command - * Bump github.com/containers/common from 0.35.4 to 0.36.0 - * Update nix pin with `make nixpkgs` - * compose test: try to get useful data from flakes - * Remove in-memory state implementation - * Fix message about runtime to show only the actual runtime - * System tests: setup: better cleanup of stray images - * Bump github.com/containers/ocicrypt from 1.1.0 to 1.1.1 - * Reflect current state of prune implementation in docs - * Do not delete container twice - * [CI:DOCS] Correct status code for /pods/create - * vendor in containers/storage v1.29.0 - * cgroup: do not set cgroup parent when rootless and cgroupfs - * Overhaul Makefile binary and release worflows - * Reorganize Makefile with sections and guide - * Simplify Makefile help target - * Don't shell to obtain current directory - * Remove unnecessary/not-needed release.txt target - * Fix incorrect version number output - * Exclude .gitignore from test req. - * Fix handling of $NAME and $IMAGE in runlabel - * Update podman image Dockerfile to support Podman in container - * Bump github.com/containers/image/v5 from 5.10.5 to 5.11.0 - * Fix slashes in socket URLs - * Add network prune filters support to bindings - * Add support for play/generate kube volumes - * Update manifest API endpoints - * Fix panic when not giving a machine name for ssh - * cgroups: force 64 bits to ParseUint - * Bump k8s.io/api from 0.20.5 to 0.21.0 - * [CI:DOCS] Fix formatting of podman-build man page - * buildah-bud tests: simplify - * Add missing return - * Bump github.com/onsi/ginkgo from 1.16.0 to 1.16.1 - * speed up CI handling of images - * Volumes prune endpoint should use only prune filters - * Cirrus: Use Fedora 34beta images - * Bump go.sum + Makefile for golang 1.16 - * Exempt Makefile changes from test requirements - * Adjust libpod API Container Wait documentation to the code - * [CI:DOCS] Update swagger definition of inspect manifest - * use updated ubuntu images - * podman unshare: add --rootless-cni to join the ns - * Update swagger-check - * swagger: remove name wildcards - * Update buildah-bud diffs - * Handle podman-remote --arch, --platform, --os - * buildah-bud tests: handle go pseudoversions, plus... - * Fix flaking rootless compose test - * rootless cni add /usr/sbin to PATH if not present - * System tests: special case for RHEL: require runc - * Add --requires flag to podman run/create - * [CI:DOCS] swagger-check: compare operations - * [CI:DOCS] Polish swagger OpertionIDs - * [NO TESTS NEEDED] Update nix pin with `make nixpkgs` - * Ensure that `--userns=keep-id` sets user in config - * [CI:DOCS] Set all operation id to be compatibile - * Move operationIds to swagger:operation line - * swagger: add operationIds that match with docker - * Cirrus: Make use of shared get_ci_vm container - * Don't relabel volumes if running in a privileged container - * Allow users to override default storage opts with --storage-opt - * Add support for podman --context default - * Verify existence of auth file if specified - * fix machine naming conventions - * Initial network bindings tests - * Update release notes to indicate CVE fix - * Move socket activation check into init() and set global condition. - * Bump github.com/onsi/ginkgo from 1.15.2 to 1.16.0 - * Http api tests for network prune with until filter - * podman-run.1.md, podman-create.1.md : Adjust Markdown layout for --userns - * Fix typos --uidmapping and --gidmapping - * Add transport and destination info to manifest doc - * Bump github.com/rootless-containers/rootlesskit from 0.14.0 to 0.14.1 - * Add default template functions - * Fix missing podman-remote build options - * Bump github.com/coreos/go-systemd/v22 from 22.3.0 to 22.3.1 - * Add ssh connection to root user - * Add rootless docker-compose test to the CI - * Use the slrip4netns dns in the rootless cni ns - * Cleanup the rootless cni namespace - * Add new docker-compose test for two networks - * Make the docker-compose test work rootless - * Remove unused rootless-cni-infra container files - * Only use rootless RLK when the container has ports - * Fix dnsname test - * Enable rootless network connect/disconnect - * Move slirp4netns functions into an extra file - * Fix pod infra container cni network setup - * Add rootless support for cni and --uidmap - * rootless cni without infra container - * Recreate until container prune tests for bindings - * Remove --execute from podman machine ssh - * Fixed podman-remote --network flag - * Makefile: introduce install.docker-full - * Makefile: ensure install.docker creates BINDIR - * Fix unmount doc reference in image.rst - * Should send the OCI runtime path not just the name to buildah - * podman machine shell completion - * Fix handling of remove --log-rusage param - * Fix bindings prune containers flaky test - * [CI:DOCS] Add local html build info to docs/README.md - * Add podman machine list - * Trim white space from /top endpoint results - * Remove semantic version suffices from API calls - * podman machine init --ignition-path - * Document --volume from podman-remote run/create client - * Update main branch to reflect the release of v3.1.0 - * Silence podman network reload errors with iptables-nft - * Containers prune endpoint should use only prune filters - * resolve proper aarch64 image names - * APIv2 basic test: relax APIVersion check - * Add machine support for qemu-system-aarch64 - * podman machine init user input - * manpage xref: helpful diagnostic for unescaped dash-dash - * Bump to v3.2.0-dev - * swagger: update system version response body - * buildah-bud tests: reenable pull-never test - * [NO TESTS NEEDED] Shrink the size of podman-remote - * Add powershell completions - * [NO TESTS NEEDED] Drop Warning to Info, if cgroups not mounted - * Fix long option format on docs.podman.io - * system tests: friendier messages for 2-arg is() - * service: use LISTEN_FDS - * man pages: correct seccomp-policy label - * rootless: use is_fd_inherited - * podman generate systemd --new do not duplicate params - * play kube: add support for env vars defined from secrets - * play kube: support optional/mandatory env var from config map - * play kube: prepare supporting other env source than config maps - * Add machine support for more Linux distros - * [NO TESTS NEEDED] Use same function podman-remote rmi as podman - * Podman machine enhancements - * Add problematic volume name to kube play error messages - * Fix podman build --pull-never - * [NO TESTS NEEDED] Fix for kernel without CONFIG_USER_NS - * [NO TESTS NEEDED] Turn on podman-remote build --isolation - * Fix list pods filter handling in libpod api - * Remove resize race condition - * [NO TESTS NEEDED] Vendor in containers/buildah v1.20.0 - * Use TMPDIR when commiting images - * Add RequiresMountsFor= to systemd generate - * Bump github.com/vbauerster/mpb/v6 from 6.0.2 to 6.0.3 - * Fix swapped dimensions from terminal.GetSize - * Rename podman machine create to init and clean up - * Correct json field name - * system tests: new interactive tests - * Improvements for machine - * libpod/image: unit tests: use a `registries.conf` for aliases - * libpod/image: unit tests: defer cleanup - * libpod/image: unit tests: use `require.NoError` - * Add --execute flag to podman machine ssh - * introduce podman machine - * Podman machine CLI and interface stub - * Support multi doc yaml for generate/play kube - * Fix filters in image http compat/libpod api endpoints - * Bump github.com/containers/common from 0.35.3 to 0.35.4 - * Bump github.com/containers/storage from 1.28.0 to 1.28.1 - * Check if stdin is a term in --interactive --tty mode - * [NO TESTS NEEDED] Remove /tmp/containers-users-* files on reboot - * [NO TESTS NEEDED] Fix rootless volume plugins - * Ensure manually-created volumes have correct ownership - * Bump github.com/rootless-containers/rootlesskit - * Unification of until filter across list/prune endpoints - * Unification of label filter across list/prune endpoints - * fixup - * fix: build endpoint for compat API - * [CI:DOCS] Add note to mappings for user/group userns in build - * Bump github.com/coreos/go-systemd/v22 from 22.1.0 to 22.3.0 - * Bump k8s.io/api from 0.20.1 to 0.20.5 - * Validate passed in timezone from tz option - * Fix system prune cmd user message with options - * WIP: run buildah bud tests using podman - * Fix containers list/prune http api filter behaviour - * System tests: reenable a bunch of skipped tests - * Generate Kubernetes PersistentVolumeClaims from named volumes - * Cleanup /libpod/images/load handler - * vendor: drop replace for github.com/syndtr/gocapability - * security: use the bounding caps with --privileged - * Bump github.com/containers/common from 0.35.0 to 0.35.3 - * Bump k8s.io/apimachinery from 0.20.4 to 0.20.5 - * Fix volumes and networks list/prune filters in http api - * Bump github.com/containers/storage from 1.25.0 to 1.28.0 - * add a dependabot config to automate vendoring - * Bump github.com/onsi/ginkgo from 1.15.1 to 1.15.2 - * network prune filters for http compat and libpod api - * test: check for io.stat existence on cgroup v2 - * test: fix test for last crun/runc - * test: simplify cgroup path - * Latest crun/runc should handle blkio-weight test - * fix user message image prune --all - * Docs: removing secrets is safe for in-use secrets - * Downgrade github.com/coreos/go-systemd/v22 - * pkg/bindings/images.Build(): fix a race condition in error reporting - * Switch all builds to pull-never - * System test cleanup - * Fix for volumes prune in http compat api - * Fix remote client timezone test - * Do not leak libpod package into the remote client - * Split libpod/network package - * fix use with localhost (testing) - * add /auth for docker compatibility - * create endpoint for querying libpod networks - * Bump github.com/sirupsen/logrus from 1.8.0 to 1.8.1 - * sdnotify tests: try real hard to kill socat processes - * Fix array instead of one elem network http api - * Delete all containers and pods between tests - * apiv2 tests: finally fix POST as originally intended - * Document CONTAINERS_CONF/CONTAINERS_STORAGE_CONF Env variables - * Removing a non existing container API should return 404 - * Docs: Add docs to access APIs inside container - * options: append CLI graph driver options - * podman load: fix error handling - * podman cp: evaluate symlink correctly when copying from container - * rm pkg/api/handlers/libpod/copy.go - * podman cp: fix copying to a non-existent dir - * podman cp: fix ownership - * podman cp: ignore EPERMs in rootless mode - * vendor buildah@v1.19.8 - * apiv2 tests: add helpers to start/stop a local registry - * Bump to v3.1.0-dev - * allow the removal of storage images - * podman-remote build does not support volumes - * Update nix pin with `make nixpkgs` - * Bump github.com/coreos/go-systemd/v22 from 22.1.0 to 22.2.0 - * [Compat API] Also print successfully tagging images in /build endpoint - -- Changelog for v3.1.0-rc1 (2021-03-08) - * Compat API: Avoid trying to create volumes if they already exist - * Bump github.com/onsi/gomega from 1.10.5 to 1.11.0 - * Allow users to generate a kubernetes yaml off non running containers - * Bump github.com/onsi/ginkgo from 1.15.0 to 1.15.1 - * turn hidden --trace into a NOP - * pkg/terminal: use c/storage/pkg/homedir - * build-arg - * Handle podman build --dns-search - * podman build --build-arg should fall back to environment - * Add support for podman build --ignorefile - * replace local mount consts with libpod/define - * separate file with mount consts in libpod/define - * Correct compat images/{name}/push response - * [NO TESTS NEEDED] Bump pre-commit-hooks version - * [ci skip] Bad formatting fix in build documentation - * Bump github.com/containernetworking/plugins to v0.9.1 - * podman-remote stop -time 0 does not work - * Do not return from c.stop() before re-locking - * Fix for podman network rm (-f) workflow - * Bump github.com/containers/buildah from 1.19.6 to 1.19.7 - * Add tests for selinux kvm/init labels - * Respect NanoCpus in Compat Create - * podman cp: support copying on tmpfs mounts - * image removal: ignore unknown-layer errors - * Fix cni teardown errors - * Use version package to track all versions - * Check for supportsKVM based on basename of the runtime - * Compat API: create volume source dirs on the host - * Makefile: add install.docker-docs-nobuild for packaging use - * Add /sys/fs/cgroup as readonly path in docs - * Add network summary to compat ps - * Fix possible panic with podman build --iidfile - * Add version field to secret compat list/inspect api - * Tidy duplicate log tests - * Fix support for podman build --timestamp - * Rewrite Rename backend in a more atomic fashion - * Use functions and defines from checkpointctl - * Move checkpoint/restore code to pkg/checkpoint/crutils - * Vendor in checkpointctl - * Support label type dict on compat build - * Makefile: install systemd services conditionally - * podman-system-service.1.md: fix timeout example - * swagger: update the libpodPutArchive operation verb - * Makefile: split install.docker-docs from install.docker - * Bump RootlessKit v0.14.0-beta.0 - * Compat api containers/json Ports field is null - * Bump github.com/cri-o/ocicni to latest master - * Refactor python tests to run against python3.9 - * APIv2 tests: make more maintainable - * [CI:DOCS] Improve release process docs - * podman rmi: handle corrupted storage better - * Enable cgroupsv2 rw mount via security-opt unmask - * podman-image-sign.1.md: typo fix - * compat api network ls accept both format options - * Enable no_hosts from containers.conf - * Correct compat images/create?fromImage response - * Fix parsing of Tmpfs field in compat create - * prune remotecommand dependency - * system test image: build it multiarch - * Updated based on Jhonce comments - * updated common to 0.35.0 - * Refactored file - * swagger: removes the schema type for PodSpecGenerator $ref - * podman-system-connection.1.md: fix copy/paste error - * Add dns search domains from cni response to resolv.conf - * Network connect error if net mode is not bridge - * Sort CapDrop in inspect to guarantee order - * podman upgrade tests - * test: ignore named hierarchies for cgroups=split - * container removal: handle already removed containers - * Bump github.com/rootless-containers/rootlesskit from 0.13.1 to 0.13.2 - * Bump k8s.io/apimachinery from 0.20.3 to 0.20.4 - * Add U volume flag to chown source volumes - * Replace Labels and Options nulls with {} in NetworkResource - * Cirrus: Temp. disable prior-fedora (F32) testing - * podman cp: test /dev/stdin correctly - * podman cp: treat /dev/stdout correctly - * cgroup: change cgroup deletion logic on v1 - * Fix podman network IDs handling - * pr-should-include-tests: recognized "renamed" tests - * --no-header flag implementation for generate systemd - * [NO TESTS NEEDED] Make binding util internal - * Two variations of --new flag added to e2e - * swagger: add missing schema properties - * bump go module to v3 - * Fix 'storage.options' indent - * Bump github.com/sirupsen/logrus from 1.7.1 to 1.8.0 - * Bump github.com/containers/buildah from 1.19.4 to 1.19.6 - * Turn on journald and k8s file logging tests - * Allow podman play kube to read yaml file from stdin - * Log working dir when chdir fails - * Fix segfault in run with memory-swap - * leak fix in rootless_linux.c fcn can_use_shortcut - * Fix journald logs with more than 1 container - * Fix journald logs --follow - * Fix journald logs --since - * fix journald logs --tail 0 - * [CI:DOCS]basic networking guide - * cp: treat "." and "/." correctly - * [CI:DOCS] [NO TESTS NEEDED] Update swagger doc for libpod container wait - * Bump k8s.io/apimachinery from 0.20.2 to 0.20.3 - * Don't switch on a single case - * Quote URL - * bindings: support simple types that implement fmt.Stringer interface - * API: fix libpod's container wait endpoint condition conversion - * Change source path resolution for volume copy-up - * podman ps --format '{{ .Size }}' requires --size option - * infra: downgrade warning to debug - * Ignore entrypoint=[\"\"] - * Bump github.com/sirupsen/logrus from 1.7.0 to 1.7.1 - * Add missing early returns in compat API - * Do not reset storage when running inside of a container - * podman kill should report rawInput not container id - * Fix an issue where copyup could fail with ENOENT - * do not set empty $HOME - * images/create: always pull image - * Fix panic in pod creation - * Bump github.com/rootless-containers/rootlesskit from 0.13.0 to 0.13.1 - * podman build: pass runtime to buildah - * correct startup error message - * Add missing params for podman-remote build - * Fix typo podman run doc in flag -pid=mode "efault" - * When stopping a container, print rawInput - * fix create container: handle empty host port - * Don't chown workdir if it already exists - * Fix broken podman generate systemd --new with pods - * fix dns resolution on ubuntu - * e2e: fix network alias test - * fix failing image e2e test - * Update troubleshooting.md - * [NO TESTS NEEDED] Refactor generated code - * Fix superfluous response.WriteHeader call in WaitContainerLibpod() - * change ps Created to unix - * Enable more golangci-lint linters - * make layer-tree lookup errors non-fatal - * Enable whitespace linter - * Enable golint linter - * Enable stylecheck linter - * Update Master to reflect the 3.0 release - * utils: takes the longest path on cgroup v1 - * container ps json format miscue - * Bump github.com/spf13/cobra from 1.1.2 to 1.1.3 - * utils: create parent cgroups - * utils: ignore unified on cgroupv1 if not present - * utils: skip empty lines - * Correct compat network prune response - * Display correct value for unlimited ulimit - * apiv2: handle docker-java clients pulling - * Rewrite copy-up to use buildah Copier - * bump to v3.1.0-dev - * [NO TESTS NEEDED] Update linter - * Bump github.com/spf13/cobra from 1.1.1 to 1.1.2 - * Add shell completion tests for secrets - * Docker APIv2 push sends digest in response body - * Fix compat networks endpoint for a empty result - * hardening flags for fedora rpmbuilds - * [CI:DOCS]First pass at release process - * Restart service when CONTAINERS_CONF changes - * Support annotations from containers.conf - * vendor github.com/containers/image v5.10.2 - * APIv2 tests: lots of cleanup - * Fix Docker APIv2 push endpoint - * generate kube: support --privileged - * Bump github.com/containers/ocicrypt from 1.0.3 to 1.1.0 - * Implement Secrets - * Bump containers/buildah to v1.19.4 - * Allow path completion for podman create/run --rootfs - * Cirrus: Send cirrus-cron report e-mail to list. - * make `podman rmi` more robust - * Implement missing arguments for podman build - * vendor latest containers/common - * add network prune - * fix logic when not creating a workdir - * Bump remote API version to 3.0.0 - * play kube selinux test case - * Fix podman network disconnect wrong NetworkStatus number - * Fix per review request - * generate kube: handle entrypoint - * play kube selinux test case - * Increase timeouts in some tests - * Add test for Docker APIv2 wait - * Implement Docker wait conditions - * Improve ContainerEngine.ContainerWait() - * Improve container libpod.Wait*() functions - * Cirrus: Collect ginkgo node logs artifacts - * Bump github.com/containers/storage from 1.24.5 to 1.25.0 - * Bump github.com/containernetworking/cni from 0.8.0 to 0.8.1 - * bindings: attach: warn correct error - * Fix invalid wait condition on kill - * Makefile: make bin/* real targets! - * typo - * Bump github.com/onsi/gomega from 1.10.4 to 1.10.5 - * Update nix pin with `make nixpkgs` - * System test for #9096 (truncated stdout) - * play kube selinux label test case - * Gating tests: diff test: workaround for RHEL8 failure - * [NO TESTS NEEDED] style: indendation - * [NO TESTS NEEDED] fixup: remove debug code - * Report StatusConflict on Pod opt partial failures - * Honor network options for macvlan networks - * Make slirp MTU configurable (network_cmd_options) - * [NO TESTS NEEDED] Generated files - * [NO TESTS NEEDED] Improve generator - * play kube selinux label issue - * Makefile: refactor ginkgo * ginkgo-remote - * Allow pods to use --net=none - * Bump github.com/onsi/ginkgo from 1.14.2 to 1.15.0 - * Update release notes for v3.0.0 - * New 'make completions' target - * add macvlan as a supported network driver - * Fix podman generate systemd --new special char handling - * Bump github.com/rootless-containers/rootlesskit from 0.12.0 to 0.13.0 - * Endpoint that lists containers does not return correct Status value - * Fix --network parsing for podman pod create - * list volumes before pruning - * Docker ignores mount flags that begin with constency - * podman generate kube ignores --network=host - * Switch podman stop/kill/wait handlers to use abi - * [CI:DOCS]build instructions for macOS - * Vendor in containers/buildah v1.19.3 - * Honor custom DNS in play|generate kube - * Podman-remote push can support --format - * Bump github.com/containers/image/v5 from 5.10.0 to 5.10.1 - * Cirrus: Build static podman-remote - * podman build --pull: refine help message and docs - * Revert "podman build --pull: use correct policy" - * Bump github.com/containers/image/v5 from 5.9.0 to 5.10.0 - * Cleanup bindings for image pull - * Don't fail if one of the cgroups is not setup - * Add support for rootless network-aliases - * Allow static ip and mac with rootless cni network - * podman build --pull: use correct policy - * Cirrus: Fix running Validate task on branches - * Fix static build cache by using cachix - * Switch podman image push handlers to use abi - * e2e tests: synchronize test results - * podman-remote ps --external --pod --sort do not work. - * Fix podman history --no-trunc for the CREATED BY field - * remote exec: write conmon error on hijacked connection - * Fix #9100 Change console mode message to debug - * Add default net info in container inspect - * Ensure the Volumes field in Compat Create is honored - * [CI:DOCS]update state of restful service - * workdir presence checks - * libpod: add (*Container).ResolvePath() - * Fixup search - * Pass DefaultMountsFile to podman build - * Ensure shutdown handler access is syncronized - * System tests: cover gaps from the last month - * Fix --arch and --os flags to work correctly - * Bump github.com/google/uuid from 1.1.5 to 1.2.0 - * Fix typo - * disable dnsname when --internal - * swagger.go: Fix compilation error - * Fix fish completion issue if the command is prefixed with a space - * Bump golang.org/x/crypto - * networking: lookup child IP in networks - * Small API test improvement for compatibility search endpoint - * podman manifest exists - * Accept and ignore 'null' as value for X-Registry-Auth - * Turn on some remote test - * Add a notice to remove pod before starting service - * libpod: move slirp magic IPs to consts - * rootlessport: set source IP to slirp4netns device - * vendor: update rootlesskit to v0.12.0 - * api: fix import image swagger definition - * podman volume exists - * Cirrus: Upload swagger YAML in every context - * [CI:DOCS] Cirrus: Skip smoke task on branch-push - * Move the cni lock file into the cni config dir - * Use random network names in the e2e tests - * [CI:DOCS] Update project name in Code of Conduct - * Set log driver for compatability containers - * Make generate systemd --new robust against double curly braces - * Fix man page for fuse-overlayfs config in rootless mode - * Cirrus: add bindings checks - * Fix handling of container remove - * make bindings generation explicit - * make bindings generation more robuts - * Revert "ginkgo: install on demand via `go get -u`" - * [CI:DOCS] fix go-md2man HTMLSpan warnings - * CI: smoke test: insist on adding tests on PRs - * podman network exists - * ginkgo: install on demand via `go get -u` - * runner.sh : deal with bash 'set -e' - * Add binding options for container|pod exists - * [CI:DOCS]Do not run compose tests with CI:DOCS - * simplify bindings generation - * make: generate bindings: use vendor - * hack/install_golangci.sh: smarter install - * golangci-lint: install to ./bin - * Create release notes for V3.0.0 - * Rename AutocompletePortCommand func - * Allow podman push to push manifest lists - * [CI:DOCS]Add README.md for golang bindings - * Turn on podman pod stats test for rootless cgroup v2 - * Fix missing podman-container-rename man page link - * Container rename bindings - * Bump to containers/buildah 1.9.2 - * Bump github.com/google/uuid from 1.1.4 to 1.1.5 - * specgen: improve heuristic for /sys bind mount - * Initial implementation of renaming containers - * Add tests for volume plugins - * Initial implementation of volume plugins - * [CI:DOCS] Add hook-script example to get_ci_vm.sh - * Makefile: add target to generate bindings - * container stop: release lock before calling the runtime - * Bump github.com/cri-o/ocicni to latest master - * Cirrus: Upd. ext. service check host list - * Bump k8s.io/apimachinery from 0.20.1 to 0.20.2 - * Bump github.com/stretchr/testify from 1.6.1 to 1.7.0 - * Cirrus: Utilize $GOPATH cache for alt_build task - * Add more information and examples on podman and pipes - * Vendor in common 0.33.1 - * CI: fix broken diagnostic message for -dev check - * test: use stringid.GenerateNonCryptoID() in more tests - * network: disallow CNI networks with user namespaces - * Reduce general binding binary size - * play kube: set entrypoint when interpreting Command - * Fxes /etc/hosts duplicated every time after container restarted in a pod - * Add 'MemUsageBytes' format option - * Remove the ability to use [name:tag] in podman load command - * More /var/run -> /run - * More /var/run -> /run - * Exorcise Driver code from libpod/define - * Fix problems reported by staticcheck - * Expose security attribute errors with their own messages - * oci: use /proc/self/fd/FD to open unix socket - * Use HTTPProxy settings from containers.conf - * Cirrus: Add cross-compile test for alternative arches - * image list: ignore bare manifest list - * Ensure that `podman play kube` actually reports errors - * Bump github.com/containers/storage from 1.24.4 to 1.24.5 - * oci: keep LC_ env variables to conmon - * Better test and idomatic code. - * add pre checkpoint - * podman build --force-rm defaults to true in code - * Adding json formatting to `--list-tags` option in `podman search` command. - * Use abi PodPs implementation for libpod/pods/json endpoint - * Add Networks format placeholder to podman ps and pod ps - * Add network filter for podman ps and pod ps - * Improve error message when the the podman service is not enabled - * Restore compatible API for prune endpoints - * Cirrus: Skip most tests on tag-push - * Add mips architecture to the cross build target - * Fix build for mips architecture follow-up - * Handle podman exec capabilities correctly - * Containers should not get inheritable caps by default - * Make podman generate systemd --new flag parsing more robust - * Switch references of /var/run -> /run - * rootless: automatically split userns ranges - * rootless: add function to retrieve uid mappings - * rootless: add function to retrieve gid mappings - * test: Add checkpoint/restore with volumes - * Include named volumes in container migration - * Use Options as CRImportCheckpoint() argument - * Use Options as exportCheckpoint() argument - * Fix podman logs read partial log lines - * Revert e6fbc15f26b2a609936dfc11732037c70ee14cba - * Cirrus: Update Fedora & Ubuntu images - * Ensure that user-specified HOSTNAME is honored - * generate systemd: do not set `KillMode` - * Bump github.com/google/uuid from 1.1.3 to 1.1.4 - * vendor containers/psgo@v1.5.2 - * Add default sysctls for pod infra containers - * Ensure we do not edit container config in Exec - * close journald when reading - * libpod API: pull: fix channel race - * Allow image errors to bubble up from lower level functions. - * test: fix variable name - * systemd: make rundir always accessible - * podman-remote fix sending tar content - * fix: disable seccomp by default when privileged. - * Compat api containers/json add support for filters - * Bump github.com/google/uuid from 1.1.2 to 1.1.3 - * Expose Height/Width fields to decoder - * Rework pruning to report reclaimed space - * Add support for Gentoo file to package query - * The slirp4netns sandbox requires pivot_root - * Update nix pin with `make nixpkgs` - * readme: Remove broken link - * Fix e2e test for `podman build --logfile` - * test: fix variables name - * exec: honor --privileged - * libpod: change function to accept ExecOptions - * Consolidate filter logic to pkg subdirectory - * sort api endpoints in documentation - * libpod: handle single user mapped as root - * Refactor kube.ToSpecGen parameters to struct - * re-open container log files - * Set NetNS mode instead of value - * add --cidfile to container kill - * Document uid/gidmap are based on subuid/gid mapping - * Bump github.com/containers/storage from 1.24.3 to 1.24.4 - * Fix podman build --logfile - * Fix missing options in volumes display while setting uid and gid - * Spelling - * play kube: fix args/command handling - * Pass down EnableKeyring from containers.conf to conmon - * Prefer read/write images over read/only images - * add pod filter for ps - * Add Security information to podman info - * Add volume filters to system prune - * podman v3 container bindings - * Fix build for mips architecture - * Bump k8s.io/apimachinery from 0.20.0 to 0.20.1 - * Update nix pin with `make nixpkgs` - * Document location of backend events file - * Fix support for rpmbuild < 4.12.0. - * system tests: set PODMAN_TIMEOUT to 120 - * remote copy - * Bump github.com/containers/common from 0.31.0 to 0.31.1 - * podman v3 pod bindings - * [CI:DOCS] Bump version on readme - * misc bindings to podman v3 - * Docker compat API - /images/search returns wrong structure (#7857) - * Close the stdin/tty when using podman as a restAPI. - * Add support for pacman package version query - * Don't accidently remove XDG_RUNTIME_DIR when reseting storage - * Always add the default gateway to the cni config file - * System tests: better diagnostics in completion test - * Bump github.com/opencontainers/selinux from 1.7.0 to 1.8.0 - * podman.service should be an exec service not a notify service - * Fix: unpause not supported for CGv1 rootless - * Disable incompatible rootless + CGroupsV1 tests - * Disable rootless pod stats tests w/ CgroupV1 - * Disable CGv1 pod stats on net=host post - * Disable pod stats tests in containerized Fedora w/ CGroupsV1 - * Disable blkio.weight test on Ubuntu - * Cirrus: Add support for Ubuntu 20.x - * Add LogSize to container inspect - * Podman image bindings for 3.0 - * contrib: drop mirror.chpc.utah.edu:443 - * libpod, conmon: change log level for rootless - * Clean up temporary file. - * Allow users to specify TMPDIR in containers.conf - * system tests: the catch-up game - * RHEL gating tests: more journald exceptions - * Add volume prune --filter support - * shell completion for the network flag - * podman events allow future time for --until - * Sign multi-arch images - * add compose test descriptions - * test-compose: rewrite to new subdir form - * add compose regression to ci - * WIP: test docker-compose - * podman: drop checking valid rootless UID - * Cleanup CNI Networks on reboot - * Fix some network compat api problems - * Fix Wrong image tag is used when creating a container from an image with multiple tags - * Handle --rm when starting a container - * Refine public key usage when remote - * podman logs honor stderr correctly - * Bindings refactor - * Ignore containers.conf sysctls when sharing namespaces - * Fix panic in libpod images exists endpoint - * Bump github.com/containernetworking/plugins from 0.8.7 to 0.9.0 - * Add --filter to podman system prune - * Fix storage.conf to define driver in the VM - * Bump github.com/containers/storage from 1.24.1 to 1.24.3 - * Properly handle --cap-add all when running with a --user flag - * security: honor systempaths=unconfined for ro paths - * Add system test for shell completion - * Bump github.com/onsi/gomega from 1.10.3 to 1.10.4 - * Honor the --layers flag - * pkg/copy: introduce a Copier - * Repeat system pruning until there is nothing removed - * Bump k8s.io/apimachinery from 0.19.4 to 0.20.0 - * Bump github.com/opencontainers/selinux from 1.6.0 to 1.7.0 - * auto updates: document systemd unit and timer - * archive: move stat-header handling into copy package - * Fix spelling mistakes - * pkg/copy: add parsing API - * make podman play use ENVs from image - * Correct port range logic for port generation - * Make `podman stats` slirp check more robust - * Add systempaths=unconfined option - * Bump github.com/containers/image/v5 from 5.8.1 to 5.9.0 - * Restore json format for fields as well as whole structs - * Do not pull if image domain is localhost - * pass full NetworkMode to ParseNetworkNamespace - * Fix network ls --filter invalid value flake - * Implement pod-network-reload - * generate kube on multiple containers - * Change name of imageVolumes in container config JSON - * Do not error on installing duplicate shutdown handler - * image sign using per user registries.d - * container cgroup path - * add comment to #8558 regression test - * Docker compat API - containers create ignores the name - * Add APIv2 test for containers-prune - * container create: do not clear image name - * Add saschagrunert and zhangguanzhang to OWNERS - * Bump github.com/containers/common from 0.30.0 to 0.31.0 - * update website link for install instructions - * Jira RUN-1106 System handlers updates - * enable short-name aliasing - * Jira RUN-1106 Volumes handlers updates - * Jira RUN-1106 Network handlers updates - * Do not mount sysfs as rootless in more cases - * Add ability to set system wide options for slirp4netns - * Vendor in containers/common v0.30.0 - * Clarify uid range requirements - * Close image rawSource when each loop ends - * Use PasswordCallback instead of Password for ssh - * More docker compat API fixes - * rewrite podman-cp - * e2e: bump pull timeout to 240 seconds - * add @Luap99 to OWNERS file - * Support Unix timestamps for `podman logs --since` - * Fix some nit - * Jira RUN-1106 Image handlers updates - * Jira RUN-1106 Container handlers updates - * Add containerenv information to /run/.containerenv - * Correct which network commands can be run as rootless - * Drop default log-level from error to warn - * podman, exec: move conmon to the correct cgroup - * Support --network=default as if it was private - * Change bindings to stop two API calls for ping - * hack/podman-socat captures the API stream - * BATS: add new load test - * Add mask and unmask option to --security-opt - * Use Libpod tmpdir for pause path - * Fix `podman images...` missing headers in table templates - * add commas between mount options - * Do not pass name argument to Load API - * target is not tag - * Fix shell completion for ps --filter ancestor - * Add support for network ids - * Validate that the bridge option is supported - * Add integration test for the bridge options - * Add podman network create option for bridge vlan - * Add podman network create option for bridge mtu - * Do not use "true" after "syslog" in exit commands - * Fix typo in tests - * Fix potential race condition in testing - * compat create should use bindings - * Add API for communicating with Docker volume plugins - * BATS: add ping test - * Document volume mounts of source directories do NOT get created - * Revert the custom cobra vendor - * Bump version in README to v2.2.0 - * network connect disconnect on non-running containers - * Bump master to v3.0.0-dev - * Update release notes for v2.2.0 - * Fix extra quotation mark in manpages. - * Fix option names --subuidname and --subgidname - * Do not ignore infra command from config files - * Revert "Allow multiple --network flags for podman run/create" - * Add APIv2 tests for kube generate - * Document docker transport is the only supported remote transport - * podman network label support - * runtime: set XDG_* env variables if missing - * Add support for persistent volume claims in kube files - * Prepare support in kube play for other volume types than hostPath - * Remove varlink support from Podman - * Fix problems with network remove - * Switch from pkg/secrets to pkg/subscriptions - * Do not validate the volume source path in specgen - * Add support for --platform - * REST API v2 - ping - fix typo in header - * REST API v2 - ping - remove newline from response to improve Docker compatibility - * squash - * Not use local image create/add manifest - * [CI:DOCS] fix misleading save/load usage - * [tutorials:mac-win-client] Fix command ensuring sshd is enabled - * Fix custom mac address with a custom cni network - * Bump to v2.2.0-dev - * Handle ps container created field as a time.Time - * test resource cleanup - * more tests - * not forcing unmount - * few more tests - * add test - * add comment - * fix: unmount container without force - * style: wsl - * fix lint - * Implement containers/{id or name}/archive - * Ensure that --net=host/pod/container/none warn with -p - -- Changelog for HEAD (2020-11-24): - * Set PATH env in systemd timer. - * Docker compat API fixes - * shell completions: remove usage of ShellCompDirectiveError - * more shell completion improvements - * Fix ip-range for classless subnet masks - * Bump github.com/containers/common from 0.27.0 to 0.29.0 - * Add podman container ps command - * clarify ps(1) fallback of `podman top` - * APIv2 - create container sets wrong entrypoint - * Enable remote shell completion without a running endpoint - * Specify what the replace flag replaces in help text - * APIv2 - strip CAP_ prefix from capabilities in json - * Make c.networks() list include the default network - * Allow containers to --restart on-failure with --rm - * REST API v2 - list of images - mandatory Created attribute - * Allow multiple --network flags for podman run/create - * fix container cgroup lookup - * Make podman service log events - * vendor in containers/storage v1.24.1 containers/image v5.8.1 - * Document containers.conf settings for remote connections - * Shell completion for podman ps and podman pod ps --filter - * Add alias for podman network rm -> remove - * add network connect|disconnect compat endpoints - * Fix sed regex to update version in version/version.go - * Github-Actions: Send e-mail on Cirrus cron failure - * Align the podman pod ps --filter behavior with podman ps - * podman-remote network rm --force is broken - * Remove build \!remote flags from test - -- Changelog for v2.2.0-rc1 (2020-11-18): - * Add release notes for v2.2.0-RC1 - * correct numbering typo - * Align the podman ps --filter behavior with docker - * Fix podman pod inspect show wrong MAC string - * Fix example for manifest push - * add network connect|disconnect compat endpoints - * Rename e2e test files to include _test.go suffix - * Client call to /play/kube incorrectly set tlsVerify - * Add an option to control if play kube should start the pod - * Swap out json-iterator for golang default - * Fix missing headers in `network ls` - * [CI:DOCS] fix an apostrophe nit in man page - * remove contrib/gate - * Remove some more excessive wrapping and stuttering - * Cleanup tutorials - * use container cgroups path - * Explain the relation between --pod and --network - * Make sure /etc/hosts populated correctly with networks - * logformatter: highlight timing results - * Bump Buildah to v1.18.0, c/storage to v1.24.0 - * Cirrus: Invalidate static cache on VM image update - * Improve the shell completion api - * use lookaside storage for remote tests - * Bump k8s.io/apimachinery from 0.19.3 to 0.19.4 - * Wrap missing container errors with container ID - * system tests: various - * Add support for volume ls --filter label=key=value - * Podman-remote build is getting ID twice - * [CI:DOCS] Touch up Podman description in man page menu - * Fix markdown tables on docs.podman.io - * short-name aliasing - * Set podman-auto-update.service Type=oneshot - * test for buildah version in container images. - * Add missing --now in systemctl start command - * Change podman build --pull=true to PullIfMissing - * Fix namespace flag parsing for podman build - * Add podman build --net alias for --network - * Refactor to use DockerClient vs APIClient - * Maintain consistent order of short and long flag names in docs - * Fix issues found with codespell - * Bump github.com/rootless-containers/rootlesskit from 0.11.0 to 0.11.1 - * Install the new shell completion logic - * Add shell completion with cobra - * Vendor in some cobra PRs to improve the completion experience. - * Add support for network connect / disconnect to DB - * Ensure we do not double-lock the same volume in create - * Cleanup error reporting - * Cirrus: update VMs - * [CI:DOCS] Restore man page cross-checker - * Cirrus: Run validation tests in CI:DOCS mode - * Add podman(1) to the list of man pages on docs.podman.io - * Set default network driver for APIv2 networks - * Add tests to make sure podman-remote logs works correctly. - * Add anchors for flag names on docs.podman.io - * migrate play kube to spec gen - * Add example of fuse-overlay to podman system reset - * Bump github.com/containers/common from 0.26.3 to 0.27.0 - * skip ipv6 e2e tests on rootless - * add e2e test for network with same subnet - * enable ipv6 network configuration options - * make network name uniq for dnsname tests - * network aliases for container creation - * system tests: skip journald tests on RHEL8 - * Update podman build man page to match buildah bud man page - * Cirrus: Detailed CPU/Memory/Time runner.sh stats - * podman-pull.1.md: add example for pulling an image by hash - * podman-import.1.md: fix paragraph formatting - * podman-import.1.md: fix shell syntax - * Update CI tests to run python docker library against API - * Stop binding layer from changing line endings - * Add support for podman search --format json - * Add --log-driver to play kube - * Show error on bad name filter in podman ps - * Use CPP, CC and flags in dep check scripts - * Fix link to point at correct content - * fix: allow volume creation when the _data directory already exists - * rootless container creation settings - * fix: podman-system-service doc time is seconds - * Bump github.com/rootless-containers/rootlesskit from 0.10.1 to 0.11.0 - * Update nix pin with `make nixpkgs` - * Use /tmp/podman-run-* for backup XDG_RUNTIME_DIR - * Only use container/storage/pkg/homedir.Get() - * Add support for mounting external containers - * Cirrus: Use F33beta VM image - * Cirrus: Simplify artifact collection - * Use ping from alpine - * Bump github.com/containers/storage from 1.23.8 to 1.23.9 - * add a PR template - * Use regex for "pod ps" name filter to match "ps" behavior - * Add tip re. typical root cause of "Exec format error" to troubleshooting.md - * Add tests for network aliases - * Make volume filters inclusive - * [CI:DOCS]Add Urvashi to podman OWNERS - * Improve error messages from failing tests - * fedora rootless cpu settings - * Test $HOME when it's parent is bind mounted with --userns=keep-id - * Update README.md - * docs: Mention mounts.conf location for non-root users - * Add test/apiv2/rest_api tests to make target - * specgen: keep capabilities with --userns=keep-id - * specgen: fix check for root user - * specgen: add support for ambient capabilities - * Add better support for unbindable volume mounts - * Bump github.com/containers/storage from 1.23.7 to 1.23.8 - * Use osusergo build tag for static build - * Change http ConnState actions between new and active - * Match build pull functionality with Docker's - * Centralize cores and period/quota conversion code - * specgen, cgroup2: check whether memory swap is enabled - * Fix dnsname when joining a different network namespace in a pod - * Bump Buildah to v1.17.0 - * manifest list inspect single image - * Remove search limit since pagination support - * spec: protect against segfault - * [CI:DOCS] Fix broken CI readme links - * Improve setupSystemd, grab mount options from the host - * specgen: split cgroup v1 and cgroup v2 code - * specgen: fix error message - * When container stops, drop sig-proxy errors to infos - * Cirrus: Workaround F32 BFQ Kernel bug - * Stop excessive wrapping of errors - * Pod's that share the IPC Namespace need to share /dev/shm - * Fix the `--pull` flag to `podman build` to match Docker - * Restore --format table header support - * Create the default root API address path - * new "image" mount type - * Cirrus: Simplify setting/passing env. vars. - * Podman often reports OCI Runtime does not exist, even if it does - * rootless: improve error message if cannot join namespaces - * NewFromLocal can return multiple images - * libpod: clean paths before check - * move from docker.io - * Cirrus: Use google mirror for docker.io - * Cirrus: Always record runc/crun versions - * Ensure that attach ready channel does not block - * Add a way to retrieve all network aliases for a ctr - * Add pod, volume, network to inspect package - * Add network aliases for containers to DB - * Add test cases to cover podman volume - * Document how to enable CPU limit delegation - * Add more details about how CPU limits work - * set resources only when specified - * Improve the journal event reading - * build(deps): bump github.com/containers/common from 0.26.0 to 0.26.3 - * Support hashed hostnames in the known_hosts file - * image list: check for all errors - * Yet another iteration on PR title plugin - * System tests: cleanup, make more robust - * pr update action: fix errors on master branch - * The cidfile should be created when the container is created - * auto update: mark it as non-experimental - * Add support for host keys for non-22 ports - * fix: podman-cp respects "--extract" flag - * add GitHub action to add non-main branch to PR title - * filter events by labels - * Bump github.com/spf13/cobra from 1.1.0 to 1.1.1 - * Bump github.com/containers/buildah from 1.16.4 to 1.16.5 - * src: nil check - * Don't error if resolv.conf does not exists - * src: add nil checks - * replace net_raw with setuid - * fix: /image/{name}/json returns RootFS layers - * APIv2 compatibility network connect|disconnect - * Tests: Check different log driver can work with podman logs - * podman create doesn't support creating detached containers - * Fix pull method selection - * set compat network driver default - * Add hostname to /etc/hosts for --net=none - * Add a Degraded state to pods - * Refactor podman to use c/common/pkg/report - * container create: record correct image name - * Add EOL to compat container logs - * save image remove signatures - * Switch use of Flags to Options - * Bump k8s.io/apimachinery from 0.19.2 to 0.19.3 - * Fix handling and documentation of podman wait --interval - * Podman build should default to not usins stdin - * Tests: Fix common flakes, and improve apiv2 test log - * Retrieve network inspect info from dependency container - * refactor api compatibility container creation to specgen - * Fix ps port output - * Ensure that hostname is added to hosts with net=host - * Add a system test to verify --runtime is preserved - * Use runtime names instead of paths in E2E tests - * Re-create OCI runtimes by path when it is missing - * When given OCI runtime by path, use path as name - * fix: neutral value for MemorySwappiness - * Make invalid image name error more specific - * System tests: remove some misleading 'run's - * --tls-verify and --authfile should work for all remote commands - * Fix host to container port mapping for simple ranges - * Always add the dnsname plugin to the config for rootless - * Make man page headings more consistent - * Update podman-remote start --attach to handle detach keys - * Update podman-remote run to handle detach keys - * Bump github.com/containers/common from 0.24.0 to 0.26.0 - * Fix panic when runlabel is missing - * Fix podman image trust show --raw output - * Fix podman-run man page heading - * Fix sorting issues in completions - * Add support for external container - * fix podman container exists and diff for storage containers - * Fix possible panic in libpod container restore - * Bump github.com/spf13/cobra from 1.0.0 to 1.1.0 - * System test additions - * Setup HOME environment when using --userns=keep-id - * Setup HOME environment when using --userns=keep-id - * Fix indentation for `podman pod inspect` - * Cirrus: Execute docker-py tests on a VM - * Restore --format table support - * Convert Split() calls with an equal sign to SplitN() - * Bump github.com/onsi/gomega from 1.10.2 to 1.10.3 - * Restore indent on JSON from `podman inspect` - * Enforce LIFO ordering for shutdown handlers - * alter compat no such image message - * Cirrus: Restore APIv2 Testing - * Cirrus: Ability to skip most tests for docs updates - * Restore --format: stats & pod ps - * Enable masking stop signals within container creation - * APIv2 tests: try again to fix them - * Add a shutdown handler package - * System tests: run with local podman, not remote - * Remove a note that the HTTP API is not yet stable. - * APIv2 tests: get them passing again - * Add support for resource limits to play kube - * Resolve #7860 - add time.RFC3339Nano into ContainerJSONBase - * Add more APIv2 tests for images: push, tag, untag, rmi and image tree. - * Include CNI networks in inspect output when not running - * Monitor for client closing stream - * pkg/spec: fix a confusing error message - * Search repository tags using --list-tags - * Fix the "err: cause" order of OCI runtime errors - * tests/e2e: Add Toolbox-specific test cases - * This PR allows users to remove external containers directly - * Fix documentation link and typo - * Restore --format table... - * Add support for resource cpu limit to generate kube - * Port V1 --format table to V2 podman - * BlobInfoCacheDir is set incorrectly when copying images - * Store cgroup manager on a per-container basis - * --format updates for images/diff.go - * add compatibility endpoint for exporting multiple images - * Restore --format 'table...' to commands - * Ports given only by number should have random host port - * Update nix pin with `make nixpkgs` - * add prerequisite section before building binaries - * newlines on all container detaches - * Cirrus: Fix obtaining a CI VM - * APIv2 compatibility rootless network fix - * Port commands to V2 --format 'table...' - * system tests: cleanup, and add more tests - * prevent unpredictable results with network create|remove - * Enable k8s configmaps as flags for play kube - * Attempt to turn on some more remote tests - * Use WaitWithDefaultTimeout in cleanup - * Move pod jobs to parallel execution - * Populate /etc/hosts file when run in a user namespace - * Cirrus: Fix running shellcheck locally - * Cirrus CI runner: refactor - * fix apiv2 /containers/$name/json return wrong value in `.Config.StopSignal` - * pkg/cgroups/createCgroupv2Path: nits - * Lowercase some errors - * Remove excessive error wrapping - * Support max_size logoptions - * Fixes remote attach and exec to signal IdleTracker - * Cirrus: Skip deep testing on branches - * logformatter: run on system tests & bindings - * Fix handling of CheckRootlessUIDRange - * Cirrus: Fix branch-validation failure - * Add TODO for adding CPU limit support - * Add support for resource memory limit to generate kube - * Fix podman-remote ps --ns broken - * fix closed the remote connection on pull causes service panic - * Add SELinux support for pods - * Cirrus: Implement podman automation 2.0 - * compat: images/create: fix tag parsing - * Fix Podman logs reading journald - * Restore "table" --format from V1 - * --rm option shold conflicts with --restart - * Bump github.com/containers/common from 0.23.0 to 0.24.0 - * libpod: check the gid is present before adding it - * podman-remote does not support most of the global flags - * Correct to latest version - * Bump github.com/containers/buildah from 1.16.2 to 1.16.4 - * image prune: remove all candidates - * spec: open fuse with --device .*/fuse - * rootless: use sync.Once for GetAvailableGids() - * rootless: move GetAvailableGids to the rootless pkg - * logformatter: add Synopsis at top of each page - * Podman containers/pods prune should throw an error if user adds args - * fix compat api privileged and entrypoint code - * Migrate container images to automation_images - * system test: untag all test - * remote: fix name and ID collisions of containers and pods - * Add additionalGIDs from users in rootless mode - * Fix some flakes in the e2e network tests. - * Update rootless_tutorial.md - * Volume prune should not pass down the force flag - * Support --http-proxy for remote builds - * fix: The container created by APIV2 has an incorrect Env and WorkDir - * misc fixes for f33 integration tests - * fix allowing inspect manifest of non-local image - * Distinguish userns vs containerized tests - * Don't disable Go modules when generating varlink - * Use local image if input image is a manifest list - * image look up: consult registries.conf - * pkg/registries: add a retiring note - * Attempt to test all Broken SkipIfRootless FIXME - * Make the e2e test network cleanup more robust. - * Fix ubuntu exec_test - * capabilities: always set ambient and inheritable - * libpod: bump up rootless-cni-infra to v3 - * rootless-cni-infra v3: fix cleaning up DNS entries - * fix remote untag - * Make all Skips specify a reason - * Fix handling of remove of bogus volumes, networks and Pods - * We already set container=podman environment variable - * Refactor IdleTracker to handle StateIdle transitions - * System tests: add podman run --tz - * System tests: corner case for run --pull - * healthchecks: return systemd-run error - * Add X-Registry-Config support - * Gating-test fix: deal with new crun error msg - * Bump github.com/sirupsen/logrus from 1.6.0 to 1.7.0 - * Apply suggestions from code review - * Adds missing . to README.md file. - * Ignore containers.conf sysctl when namespaces set to host - * System tests: reenable some skipped tests - * Journald log driver test - * fix for compatibility volume creation - * Add section about current differences - * Fix network remove for the podman remote client - * Fix podman network rm --force when network is used by a pod - * Remove SkipIfRootless if possible, document other calls - * Properly handle podman run --pull command - * Updating on supported restart policy - * Add support for slirp network for pods - * rootless: fix hang when newidmap is not installed - * Remove some SkipIfRootess flags from tests - * Bump github.com/containers/common from 0.22.0 to 0.23.0 - * HTTP Attach: Wait until both STDIN and STDOUT finish - * build: honor --runtime setting - * remote load: check if input is directory - * stats: break out CLI options - * new endpoint: /libpod/containers/stats - * apiv2 container limit differ from docker-api - * system tests: helpers: safer parse_table - * system tests: new test for run --log-driver - * set interactive mode with compat create endpoint - * Allow filtering on pod label values - * Remove final v2remotefail failures - * Fix a bug where log-driver json-file was made no logs - * e2e tests: SkipIfRemote(): add a reason - * stats refactor - * Systemd should be able to run as rootless - * Bump github.com/containers/buildah from 1.16.1 to 1.16.2 - * Examine all SkipIfRemote functions - * fix build with varlink - * Bump version in README to v2.1.0 - * Include cgroup manager in `podman info` output - * Add Server header to API service responses - * Bump to v2.2.0-dev - * podman save: fix redirect of multi-images - * pkg/hooks: support all hooks - * Print nice error message when python is not installed - * add missing return for compat kill - * system tests: new tests - * Evict containers before removing via V2 API - * Cirrus: Add gpg2 to Ubuntu images - * Fix mismatch between log messages and behavior of libpod.LabelVolumePath. - -- Changelog for v2.1.0 (2020-09-22): - * Update release notes for v2.1.0 Final Release - * Fix up attach tests for podman remote - * update stale bot - * [CI:DOCS] Add 'In Progress' note to CONTRIBUTING.md - * Restore 'id' stanza in pull results - * Fix podman image unmount to only report images unmounted - * libpod: bumps up rootless-cni-infra to 2 - * stats: log errors instead of sending 500 - * Fix incorrect parsing of create/run --volumes-from - * rootless-cni-infra: fix flakiness during bringing up lo interface - * Fix handling of podman-remote stop --ignore - * Refactor version handling in cmd tree - * Preserve groups in exec sessions in ctrs with --user - * Install bats as root - * Makefile: Fix broken libpodimage targets - * stats: detect closed client connection - * stats endpoint: write OK header once - * handle the play kube and generate kube for with restartPolicy - * fix the .Path and .Args when use the infra-command - * Update nix pin with `make nixpkgs` - * fix a typo of login.1.md - * Bump github.com/rootless-containers/rootlesskit from 0.10.0 to 0.10.1 - * enable --iidfile for podman-remote build - * update github.com/docker/docker and relevant deps - * Make Go builds more consistent - * dependabot-dance: new tool for managing revendor PRs - * WIP: Fix remote logs - -- Changelog for v2.1.0-rc2 (2020-09-17) - * Update release notes for Podman v2.1.0-RC2 - * Fix play_kube_test deployment template - * fix missing completion in podman run - * Bump k8s.io/apimachinery from 0.19.1 to 0.19.2 - * image list: return all associated names - * Add labels to a pod created via play kube - * Refactor remote pull to provide progress - * --mount: support arbitrary mount-argument order - * Fix podman pod create --infra-command and --infra-image - * Fix "rootless-cni-infra + runc fails with ENODEV" - * podman version and --version: fix format, exit - * Supports import&run--signature-policy - * Fix CI breakage due to PR collision - * [CI:DOCS]update owners file - * Refactor API version values - * Fix --systemd=always regression - * Correct HTTP methods for /containers/{id}/archive - * events endpoint: header: do not wait for events - * run/create: record raw image - * rootless CNI: extract env and cmd from image - * libpod: rootless CNI image: use quay - * move `rootless-cni-infra` image to quay.io - * vendor github.com/containers/image/v5@v5.6.0 - * podman wait accept args > 1 - * Usability: prevent "-l" with arguments - * Document the connection path for podman --remote - * Refactor API build endpoint to be more compliant - * pull types allow initial caps - * Determine if resolv.conf points to systemd-resolved - * Bump to v2.1.0-dev - * Fix completions for namespaces - -- Changelog for v2.1.0-rc1 (2020-09-11) - * Add release notes for Podman v2.1.0-RC1 - * Vendor in containers/buildah 1.16.1 - * Vendor in containers/common v0.22.0 - * system df: fix image-size calculations - * add @edsantiago to OWNERS file - * sort OWNERS file - * remote run: fix error checks - * Fix up errors found by codespell - * Document --read-only --rootfs requirements - * Force Attach() to send a SIGWINCH and redraw - * run_networking e2e test: add cleanup to some tests - * play/generate: support shareProcessNamespace - * system tests: cleanup - * allowed underscores to remain in name for YAML - * Add read-only mount to play kube - * Add auth.json(5) link to login/logout docs - * libpod: read mappings when joining a container userns - * Make an entry in /etc/group when we modify /etc/passwd - * podman container runlabel should pull the image if it does not exist - * Bump k8s.io/apimachinery from 0.19.0 to 0.19.1 - * vendor containers/storage v1.23.5 - * remote run: consult events for exit code - * Cirrus: Obsolete CI:IMG process & related files - * Fix podman container runlabel --display - * Make oom-score-adj actually work - * compat kill: only wait for 0 signal and sigkill - * remote kill: don't wait for the container to stop - * Fix podman ps -l - * generate systemd: catch `--name=foo` - * Fix podman build --logfile - * fix APIv2 pods top of non-exist pod gets two response value - * Extend bash completion to cover new flags - * Update man page of `manifest add` - * manifest push: handle cert-dir flag - * Extend flags of `manifest add` - * Show c/storage (Buildah/CRI-O) containers in ps - * rootless: support `podman network create` (CNI-in-slirp4netns) - * add contrib/rootless-cni-infra - * [CI:DOCS] Update remote tutorials - * Don't setup the Image/ContainerEngine when calling a cmd with subcmds - * Makefile: add cross compile targets for more arches - * Migrate away from docker.io - * podman stop: do not cleanup for auto-removal - * Bump github.com/onsi/gomega from 1.10.1 to 1.10.2 - * support multi-image (docker) archives - * Fix typo in the remove network api doc - * APIv2 Add network list filtering - * Bump github.com/onsi/ginkgo from 1.14.0 to 1.14.1 - * make image parent check more robust - * Fix unconfined AppArmor profile usage for unsupported systems - * Bump github.com/containers/storage from 1.23.2 to 1.23.4 - * Add global options --runtime-flags - * [CI:DOCS] Add macos build to ci - * Fix system df inconsistent - * [CI:DOCS] Add note on run image fuse problem - try 2 - * WIP: update VM images - * APIv2 add generate systemd endpoint - * We should not be mounting /run as noexec when run with --systemd - * Bump github.com/gorilla/mux from 1.7.4 to 1.8.0 - * Don't create ~/.config after removing storage.conf - * Update master with release notes for v2.0.6 - * APIv2 test: add more tests for containers - * Ensure rootless containers without a passwd can start - * use crio runc on CICID ubuntu - * bindings: reenable flaky(?) pause/unpause test - * handle play kube with pod.spec.hostAliases - * Bump github.com/google/uuid from 1.1.1 to 1.1.2 - * Bump k8s.io/api from 0.18.8 to 0.19.0 - * play kube: handle Socket HostPath type - * Small tweaks to readme scope section - * Update Master to reflect v2.0.5 release - * fix panic when checking len on nil object - * Add support for variant when pulling images - * Document override-arch and override-os - * Delete prior /dev/shm/* - * Don't remove config files with podman system reset - * Just use `rm` for helper command to remove storage - * Bump github.com/containernetworking/plugins from 0.8.6 to 0.8.7 - * Fix log level case regression - * Use environment from containers.conf - * Bump github.com/containers/conmon - * Bump github.com/gorilla/schema from 1.1.0 to 1.2.0 - * Bump k8s.io/apimachinery from 0.18.8 to 0.19.0 - * abi: trim init command - * [CI:DOCS] Switch more references from libpod to podman - * Switch to containers/common for seccomp - * Fix up some error messages - * Ensure pod REST API endpoints include ctr errors - * Update c/storage to v1.23.2 - * BATS: fix corner case in --userns=keep-id test - * [CI:DOCS] Update podman-remote docs - * Send HTTP Hijack headers after successful attach - * fix podman generate kube with HostAliases - * [CI:DOCS] Making docs build on mac - * Remove test comment for now succeeding tests - * Update vendor of buildah to latest code - * fix apiv2 will create containers with incorrect commands - * [CI:DOCS] fix swagger api docs - * Add missing autocomplete - * Update nix pin with `make nixpkgs` - * podman: add option --cgroup-conf - * vendor: update opencontainers/runtime-spec - * In podman 1.* regression on --cap-add - * error when adding container to pod with network information - * fix /libpod/pods/json returns null when there are no pods - * fix pod creation with "new:" syntax followup + allow hostname - * [CI:DOCS] Include Go bindings tutorial - * Unmount c/storage containers before removing them - * Cirrus: special-case CI colon-IMG and colon-DOCS only in subject - * Add support for --connection - * system tests: enable more remote tests; cleanup - * Note port publishing needs in pods for create/run - * Cirrus: Increase integration-testing timeout - * Bump github.com/containers/image/v5 from 5.5.1 to 5.5.2 - * generate systemd: quote arguments with whitespace - * Ensure DefaultEnvVariables is used in Specgen - * Support sighup reload configuration files - * fix podman version output to include git commit and builttime - * Don't limit the size on /run for systemd based containers - * abi: fix detection for systemd - * fix podman create/run UTS NS docs - * Remove help/usage from --remote pre-check - * flake fix: podman image trust - * e2e tests: use actual temp dirs, not "/tmp/dir" - * Re-disable sdnotify tests to try to fix CI - * Clean up pods before returning from Pod Stop API call - * Use `bash` binary from env instead of /bin/bash for scripts - * Wait for reexec to finish when fileOutput is nil - * Bump k8s.io/api from 0.18.6 to 0.18.8 - * Bump github.com/containers/storage from 1.21.2 to 1.23.0 - * podman support for IPv6 networks - * Add pointer to troubleshooting in issue template - * Bump k8s.io/apimachinery from 0.18.6 to 0.18.8 - * system tests: enable sdnotify tests - * Ensure pod infra containers have an exit command - * podman.service: use sdnotiy - * run, create: add new security-opt proc-opts - * Add support for setting the CIDR when using slirp4netns - * add event for image build - * podman-remote fixes for msi and client - * podman save use named pipe - * Change /sys/fs/cgroup/systemd mount to rprivate - * Add parameter verification for api creation network - * add xz as a recommended pkg - * Makefile: use full path for ginkgo - * Replace deepcopy on history results - * Fix hang when `path` doesn't exist - * Cross-reference *.rst files too - * Ensure correct propagation for cgroupsv1 systemd cgroup - * Man pages: assert that subcommands are in order - * Use set for systemd commands - * Enable systemd mode for /usr/local/sbin/init - * Allow specifying seccomp profiles for privileged containers - * Update nix pin with `make nixpkgs` - * Add the Status field in the ps --format=json - * Add missing pages for docs.podman.io - * Align images with Buildah - * Error pass through for more accurate error reporting - * remove --latest for all remote commands - * Remove TEST_REMOTE_CLIENT from RCLI - * Fix handling of working dir - * Default .Repository and .Tag values to - * generate systemd: fix error handling - * Do not use image CMD if user gave ENTRYPOINT - * Unconditionally retrieve pod names via API - * system tests: podman-remote, image tree - * [CI:DOCS] BZ1860126 - Fix userns defaults in run man page - * changes to support outbound-addr - * image list: speed up - * fix podman logs --tail when log is bigger than pagesize - * [CI:DOCS] Update podmanimages README.md - * Ensure that exec errors write exit codes to the DB - * podman-remote send name and tag - * Refactor parsing to not require --remote to be first - * Handle podman-remote run --rm - * correct go-binding key for volumes - * HACK HACK try debugging build - * Retry pulling image - * fix bug podman sign storage path - * validate fds --preserve-fds - * Remove duplicated code - * Improve error message when creating a pod/ctr with the same name - * podman: support --mount type=devpts - * rootless: system service joins immediately the namespaces - * docker-compose uses application/tar - * Missing return after early exit - * Ensure WORKDIR from images is created - * Bump to Buildah 1.16.0-dev in upstream - * Do not set host IP on ports when 0.0.0.0 requested - * Reenable remote system tests - * implement the exitcode when start a container with attach - * Install auto-update services for users - * Fix test failure regarding unpackaged files. - * Install auto-update systemd service and timer. - * podman.service: drop install section - * Remove some unnecessary []byte to string conversions - * Speedup static build by utilizing CI cache on `/nix` folder - * API returns 500 in case network is not found instead of 404 - * Change recommended systemd unit path for root. - * Update master README and release notes for v2.0.4 - * Ensure libpod/define does not include libpod/image - * Fix podman service --valink timeout - * Add versioned _ping endpoint - * fix pod creation with "new:" syntax - * Cirrus: Utilize freshly built images - * Cirrus: Install golang 1.14 on Ubuntu - * Cirrus: Add python packages to images - * Make `search --no-trunc` work for podman remote - * API: Fix 'podman image search` missing description - * Add test case for description being present in search result - * Fix close fds of exec --preserve-fds - * volumes: do not recurse when chowning - * Handle single character images - * rootless: add a check for the host id included in the range - * fix swapped mem_usage/percent fields - * rootless: child exits immediately on userns errors - * rootless: do not ignore errors if mappings are specified - * add {{.RunningFor}} placeholder in ps --format - * fix close fds of run --preserve-fds - * fix podman system df format error - * Ensure that 'rmi --force' evicts Podman containers - * System tests: new system-df and passwd tests - * Binding the same container port to >1 host port is OK - * Return NamesHistory when returning remote images - * Don't crash when giving bogus format commands - * bindings: skip flaky pause/unpause test - * logformatter: more libpod-podman fallout - * [CI:DOCS] apiv2 fix volumes not inculded field - * Fix `podman image search` missing description - * Specifying --ipc=host --pid=host is broken - * Fix building from http or '-' options - * System tests: add environment, volume tests - * Add podman image mount - * Switch all references to github.com/containers/libpod -> podman - * compat/info.go: TrimPrefix(CGroupsVersion, "v") - * Bump github.com/rootless-containers/rootlesskit from 0.9.5 to 0.10.0 - * add newline to output in error message - * Cleanup handling of podman mount/unmount - * Corrects typo in the name of the Linux package shadow-utils. - * When chowning we should not follow symbolic link - * Update transfer doc - * test/apiv2: add a simple events test - * API events: fix parsing error - * CI - various fixes - * Remove 'experimental' from API doc - * replace the html/template package with text/template - * update configuration for rootless podman - * Fix exit code example in podman-run.1.md - * Make changes to /etc/passwd on disk for non-read only - * Update release notes and README on master for v2.0.3 - * Update system.rst - * The `podman start --attach` command should not print ID - * Refactor container config - * Fix typos on documentation 'What is Podman' page - * CI: fix rootless permission error - * Bump github.com/containers/common from 0.17.0 to 0.18.0 - * [WIP] Refactor podman system connection - * Publish IP from YAML (podman play kube) - * Turn on a bunch more remote tests - * logformatter: handle podman-remote - * Cirrus: Switch to freshly built image - * Cirrus: Add packages that provide htpasswd - * Cirrus: Ensure GOPATH is properly set during image-builds - * CI: attempt to fix flake in login test - * Support default profile for apparmor - * Bump github.com/containers/storage from 1.21.1 to 1.21.2 - * Bump github.com/containers/common from 0.16.0 to 0.17.0 - * Enable a bunch of remote tests - * Enable --remote flag - * Add --umask flag for create, run - * fix play kube doesn't override dockerfile ENTRYPOINT - * Do not print an error message on non-0 exec exit code - * Document proxy env var precedence - * BATS help-message test: improve diagnostics - * Add noop function disable-content-trust - * Fix Generate API title/description - * docs: Clarify how env var overriding works - * Update the README to reflect the libpod move - * make localunit: record coverage - * unit tests: root check - * docs: Fix formatting mistake - * logformatter: update MAGIC BLOB string - * Switch references from libpod.conf to containers.conf - * BATS tests: more resilient remove_same_dev_warning - * Add support for overlay volume mounts in podman. - * Re-enable a generate kube test that failed on Ubuntu - * events endpoint: backwards compat to old type - * podman.service: set type to simple - * podman.service: set doc to podman-system-service - * podman.service: use default registries.conf - * podman.service: use default killmode - * podman.service: remove stop timeout - * events endpoint: fix panic and race condition - * systemd: symlink user->system - * fix: system df error when an image has no name - * document CAP_SYS_ADMIN required for systemd PrivateNetwork - * Cleanup nix derivation for static builds - * Used reference package with errors for parsing tag - * abi: set default umask and rlimits - * docs: document the new slirp4netns options - * network, slirp4netns: add option to allow host loopback - * libpod: pass down network options - * The compat create endpoint should 404 on no such image - * Bump github.com/containers/common from 0.15.2 to 0.16.0 - * Bump k8s.io/api from 0.18.5 to 0.18.6 - * Bump k8s.io/apimachinery from 0.18.5 to 0.18.6 - * Bump github.com/containers/conmon - * vendor golang.org/x/text@v0.3.3 - * Fix `podman system connection` panic - * Preserve passwd on container restart - * Fix & add notes regarding problematic language in codebase - * Error on rootless mac and ip addresses - * allow switching of port-forward approaches in rootless/using slirp4netns - * Fix "Error: unrecognized protocol \"TCP\" in port mapping" - * APIv2 tests: fix race condition causing CI flake - * system tests: check for masked-device leaks - * system tests: new tests for run, exec - * Bump github.com/uber/jaeger-client-go - * Bump github.com/containers/storage from 1.21.0 to 1.21.1 - * Fix lint - * Add SystemdMode to inspect for containers - * play-kube: add suport for "IfNotPresent" pull type - * Mask out /sys/dev to prevent information leak from the host - * Fix handling of entrypoint - * docs: user namespace can't be shared in pods - * When determining systemd mode, use full command - * Populate remaining unused fields in `pod inspect` - * Include infra container information in `pod inspect` - * [CI:DOCS]Do not copy policy.json into gating image - * Fix systemd pid 1 test - * Remove outdated seccomp policy - * Correctly print STDOUT on non-terminal remote exec - * Pids-limit should only be set if the user set it - * Don't setup AppArmor provile for privileged pods - * Ensure sig-proxy default is propagated in start - * Fix container and pod create commands for remote create - * version/info: format: allow more json variants - * Bump github.com/containers/storage from 1.20.2 to 1.21.0 - * Fix: Correct connection counters for hijacked connections - * Fix: Hijacking v2 endpoints to follow rfc 7230 semantics - * Remove hijacked connections from active connections list - * Remove all instances of named return "err" from Libpod - * Vendor in new version of Buildah - * Remove dependency on github.com/opencontainers/libpod/configs - * logs: enable e2e tests - * log API: add context to allow for cancelling - * Fix saving in oci format - * APIv2:fix: Get volumes from `Binds` when creating - * fix API: Create container with an invalid configuration - * Update release notes on Master for v2.0.2 - * Minor: Remove two inaccurate comments - * Cirrus: Rotate keys post repo. rename - * fix race condition in `libpod.GetEvents(...)` - * Add username to /etc/passwd inside of container if --userns keep-id - * Add support for Filter query parameter to list images api - * Disable mount tests as rootless - * Change buildtag for remoteclient to remote for testing - * BATS system tests for new sdnotify - * Implement --sdnotify cmdline option to control sd-notify behavior - * Fix bug where `podman mount` didn't error as rootless - * move go module to v2 - * Bump github.com/onsi/ginkgo from 1.13.0 to 1.14.0 - * auto-update: clarify systemd-unit requirements - * podman ps truncate the command - * Set engine env from common config - * Fix issue #6803 Container inspect endpoint returns null for NetworkSettings/Ports - * Bump imagebuilder to v1.1.6 in upstream - * Add --tz flag to create, run - * Print errors from individual containers in pods - * stop podman service in e2e tests - * Fix `system service` panic from early hangup in events - * Bump github.com/opentracing/opentracing-go from 1.1.0 to 1.2.0 - * APIv2:fix: Handle docker volume force as expected - * APIv2: Add docker compatible volume endpoints - * Bump k8s.io/api from 0.18.4 to 0.18.5 - * test.apiv2: add testing for container initializing - * Bump github.com/containers/common from 0.14.3 to 0.15.1 - * Created timestamp returned by imagelist should be in unix format - * APIv2 tests: usability: better test logging - * docs: recommend alternatives to podman inspect - * utils: fix parsing of cgroup with : in the name - * Bump k8s.io/apimachinery from 0.18.4 to 0.18.5 - * Set TMPDIR to /var/tmp by default if not set - * fix: Don't override entrypoint if it's `nil` - * Add a note on the APIs supported by `system service` - * test: add tests for --user and volumes - * container: move volume chown after spec generation - * libpod: volume copyup honors namespace mappings - * Set console mode for windows - * systemd system test: run auto-update - * Allow empty host port in --publish flag - * Fix a bug with APIv2 compat network remove to log an ErrNetworkNotFound instead of nil - * Fixes --remote flag issues - * Ensure umask is set appropriately for 'system service' - * system tests: add pod, inspect testing - * specgen: fix order for setting rlimits - * Revert sending --remote flag to containers - * vendor github.com/containers/common@v0.14.3 - * podman: add new cgroup mode split - * systemd generate: allow manual restart of container units in pods - * e2e inspect: HostConfig.SecurityOpt - * generate systemd: improve pod-flags filter - * Print port mappings in `ps` for ctrs sharing network - * Fix python dockerpy tests - * Add support for dangling filter to volumes - * Friendly amendment for pr 6751 - * Set syslog for exit commands on log-level=debug - * Add containers.conf default file for windows and MAC Installs - * Docs: consistency between man / --help - * utils: drop default mapping when running uid!=0 - * podman run/create: support all transports - * Fix inspect to display multiple label: changes - * podman untag: error if tag doesn't exist - * Set stop signal to 15 when not explicitly set - * libpod: specify mappings to the storage - * APIv2: Return `StatusCreated` from volume creation - * APIv2:fix: Remove `/json` from compat network EPs - * Fix ssh-agent support - * APIv2:doc: Fix swagger doc to refer to volumes - * BATS tests: new too-many-arguments test - * Reformat inspect network settings - * Add podman network to bash command completions - * Fix typo in manpage for `podman auto update`. - * Add tests for --privileged with other flags - * Add JSON output field for ps - * V2 podman system connection - * wip - * system tests: invoke with abs path to podman - * image load: no args required - * system tests: new rm, build tests - * Fix conflicts between privileged and other flags - * Re-add PODMAN_USERNS environment variable - * libpod/containers/json: alias last -> limit - * Bump required go version to 1.13 - * Makefile: allow customizable GO_BUILD - * Add explicit command to alpine container in test case. - * "pod" was being truncated to "po" in the names of the generated systemd unit files. - * Use POLL_DURATION for timer - * rootless_linux: improve error message - * Stop following logs using timers - * Add container name to the /etc/hosts within the container - * Update release notes for v2.0.0 - * Update README to reflect that v2.0.0 has been released - * Bump master to v2.1.0-dev following release of v2.0 - * Fixes #6670 - * Correct logic for demux'ing channels - * Account for non-default port number in image name - * correct the absolute path of `rm` executable - * Poll on events for file reading - * Add --preservefds to podman run - * podman images --format json: pretty print - * Fix podman build handling of --http-proxy flag - * search: allow wildcards - * CI: force registry:2.6 - * Fix remote docs - * Allow recursive dependency start with Init() - * Bump k8s.io/apimachinery from 0.18.3 to 0.18.4 - * unflake rmi tests - * Bump k8s.io/api from 0.18.3 to 0.18.4 - * Bump go.etcd.io/bbolt from 1.3.4 to 1.3.5 - * Podman system service is no longer experimental - * Handle dropping capabilties correctly when running as non root user - * Don't ignore --user flag in rootless --userns keepid - * Bump to v2.0.0-dev - * Makefile: install.varlink needs to create dirs - * Do not share container log driver for exec - -- Changelog for v2.0.0-rc7 (2020-06-17) - * Bump Buildah to v1.15.0 - * Move logs functionality to separate file for APIv2 - * generate systemd: `ExecStopPost` for all units - * Revert #6591 to fix issue with failed tests - * vendor github.com/containers/image/v5@v5.5.1 - * Add support for the unless-stopped restart policy - * fix misc remote build issues - * "streaming output" logs test: fix flake - * Fix handling of old oci hooks - * [CI:DOCS] Fixes #6548 - * Re-add resource limit warnings to Specgen - * Add to lines returen in podman-remote logs - * Vendor containers/common v0.14.0 - * Show Anon, GID, UID in v2 volumes - * Fix podman inspect on overlapping/missing objects - * Fix --init and --init-path - * Fix podman-remote images - * Revert "Change Varlink systemd unit to use `system service`" - * Bump github.com/containers/conmon - * handlers/compat: fix lint error - * auto-update: use image's arch - * APIv2 tests: Add some tests for podman pods - * Add deprecated message to varlink command - * Handle errors on attach properly - * fix podman cp can create an extra directory level - * Remove redundant break in for loop. - * Bump to v2.0.0-dev - * generate systemd: `--replace` on named containers/pods - * pod create --replace - * {create,run} --replace - * Bump github.com/uber/jaeger-client-go - * Bump github.com/onsi/ginkgo from 1.12.3 to 1.13.0 - * Adds more docker py test - * The string field of Built was missing from server - * Add some additional fields to imageinspect - * Do not print error message when container does not exist - * Changed from t.StopAtEOF() to t.Stop() and added error check - * Fix -f logs to stop when a container exits - * Add the missing return - * Fix -f logs follow with stopped container - -- Changelog for v2.0.0-rc6 (2020-06-15) - * Change Varlink systemd unit to use `system service` - * Turn on More linters - * Do not default WorkingDir to / on client side - * Reassemble filters on the server side - * Bump github.com/containers/common from 0.13.0 to 0.13.1 - * [CI:DOCS] Fix carriage returns in API v2 header - * Fix missing code during in_podman build - * update document login see config.json as valid - * [CI:DOCS] Add quick start directions to APIv2 Dock - * Fix builds for RDO - * podman: create scope only if --cgroup-manager=systemd - * libpod: fix check for slirp4netns netns - * e2e: sanity check --infra-conmon-pidfile - * generate systemd: wrap pod/ctr lookup errors - * docs: create/run fix --pod-id-file description - * generate systemd: create pod template - * generate systemd: refactor - * add (*Pod).CreateCommand() - * generate systemd: rename source files - * generate systemd: rephrase lookup error - * pod create: add `--infra-conmon-pidfile` - * generate systemd: rename "cid" to "ctr-id" - * container-{create,run}: add `--pod-id-file` - * podman-pod{rm,start,stop}: support --pod-id-file - * systemd/generate: remove unused infra container field - * pod config: add a `CreateCommand` field - * Fixed bug where 'podman log ' would truncate some lines. - * Enable IPv6 port binding - * Bump to v2.0.0-dev - * container: do not set hostname when joining uts - * container: make resolv.conf and hosts accessible in userns - * WIP: Enable (and disable) remote testing - * fix api fails with 'strconv.ParseUint: parsing "tcp": invalid syntax' - * Fix play kube report printing when no containers are created - * Fix missing doc for field in PlayKubePod - * Update comment related to seccomp profiles in play kube - * Consistent Yaml convention througout play kube tests - * Fix podman generate tests that relied on play kube - * Add tests for Deployment Kind and minor fix for play kube output - * Fix existing tests - * Modify PlayKubeReport to preserve pod->container mapping - * supporting k8s Deployment objects - -- Changelog for v2.0.0-rc5 (2020-06-10) - * Fix Id->ID where possible for lint - * Fixup issues found by golint - * podman-events: clarify streaming behaviour - * Cirrus: Include packages for containers/conmon CI - * Ensure signal validation happens first in pod kill - * Bump github.com/json-iterator/go from 1.1.9 to 1.1.10 - * Bump github.com/containers/common from 0.12.0 to 0.13.0 - * Improve swagger+CORS metadata docs - * Ensure Conmon is alive before waiting for exit file - * Bump github.com/stretchr/testify from 1.6.0 to 1.6.1 - * e2e: disable checkpoint test on Ubuntu - * force bats version to v1.1.0 - * Enable Ubuntu tests in CI - * Modify py test to start stop system service for each test - * Add parallel operation to `podman stop` - * Fix handling of systemd. - * Add parallel execution code for container operations - * Fix handling of ThrottleWriteIOPSDevice - * Bump github.com/seccomp/containers-golang from 0.4.1 to 0.5.0 - * Strip defaults from namespace flags - * Ensure that containers in pods properly set hostname - * Adds docker py regression test. - * Turn on the podman-commands script to verify man pages - * Attempt to turn on special_testing_in_podman tests - * Bump to v2.0.0-dev - -- Changelog for v2.0.0-rc4 (2020-06-04) - * /images/.../json: fix port parsing - * BATS and APIv2: more tests and tweaks - * Vendor in container/storage v1.20.2 - * add socket information to podman info - * Namespace fields were set with bogus values - * When stopping containers locally, ensure cleanup runs - * Remove use of ABISupport buildtag - * fix remote test --ignore & turn on more tests - * Ensure that image/container inspect are specialized - * turn on remote stop_test - * V2 Add support for ssh authentication methods - * Add a few CVE entries to changelog.txt - * Add more Remote tests - * RHEL8 and Centos8 don't have oci-runtime yet - * test.apiv2: add test cases for committing an image from a container - * Turn on remote rm_test --cidfile - * Properly follow linked namespace container for stats - * Fix a segfault in `podman inspect -l` w/ no containers - * Remove reference to "upcoming" RHEL 7.7 - * Bump Conmon in COPR spec - * Enable detached exec for remote - * check --user range for rootless containers - * images --no-trunc: fix ID formatting - * make env handling os dependent - * Bump github.com/containers/conmon - * Bump github.com/onsi/ginkgo from 1.12.2 to 1.12.3 - * Update vendor containers/psgo - * Bump github.com/opencontainers/runc from 1.0.0-rc9 to 1.0.0-rc90 - * Bump github.com/coreos/go-systemd/v22 from 22.0.0 to 22.1.0 - * Combine the code of dealing with 'readonly' and 'ro'. - * Add bindings for exec and enable attached remote - * Add information on detach-keys - * system tests : more tests - * Add support for format {{.Label}} - * turn on remote testing for images. podman-remote build now works. - * Add invalid value to error message - * Fix leak of empty tarball - * Update man pages for --ip with CNI networks - * [CI:DOCS] update httpd location in tutorial - * default build without `varlink` tag - * Bump to v2.0.0-dev - * compat handlers: add X-Registry-Auth header support - * Don't build code on remoteclient - * v2 copy endpoints - * Bump github.com/rootless-containers/rootlesskit from 0.9.4 to 0.9.5 - * system tests: enable skopeo REGISTRY_AUTH_FILE - -- Changelog for v2.0.0-rc3 (2020-05-29) - * Bump github.com/stretchr/testify from 1.5.1 to 1.6.0 - * V2 verify JSON output is consistent and doesn't drift - * Vendor in containers/common v0.12.0 - * Ensure that signal names can be parsed on Windows - * fix `ps --last=N` - * test.apiv2: add testing for image and deal with API returning binary - * specgen: fix segfault - * Add streaming ability to endpoint - * Fix builds on 32 bit arches - * v2 libpod push: correct docs - * container stats: fix --no-stream race - * Add --format to pod inspect - * Add support for `readonly` option to --mount - * docs: fix typo - * V2 Fix interface nil checks - * [CI:DOCS] Tweak casing in rootless doc - * podman-registry: many unrelated fixes - * Fix Dockerfile - * Bump github.com/opencontainers/selinux from 1.5.1 to 1.5.2 - * podman-registry helper script: handle errors - * Makefile: customizable $REMOTETAGS - * add section on rootless volumes - * [CI:DOCS] Prepare image to turn on podman-commands test - * Vendor in latest containers/buildah - * Turn on Fedora testing - * [CI:DOCS] Fix readthedocs link - * [CI:DOCS]add crun to gating image - * network compatibility endpoints for API - * Add MethodNotAllowedHandler() to add in debugging - * Follow up PR to fix issues found in #6341 - * Bump to v2.0.0-dev - * [CI:DOCS]Add conmon to gating image - * Attempt to turn on build_without_cgo tests - * Attempt to turn on additional build tests - * Added new flags to 'podman generate systemd' to change the unit name prefix - * Enable rootless tests for podman remote - * V2 enable remote logs and testing - -- Changelog for v2.0.0-rc2 (2020-05-22) - * Attempt to turn on integration tests - * Removes remote system reset functionality. skip e2e test for remote. - * Attempt to turn on special_testing_endpoing tests - * Attempt to turn on varlink tests - * Attempt to turn on rpmbuild tests - * Bump github.com/containers/common from 0.11.2 to 0.11.4 - * Enables iidfile test as issue fixed now - * [CI:DOCS] Docs revamp. - * Fix podman-remote start tests - * podman version --format ... was not working - * Display human build date in podman info - * remote manifest test - * Turn on more remote tests - * v2 podman-remote build - * Fix podman-remote stop --all to handle not running containers - * Enable rmi test - * Bump github.com/opencontainers/go-digest from 1.0.0-rc1 to 1.0.0 - * Remove github.com/libpod/libpod from cmd/pkg/podman - * Start testing with cross compilation - * Fixes podman pod create --pod-id-file #6292 - * remote untag test - * Get proper exit code when running or starting a container. - * vendor: update seccomp/containers-golang to v0.4.1 - * Bump github.com/containers/storage from 1.19.2 to 1.20.1 - * Bump github.com/onsi/ginkgo from 1.12.0 to 1.12.2 - * Handle filters correctly for podman prune - * Fix remote handling of podman images calls - * Bump k8s.io/api from 0.18.2 to 0.18.3 - * Bump github.com/onsi/gomega from 1.10.0 to 1.10.1 - * Enable system prune test remote client - * Fix build on OS X - * Update Derivative API tutorial to reflect the HTTP API - * Turn off 'noexec' option by default for named volumes - * enable remote integration tests for init - * Add a test for detached exec - * Update manpage for `podman exec` to include detach flag - * Enable cleanup processes for detached exec - * Add ability to clean up exec sessions with cleanup - * Add CLI frontend for detached exec - * Add backend code for detached exec - * Add exit commands to exec sessions - * enable pod_create remote integration tests - * Fix remote integration for healthchecks - * Fix create_test for remote integration - * govern remote attach and start - * Test fixes for remote integration - * V2 API Version Support - * Print container state when erroring that it is improper - * system tests: more podman-pod tests - * don't skip log tests unless remote - * [CI:DOCS] Image tree endpoint should return 404 - * oci conmon: tell conmon to log container name - * add go-bindings for `hack/podman-registry` - * New tool: hack/podman-registry, manages local registry - * Testcase added for network commands - * format option added to network inspect command. - * filter option added to network ls command. - * Fix mountpont in SecretMountsWithUIDGID - * Update troubleshoot page - * v2 enable remote integration tests - * Get MAC, Windows and Linux podman-remote from latest version links. - * V2 Implement terminal handling in bindings attach - * Fix EOM for SendFile - * Bump to v2.0.0-dev - * Give `auto-update` ability to use per-container authfile specified by label. - * system tests: small fixes for rawhide+cgroups v1 - * Add HairpinMode to our CNI configs - -- Changelog for v2.0.0-rc1 (2020-05-18) - * v2endpoint remove image path correction - * Drop APIv2 resize endpoint - * Drop a debug line which could print very large messages - * v2 podman remote attach, start, and run - * Fix lint - * Remove duplicated exec handling code - * Fix lint - * Update API documentation for Inspect - * Parameters for ExecStart are body, not query - * Prune stale exec sessions on inspect - * Remove exec sessions on container restart - * Fix start order for APIv2 exec start endpoint - * Don't fail when saving exec status fails on removed ctr - * Add APIv2 handler for resizing exec sessions - * Ensure that Streams are set to defaults for HTTP attach - * Wire in endpoint for ExecStart - * Add an initial implementation of HTTP-forwarded exec - * Make convenience boxed true/false easier to use - * Use the libpod.conf cni_config_dir option for inspect and delete - * Cirrus: Refresh VM Images, Add Ubuntu 20 LTS - * Cirrus: Fix image-name hints - * Cirrus: Update Ubuntu 18 to 20 - * fix bug --format {{json.}} of events - * V2 Update attach bindings to use Readers/Writers vs chan - * Ensure that cleanup runs before we set Removing state - * Fix two coverity issues (unchecked null return) - * Fix REMOTETAGS - * Cleanup OCI runtime before storage - * Default podman.spec to use crun - * Fix checkpoint --leave-running - * Bump github.com/containers/storage from 1.19.1 to 1.19.2 - * Bump github.com/containernetworking/plugins from 0.8.5 to 0.8.6 - * Update release notes and version on master - * WIP V2 attach bindings and test - * [CI:DOCS]remove libpod.conf from spec - * enable remote image tree - * Bump github.com/containers/conmon - * Bump gopkg.in/yaml.v2 from 2.2.8 to 2.3.0 - * system tests: add volume tests - * cgroup: skip unified if we are using v1 - * enable podman v2 networking for remote client - * Remove libpod.conf from repo - * add podman remote system df - * vendor crio/ocicni@v0.2.0 - * test: enable networking test for rootless - * rootless: do not set pids limits with cgroupfs - * auto-update: support authfiles - * Add netgo build tag to static binary - * Adds tunnel routes for system reset. - * add port to podman remote command - * Bump github.com/containers/image/v5 from 5.4.3 to 5.4.4 - * Bump github.com/containers/common from 0.11.1 to 0.11.2 - * Some BATS cleanup: run and systemd tests - * v2podman image sign - * shm_lock_test: add nil check - * Add podman static build - * enable rootless mount tests - * spec: fix order for setting rlimits - * enable rootless integration testing - * [CI:DOCS] Add Security Policy - * V2 Impliment tunnelled podman version - * Ensure `podman inspect` output for NetworkMode is right - * Fix bug where pods would unintentionally share cgroupns - * bindings tests for container remove and inspect - * Add remaining annotations for `podman inspect` - * v2 podman unshare command - * Update the Podman readme - * v2 podman search rootless - * Fix `podman pod create --infra=false` - * default to tunnel without ABISupport tag - * abi: do not attempt to setup rootless if euid==0 - * fix pod stats flake - * set binding tests to required - * Fix handling of overridden paths from database - * Fix typo in path - * Makefile: fix a dependency issue - * Fixed typo on podman network create man - * fix and enable systemd system tests - * Bump github.com/onsi/gomega from 1.9.0 to 1.10.0 - * auto-update - * set --conmon-pidfile - * Fix parsing of --network for `podman pod create` - * Add podman-remote-static target - * podman: split env variables in env and overrides - * v2trust set and show - * container runlabel - * enable login/logut unspecified args - * [CI:DOCS] Add link to Tutorials to docs homepage - * Enables port test - * CI:DOCS: Document API docs + CORS maintenance - * Update manpages for image volumes and MAC address - * Updated heading from 5 to 6 in link. - * add {generate,play} kube - * Manifest remove, push - * Reenable systemd E2E tests - * Revert commit 016a91 already accepted. - * Updated heading from 5 to 6 in the link. - * Add small fixes for 'podman run' from diffing inspect - * manifest annotate - * Bump k8s.io/api from 0.17.4 to 0.18.2 - * Bump github.com/containers/storage from 1.19.0 to 1.19.1 - * Eliminate race condition on podman info - * v2 system subcommand - * v2 podman stats - * BATS help test: check usage string - * Rework port parsing to support --expose and -P - * [CI:DOC] Add linger to troubleshooting - * Fix errors found when comparing podman v1 --help versus V2 - * Updated the broken links for the docs. - * Updated the broken links for the docs. - * image removal: refactor part 2 - * build(deps): bump github.com/uber/jaeger-client-go - * Bump github.com/sirupsen/logrus from 1.5.0 to 1.6.0 - * [CI:DOC]Use full repo name in podmanimage Dockerfiles - * Fix errors found in coverity scan - * Remove skip on containers.conf tests - * cgroupsns was not following containers.conf - * Properly handle default capabilities listed in containers.conf - * Properly handle containers.conf devices - * [CI:DOCS] Bring README.md up to date - * And system prune feature for v2. - * Fix errors found in coverity scan - * check --get-login when login - * search --limit compatible with docker - * add provided cni networks to spec gen - * fix commands without input - * System tests: help messages: check required-arg - * v2networking enable commands - * V2 Commands that require ParentNS (rootful) are report error - * Cirrus: Utilize new cache images - * Cirrus: Utilize new base images - * cirrus: Update to Fedora 32 proper - * Enable prune integration test. Fixes container prune. - * test: enable start tests - * podman, start: propagate back the raw input - * test: enable remaining run tests - * test: enable entrypoint tests - * test: enable create tests - * cmd, podman: do not override entrypoint if unset - * cmd, podman: use String instead of variable+StringVar - * cmd, podman: handle --pod new:POD - * create: propagate override-arch and override-os - * testv2: enable attach test - * V2 enable ps tests - * enable final system test - * V2 restore podman -v command - * V2 Restore images list tests - * enable search tests - * pull/search options: tls verify -> skip - * test: enable cp tests - * login system test: enable "push ok" - * enable the push e2e tests - * push: fix --tls-verify - * push: simplify cmd - * rootlessport: use two different channels - * specgen: honor slirp4netns - * rootless: move ns open before fork - * push: fix push with one argument - * enable inspect tests - * generate systemd - * Update release notes and README for 1.9.1 release - * Update podmanimage files to adjust perms on containers.conf for rootless - * User specified environment happen after other environments are set - * system tests must pass - * Fixes podman save fails when specifying an image using a digest #5234 - * Fix typos in rm messages - * check image media/manifest type for healthchecks - * test: enable exec tests - * pkg, specgen: do not hardcode user=0 in the config if not specified - * specgen: remove dead code - * cmd: set correct parent for container exec - * Set up ulimits for rootless containers. - * enable build tests - * enable volume integration tests - * separate healthcheck and container log paths - * install.md: Fix typo - * Improve Entrypoint and Command support - * Add support for volumes-from, image volumes, init - * Fix NewSpecGenerator args in pkg/bindings/test - * enable load integration tests - * test: enable all pod tests - * libpod: set hostname from joined container - * namespaces: accept pod namespace - * pkg, ps: add namespaces methods - * enable integration tests for restart - * Make podman container list == podman ps - * test: enable pod rm tests - * pkg, pods: report pod rm errors - * pkg, pods: pod rm honors --ignore - * test: enable pod restart tests - * pkg, pods: not lose pod start/restart errors - * test: enable pod stop tests - * pkg, pods: honor --ignore for pod stop - * test: enable pod create tests - * specgen: relax test to accept default network - * spec, pod: honor --dns - * spec: propagate --no-hosts to specgen - * sort .gitignore - * .gitignore: add pkg/api/swagger.yaml - * build(deps): bump github.com/rootless-containers/rootlesskit - * implement pod stats - * test: fix check for pause on cgroup v2 - * test: fix pause tests - * cmd, ps: add .Status as synonym for .State - * test: enable healthcheck tests - * podman: handle --no-healthcheck - * specgen: read healthchecks from the image - * podman: special case health-cmd none - * Enable pod inspect integration test - * Enable pod prune integration test - * enable run_restart integration tests - * enable run_ns integration tests - * enable run_signal integration tests - * Enable these tests - * Enable container inspect integration tests - * Enable pod ps integration tests - * Cleanup man pages for pull and push - * Adding system prune for podman v2 - * V2 tests: enables commit tests - * Add --os to manifest add - * containers, init: skip invalid state errors with --all - * podman: assume user namespace if there are mappings - * Do not join pod namespaces without an infra ctr - * podman: implement userns=keep-id - * Cirrus: Utilize new VM images - * Cirrus: Unify package installation - * test: enable cgroup parent tests - * podman: fix --log-opt=path=%s - * podman: fix --http-proxy - * podman: fix podman --group-add - * test: fix --host-env test - * podman: fix --cgroups=disabled - * test: enable some run_test.go tests that pass now - * podman: add support for --rootfs - * Bump github.com/containers/common from 0.9.4 to 0.9.5 - * specgen: fix error message - * create: move validate after setting default ns - * remove blank line - * set bigfilestemporarydir for pull - * Fix SELinux functions names to not be repetitive - * foo: delete spurious file - * Makefile: include -nobuild install targets - * podman: handle namespaces specified on the CLI - * specgen: do not always set shmsize - * pkg: fix shmsize error message - * Stop wrapping pull messages - * manifest create,add,inspect - * V2 Restore rmi tests - * V2 restore libpod.Shutdown() when exiting podman commands - * Turn on version.go except for -v check - * Fix podman push and podman pull to check for authfile - * Enable basic volumes support in Podmanv2 - * Move selinux labeling support from pkg/util to pkg/selinux - * Fix integration tests for untag - * Instrumentation to answer #5765 - * test rootless_storage_path from strorage.conf - * V2 Restore exists E2E tests - * Fix podman rm to have correct exit codes - * Fix v2 test podman info - * Fix handling of --cidfile on create/run - * vendor in containers/common v0.9.4 - * Handle hostname flag from client - * Add support for devices from command line - * Fix handling of CGroupsParent and CGroupsMode - * Throw error on IPv6 ip addresses - * Force integration tests to pass - * Modify namespace generation code for specgen - * Bump to github.com/containers/common to v0.9.2 - * my bad - * Provide a json variable pointing to a configured json API - * podmanv2 cp - * gate/README.md Fix link to .cirrus.yml and reword - * add entrypoint from image where needed - * Makefile: fix broken chcon for podman-remote - * podmanv2 container subcommands - * v2podman port - * v2: implement log{in,out} - * Move Fedora dependencies for building podman into separate file - * v2, podman: plug --userns=auto - * podman: do not set empty cgroup limit blocks - * Handle annotations passed in via the client - * Need to set the Entrypoint - * Fix podman inspect to return errors on failure - * pkg: implement rlimits - * podman rmi: refactor logic - * Add support for containers.conf to podmanimages - * Update podman to use containers.conf - * Fix podman inspect to accept -l and -s fields - * Handle Linux Capabilities correctly - * Add functions to return image informations - * V2 Rmove existing unix domain socket on startup - * Cirrus: Add support for Fedora 32 - * Cirrus: More Ubuntu 19 + Fedora 31 - * V2 podman image tree - * V2 Fix --latest for podman diff commands - * rootless: move join namespace inside child process - * rootless: skip looking up parent user ns - * common: setting cgroup resources correctly - * Update pod inspect report to hold current pod status. - * Pull images when doing podman create - * Return labes in API (fixes #5882) - * Make `find` ignore dot files - * Cleanup network option parsing - * enable integration testing - * V2 Fix support for tcp://[::] connections - * Add pod prune for api v2. - * We were not handling the user option on create - * Fixes for system tests - * Enable some testing - * Log formatter: add BATS summary line - * Bump github.com/containers/psgo from 1.4.0 to 1.5.0 - * podmanV2: implement build - * Fix bug where two configurations had been created - * Podman V2 birth - * V2 Enable rootless - * Add SELinux volume information to troubleshoot.md - * podman v2 remove bloat v2 - * allow filters to work when listing containers - * Update podman-generate-systemd man page - * .gitignore: ignore v2 remote - * Bump github.com/containers/common from 0.9.0 to 0.9.1 - * Add version to podman info command - * Add basic structure of output for APIv2 pod inspect - * v2 bloat pruning phase 2 - * Add support for selecting kvm and systemd labels - * Fix up SELinux labeling - * podmanv2 fix runtime assignment - * Cirrus: Fix gate container build failure - * logformat: handle apiv2 results, add anchor links - * Update README to reflect that latest version is v1.9.0 - * Ability to prune container in api V2 - * Bump to v2.0.0-dev - * podmanv2 events - * test case added for image prune cache image - * note for skipping cache image added. - * image prune skips images with child images. - * swagger-check: new CI tool to cross-check swagger - * auto update: skip non-image policies - * build(deps): bump github.com/containers/common from 0.8.1 to 0.9.0 - * logformat: handle apiv2 results, add anchor links - * If possible use the pod name when creating a network - -- Changelog for v1.9.0 (2020-04-15) - * podmanV2: fix nil deref - * v2specgen prune libpod - * More system test fixes on regressions - * Add support for the global flags and config files - * Bump to v1.9.0-dev - -- Changelog for v1.9.0-rc2 (2020-04-14) - * Update release notes for v1.9.0-RC2 - * v2podman ps revert structure changes - * podmanv2 mount and umount - * Fix invalid container path comparison for pid cgroup - * v2podman add container init - * Need to set security options even if user does not specify options - * podmanv2 version format variable name change - * Fixes for load and other system tests - * Improve APIv2 support for Attach - * Refactor service idle support - * podmanv2 history and image remove templates - * Bump to v1.9.0-dev - * rootless: use snprintf - * podmanV2: implement search - -- Changelog for v1.9.0-rc1 (2020-04-13) - * build(deps): bump github.com/containers/buildah from 1.14.7 to 1.14.8 - * Update release notes for v1.9.0-RC1 - * v2podman container cleanup - * podmanV2: implement logs - * test: enable preserve fds test for crun - * test: fix exec preserve-fds test - * Set exit codes on errors. - * Run (make vendor) - * Fix (make vendor) - * update the latest version to 1.8.2 - * add tests for kill and exists - * v2podman ps alter formats - * run/create were processing options after the image name - * V2 podman system service - * man page: add note about issue with SELinux - * Bump Buildah to v1.14.7 - * Bump containers/image to v5.4.3 - * V2 podman diff(changes) support - * podman info needs to be run within the user namespace - * podmanv2 images user format - * podmanv2 info - * vendor c/image v5.4.2 - * Do not error on pids.current stats if ctr.path is empty - * fix rootless login/logout tests - * v2podman run - * refactor info - * podmanv2 ps - * userns: support --userns=auto - * podmanv2 start - * build(deps): bump github.com/containers/common from 0.8.0 to 0.8.1 - * build(deps): bump github.com/containers/storage from 1.18.1 to 1.18.2 - * build(deps): bump github.com/opencontainers/selinux from 1.4.0 to 1.5.0 - * v2podman attach and exec - * v2podman container create - * Cleanup whether to enter user namespace for rootless commands - * podmanv2 save image - * podmanv2 version - * checkpoint: handle XDG_RUNTIME_DIR - * checkpoint: change runtime checkpoint support test - * Pass path environment down to the OCI runtime - * podmanv2 checkpoint and restore - * Bump github.com/containers/common from 0.6.1 to 0.8.0 - * test/e2e/run_volume_test: use unique mount point - * test/e2e/run_volume_test.go: mv dockerfile decl - * test/e2e/run_volume_test: only create dir once - * Fix environment handling from containers.conf - * podmanV2: implement push - * pkg/spec.InitFSMounts: optimize - * utils: delete dead code - * attach: skip shutdown on errors - * attach: fix hang if control path is deleted - * pkg/spec.InitFSMounts: fix mount opts in place - * podmanv2 export - * podmanv2 import - * podmanv2-retry - new helper for testing v2 - * podmanv2 load - * podmanv2 pod inspect - * V2 podman inspect - * Fix repos for CentOS 7 RPM build - * podman v2 image tag and untag - * podmanv2 pod ps - * Touch up mailing list address in README.md - * add systemd build tag to podman builds - * Bump github.com/rootless-containers/rootlesskit from 0.9.2 to 0.9.3 - * Switch to using --time as opposed to --timeout to better match Docker. - * podmanV2: implement pull - * pkg/spec/initFSMounts: fix - * Cirrus: Remove darwin/windows builds in gate-job - * Cirrus: Update VM Images - * Cirrus: Minor docs update - * Revert "Default CPUShares in Inspect are 1024" - * fix more swagger inconsistencies - * V2 Move varlink home - * Bump github.com/containers/conmon - * Bump github.com/spf13/cobra from 0.0.6 to 0.0.7 - * rootless: make cgroup ownership detection not fatal - * podmanv2 enable healthcheck run - * Update vendor of boltdb and containers/image - * swagger: top: remove "Docker" from the identifiers - * podmanv2: implement pod top - * v2 api: implement pods top endpoint - * podmanv2 commit - * Bump to buildah v1.14.5 (Edit 2020-06-03: Addresses CVE-2020-10696) - * Add support for containers.conf - * API v2 tests: usability improvements - * Sanitize port parsing for pods in play kube - * podmanv2 pod create using podspecgen - * use `pause:3.2` image for infra containers - * Add support for specifying CNI networks in podman play kube - * Fix typo in pod create - * podmanV2: implement top - * Fix Markdown typo in podman-create.1.md - * V2 podman image prune - * Support label filters for podman pod ps. - * podmanv2 container inspect - * podmanv2 pod subcommands - * Add bindings for Container Exec Create + Inspect - * apiv2 add default network in specgen - * slirp: enable seccomp filter - * V2 podman image rm | podman rmi [IMAGE] - * V2 podman image - * podmanv2 add pre-run to each commmand - * Ensure that exec sends resize events - * enable linting on v2 - * Bump github.com/rootless-containers/rootlesskit from 0.8.0 to 0.9.2 - * Bump github.com/containers/storage from 1.16.5 to 1.16.6 - * V2 podman images/image list - * podmanv2 volumes - * Combine GlobalFlags and EngineFlags into EngineOptions - * Complete podmanV2 history command - * rootlessport: use x/sys/unix instead of syscall - * podmanv2 exit code - * Bump github.com/sirupsen/logrus from 1.4.2 to 1.5.0 - * Correctly document libpod commit endpoint - * Implement APIv2 Exec Create and Inspect Endpoints - * apiv2 container commit for libpod - * Add image signing with GPG tutorial - * podmanv2 add core container commands - * Improved readability in image json output - * podmanv2 volume create - * Add stubs for cmd/podman in non-Linux local mode - * Make libpod/lock/shm completely Linux-only - * Add stubs for pkg/adapter/terminal_linux.go - * Add a stub for libpod.Container.Top - * Make cmd/podman/shared.GenerateCommand tests Linux-only - * Fix the libpod.LabelVolumePath stub - * Only run TestGetImageConfigStopSignal on Linux - * Fix the pkg/specgen/SpecGenerator.getSeccompConfig stub - * podmanv2 pod exists - * when removing networks for tests, force should be used - * Add basic structure of a spec generator for pods - * [CI:DOCS]fix type issue in pod binding test - * podmanv2 enable remote wait - * fix remote connection use of context - * use boolreport for containerexists response - * podmanv2 container exists|wait - * Add APIV2 service files - * Attempt manual removal of CNI IP allocations on refresh - * Implemented --iidfile for podman commit - * Add guildline for writing podman V2 CLI commands - * Use creds form PullImage remote - * Fix docker man page links - * Bump to v1.8.3-dev - * [CI:DOCS]remove podmanv2 binary - * Cirrus: Update VM images - * Cirrus-CI: Fix source path of vendor task - * Cirrus: Enable future installing buildah packages - * Cirrus: Include packages for buildah CI - * Cirrus: Update Ubuntu base images - * Cirrus: Use opensuse open build Ubuntu packages - * Update release notes for v1.8.2 final release - * rootlessport: handle SIGPIPE - * apiv2 add bindings for logs|events - * Bump github.com/containers/common from 0.5.0 to 0.6.1 - * Add inspect for exec sessions - * Add structure for new exec session tracking to DB - * Populate ExecSession with all required fields - * Fix path of tmp_dir - * Cirrus: Disable non-docs release processing - * container prune remove state created, configured - * Cirrus: Log libseccomp package version - * docs: mention that "podman version" prints out Remote API Version - -- Changelog for v1.8.2 (2020-03-19) - * fix reported compat issues - * Don't include SUBDIR in windows.zip - * rootless: fix usage with hidepid=1 - * V2 podman command - * serve swagger when present - * swagger: more consistency fixes - * Vendor in containers/buildah v1.14.3 - * Reduce CPU usage when --timeout=0 - * New test: man page cross-ref against --help - * podman: avoid conmon zombie on exec - * Filter pods through pod list api - * Bump to v1.8.2-dev - * Fix vendoring on master - * fix timeout file flake - * auto updates - * pkg/systemd: add dbus support - -- Changelog for v1.8.2-rc1 (2020-03-17) - * Update release notes for v1.8.2-rc1 - * Fix vendoring on master - * Update containers/storage to v1.16.5 - * config: make warning clearer - * Four small CI fixes: - * fix systemd generate tests - * apiv2 addition of manifests - * add os|arch attributes when building - * Missing double quotes in troubleshooting guide. - * force run container detached if container CreateCommand missing the detach param - * Bump github.com/containers/common from 0.4.2 to 0.5.0 - * Bump k8s.io/api from 0.17.3 to 0.17.4 - * Bump github.com/fsnotify/fsnotify from 1.4.7 to 1.4.9 - * eat signal 23 in signal proxy - * add apiv2 healthcheck code - * turn off color-mode for bindings - * remove imagefilter for varlink remote client - * Bump github.com/containers/storage from 1.16.2 to 1.16.3 - * run --rmi test: make it work - * rootlessport: detect rootless-child exit - * create: do not calculate image size - * Follow up changes from #5244 - * man page cross-reference fixes: part 2 - * Update version in README to v1.8.1 - * [CI:DOCS]Add libpod event endpoint - * Bump to v1.8.2-dev - * Update start stop api to use pod status function. - * Fix bug podman reset to not remove $XDG_RUNTIME_DIR - -- Changelog for v1.8.1 (2020-03-11) - * man pages: fix inconsistencies - * Update release notes for v1.8.1 final release - * build for amd64|arm|ppc64le - * update systemd & dbus dependencies - * Refactor handler packages - * Remove nonexistent --set arg from runlabel documentation - * hide --trace flag - * podman --help: mention defaults of bools - * docs: clarify that --syslog expects an argument - * Bump to v1.8.1-dev - * commands: rename file and add likns to readthedocs - -- Changelog for v1.8.1-rc4 (2020-03-09) - * Revert "exec: get the exit code from sync pipe instead of file" - * Revert "Exec: use ErrorConmonRead" - * Revert "exec: fix error code when conmon fails" - * rootles tutorial: remove systemd unit example - * generate systemd: add `default.target` to INSTALL - * Bump github.com/containers/storage from 1.16.1 to 1.16.2 - * use storage/pkg/ioutils - * use storage/pkg/homedir - * Fix spelling mistakes in code found by codespell - * add default network for apiv2 create - * Bump to v1.8.1-dev - * Allow users to set TMPDIR environment - * Fix upstream dockerfile and add 'by hand' ctrfile - * Cirrus: Fix fedora-minimal mirroring - * fix security-opt generate kube - -- Changelog for v1.8.1-rc3 (2020-03-06) - * Update release notes for v1.8.1-RC3 - * Part 2: try to clean up the long image instance names - * WIP: Try renaming long cirrus job names - * vendor: update github.com/containernetworking/cni to v0.7.2-0.20200304161608-4fae32b84921 - * Removed extraneous comments and defaults plus amended variable declaration - * Removed the unnecessary code - * Implemented size parameter on GetContainer - * Implement size parameter on ListContainers - * Map configured status to created to match docker API states - * Fix to remove null entry from end of images json - * Register handlers without version to align with docker API - * golangci: enable goimports - * generate systemd: remove leading slashes - * exec: fix error code when conmon fails - * Vendor buildah 1.14.2 - * env: don't set "container" env - * Fix podman image sign help output - * avoid adding to nil map - * Exec: use ErrorConmonRead - * exec: get the exit code from sync pipe instead of file - * generate systemd: add network dependencies - * Bump to Buildah v1.14.1 - * APIv2 tests: add tests for stop - * Add the rmi flag to podman-run to delete container image - * consolidate env handling into pkg/env - * CI: format cirrus logs - * Update docs/source/markdown/podman-build.1.md - * Allow devs to set labels in container images for default capabilities. - * CI: add API v2 tests - * more swagger fixes - * Bump github.com/opencontainers/selinux from 1.3.2 to 1.3.3 - * Add validate() for containers - * Cirrus: Fix gate image & false-positive exits - * Update pod bindings and Add test to validate prune pod apiv2 binding. - * Fix wrong condition in bindings test - * Ensure that exec sessions inherit supplemental groups - * Cirrus: Update VM images - * Cirrus: Force runc use in F30 - * rework apiv2 wait endpoint|binding - * build: specify input fd to buildah - * Cirrus: Remove unnecessary handle_crun workaround - * Cirrus: Print env. vars at end of setup. - * Cirrus: Fix not growing Fedora root - * network create should use firewall plugin - * add firewall plugin (no backend) to default cni config - * binding tests for volumes - * Bump to v1.8.1-dev - * container Exists: fix URL - * CI: package_versions: include hostinfo, kernel - * Review comments - * [WIP] Add cmd flag to show container name in log - -- Changelog for v1.8.1-rc2 (2020-02-27) - * Update release notes for v1.8.1-rc2 - * Vendor in latest containers/buildah - * kill test: clean up warnings; document better - * curb flakes in integration tests - * spec: allow container alias name in lookup - * add epoch for specfile - * fix trivial typo - * Add support for multiple CNI networks in podman inspect - * Remove 1 sec delay - * Temp. skip "remove pause by id" bindings test - * Fix kill test obtaining CID - * System Tests: Force default signal handlers - * Fix cgroupsv2 run test, unexpected output - * Cirrus: SELinux Enforcing for F31 w/ CGv2 - * Cirrus: collect podman system info - * Cirrus: F31: Force systemd cgroup mgr - * Cirrus: Temp. disable F31 p-in-p testing - * Cirrus: Handle runc->crun when both are possible - * Cirrus: Use deadline elevator in F31 - * Cirrus: Support testing with F31 - * rootless: become root only if the pause file is specified - * rootless: fix segfault when open fd >= FD_SETSIZE - * apiv2 tests: add more pod tests, timing check - * Update vendor of buildah and containers/common - * build: move initialization after SetXdgDirs - * utils: relax check for directory to use - * add apiv2 tests for podman pause and stop - * always run the docs task on post-merge - * Fixed build_rpm.sh script for Fedora 30 - * Add basic deadlock detection for container start/remove - * Friendly amendment: tests, and a help message - * fix port list by container with port - * more image binding tests - * docs: symlink to host device is resolved - * Add --no-healthcheck command to create/run - * enable ci on go binding tests - * add more image tests for go bindings - * Bump to v1.8.1-dev - * build(deps): bump github.com/opencontainers/selinux from 1.3.1 to 1.3.2 - -- Changelog for v1.8.1-rc1 (2020-02-21) - * Update release notes for v1.8.1 - * disable generation of cni firewall plugin - * search endpoint failure correction - * Remove ImageVolumes from database - * Upgrade make package-install for fedora31 - * Flake fix: race condition in same-IP test - * Add support for ssh:// and unix:// podman clients - * search test on fedora registry: retry 5 times - * Swagger: yet more fixes - * Login test: use --password-stdin - * implement reverse reader for log reads - * podman images: add --filter=since=XX - * populate resolv.conf with dnsname responses when in usernamespace - * Beautify podman bridge CNI config - * build(deps): bump github.com/spf13/cobra from 0.0.5 to 0.0.6 - * Warn user about --password cli option in login - * build(deps): bump github.com/stretchr/testify from 1.5.0 to 1.5.1 - * Swagger: fix one incorrect comment - * apiv2 container create using specgen - * Add test to validate the pod bindings api - * Update to the latest version of buildah - * New login and push tests - * Add network options to podman pod create - * Fixed syscall.Signal not convertable by decoder - * Fixed typo in KillContainer - * build(deps): bump github.com/containers/storage from 1.15.8 to 1.16.0 - * build(deps): bump github.com/stretchr/testify from 1.4.0 to 1.5.0 - * libpod.conf: clarify `label` description - * set process labels in pkg/spec - * libpod/config: use built-in TOML instead of manually merging - * Fixed CreateImageFromImage not respecting supplied Tag parameter - * Add installation of pre-commit to Makefile - * fix mandatory parameter in login/logout - * adds missing query struct tags and exports the fields - * Swagger: fix inconsistencies (try #2) - * Update mux rules to allow slashes in image names - * rootless: fix a regression when using -d - * Misc typo fixes - * Use cleaned destination path for indexing image volumes - * Add ability for pods to use the host network - * stats: Expose CPU usage in API - * rootless: check if the conmon process is valid - * apiv2: Fixup /containers/json filters documentation - * apiv2: Enable filtering images by ID - * Fix handler and systemd activation errors - * podman-ps: support image IDs - * Refactor image tree for API usage - * Update documentation of commit command to show image reference is optional - * Rework label parsing - * add caching for binding tests - * apiv2 libpod container logs - * add pkg/signal - * add pkg/capabilities - * build(deps): bump github.com/rootless-containers/rootlesskit - * Fix SELinux labels of volumes - * podman(1): fixes - * fix bug "" disable detach keys - * Fixed a bug about bash automatically complete - * Enhance fuse-overlayfs instructions. - * README: fix docs links - * Fix up play kube to use image data - * build(deps): bump k8s.io/api from 0.17.2 to 0.17.3 - * Only set --all when a status filter is given to ps - * use quay.io/libpod/fedora-minimal for reliability - * filtering behavior correction - * support device-cgroup-rule - * rootlessport: drop Pdeathsig in favor of Kill - * rootlessport: fix potential hang - * add pkg/seccomp - * Do not copy up when volume is not empty (Edit 2020-06-03: Addresses CVE-2020-1726) - * api: pull: fix reference parsing - * cmd/podman/pull: refactor code - * stats: add SystemUsage - * build(deps): bump k8s.io/apimachinery from 0.17.2 to 0.17.3 - * build(deps): bump github.com/gorilla/mux from 1.7.3 to 1.7.4 - * HTTP 304 (NotModified) is not an error! - * API v2 tests: catch up to moving target - * api: fix the CPU stats reported - * apiv2 stream events - * Fix container filters - * API v2: pods: fix two incorrect return codes - * Rewire ListContainers for APIv2 libpod - * podman build -f completions - * swagger: fix /libpod/images/{import,load,pull} - * Make: s/uname -o/uname -s/ - * container create: relax os/arch checks - * replace prow images test - * Remove incorrect validation of --change for commit - * [CI:DOCS] Update Code of Conduct to Containers variant - * Add test cases to validate remove and list images api. - * images --format compatible with docker - * bash-completions: Add missing subcommands in 'podman system' - * doc: Fix examples for 'podman system service' - * v2 api: /libpod/images/import - * v2 api: /libpod/images/load - * v2 api: /libpod/images/pull - * docs: add workaround for --device with rootless containers (II) - * Fix varlink code generation target. - * Modify Runtime.getImage to return a storage.Image - * Document an aspect of newFromStorage behavior - * Introduce a Runtime.newImage constructor - * Move Image.getLocalImage to Runtime.getLocalImage - * Remove the getLocalImage() call from Image.Size - * Use Runtime.NewFromLocal instead of open-coded copies - * Trivial simplification - * Create two separate newImage instances in Runtime.New - * Call NewImageRuntimeFromStore from NewImageRuntimeFromOptions - * Update readme to 1.8.0 release - * Refactor runtime functions to pass options structure - * build(deps): bump github.com/containers/image/v5 from 5.2.0 to 5.2.1 - * LibpodAPI.BuildImage: don't require a name for the new image - * Bump to v1.8.1-dev - * Cirrus: Never run prune on other branches - * Add dockerfile to mirror fedora-minimal - * Add /swagger/ endpoint to serve swagger yaml to clients - * Add backend code for pod network options - -- Changelog for v1.8.0 (2020-02-06) - * [CI:DOCS]update contrib systemd user - * [CI:DOCS]fix systemd files for apiv2 - * Update release notes for final release of v1.8.0 - * Move podman-service to podman-system-service - * Only modify conmon cgroup if we have running containers - * fix swagger docs and make sure docs validation runs - * Special case memory-swap=-1 - * vendor github.com/mtrmac/gpgme@v0.1.2 - * vendor github.com/containers/image/v5@v5.2.0 - * Add Containerfile location e2e test - * [CI:DOCS]addition of specgen package - * {CI:DOCS] run gofmt before lint - * build(deps): bump github.com/onsi/ginkgo from 1.11.0 to 1.12.0 - * Close tarSource when finished using it - * Force --all when --filter is passed to podman ps - * Initial implementation of a spec generator package - * Fix wrong Containerfile location on build - * Wrap error for failing ImageSize calls - * swagger: v2: libpod/images/{import,load,pull} - * seperate container create network options - * Cirrus: Fix gate task + make lint|validate - * Add a binding test to check image tag and list commands. - * Update /_ping support - * [CI:DOCS]add apiv2 endpoints for exec - * build(deps): bump github.com/containers/storage from 1.15.7 to 1.15.8 - * build(deps): bump github.com/onsi/gomega from 1.8.1 to 1.9.0 - * Tear down network when restarting containers - * Move install.md to podman.io, leave link page - * Update XML to not embed quote in PATH on windows - * Bump to v1.8.0-dev - -- Changelog for v1.8.0-rc1 (2020-01-31) - * Fix a syntax error in hack/release.sh - * Minor update to release notes - * sigproxy: return after closing the channel - * fix longname handling for bindings - * Update release notes for v1.8.0 - * compat container names begin with / - * Assure validate includes lint - * make image reference for commit optional - * adjusts install.md (Ubuntu): replaces ${NAME} with hard-encoded Ubuntu to support all *buntu flavors - * adjusts install.md (Ubuntu): registries.conf setup is now in containers-image package - * markdown: fix erroneous asterisk markup for options - * speed up Makefile - * Makefile: systemd: echo instead of warn - * Makefile: remove redundant BUILDFLAGS - * Makefile: consistent PHONY use - * Makefile: remove dead vagrant target - * Makefile: move systemd buildtag check - * rootless: enable shortcut only for podman - * test: honor TEMPDIR variable - * Cirrus: Set EPOCH_TEST_COMMIT during gate task - * Deprecate & remove IsCtrSpecific in favor of IsAnon - * apiv2 binding test fixes - * history: fix size computing - * run `varlink_generate` on Linux only - * display file name of bad cni conf - * Throw error on invalid sort value - * rootless login/logout tests fail - * Update remote client bridge documentation. - * honor pull policy in play kube - * docs: replace '~' with $HOME in markdown as '~' isn't rendered properly - * install.md: registries.conf setup in containers-image package - * [CI:DOCS]Binding overhauls - * docs: fix incomplete heading underlining in network.rst - * build(deps): bump github.com/rootless-containers/rootlesskit - * docs: add missing hyphen for '-t' option, command '$' prompts - * build(deps): bump github.com/opencontainers/selinux from 1.3.0 to 1.3.1 - * [CI:DOCS]rootless exec cannot join root namespace - * expose --arch-override option for pull - * Add link from docker.sock to podman.sock - * inspect image healthchecks - * [CI:DOCS]Add copr link to fedora install page - * Hidden remote flags can be nil - * docs: add boolean values and defaults to "man podman-history" options - * docs: remove reference to "sudo" in "podman exists" examples - * docs: fix system-prune markdown; reword for clarity - * docs: clean up "man podman-rm", "man podman-rmi" - * install.md: mention availability of OpenEmbedded recipes - * Cleanup man pages exit code descriptions - * APIv2 review corrections #3 - * camelcase: fix lint reports - * fork fatih/camelcase - * Refactor time parsing to be more liberal in accepted values - * apparmor: allow receiving of signals from 'podman kill' - * Add query parameter converters for complex types - * Review corrections pass #2 - * build(deps): bump gopkg.in/yaml.v2 from 2.2.7 to 2.2.8 - * Default CPUShares in Inspect are 1024 - * markdown: fix grammar/formatting, standardize on markdown - * build(deps): bump k8s.io/api from 0.17.0 to 0.17.2 - * build(deps): bump github.com/pkg/errors from 0.9.0 to 0.9.1 - * build(deps): bump github.com/containers/conmon - * build(deps): bump github.com/json-iterator/go from 1.1.8 to 1.1.9 - * build(deps): bump github.com/uber/jaeger-client-go - * build(deps): bump github.com/containernetworking/plugins - * seccomp policy: expect profile in config label - * build(deps): bump github.com/vishvananda/netlink from 1.0.0 to 1.1.0 - * build(deps): bump github.com/containers/storage from 1.15.5 to 1.15.7 - * Update README.md to reference latest version - * Enable swagger validation for each PR - * Fix example format in system df man - * markdown: fix formatting of commands at bottom of podman-exec - * markdown: reword 'podman-inspect' to properly explain '--size' - * correct search-and-replace error - * Update release script to not manage epoch - * markdown: remove extraneous backquote from "podman rmi" - * markdown: fix formatting/content typos in migrate man page - * Update RELEASE_NOTES for v1.7.1 - * Add service endpoint - * Cirrus: Fix logic typo - * Update build images - * Cirrus: No upload snap for docs job - * [CI:DOCS]First pass at review comments - * go.mod: fix parse error - * Use cgroupv2 super magic from golang.org/x/sys/unix - * Disable go mods on varlink builds in spec - * [CI:DOCS] Add logo and dev statement - * rootless: set C variables also on shortcut - * [CI:DOCS]static files end up in _static on rtd - * [CI:DOCS] Correct link syntax - * [CI:DOCS]Connect API docs and RTD - * post-process swagger yaml for publish - * Tests for API v2 - * Minor: Bugfix in upload image - * Update `tag` documentation regarding 'alias' usage - * update install instructions for Debian, Raspbian and Ubuntu - * oci_conmon: do not create a cgroup under systemd - * Add an API for Attach over HTTP API - * systemdgen: specify --cgroups=disabled-conmon for --new - * podman: add new option --cgroups=no-conmon - * systemdgen: add --ignore flag to generic services - * e2e/run_signal_test.go: make it more robust - * hack/install_golangci.sh: check env vars - * Remove c.String(net) - * make binaries: include service - * service: don't block sigterm - * Cirrus: remove workaround for cleaning /go/bin - * [CI:DOCS]swagger cleanup and left-hand nav - * Add APIv2 CLI example POC - * api: stats: fix typo - * api: utils: add an `IsLibpodRequest` handler - * refactor top code - * top: use a separate pipe for the error stream - * v2 api: top improvements - * v2: stats: drop redundant sleep when streaming - * v2: stats: libpod: use generic handler - * v2: stats: rigorous error checks - * v2: stats: fix errors - * v2: stats: do not ignore errors - * v2: stats: remove windows-specific fields - * make .install.golangci-lint: force specific version - * Makefile: remove gometalinter - * contrib/gate/Dockerfile: bump to F31 - * [CI:DOCS]swagger corrections - * Bump to Buildah v1.13.1 - * oci_conmon: not make accessible dirs if not needed - * Enable pre-commit tool linting - * .gitignore: ingore *.coverprofile from unit tests - * make lint: include unit tests - * .golangci.yml: move swagger.go from Makefile - * make lint: include docs/ - * make lint: include pkg/tracing - * revert accidental change from codespell pr. - * swagger documentation updates - * Do not configure CNI when slirp4netns is requested - * clarify container prune --force - * more BATS tests - * gating: clean /go/bin to install fresh tools - * make lint: enable gocritic - * linter: blacklist linters instead of whitelisting - * bump golangci-lint to 1.18.0 - * rm contrib/perftest - * remove `.tool/lint` - * docs: --privileged docs completeness, consistency - * [Makefile] Ensure .gopathok dependency is met for varlink - * Add codespell to validate spelling mistakes in code. - * libpod: fix --userns=keep-id with big UIDs - * fix e2e test failure - * Cirrus: Fix libpod base images going stale - * address review comments before merge - * [CI:DOCS]update apiv2 documentation with swagger goods - * Initial commit on compatible API - * cp: drop check for rootless - * test: fix error message - * log: support --log-opt tag= - * Fix Makefile ref libseccomp branch as a commit - * policy for seccomp-profile selection - * podman-generate-systemd --new - * shared/create.go: s/data/imageData/ - * rootlessport: honor ctr.runtime.config.TmpDir - * rootlessport: remove state dir on exit - * Usage messages: show possible option values - * Update podmanimage build process - * exec: fix pipes - * fix lint - pkg/varlinkapi/virtwriter - * fix lint - pkg/util: func comment - * fix lint - pkg/spec - * fix lint in pkg/rootless - * fix lint - pkg/network: comment exported types - * fix lint - pkg/adapter: comment exported API - * fix lint - ignore image.ImageDeleteResponse definition - * fix lint - drop else block - * fix lint: add comment for NameRegex and error - * fix lint: correct func identifier in comment - * fix lint: "guarantess" is a misspelling of "guarantees" - * rootless: use RootlessKit port forwarder - * Add `untag` sub-command - * Update demo for the inspect command - * Fix podman-remote info to show registry data - * packaging: validate installed rpms - * github stale workflow: rephrase and bump close time - * Don't show PASS on success for gitvalidate - * Bump gitvalidation epoch - * Bump to v1.7.1-dev - * play kube: make seccomp handling better conform to k8s - * fix bug copy from container directory - * Add history names to image inspect data - -- Changelog for v1.7.0 (2020-01-06) - * (minor) fix broken links to container-policy.json.5 - * Generate binaries only if they are changes in src code. - * Fix presentation of man page tables - * Bump gitvalidation epoch - * Bump to v1.7.0-dev - -- Changelog for v1.7.0-rc2 (2020-01-02) - * Update release notes with further changes from 1.7.0 - * refactor network commands - * Fix race condition in kill test leading to hang - * Ensure 'make uninstall' remove bin and conf files. - * Add the pod name when we use `podman ps -p` - * Ensure SizeRw is shown when a user does 'inspect --size -t container'. - * signal parsing - better input validation - * The --quiet flag does not conflict with templates in ps - * add struct response for removal of images - * Update containers/storage to v1.15.4 - * Update containers/storage to v1.15.4 - * zsh completion: ignore multi-line output in Flags - * build(deps): bump github.com/containers/image/v5 from 5.0.0 to 5.1.0 - * if container is not in a pid namespace, stop all processes - * update c/buildah to v1.12.0 - * Remove volumes after containers in pod remove - * libpod: drop arbitrary memory limit of 4M - * docs: add workaround for --device with rootless containers - * install.md: openSUSE dependencies - * Use systemd/sd-daemon.h headers for systemd presence - * Allow the injection of TESTFLAGS - * Remove coverprofile from the repository - * troubleshooting.md: rebased master and bumped 18 to 19 - * Fix F30-F31 migration for Podman 1.7.0 - * runtime.go: show registries data and search table - * container config: add CreateCommand - * Fixed the path of hack scripts in spec file - * runtime.go: show search table in podman info - * podman info man: example update - * podman: mirror information - * Reap exec sessions on cleanup and removal - * [Makefile] `LDFLAGS` is reserved for the GCC linker - * podman images history test - clean up - * Bump gitvalidation epoch - * Bump to v1.7.0-dev - * allow exec to read files of environment variables - * Correctly export the root file-system changes - * build(deps): bump github.com/uber/jaeger-client-go - -- Changelog for v1.7.0-rc1 (2019-12-11) - * Update release notes for 1.7.0 - * docs: update podman-{pod-,}top man pages - * build(deps): bump github.com/containers/psgo from 1.3.2 to 1.4.0 - * Update containers/storage to v1.15.3 - * move image filters under libpod/images - * Re-add Fedora 31 migration code. - * macvlan networks - * Return empty runtime directory if we're not rootless - * build(deps): bump github.com/containers/storage from 1.15.0 to 1.15.2 - * Use terminal detach keys sequence specified in the config file - * Add ONBUILD support to --change - * Move Commit() to new parsing for --change - * test for #3920 (improper caching of tarballs in build) - * Enable multi-platform rpm building - * Completely rework --change parsing - * Avoid git warnings by using detach on checkout - * Improve hack/get_release_info.sh - * Bump Buildah to v1.11.6 - * rootless: enable stats test on cgroup v2 - * Improve dnf tests inside build_rpm.sh - * libpod: fix stats for rootless pods - * rootless: add fallback for renameat2 at runtime - * Attempt to install go-md2man only if missing - * Quick grammar touchup in rootless.md - * Allow chained network namespace containers - * Ensure volumes reacquire locks on state refresh - * Ignore ErrCtrRemoved when removing a container - * Add comment on rootless containers creating device nodes - * Updates on making doc building and debug optional - * troubleshooting: warn about secure boot - * libpod: fix case for executable file not found errors - * build: improved main makefile - * build: improved prepare.sh - * Fix podman-remote version to print client and server - * man page updated with examples of filter option - * install.md: added slirp4netns dependency to ubuntu - * Add podman system reset command - * fix commands.go to get links from correct directory - * Do not initialize store on rootless podman - * filter added to container prune command - * Disable checkpointing of containers started with --rm - * Make doc building in spec optional - * Donot install btrfs in RHEL/CentOS-8 - * oci: print only matching part for the errors - * command output fixed as per docker equivalent - * Detect Python executable in Makefile - * Improved build_rpm.sh - * Add support for image name history - * Remove containers when pruning a stopped pod. - * Allow --ip and --mac to be set when joining a CNI net - * Document other bind options on --volumes flag - * podman {pod,} rm/stop: add --ignore flag - * Discard errors from Shutdown in `system renumber` - * e2e/prune: run two top containers - * build(deps): bump github.com/containers/storage from 1.13.5 to 1.14.0 - * build(deps): bump gopkg.in/yaml.v2 from 2.2.5 to 2.2.7 - * build(deps): bump github.com/pkg/profile from 1.3.0 to 1.4.0 - * document updated for filter and until options - * filter added to image pruge command. - * config: use EventsLogger=file without systemd - * Error on netns not exist only when ctr is running - * Add ContainerStateRemoving - * play kube: handle seccomp labels - * podman rm/stop --cidfile - * container-restore: Fix restore with user namespace - * Add new test suite for build - * Also delete winsz fifo - * use pause image for check all - * timestamp related functions added - * Bump to Buildah v1.11.5 - * container create: os/arch check - * history: rewrite mappings - * codespell: spelling corrections - * Cirrus: Use branch-specific container tags - * warning added before image prune command - * create a separate install target for seccomp - * Add annotations in play kube - * Add pod annotations to container - * Add missing information to podman.1 man page - * Add support for make vendor-in-container - * Split up create config handling of namespaces and security - * test: add tests for --mac-address - * mount: add new options nocopyup|copyup for tmpfs - * Bump github.com/uber/jaeger-client-go - * libpod/config: default: use `crun` on Cgroups v2 - * podman images --digest: always list a digest - * events: make sure the write channel is always closed - * Add support for RunAsUser and RunAsGroup - * cni: enable tuning plugin - * podman: add support for specifying MAC - * vendor: updated ocicni for MAC address - * Makefile: add vendor-in-container - * rootless: provide workaround for missing renameat2 - * rootless: use SYS_renameat2 instead of __NR_renameat2 - * Add Kata Containers runtimes to libpod.conf - * help message: don't parse the config for cgroup-manager default - * fix bug check nonexist authfile - * Allow users to disable detach keys - * namespaces: by default create cgroupns on cgroups v2 - * Update installation - Ubuntu. [skip ci] - * pulling unqualified reference: make sure it's a docker reference - * Bump gopkg.in/yaml.v2 from 2.2.4 to 2.2.5 - * Set SELinux labels based on the security context in the kube.yaml - * Add links to readthedocs on docs/readme - * Bump development version to 1.6.4-dev - * Bump version in README to v1.6.3 - * Add release notes for v1.6.3 - * slirp4netns: fix timeout - * docs: Update "podman container rm -v" description - * logo: correct light source reflection - * stats: fix calculation for the CPU time - * [docs] Ensure we include section 5 documentation - * [Makefile] Fix docker documentation install and generation - * Fixed the JSON go template format for the 'info' action - * runtime: Fix typo - * Update link to Commands documentation - * cgroups: read correctly the CPU stats - * [CI:DOCS] make docs only prs - * Update rootless shortcomings with cgroup V2 information - * Bump github.com/onsi/gomega from 1.7.0 to 1.7.1 - * Validate contextdir on build - * Vendor in latest containers/buildah - * Bump github.com/onsi/ginkgo from 1.10.1 to 1.10.3 - * Refactor test to prevent panic - * logs: support --tail 0 - * Update document formatting and packaging code - * Restructure documentation dir - * add libpod/config - * Switch to bufio Reader for exec streams - * container start: fix regression when using name - * Fix selinux test for exec - * Cirrus: Disable F29 testing - * Wait for `mount` command to finish when mounting volume - * Cirrus: Fix upload_release_archive on branch or tag - * Fix cp from pipe - * libpod, rootless: create cgroup for conmon - * Bump github.com/json-iterator/go from 1.1.7 to 1.1.8 - * seccomp: use github.com/seccomp/containers-golang - * build: drop support for ostree - * stale action: add exempt-issue-label - * Processes execed into container should match container label - * Set default seccomp.json file for podman play kube - * images: distinguish between tags and digests - * API: report multiple digests for images - * pull/create: add --override-arch/--override-os flags - * image: don't get confused by lists - * Add e2e tests for manifest list support - * bump containers/image to v5.0.0, buildah to v1.11.4 - * goland autocorrections - * Makefile: fix embedding gitCommit - * Cirrus: Fix minor python deprecation warning - * Cirrus: Only upload tagged releases - * Fix spelling mistakes - * libpod: if slirp4netns fails, return its output - * update conmon to v2.0.2 in in_podman image - * bump cirrus images - * require conmon v2.0.1 - * require conmon v2.0.0 - * GitHub stale action - * enable dnsplugin for network create - * Add ensureState helper for checking container state - * Cleanup man pages - * Log warn instead of error for removing nonexistant container - * systemd: mask /sys/fs/cgroup/systemd/release_agent - * Add multiple networks explanation to docs - * rootless: raise an error with --network= - * Initial dump of man pages and first menus - * Return a better error for volume name conflicts - * Add documentation on options to volume create manpage - * Image volumes should not be mounted noexec - * stats: list all running containers unless specified otherwise - * rootless: detect no system session with --cgroup-manager=systemd - * add pip requirements file for rtd - * Initial checking for readthedocs - * Fix sig-proxy=false test and use image cache - * Add parsing for UID, GID in volume "o" option - * exec: remove unused var - * Rewrite backend for remote 'volume inspect' - * rootless: write storage overrides to the conf file - * Markdown Formatting Fixes - * Show volume options in 'volume inspect' - * System tests: make sure exec pid hash w/o leaking - * Bump gitvalidation epoch - * Bump to v1.6.3-dev - * check existing bridge names when creating networks - * Add support for anonymous volumes to `podman run -v` - * troubleshooting.md: document lingering mode - * rootless: do not enable lingering mode - * Add ability to redirect bash for run -i - * play kube: Container->Ctr - * play kube: refactor test suite - -- Changelog for v1.6.2 (2019-10-17) - * Finalize release notes for v1.6.2 - * rootless: drop dependency on docker - * Bump gitvalidation epoch - * Bump to v1.6.2-dev - * Refactor tests when checking for error exit codes - * Attach stdin to container at start if it was created with --interactive - -- Changelog for v1.6.2-rc1 (2019-10-16) - * Add release notes for Podman 1.6.2 - * start: print full container ID - * Add a MissingRuntime implementation - * rootless v2 cannot collect network stats - * inspect: rename ImageID go field to Image - * systemd: accept also /sbin/init - * Unwrap errors before comparing them - * vendor github.com/containers/storage@v1.13.5 - * Ensure volumes can be removed when they fail to unmount - * Fix sample's JSON syntax error in oci-hooks.5.md - * change error wording when conmon fails without logs - * images: empty list is valid json with --format=json - * Allow giving path to Podman for cleanup command - * Touch up bad math in run man page - * Add squash-all, fix squash option in build - * tests: enable ps --size tests for rootless - * container: initialize results list - * Make user io.podman.service unit WantedBy=default.target - * rootless: do not set PIDs limit if --cgroup-manager=cgroupfs - * Update build man page with latest Buildah changes - * Fix default path for auth.json - * When restoring containers, reset cgroup path - * Migrate can move containers to a new runtime - * Move OCI runtime implementation behind an interface - * show uid_map in podman info - * cli: support --systemd=always - * systemd: expect full path /usr/sbin/init - * catch runc v2 error - * Respect --sig-proxy flag with podman start --attach - * rootless: automatically recreate the pause.pid file - * rootless: do not close files twice - * refresh: do not access network ns if not in the namespace - * Cirrus: Produce and collect varlink output - * io.podman.socket: drop Also=multi-user.target - * Cirrus: Remove broken/failing testing_crun task - * Cirrus: Use new VM cache images - * Cirrus: Install conmon in Fedora VMs - * vendor c/psgo@v1.3.2 - * troubleshooting: fix useradd no-log-init argument - * Setup a reasonable default for pids-limit 4096 - * Update c/image to v4.0.1 and buildah to 1.11.3 - * When evicting containers, perform a normal remove first - * Bump gopkg.in/yaml.v2 from 2.2.3 to 2.2.4 - * podman network create: validate user input - * Cirrus: Simplify package NVR logging - * Docs: Update links, add links to latest - * Cirrus: Fix log URIs & add optional $ALSO_FILENAME - * Raise start_test polling interval - * system tests: info: deal with hyphen in username - * Bump gitvalidation epoch - * Bump to v1.6.2-dev - * Apply changes also to the windows implementation - * System-tests: Use bash explicitly - * Podman 1.6.0 has been released, update the README - * Add api link to tutorials - * Bump gopkg.in/yaml.v2 from 2.2.2 to 2.2.3 - * Allow setting default parameters with env vars - * Avoid hard-coding path to varlink and podman - * Allow changing IdentityFile and to IgnoreHosts - * rm: add containers eviction with `rm --force` - -- Changelog for v1.6.1 (2019-10-02) - * Update release notes for v1.6.1 - * Bump gitvalidation epoch - * Bump to v1.6.1-dev - * rootless: allow cgroupfs manager on cgroups v2 - * system tests: reenable skipped tests - -- Changelog for v1.6.1-rc1 (2019-10-02) - * rootless: set DBUS_SESSION_BUS_ADDRESS if it is not set - * install.md: add libbtrfs-dev for Debian build - * Bump github.com/onsi/gomega from 1.5.0 to 1.7.0 - * Cirrus: Show names/versions of critical packages - * network: add workaround for slirp4netns --enable-sandbox issue - * rootless: do not attempt a CNI refresh - * Bump github.com/containernetworking/plugins from 0.8.1 to 0.8.2 - * network: hide EPERM warning when rootless - * networking: fix segfault when slirp4netns is missing - * Bump gitvalidation epoch - * Bump to v1.6.1-dev - * Move derivitive doc so it won't be treated as a manpage - * catatonit: clone and build - * bump catatonit to v0.1.4 - -- Changelog for v1.6.0 (2019-09-30) - * info: add cgroups2 - * Finalize release notes for 1.6.0 final - * Bump github.com/onsi/ginkgo from 1.8.0 to 1.10.1 - * Bump github.com/docker/docker-credential-helpers from 0.6.2 to 0.6.3 - * Bump github.com/stretchr/testify from 1.3.0 to 1.4.0 - * Bump github.com/uber/jaeger-client-go - * Bump github.com/spf13/pflag from 1.0.3 to 1.0.5 - * update c/storage to v1.13.4 - * Cirrus: Minor, fix env. var. intention - * new examples added updated two examples with supported CMD and ENTRYPOINT syntax. - * new testcase for podman import --change added - * syntax updated for podman import --change - * Correct use of reexec.Init() - * Add a missing escape in the Makefile - * Change ginkgo Wait() to Eventually() test - * Set log-level immediately, before rootless setup - * Cirrus: Implement newly built VM images - * Add README note about security reporting process. - * Cirrus: Disable boottime Ubuntu package update - * Move noCache logic lower in stack - * cirrus: Add bash-completion support - * Add an error for pods without a name - * Make links relative in Tutorial README - * docs/podman-derivative-api.md: New file - * fix cp none exists dest path ends with '/' - * Dockerfile.fedora: install packages to build catatonit - * README: add Communications section - * drop OWNERS link for CONTRIBUTING.md - * Bump gitvalidation epoch - * Bump to v1.6.0-dev - * Handle conflict between volumes and --read-only-tmpfs - * Cirrus: Upload windows MSI release file - * conditionally send stdin on remote run - * Cirrus: VM Image accounting doc update - * Force a CNI Delete on refreshing containers - * Document the required varlink build args - * Update mac_client link - * Cirrus: Fail early on CI script unit test - * Unconditionally remove conmon files before starting - -- Changelog for v1.6.0-rc2 (2019-09-24) - * Add release notes for new-in-RC2 changes - * system tests: run test: reenable and fix - * play kube: Only support pod kind in k8s yaml - * runtime: fix logic to disable SDNotify - * add list mount tests - * Make netns bind mount shared - * Add Kata Containers support - * rootless: Rearrange setup of rootless containers - * Document the 'system' event types for 'podman events' - * Cirrus: Add upload_snap to success dependencies - * Cirrus: Add snapcraft credentials - * Cirrus: Upload snap only on merges to master - * Cirrus: Push snap continuously - * exec: set HOME also with exec sessions - * execuser: look at the source for /etc/{passwd,group} overrides - * We need to convert libpod.conf files in user homedir for cgroupv2 - * Cirrus: Temporarily disable testing on Ubuntu 19 - * Cirrus: disable Evil Units in base-images - * Cirrus: Add latest ubuntu - * Cirrus: More podbot/success improvements - * Cirrus: Fix success script - * Cirrus: Update podbot credentials - * container: make sure $HOME is always set - * Move rootless and Mac to Tutorials page - * fix trivial type for event logger - * Support podman-remote help on windows - * Clean destination paths during mount generation - * tests: use crun package - * Add a note on systemd shortcomings in rootless containers - * support non-standard ssh port for remote-client - * Add links to the Mac tutorial in the main tutorial - * Vendor c/storage 1.13.3 - * System-test: Temporarily disable 030-run - * Fix exit code failure - * exec: fix --preserve-fds - * networking: use --enable-sandbox if available - * Add 'relabel' to --mount options - * Bump Gitvalidation epoch - * Bump to v1.6.0-dev - * Unmounting a container that is already unmounted is OK - * Check for rootless before checking cgroups version in spec_test. - * Skip spec_test for rootless envs without cgroup v2. - * fix unit test to use Expect - * Cirrus: Prevent resident pollution - -- Changelog for v1.6.0-rc1 (2019-09-16) - * Fix default to pause in podman cp - * Update release notes for v1.6.0 - * Vendor Bulidah 1.11.2 - * get runtime for podman-remote push earlier - * rootless: report the correct error - * Report errors when trying to pause rootless containers - * Do not support wildcards on cp (Edit 2020-06-03: Addresses CVE-2019-18466) - * Podman-remote run should wait for exit code - * Use exit code constants - * exec: Register resize func a bit later - * clean up after healthcheck execs - * enhance podman network rm - * Add podman icon to installer - * Test that PTYs created by 'podman exec --tty' have the ONLCR flag - * Prevent podman varlink socket fight - * Touch up some bad grammar in rootless doc - * linux: fix systemd with --cgroupns=private - * rootless: run pause process in its own scope - * rootless: automatically create a systemd scope - * utils: use the user session for systemd - * Support building Windows msi file - * Add cgroup v2 info to rootless tutorial - * fix podman sign signature store for rootless - * podman-remote image trust is broken - * Cirrus: Fix unnecessary setsebool - * Add further fields to StorageContainer - * Volume lookup needs to include state to unmarshal into - * Do not prune images being used by a container - * Add support for launching containers without CGroups - * add lint and manpage check to make validate - * Add `ContainerManager` annotation to created containers - * When first mounting any named volume, copy up - * Add function for looking up volumes by partial name - * hack/man_page_checker - improve diagnostics - * podman network create - * Fixup `util.GetRootlessConfigHomeDir` permission requirements - * Fixup Makefile for BSD systems, e.g. macOS - * Replace "podman" with "Podman" - * Add instructions for mounting named volumes from the host for `podman run` - * Add instruction for using fuse-overlayfs as the rootless storage driver - * Fix podman import bash completions - * Turn off journald in podmanimages on quay.io - * build: pass down the cgroup manager to buildah - * mac_client.md - * Ignore ENOENT on umount of SHM - * play kube: fix segfault - * Return information about mount_program (fuse-overlayfs) - * Ensure good defaults on blank c/storage configuration - * Correctly report errors on unmounting SHM - * Add ability for volumes with options to mount/umount - * Fixup README.md to give proper information - * Add volume state - * Change volume driver and options JSON tags - * Update buildah to v1.11.0 - * Set TMPDIR to /var/tmp by default - * cli-flags: use a consistent format for - * Fix unit tests missing comparative for 'Expect' - * System tests: support for crun on f31/rawhide - * libpod: avoid polling container status - * Add test to verify noexec works with volume mounts - * Cirrus: Update e-mail -> IRC Nick table - * handle dns response from cni - * pkg/util: use rootless function to read additional users - * Enable hack/man-page-checker in CI - * rootless: detect user namespace configuration changes - * rootless.md: add systemd unit example - * docs: add note about failing rhel7 systemd on cgroups v2 - * spec: provide custom implementation for getDevices - * spec: do not set devices cgroup when rootless - * rootless: bind mount devices instead of creating them - * Add command aliases to SYNOPSIS section - * Exclude podman-remote - * Cirrus: On success, add IRC nick mention to msg - * Fix table spacing - * Revert the descriptive text for podman-remote - * WIP - ignore man pages for commands besides podman - * podman-remote is not a subcommand - * Fix formatting and enable hack/man-page-checker - * Cirrus: Load base-image names indirectly - * Cirrus: Remove image_prune YAML-alias workaround - * Fix links to manpages - * Makefile: use go proxy - * man: events-logger → events-backend - * dont panic when using varlink commit and uppercase image names - * Add a test for the new suid/exec/dev options - * Fix addition of mount options when using RO tmpfs - * Allow :z and :Z with ProcessOptions - * Set base mount options for bind mounts from base system - * Don't double-process tmpfs options - * Add support for 'exec', 'suid', 'dev' mount flags - * Update buildah to current master - * Cirrus: Reimplement release archive + upload - * Readme: Links for automatic binary releases - * Re-add locks to volumes. - * image: remove unused Decompose method - * Temporarily disable systemd test for CGroups V2 - * Add an integration test for systemd in a container - * clean up after remote build - * Cirrus: Block CNI use of google VPCs - * Add snap build test to success and release check - * Run `apt-get update` to avoid missing package while building - * Use snapcraft on Ubuntu 18.04 for libostree-dev - * Test build snap with Cirrus CI - * Update varlink doc and code - * podman cp: big set of system tests - * add iproute to podman in podman image - * Cirrus: Enable VM image housekeeping - * clean up after remote build - * Adjust name of Podman CNI network bridge - * Update cni config instructions - * Fix minor typos in podman-run docs. - * Fix link format in rootless_tutorial.md. - * Need to include command name in error message - * podman-remote: cp crashes - * generate systemd: support pods and geneartig files - * Dockerfile.fedora: install cni plugins package - * Add --digestfile option to push - * generate systemd: drop support for remote clients - * exec: run with user specified on container start - * Dockerfile*: fix build for CNI plugins - * Touchup README with Buildah build usage - * Dockerfile.*: bump CNI plugins commit - * Implement healthcheck for remote client - * networking: use firewall plugin - * Flake fix: build test timeout - * Fix error message on podman stats on cgroups v1 rootless environments - * test: enable all tests for crun - * test: fix return code check for missing workdir - * Fix directory pull image name for OCI images - * .cirrus.yml: use crun from git master - * libpod, pkg: lookup also for crun failures - * libpod.conf: add crun to runtime_supports_json - * containers, create: debug message on failed deletion - * libpod: still attempt to read the oci log file if not output - * Issue template update to include package info - * Allow customizing pod hostname - * add --cert-dir image sign - * Cirrus: Minor: Simplify crun test task - * Create framework for varlink endpoint integration tests - * Cirrus: Confirm networking more - * inclusion of podman network - * do not activate sd_notify support when varlink - * Remove --tmpfs size default - * cirrus: enable cgroups v2 tests with crun - * tests: skip pause tests if freezer is not available - * tests: enable run tests for cgroups v2 - * tests: enable cpu tests for cgroups v2 - * tests: enable memory tests for cgroups v2 - * runtime: honor --runtime flag to build - * test: fix option name - * Add support & documentation to run containers with different file types - * Use GetRuntimeDir to setup auth.json for login - * add --pull flag for podman create&run - * Fix typos - * Update Varlink API documentation for volumes changes - * Swap 'volume inspect' frontend to use the new backend - * Implement backend for 'volume inspect' - -- Changelog for v1.5.1 (2019-08-15) - * Add release notes for v1.5.1 - * Set Pod hostname as Pod name - * tests for exit status on podman run --rm - * performance fix for podman events with large journalds - * pkg/cgroups: use DBUS session when rootless - * Fix play kube command in pod yaml - * removMergeDir from inspect result if not mounted - * Running Podman with a nonexistent hooks dir is nonfatal - * Cirrus: Install varlink on Ubuntu - * Cirrus: Install varlink on Fedora - * Add missing stage-packages in snapcraft.yaml. - * Add RHEL and SUSE to snap doc - * start groundwork for adding snap - * Add user systemd service and socket - * Small optimization - only store exit code when nonzero - * Fix container exit code with Journald backend - * Revert "Cirrus: Temp. workaround missing imgprune image" - * Homebrew installation in install.md - * varlink endpoint for containerstats requires root - * Adjust get_ci_vm.sh for substitution - * Cirrus: Add verification for cgroupv2 image - * Cirrus: Add experimental fedora VM image & test - * image: add user agent to Docker registry options - * Cirrus: Minor, use newer Ubuntu base image - * tests: disable some tests currently failing when not using runc - * containers: look also for 'file not found' in the error message - * cirrus: add tests with crun on Fedora 30 - * rootless: cherry-pick runtime from the system configuration - * cirrus: install crun - * cmd: drop check for euid==0 - * storage: drop unused geteuid check - * cmd, stats: fix check for rootless mode - * oci: drop check for euid==0 - * build: use the configured runtime - * Adjust read count so that a newline can be added afterwards - * Fix incorrect use of realloc() - * Bump gitvalidation epoch - * Bump to v1.5.1-dev - * Fix a couple of errors descovered by coverity - * Test that restored container does not depend on the original container - * Fix up ConmonPidFile after restore - * Cirrus: Enable updates-testing repo for Fedora - * enable windows remote client - * implement 'make remotesystem' - * Squish a few tpyo nits in container.go doc - * Cirrus: Add Second partition for storage testing - -- Changelog for v1.5.0 (2019-08-09) - * vendor github.com/containers/storage@v1.13.2 - * Improve dns-search validation, empty domains now return an error - * fix create&run getting --authfile from cli - * Add release notes for v1.5.0 - * Touch up build man page - * podman-container-runlabel(1): drop note - * make rmi messages more compatible with docker - * Add conmon probe to runtime construction - * fix copy change file owner if cp from container - * Vendor Buildah 1.10.1 - * Allow the passing of '.' to --dns-search - * add make to make installs - * namespaces: fix Container() call - * Add a test for verifying ENTRYPOINT and CMD - * fix port early return - * Allow --ro=[true|false] with mount flag - * refer to container whose namespace we share - * add test to verify hostname is shared in a pod - * Properly share UTS namespaces in a pod - * When populating CMD, do not include Entrypoint - * systemd library conflict with seektail and addmatch - * pod top test: reenable - * cgroup: fix regression when running systemd - * Add invalid credentials fix to docs - * Revert "rootless: Rearrange setup of rootless containers" - * restore: correctly set StartedTime - * container stop: kill conmon - * honor libpod.conf in /usr/share/containers - * fix system df crashes on unnamed images - * Don't log errors to the screen when XDG_RUNTIME_DIR is not set - * various fixes for varlink endpoints - * add eventlogger to info - * Add handling for empty LogDriver - * Add rootless NFS and OverlayFS warnings to docs - * podman events format json - * add godoc link to readme - * restore: added --ignore-static-ip option - * System tests: resolve hang in rawhide rootless - * fix search output limit - * Add capability functionality to play kube - * Use "none" instead of "null" for the null eventer - * Deduplicate capabilities in generate kube - * Fix typo - * Pass on events-backend config to cleanup processes - * Print Pod ID in `podman inspect` output - * go build: use `-mod=vendor` for go >= 1.11.x - * Use buildah/pkg/parse volume parsing rather then internal version - * github.com/containers/storage v1.12.13 - * Add new exit codes to rm & rmi for running containers & dependencies - * Add runtime and conmon path discovery - * systemd, cgroupsv2: not bind mount /sys/fs/cgroup/systemd - * Ensure we generate a 'stopped' event on force-remove - * Fix Dockerfile - a dependency's name was changed - * System events are valid, don't error on them - * Do not use an events backend when restoring images - * Expose Null eventer and allow its use in the Podman CLI - * Force tests to use file backend for events - * Add a flag to set events logger type - * Fix test suite - * Retrieve exit codes for containers via events - * podman: fix memleak caused by renaming and not deleting the exit file - * Cirrus: Fix release dependencies - * Cirrus: Fix re-run of release task into no-op. - * e2e test: check exit codes for pull, save, inspect - * rootless: Rearrange setup of rootless containers - * Add comment to describe postConfigureNetNS - * Vendor in buildah 1.9.2 - * Build fix for 32-bit systems. - * Set -env variables as appropriate - * Touch up input argument error on create - * Update libpod.conf to be NixOS friendly - * Allow info test to work with usernames w/dash - * Touch up XDG, add rootless links - * Fix the syntax in the podman export documentation example - * fix `podman -v` regression - * Move random IP code for tests from checkpoint to common - * Fix commit --changes env=X=Y - * Update pause/unpause video links and demo - * Cirrus: Remove fixed clone depth - * podman: support --userns=ns|container - * pods: do not to join a userns if there is not any - * Documenation & build automation for remote darwin - * Cirrus: Bypass release during image-building - * Use systemd cgroups for Ubuntu - * Cirrus: Ubuntu: Set + Test for $RUNC_BINARY - * Cirrus: Simplify evil-unit check in image - * Cirrus: Silence systemd-banish noise - * Cirrus: Fix image build metadata update - * Cirrus: Fix missing -n on CentOS - * Cirrus: Remove disused COMMIT variables - * Improved hooks monitoring - * Fix possible runtime panic if image history len is zero - * When retrieving volumes, only use exact names - * fix import not ignoring url path - * Document SELinux label requirements for the rootfs argument - * Fixes issue #3577. - * refactor to reduce duplicated error parsing - * remove debug prints - * Re-add int64 casts for ctime - * fix build --network=container - * Fix a segfault on Podman no-store commands with refresh - * always send generic error in case io fails - * only use stdin if specified - * buffer errChan - * move handleTerminalAttach to generic build - * remove unnecessary conversions - * add detach keys support for remote - * move editing of exitCode to runtime - * Update e2e tests for remote exec - * Finish up remote exec implementation - * golangci-lint cleanup - * install.md: mention all build tags - * golangci-lint phase 4 - * Change wait to sleep in podmanimage readme - * bump cirrus images to get new conmon - * Implement conmon exec - * bump conmon to 1.0.0-rc2 - * Cirrus: Temp. workaround missing imgprune image - * vendor github.com/containers/image@v2.0.1 - * golangci-lint round #3 - * Remove debug message - * Cleanup Pull Message - * Cirrus: Fix post-merge env. var. not set. - * mkdir -p /etc/cni/net.d requires sudo - * Add support for listing read/only and read/write images - * support podman ps filter regular expressions - * rootless: add rw devices with --privileged - * Cirrus: Minor scripting typo fix - * fix --dns* and --network not set to host conflict - * podman-remote make --size optional in ps - * Remove exec PID files after use to prevent memory leaks - * Add DefaultContent API to retrieve apparmor profile content - * libpod: support for cgroup namespace - * Make GOPATH-related symlinking more precise - * Populate inspect with security-opt settings - * Properly retrieve Conmon PID - * Move the HostConfig portion of Inspect inside libpod - * Fix play kube command - * spec: rework --ulimit host - * Cirrus: Add image-test for locked dpkg - * Cirrus: Use images w/o periodic svcs - * Cirrus: Disable most periodic services/timers - * dependency/analyses: simplify scripts - * dependency-tree analysis: direct and transitive - * analyses: README: consistent code examples - * analyses: README: fix typos - * analyses: add dependency-tree.sh - * analyses: add README.md - * hack/analyses -> dependencies/analyses - * hack/analyses/go-archive-analysis.sh: fix sorting - * add hack/analyses/nm-symbols-analysis.sh - * analyse package sizes - * Completion: complete "--health-start-period" in bash - * Make the healthcheck flags compatible with Docker CLI - * healthcheck: reject empty commands - * create: ignore check if image has HEALTHCHECK NONE - * create: apply defaults on image healthcheck options - * healthcheck: improve command list parser - * Completion: --no-healthcheck is not an option - * Cirrus: Abstract destination branch refs. - * Cirrus: Print images that should be pruned - * create: improve parser for --healthcheck-command - * Improves STD output/readability in combination with debug output. - * Fix the double replySendFile() - * Cirrus: Update to freshly built cache-images - * Cirrus: Execute system-tests during image-validation - * Cirrus: Fix missing removal of packaged podman - * cgroupsv2: do not enable controllers for the last component - * spec: fix userns with less than 5 gids - * Fix spelling mistakes in man pages and other docs - * Add glob parsing for --env flag - * Add support for -env-host - * cgroups: fix a leak when using cgroupfs - * cgroups: attempt a recursive rmdir - * Fix a bug where ctrs could not be removed from pods - * golangci-lint pass number 2 - * Add tests for --ignore-rootfs checkpoint/restore option - * Add --ignore-rootfs option for checkpoint/restore - * Fix typo in checkpoint/restore related texts - * Include root file-system changes in container migration - * Add function to get a filtered tarstream diff - * Correctly set FinishedTime for checkpointed container - * first pass of corrections for golangci-lint - * Cirrus: Fix #3543: Failure in 'release' task - * fix bug convert volume host path to absolute - * Cirrus: Fix 473d06045 / enable build_without_cgo - * account for varlink calls that dont use more - * runtime: drop spurious message log - * Ensure we have a valid store when we refresh - * cgroups: skip not existing cpuacct files - * cgroups: support creating cgroupsv2 paths - * make localsystem: wipe all user config state - * podman: create and run honors auth file location - * healthcheck: support rootless mode - * Use random IP addresses during checkpoint/restore tests - * Fix podman-remote usage message to display `podman-remote` instead of `podman` - * rootless.md: Include GPFS as a parallel filesystem - * speed up rootless tests - * podman: add --ulimit host - * docs: fix --healthcheck-command option - * code cleanup - * fix integration flake tests - * CONTRIBUTING.md: fix project paths - * get last container event - * Do not hardcode podman binary location in generate systemd. - * Move skipping systemd tests to early setup. - * Reload systemd daemon on creation of units location dir in tests. - * Add debug information to "generate systemd" test. - * Use default conmon pidfile location for root containers. - * Use conmon pidfile in generated systemd unit as PIDFile. - * Cirrus: Automate releasing of tested binaries - * trivial cleanups from golang - * ps should use nostore when possible - * libpod: discerne partial IDs between containers and pods - * Added instruction to enable the user namespaces permanenty in Manjaro - * Addressed code review comments - * Updated install.md for Manjaro Linux - * Vendor latest OCICNI version - * Bump current version in README - * Wipe PID and ConmonPID in state after container stops - * Store Conmon's PID in our state and display in inspect - * Restart failed containers in tests - * Improve parsing of mounts - * Add test for generate kube with volumes - * Bump gitvalidation epoch - * Bump to v1.4.5-dev - * Fix rootless detection error for pause & unpause - * Deduplicate volumes - * cirrus: add test for compiling without cgo - * lock: new lock type "file" - * runtime: allow to specify the lock mechanism - * lock: disable without cgo - * spec: move cgo stuff to their own file - * rootless: allow to build without cgo - * attach: move cgo bits to a different file - * vendor: update containers/psgo - * Update the testing documentation with system tests. - * Pass along volumes to pod yaml - * Configure container volumes for generate kube - * configure runtime without store - * Add RUN priv'd test for build - * Cirrus: Use packaged dependencies - * Add exec after checkpoint/restore test - * Provide correct SELinux mount-label for restored container - * Track if a container is restored from an exported checkpoint - * libpod/container_internal: Make all errors loading explicitly configured hook dirs fatal - -- Changelog for v1.4.4 (2019-07-02) - * Fix release notes - * Ensure locks are freed when ctr/pod creation fails - * Update release notes for 1.4.4 - * stats: use runtime.NumCPU when percpu counters are not available - * cgroups: fix times conversion - * Update to containers/storage v1.12.13 - * rootless: do not join namespace if it has already euid == 0 - * Exclude SIGTERM from blocked signals for pause process. - * Remove umount command from remote client. - * rootless: enable linger if /run/user/UID not exists - * Makefile: set GO111MODULE=off - * libpod removal from main (phase 2) - * runtime: do not attempt to use global conf file - * runtime: use GetRootlessUID() to get rootless uid - * Remove refs to crio/conmon - * Handle images which contain no layers - * Add tests that we don't hit errors with layerless images - * stats: fix cgroup path for rootless containers - * pkg, cgroups: add initial support for cgroup v2 - * util: drop IsCgroup2UnifiedMode and use it from cgroups - * vendor: drop github.com/containerd/cgroups - * libpod: use pkg/cgroups instead of containerd/cgroups - * pkg: new package cgroups - * Remove unnecessary blackfriday dependency - * libpod: fix hang on container start and attach - * podman: clarify the format of --detach-keys argument - * libpod: specify a detach keys sequence in libpod.conf - * Fix parsing of the --tmpfs option - * Fix crash for when remote host IP or Username is not set in conf file & conf file exists. - * Bump gitvalidation epoch - * Bump to v1.4.4-dev - * Cirrus: More tests to verify cache_images - * Update release notes for 1.4.3 release - * remove libpod from main - -- Changelog for v1.4.3 (2019-06-25) - * Update 'generate kube' tests to verify YAML - * Use a different method to retrieve YAML output in tests - * update dependencies - * Fix tests - * Change Marshal -> Unmarshal in generate kube tests - * Add test for generate kube on a pod with ports - * Only include ports in one container in Kube YAML - * Support aliases for .Src and .Dst in inspect .Mounts - * Fix a segfault in 'podman ps --sync' - * migrate to go-modules - * Makefile: add go-get function - * rootless: add an entry to /etc/hosts when using slirp4netns - * libpod.conf: add runtime crun - * Fix configs location in rootless tutorial. - * Add additional debugging when refreshing locks - * Fix gofmt - * Adjust names to match struct tags in Inspect - * Fix inspect --format '{{.Mounts}}. - * runtime.go: Add /usr/local/{s,}bin - * include make podman target in install instructions - * Add /usr/local/{s,}bin to conmon paths - * update cirrus image - * Update conmon to include attach socket unlink - * Add --latest, -l to 'podman diff' - * Build cgo files with -Wall -Werror - * Add some missing periods to the readme - * fix bug creats directory copying file - * Support Reproducible Builds by removing build path - * Support SOURCE_DATE_EPOCH - * Properly initialize container OCI runtime - * vendor containers/storage v1.12.11 - * Handle containers whose OCIRuntime fields are paths - * Properly handle OCI runtime being set to a path - * add windows bridge format - * Make configuration validation not require a DB commit - * Avoid a read-write transaction on DB init - * Fix execvp uage in rootless_linux.c - * Handle possible asprintf failure in rootless_linux.c - * Fix format specifiers in rootless_linux.c - * Print container's OCI runtime in `inspect` - * Make a missing OCI runtime nonfatal - * Begin adding support for multiple OCI runtimes - * docs: add note to system migrate - * Fix documentation for log-driver - * Minor roadmap adjustment in README - * Spoof json-file logging support - * Add tests for cached and delegated mounts - * Vendor in logrus v1.4.2 - * Add RUN with priv'd command build test - * Bump gitvalidation epoch - * Bump to v1.4.3-dev - * fix broken healthcheck tests - * Allow (but ignore) Cached and Delegated volume options - * Fix example in oci-hooks.5.md - * First pass rootless tutorial - * Correctly identify the defaults for cgroup-manager - * Cirrus: Fix F30 ssh guarantee - * Cirrus: Add support for testing F30 - * Bump gate-container up to F30 - -- Changelog for v1.4.2 (2019-06-18) - * Update release notes for Podman 1.4.2 - * updating podman logo files - * fix port -l timing with healthchecks - * Bump Buildah to v1.9.0 - * Swap to using the on-disk spec for inspect mounts - * Replace podman.svg; closes #3350 - * cmd, docs, test: fix some typos - * run BATS tests in Cirrus - * Move the Config portion of Inspect into libpod - * Add remote client logging to a file - * Fix subgidname option in docs for podman run - * stop/kill: inproper state errors: s/in state/is in state/ - * test: add test for logs -f - * kill: print ID and state for non-running containers - * API.md: fix few typos - * docs/podamn.1.md: fix typo: remove double the - * CONTRIBUTING.md: fix typo - * Remove unnecessary var type to fix lint warning - * Move installPrefix and etcDir into runtime.go - * Improve DESTDIR/PREFIX/ETCDIR handling - * Bump gitvalidation epoch - * Bump to v1.4.2-dev - * Change container command to contained - * Cirrus: Simplify log collection commands - * Accidently removed /run/lock from systemd mounts - * Add warning while untagging an image podman-load - * podman copy files to the volume with a container - -- Changelog for v1.4.1 (2019-06-14) - * Completely disable global options test - * Update release notes for 1.4.1 - * Skip runlabel global options test for podman-in-podman - * pkg/apparmor: fix when AA is disabled - * Fix ENV parsing on `podman import` - * Fix storage-opts type in Cobra - * Use the logical registry location instead of the physical one in (podman info) - * Update containers/image to v2.0.0, and buildah to v1.8.4 - * Document exit codes for podman exec - * Add --storage flag to 'podman rm' (local only) - * When creating exit command, pass storage options on - * Bump cirrus images - * Mention the new Podman mailing list in contributing.md - * Update 1.4.0 release notes with ID -> Id in inspect - * Bump conmon to 0.3.0 - * Cirrus: Guarantee ssh is running for rootless - * Purge all use of easyjson and ffjson in libpod - * Split mount options in inspect further - * storage: support --mount type=bind,bind-nonrecursive - * oci: allow to specify what runtimes support JSON - * storage: fix typo - * oci: use json formatted errors from the runtime - * Make Inspect's mounts struct accurate to Docker - * Provide OCI spec path in `podman inspect` output - * If container is not in correct state podman exec should exit with 126 - * rootless: use the slirp4netns builtin DNS first - * Add --filename option to generate kube - * Fix podman-remote to user default username - * Prohibit use of positional args with --import - * BATS tests - get working again - * Add a test for 'podman play kube' to prevent regression - * Cirrus: New images w/o buildah - * Remove source-built buildah from CI - * standardize documentation formatting - * Touchup upstream Dockerfile - * only set log driver if it isn't empty - * Fix cgo includes for musl - * When you change the storage driver we ignore the storage-options - * Update vendor on containers/storage to v1.12.10 - * Bump gitvalidation epoch - * Bump to v1.4.1-dev - * Default 'pause' to false for 'podman cp' - * Update c/storage to 9b10041d7b2ef767ce9c42b5862b6c51eeb82214 - * Fix spelling - * fix tutorial link to install.md - * Cirrus: Minor cleanup of dependencies and docs - * Begin to break up pkg/inspect - * docs: Add CI section and links - -- Changelog for v1.4.0 (2019-06-07) - * Update release notes for v1.4.0 - * Update release notes for v1.4.0 - * Disable a very badly flaking healthcheck test - * rootless: skip NS_GET_PARENT on old kernels - * Cirrus: Track VM Image calling GCE project - * remove -c for podman remote global options - * Vendor Buildah v1.8.3 - * Cirrus: Disable testing on F28 (EOL) - * migration: add possibility to restore a container with a new name - * Inherit rootless init_path from system libpod.conf - * Also download container images during restore - * Include container migration into tutorial - * Add man-pages for container migration - * Added bash completion for container migration - * Add test case for container migration - * Added support to migrate containers - * Added helper functions for container migration - * Fix restore options help text and comments - * fix timing issues with some tests - * pkg/varlinkapi/virtwriter/virtwriter.go: simplify func Reader - * rootless: block signals on re-exec - * cirrus: minor cleanup and refactoring - * manpage: podman-tool table: un-confuse version and varlink - * Create Dockerfiles for podmanimage - * rootless: use TEMP_FAILURE_RETRY macro - * rootless: fix return type - * rootless: make sure the buffer is NUL terminated - * split rootless local and remote testing - * Fix podman cp test by reordering operations - * Small fix to readme to force tests to run - * Do not set tmpcopyup on /dev - * do not run remote tests inside container - * podman remote-client commit - * Fix podman cp tests - * podman-remote.conf enablement - * Error when trying to copy into a running rootless ctr - * rootless: skip check fo /etc/containers/registries.conf - * We can't pause rootless containers during cp - * Fix bug in e2e tests for podman cp - * Tolerate non-running containers in paused cp - * Add test to ensure symlinks are resolved in ctr scope - * Add --pause to podman cp manpage and bash completions - * Pause containers while copying into them - * Use securejoin to merge paths in `podman cp` - * use imagecaches for local tests - * add dns flags to docs - * add missing container cp command - * Podman logs man page shouldn't include timestamps - * Fix the varlink upgraded calls - * hack: support setting local region/zone - * document missing container update command - * Add --follow to journald ctr logging - * Address comments - * Implement podman logs with log-driver journald - * bump go-systemd version - * Added --log-driver and journald logging - * Update completions and docs to use k8s file as log driver - * bump conmon to v0.2.0 - * runtime: unlock the alive lock only once - * rootless: make JoinUserAndMountNS private - * Revert "rootless: change default path for conmon.pid" - * rootless: enable loginctl linger - * rootless: new function to join existing conmon processes - * rootless: block signals for pause - * Update install.md ostree Debian dependencies. - * fix bug dest path of copying tar - * podman: honor env variable PODMAN_USERNS - * userns: add new option --userns=keep-id - * warn when --security-opt and --privileged - * baseline tests: apparmor with --privileged - * rootless: store also the original GID in the host - * Fix a potential flake in the tests for podman cp - * cirrus: update images w/ zip pkg - * Cirrus: Add zip package to images - * rootless: fix top huser and hgroup - * vendor: update psgo to v1.3.0 - * apparmor: don't load/set profile in privileged mode - * hack: ignore from all VCS files when tarballing - * hack: shrink xfer tarball size - * hack: Display IP address of VM from script - * document nullable types - * Add test cases for login and logout - * Remove unused return statement in kube volume code - * Fix play kube when a pod is specified - * Fix a 'generate kube' bug on ctrs with named volumes - * Add test for image volume conflict with user volume - * Cirrus: Fix missing CRIO_COMMIT -> CONMON_COMMIT - * When superceding mounts, check for opposite types - * make remote resize channel buffered - * Cirrus: workaround root expand failure - * Cirrus: Stub in F30 support - * Cirrus: fixups based on review feedback - * Cirrus: Overhaul/Simplify env. var setup - * Cirrus: Run tests on test-built cache-images - * Cirrus: Support testing of VM cache-image changes - * Cirrus: Remove "too new" runc hack - * libpod: prefer WaitForFile to polling - * Remove conmon from fedora install instructions - * rootless: force resources to be nil on cgroup v1 - * Fixup Flags - * Minor fix filtering images by label - * container: move channel close to its writer - * util: fix race condition in WaitForFile - * Update vendor of buildah and containers/images - * Add Jhon Honce (@jwhonce on github) to OWNERS - * Don't set apparmor if --priviliged - * docs/libpod.conf.5: Add "have" to "higher precedence" typo - * Output name of process on runlabel command - * Minor fix splitting env vars in podman-commit - * Fixup conmon documentation - * troubleshooting.md: add note about updating subuid/subgid - * system: migrate stops the pause process - * rootless: join namespace immediately when possible - * rootless: use a pause process - * migrate: not create a new namespace - * install.remote should be separate for install.bin - * Cirrus: Confirm networking is working - * Use containers/conmon - * Fix a typo in release notes, and bump README version - * s|kubernetes-sigs/cri-o|cri-o/cri-o|g - * Bump github.com/containers/storage to v1.12.7 - * remote: version: fix nil dereference - * Bump gitvalidation epoch - * Bump to v1.3.2-dev - * Add connection information to podman-remote info - * unshare: define CONTAINERS_GRAPHROOT and CONTAINERS_RUNROOT - * Touchup run man page - * unshare: use rootless from libpod - * Replace root-based rootless tests - * rootless: default --cgroup-manager=systemd in unified mode - * create: skip resources validation with cgroup v2 - * rootless, spec: allow resources with cgroup v2 - -- Changelog for v1.3.1 (2019-05-16) - * More release notes - * Add unshare to podman - * Release notes for 1.3.1 - * Kill os.Exit() in tests, replace with asserts - * Minor capitalization fix in Readme - * Add debug mode to Ginkgo, collect debug logs in Cirrus - * set default event logger based on build tags - * Add VarlinkCall.RequiresUpgrade() type and method - * Ensure that start() in StartAndAttach() is locked - * When removing pods, free their locks - * network: raise a clearer error when using CNI - * Fix libpod.conf option ordering - * split remote tests from distro tests - * varlink: fix usage message, URI is now optional - * Update containerd/cgroups to 4994991857f9b0ae - * healthcheck benign error - * Add `systemd` build tag - * podman: fix events help string - * When removing a pod with CGroupfs, set pids limit to 0 - * Add fix for an issue breaking our CI - * Use standard remove functions for removing pod ctrs - * implement cp reads tar file from stdin/stdout - * Add information when running podman version on client - * add varlink bridge - * Add negative command-line test - * Preserve errors returned by removing pods - * Improve robustness of pod removal - * enable integration tests for remote-client - * fix podman-remote ps --ns - * podman-run|create man updates - * Update installation instructions - * remote-podman checkpoint and restore add to container submenu - * Remove tests for deprecated podman-refresh command - * When refreshing after a reboot, force lock allocation - * Do not remove volumes when --rm removes a container - * add unit tests for generate systemd - * Bump gitvalidation epoch - * Bump to v1.3.1-dev - * Upgrade to latest criu and selinux-policy - * Only run checkpoint/restore tests on Fedora >= 29 - * Fix API.md - * Cirrus: Add missing task dependencies - * Cirrus: Add check for make varlink_api_generate - -- Changelog for v1.3.0 (2019-05-06) - * Update release notes for 1.3.0 release - * Bump to Buildah v1.8.2 - * Document events logger options in libpod.conf manpage - * Try and fix restart-policy tests - * fix logout message if login only with docker - * Fix manpage typos - * Small code fix - * Fix 'restart' event in journald - * change from sysregistries to sysregistriesv2 - * Address review comments on restart policy - * Add a test for restart policy - * Add a restart event, and make one during restart policy - * Restart policy should not run if a container is running - * Restart policy conflicts with the --rm flag - * Move to using constants for valid restart policy types - * Add manpage information for restart policy - * Add support for retry count with --restart flag - * Sending signals to containers prevents restart policy - * Add container restart policy to Libpod & Podman - * Add a StoppedByUser field to the DB - * top: fallback to execing ps(1) - * clean up shared/parse/parse.go - * Generate systemd unit files for containers - * Fix podman-in-podman volume test - * Cirrus: Add pipefail confirmation check - * Cirrus: timestamp all output script output - * Update c/storage to v1.12.6 - * Fix typo in init manpage - * Add an InvalidState varlink error for Init - * Bump Buildah to v1.8.1, ImageBuilder to v1.1.0 - * Add variable for global flags to runlabel - * docs: Fix typo "healthcheck" pt2 - * cirrus lib.sh: refactor req_env_var() - * Remove two bits of dead code - * http-proxy: improve docs - * Small fixes for #2950 - * container: drop rootless check - * Add basic structure of podman init command - * Move handling of ReadOnlyTmpfs into new mounts code - * Begin adding volume tests - * Ensure that named volumes have their options parsed - * Add options parsing for tmpfs mounts - * Use EqualValues instead of reflect equality - * Hit a number of to-do comments in unified volumes code - * Fix options for non-bind and non-tmpfs volumes - * Migrate unit tests from cmd/podman into pkg/spec - * Migrate to unified volume handling code - * Always pass pod into MakeContainerConfig - * Remove non-config fields from CreateConfig - * Add a new function for converting a CreateConfig - * podman-remote port - * install.md contains hints for rootless setup on arch linux - * auto pass http_proxy into container - * enable podman-remote on windows - * Use 'sudo tee' in tutorial so install works as non-root - * Refactor container cleanup to use latest functions - * Move --mount in run man page - * Add details on rootless Podman to the readme - * podman-remote stop - * correct upstream vndr issues - * runtime: pass down the context - * system: add new subcommand "migrate" - * Vendor in latest buildah code - * remove manual install of libsystemd-dev - * Vendor in latest containers/storage - * Add --read-only-tmpfs options - * Fix remote-client testing reports - * podman-remote prune containers - * Do not hard fail on non-decodable events - * update psgo to v1.2.1 - * Add System event type and renumber, refresh events - * enable podman remote top - * fix login supports credHelpers config - * Cirrus: Collect audit log on success and failure - * Add a debug message indicating that a refresh occurred - * image: rework parent/child/history matching - * images: add context to GetParent/IsParent/Remove/Prune... - * build podman-remote with Dockerfile. - * point to 3rd party tools for `docker-compose` format - * Update vendor of container/storage - * journald event logging - * podman remote-client restart containers - * Cirrus: Use freshly built images - * Cirrus: Bump up runc commit - * Cirrus: fix obsolete Ubuntu package - * Cirrus: Install libsystemd-dev on Ubuntu - * pull: special case all-tags semantics - * Fix test compile - * Trim whitespace from ps -q before comparing - * Enhance tests for stop to check results - * Add extra CI tests for stopping all containers - * Fix podman stop --all attempting to stop created ctrs - * Cirrus: Temp. override container-selinux on F29 - * Refactor of 'podman prune' to better support remote - * bats - various small updates - * podman-remote pause|unpause - * Internal names do not match external names - * Add header to play kube output - * Clean up after play kube failure - * rootless: not close more FDs than needed - * Fix COPR builds to start working again - * Fix podman command --change CMD - * podman-remote start - * Vendor in latest Buildah - * Added remote pod prune - * Add podman pod prune - * podman-remote container commands - * Fix segfaults attribute to missing options - * Call the runtime with WithRenumber() when asked - * Add File mounts to play kube - * cmd, pkg: drop commented code - * pod: drop dead code - * rootless, mount: not create namespace - * Incorporate image and default environment variables in play kube - * Validate ENV/LABEL Change options in varlink - * oci: fix umount of /sys/kernel - * Revert "rootless: set controlling terminal for podman in the userns" - * Remove old crio reference from man pages - * create: fix segfault if container name already exists - * adding uidmap to install steps for ubuntu - * podman-remote generate kube - * rootless: do not block SIGTSTP - * rootless: set controlling terminal for podman in the userns - * Use GetContainer instead of LookupContainer for full ID - * pull: exit with error if the image is not found - * Use the same SELinux label for CRIU log files - * pull: remove cryptic error message - * new uidmap BATS test: fix - * adding additional update, needed for install - * Fix README.md -> rootless.md link - * Fixes for podman-remote run and attach - * remote-client checkpoint/restore - * Expand debugging for container cleanup errors - * spec: mask /sys/kernel when bind mounting /sys - * Add --include-volumes flag to 'podman commit' - * oci: add /sys/kernel to the masked paths - * userns: prevent /sys/kernel/* paths in the container - * imagefilter dangling handling corrected - * rootless: fix segfault on refresh if there are containers - * Add demo script and cast to images - * Initial remote flag clean up - * (minor): fix misspelled 'Healthcheck' - * BATS tests: start supporting podman-remote - * Add the ability to attach remotely to a container - * Print header for 'podman images' even with no images - * podman-remote ps - * Re-run (make vendor) to drop the now unnecessary collation code and tables - * Potentially breaking: Make hooks sort order locale-independent - * Implement podman-remote rm - * ps: now works with --size and nonroot - * Update invalid name errors to report the correct regex - * cirrus: enable remote tests for rootless - * test: fix remote tests for rootless - * test: enable userns e2e tests for rootless - * CI check for --help vs man pages: usability fix - * podman-remote create|run - * Correct varlink pull panic - * add image rmi event - * Revert "Switch to golangci-lint" - * Document shortcomings with rootless podman - * podman: enable kube for rootless - * kube: correctly set the default for MemorySwappiness - * rootless: enable healthcheck tests - * Respect image entrypoint in play kube - * Increase CI resources to help avoid hitting timeouts - * podman-remote image tree - * Added port forwarding and IP address hint. - * fix bug podman cp directory - * Fix E2E tests - * Drop LocalVolumes from our the database - * Major rework of --volumes-from flag - * Volume force-remove now removed dependent containers - * Add handling for new named volumes code in pkg/spec - * Create non-existing named volumes at container create - * Switch Libpod over to new explicit named volumes - * Add named volumes for each container to database - * Add varcheck linter - * Add deadcode linter - * Update lint to use golangci-lint - * Update registrar unit tests to match them of cri-o - * Update run tests to be skipped when not supported - * Fix Dockerfile dependencies for packer tests - * Update Dockerfile to use golang:1.12 image - * Fix a potential segfault in podman search - * Improve podman pod rm -a test - * Cirrus: Update F28 -> F29 container image - * --size does not work with rootless at present - * add remote-client diff - * Cirrus: Support special-case modes of testing - * rootless: use a single user namespace - * rootless: remove SkipStorageSetup() - * Update cri-o annotations - * Update README with current version - * docs/podman*.md: fix numerous option typos and spacing errors - * docs/podman-rm.1.md: delete "Not yet implemented" msg for volume removal - * docs/podman-inspect.1.md: add missing option hyphen for "-t" - * Bump gitvalidation epoch - * Bump to v1.3.0-dev - * Fix location of libpod.conf - * Capitalize global options help information - -- Changelog for v1.2.0 (2019-03-30) - * Update release notes for v1.2.0 - * Remove wait event - * Vendor Buildah 1.7.2 - * Add locking to ensure events file is concurrency-safe - * Alter container/pod/volume name regexp to match Docker - * test: test that an unprivileged user cannot access the storage - * userns: do not use an intermediate mount namespace - * volumes: push the chown logic to runtime_volume_linux.go - * Cleanup image2 -> image for imports - * Set blob cache directory based on GraphDriver - * utils: call GetRootlessRuntimeDir once - * rootless: set sticky bit on rundir - * oci: drop reference to runc - * Fix lint - * Ensure that we make a netns for CNI non-default nets - * rootless: change env prefix - * vendor buildah, image, storage, cni - * Default to SELinux private label for play kube mounts - * Add watch mode to podman ps - * Add all container status states to the podman-ps manual page. - * fix bug `system df` add blank space to the output - * fix bug remote-podman images --digests - * Use spaces instead of tab for JSON marshal indent - * Fix gofmt - * Remove ulele/deepcopier in favor of JSON deep copy - * doc: add note that pod publish ports are static once defined - * Sigh; disable pod-top test, it's unreliable (#2780) - * Resolve review comments - * Add a test that --add-host conflicts with --no-hosts - * Add manpages and completions for dns=none and no-hosts - * Add --no-hosts flag to disable management of /etc/hosts - * Add for --dns=none to disable creation of resolv.conf - * Add support to disable creation of network config files - * system df: reject invalid arguments - * rootless: fix regression when using exec on old containers - * Touchup commands.md - * size is optional for container inspection - * Add three test cases for podman attach test - * system df to show podman disk usage - * Add "died" event - * docs/podman-pod-create.1.md: add example with port mapping - * podman health check phase3 - * userns: use the intermediate mountns for volumes - * volume: create new volumes with right ownership - * utils: drop dead function - * troubleshooting: explain setup user: invalid argument - * Cirrus: Verify manpages for all subcommands exist - * Make "stopped" a valid state that maps to "exited" - * fix Bug 1688041-podman image save removes existing image - * podman: do not split --env on comma - * Need to pass the true paramater with --syslog in cobra - * Fix man page to mention race condition - * docs/podman-run.1.md: remove extra whitespace in --read-only - * man pages - consistency fixes - * Add new key and never-expiring test certificate - * Cirrus: Run vendor check in parallel - * Cirrus: Various fixes for rootless testing - * ps: fix segfault if the store is not initialized - * tests: re-enable some tests for rootless mode - * rootless: implement pod restart - * rootless: reimplement restart with rootless.Argument() - * test: fix SkipIfRootless() helper - * rootless, rm: fix retcode when the container is not found - * rootless: fix ps command - * rootless: fix pod kill - * Enable rootless integration tests - * BATS: new tests, and improvements to existing ones - * podman umount: error out if called with no args - * Export ConmonPidFile in 'podman inspect' for containers - * support GO template {{ json . }} - * Incorporate user from image inspect data in play kube - * Cirrus: Disable master-success IRC notices - * Cleanup messages on podman load - * Cirrus: Update VM Cache images - * podman logs on created container should exit - * Fix cut and paste errors in podman-pod-inspect - * rootless: fix pod top - * pod: fix segfault when there are no arguments to inspect - * output of port grouping in ps command added as example - * utils: split generation and writing of storage.conf - * Cirrus: Fix post-merge failure notice - * utils: avoid too long tmp directory - * podman image tree: fix usage message - * Cirrus: Notify on IRC if post-merge testing fails - * rootless: change default path for conmon.pid - * Add CLI storage conf example to run manpage - * Integration test tweaks - * display logs for multiple containers at the same time - * Make 'podman rm' exit with 125 if it had a bogus & a running container - * rootless: write the custom config file before reload - * Add support for SCTP port forwarding - * Make sure buildin volumes have the same ownership and permissions as image - * rootless: do not override user settings - * runtime: refactor NewRuntime and NewRuntimeFromConfig - * events: use os.SEEK_END instead of its value - * container: check containerInfo.Config before accessing it - * rootless: use Geteuid instead of Getuid - * rootless: use /tmp/libpod-rundir-$EUID for fallback - * build: fix build DIR -t TAG - * testcase added for listing range of ports in ps command - * port grouping in ps command output - * Update pull and pull-always in bud man page - * cirrus: upgrade slirp4netns - * rootless: fix CI regression when using slirp4netns - * save-load-export: clear cli-parsing default - * Bump timeout on a podman info test to default - * Replace skopeo-containers with containers-common - * slirp4netns: use --disable-host-loopback - * slirp4netns: set mtu to 65520 - * Tree implementation for podman images - * Replace buildah with podman in build doc - * zsh completion - * Usage messages: deduplicate '(default true)' et al - * Corrected detach man pages and code comments - * Add --replace flag to "podman container runlabel" - * rm: fix cleanup race - * Add gating tasks - * Add 'podman events' to podman(1) - * Vendor docker/docker, fsouza and more #2 - * Usability cleanup for 'inspect' - * Add event on container death - * Update vendor of Buildah and imagebuilder - * minor typo fix in 'podman top' usage - * healtcheck phase 2 - * Add event logging to libpod, even display to podman - * Fix SELinux on host shared systems in userns - * Fix broken link in io.podman.varlink - * move formats pkg to and vendor from buildah - * Ensure that tmpfs mounts do not have symlinks - * Update troubleshooting guide for Podman-in-Podman - * Buffer stdin to a file when importing "-" - * vendor psgo v1.2 - * preparation for remote-client create container - * Initialize field in InfoHost struct - * rootless: allow single mappings - * Remove --rm and --detach don't coexist note - * rootless: fix pod stop|rm if uid in the container != 0 - * rootless: fix rm when uid in the container != 0 - * rootless: disable pod stats - * rootless: do not create automatically a userns for pod kill - * rootless: support a custom arg to the new process - * slirp4netns: add builtin DNS server to resolv.conf - * errors: fix error cause comparison - * libpod: allow to configure path to the network-cmd binary - * build: honor --net - * pull: promote debug statement to error - * Fix generation of infra container command - * Remove an unused if statement I added - * Don't delete another container's resolv and hosts files - * Fix a potential segfault during infra container create - * We don't use crio-umount.conf - * Move secrets package to buildah - * Add troublshoot information about SELinux labeling of containers/storage - * test docs fixups - * Default to image entrypoint for infra container - * ginkgo status improvements - * rootless: propagate errors from info - * podman play kube defaults - * container runlabel respect $PWD - * Remove 'podman ps' restarting filter and fix stopped - * label parsing in non-quoted field - * More cleanup for failures on missing commands. - * add podman-healthcheck(1) to podman(1) - * Implement review feedback - * new system tests under BATS - * fix bug in podman images list all images with same name - * Fix help commands to show short and long description. - * implement showerror and accept HOST_PORT env which defaults to 8080 - * create: join also the mount ns of the dependency - * rootless: exec join the user+mount namespace - * oci: make explicit the extra files to the exec - * add test to cover networking - * tests to cover locks and parallel execution #2551 - * Yet another seemingly minor tweak to usage message - * Change LookupContainer logic to match Docker - * Implement podman-remote wait command and container subcommand - * Cirrus: Use imgts container to record metadata - * System-test: Documentation and TODO list - * podman-remote pod top|stats - * fix bug --device enable specifying directory as device - * add flag --extract tar file in podman cp - * Fix incorrect pod create failure - * libpod/container_internal: Split locale at the first dot, etc. - * Add volume mounting to podman play kube - * podman healthcheck run (phase 1) - * Append hosts to dependency container's /etc/hosts file - * rootless: fix clone syscall on s390 and cris archs - * Cirrus: Add dedicated rootless mode testing - * rootless: fill in correct storage conf default - * rm: set exit code to 1 if a specified container is not found - * Support filter image by reference to the repo name - * Bump gitvalidation epoch - * Bump to v1.2.0-dev - * Support podman-remote kill container(s) - * cirrus: Drop ginkgo, gomega, easyjson install - * Cirrus: Stop testing on RHEL - * Cirrus: Stop testing on RHEL - * Globally increase test timeout to 90-minutes - * cirrus: Drop ginkgo, gomega, easyjson install - * Cirrus: Add BATS package for all platforms - * Globally increase test timeout to 90-minutes - * exec: support --preserve-fds - * get_ci_vm.sh: Fix conflicting homedir files - -- Changelog for v1.1.2 (2019-03-04) - * Fix #2521 - * Update release notes for v1.1.2 - * Change timestamp format for podman logs - * Don't extract tar file in podman cp - * runtime: fill a proper default tmpdir when --config is used - * Add additional defense against 0-length log segfaults - * When logging with timestamps, append only until newline - * Ensure that each log line is newline-terminated - * A few more usage-message tweaks - * Add missing short flag -l for run/create - * Fix aliased commands to actually work - * Support podman-remote stop container(s) - * Add tests to make sure podman container and podman image commands work - * Bump gitvalidation epoch - * Bump to v1.2.0-dev - -- Changelog for v1.1.1 (2019-03-01) - * Update release notes for v1.1.1 - * Pull image for runlabel if not local - * Fix SystemExec completion race - * Fix link inconsistencies in man pages - * Verify that used OCI runtime supports checkpoint - * Should be defaulting to pull not pull-always - * podman-commands script: refactor - * Move Alias lines to descriptions of commands - * Fix usage messages for podman image list, rm - * Fix -s to --storage-driver in baseline test - * No podman container ps command exists - * Allow Exec API user to override streams - * fix up a number of misplace commands - * rootless, new[ug]idmap: on failure add output - * [ci skip] Critical note about merge bot - * podman port fix output - * Fix ignored --time argument to podman restart - * secrets: fix fips-mode with user namespaces - * Fix four errors tagged by Cobra macro debugging - * Clean up man pages to match commands - * Add debugging for errors to Cobra compatibility macros - * Command-line input validation: reject unused args - * Fix ignored --stop-timeout flag to 'podman create' - * fixup! Incorporate review feedback - * fixup! missed some more: - * fixup! Correction to 'checkpoint' - * Followup to #2456: update examples, add trust - * podman create: disable interspersed opts - * fix up a number of misplace commands - * Add a task to Cirrus gating to build w/o Varlink - * Skip checkpoint/restore tests on Fedora for now - * Fix build for non-Varlink-tagged Podman - * Remove restore as podman subcommand - * Better usage synopses for subcommands - * Bump gitvalidation epoch - * Bump to v1.2.0-dev - * Centralize setting default volume path - * Ensure volume path is set appropriately by default - * Move all storage configuration defaults into libpod - * rename pod when we have a name collision with a container - * podman remote-client readme - -- Changelog for v1.1.0 (2019-02-26) - * Vendor in latest buildah 1.7.1 - * volume: do not create a volume if there is a bind - * Only remove image volumes when removing containers - * Fix podman logs -l - * start pod containers recursively - * Update release notes for v1.1.0 - * vendor containers/image v1.5 - * Record when volume path is explicitly set in config - * Add debug information when overriding paths with the DB - * Add path for named volumes to `podman info` - * Add volume path to default libpod.conf (and manpage) - * Validate VolumePath against DB configuration - * When location of c/storage root changes, set VolumePath - * docs: cross-reference `podman-{generate,play}-kube` - * README: refine "Out of scope" section - * oci: improve error message when the OCI runtime is not found - * Label CRIU log files correctly - * Add num_locks to the default libpod config - * podman-remote pod pause|unpause|restart - * podman: fix ro bind mounts if no* opts are on the source - * Change exit code to 1 on podman rmi nosuch image - * README.md: rephrase Buildah description - * README: update "out of scope" section - * Change exit code to 1 on podman rm nosuch container - * podman-remote create|ps - * remove duplicate commands in main - * issue template: run `podman info --debug` - * Fix play to show up in podman help - * Switch defaults for podman build versus buildah - * In shared networkNS /etc/resolv.conf&/etc/hosts should be shared - * Allow dns settings with --net=host - * Fix up handling of user defined network namespaces - * Enable more podman-remote pod commands - * tests, rootless: use relative path for export test - * rootless: force same cwd when re-execing - * Vendor Buildah v1.7 - * Exit with errors not just logging error - * cmd: support rootless mode for cp command - * hide --latest on the remote-client - * Improve command line validation - * make remote-client error messaging more robust - * podman: --runtime has higher priority on runtime_path - * podman-remote pod inspect|exists - * Cirrus: Install Go 1.11 on Ubuntu VMs - * Cirrus: Add 20m extra timeout for Ubuntu - * Introduce how to start to hack on libpod. - * update: remove duplicate newline - * Fix typo in comment - * podman-remote load image - * Do not make renumber shut down the runtime - * Add podman system renumber command - * Add ability to get a runtime that renumbers - * Recreate SHM locks when renumbering on count mismatch - * Move RenumberLocks into runtime init - * Remove locks from volumes - * Expand renumber to also renumber pod locks - * Add ability to rewrite pod configs in the database - * Add initial version of renumber backend - * Add a function for overwriting container config - * enable podman-remote pod rm - * vendor containers/image v1.4 - * Adjust LISTEN_PID for reexec in varlink mode - * Update c/storage vendor to v1.10 release - * add newline to images output - * podman-remote save [image] - * hack/tree_status.sh: preserve new lines - * remove duplicate kill from `podman --help` - * iopodman.SearchImages: add ImageSearchFilter to Varlink API - * image.SearchImages: use SearchFilter type - * SearchImages: extend API with filter parameter - * podman-search: refactor code to libpod/image/search.go - * podman-search: run in parallel - * Ensure that userns is created for stopped rootless pods - * Podman pod create now errors on receiving CLI args - * podman-remote pull - * Don't start running dependencies - * Fifth chunk of Cobra Examples - * Add 4th chunk of Cobra Examples - * OpenTracing support added to start, stop, run, create, pull, and ps - * packer: Make Makefile host arch sensitive - * Add 3rd chunk of Cobra examples - * pod infra container is started before a container in a pod is run, started, or attached. - * Add registry name to fields returned by varlink image search - * Second chunk of Cobra help - * podman: honor --storage-opt again - * docs: mention the new OCI runtime configuration - * libpod: honor runtime_path from libpod.conf - * rootless: open the correct file - * Fix `podman login` lying problem - * Fix error code retrieval for podman start --attach - * Enable --rm with --detach - * Add examples for Cobra - * Add tlsVerify bool to SearchImage for varlink - * Fix volume handling in podman - * enable podman-remote volume prune - * add build to main and as subcommand to image - * --password-stdin flag in `podman login` - * 'podman cp' copy between host and container - * podman-remote build - * Vendor in latest c/storage and c/image - * show container ports of network namespace - * podman-remote volume inspect|ls - * build varlink without GOPATH - * completions: add --pod to run/create - * Parse fq name correctly for images - * Try disabling --rm on notify_socket test - * podman-remote push - * get_ci_vm : allow running without sudo - * Only build varlink when buildtag is available - * Remove a lot of '--rm' options from unit tests - * Address review comments on #2319 - * Retain a copy of container exit file on cleanup - * Fix manual detach from containers to not wait for exit - * varlink: Rename `SearchImage` to `SearchImages` - * varlink: Rename `ContainerInList` to `Container` - * varlink: Rename `ImageInList` to `Image` - * varlink: Simplify GetVersion() call - * varlink: Return all times in RFC 3339 format - * Makefile: Don't include quotes around GIT_COMMIT - * varlink: Remove the Ping() method - * podman: Show error when creating varlink listener failed - * varlink: Remove `NotImplemented` type - * Don't show global flags except for podman command - * podman-remote volume rm - * Remove urfave/cli from libpod - * podman-remote volume create - * Separate remote and local commands - * lock and sync container before checking mountpoint - * oci: do not set XDG_RUNTIME_DIR twice - * pod: drop not valid check for rootless - * Podman pod stats -- fix GO template output - * Add troubleshooting information about running a rootless containers. - * Add --all-tags to pull command - * Add common_test.go to single test instructions - * Remove container from storage on --force - * do not crash when displaying dangling images - * Add volume mounts to PS output - * Update image-trust man with further comments - * Migrate to cobra CLI - * Remove some dead type declarations - * Fix down/missing registry.access.redhat.com - * cleanup: use the correct runtime - * make vendor: always check for latest vndr - * install.md: add section about vendoring - * Add varlink generate to the make documentation - * Mention OSes that pass the build - * Generate make helping message dynamicaly. - * Makefile: minor fix to reenable system tests - * Add StartPeriod to cmd/podman/docker.HealthConfig - * Unconditionally refresh storage options from config - * rootless: do not override /dev/pts if not needed - * Fix handling of memory limits via varlink - * Add documentation on running systemd on SELinux systems - * Cirrus: add vendor_check_task - * cleanup vendor directory - * Revert "Vendor containers/buildah" - * e2e tests: sigproxy: fix rare hang condition - * Preserve exited state across reboot - * Apply 50min timeout to integration tests - * Capatilize all usage and descriptions - * Add podman system prune and info commands - * podman-remote import|export - * tests: allow to override the OCI runtime - * rootless: copy some settings from the global configuration - * Vendor containers/buildah - * Increase e2e info/json test exit timeout - * Touch up image-trust man - * Rework Podman description - * vendor latest containers/image - * Reduce Dockerfile based build time for libpod. - * libpod/image: Use RepoDigests() in Inspect() - * add Pod Manager References - * Add support for short option -f in podman version - * Add support for short option -s in podman inspect - * Add support for short option -f - * Changes to container runlabel for toolbox project - * Fix regression in ps with custom format - * Set SELinux type on bin/podman after install - * Cirrus: Add RHEL-7 testing - * For consistency in usage output the verbs changed from 3rd person to 1st person. - * podman image prune -- implement all flag - * Alter varlink API for ListContainerMounts to return a map - * Make --quiet work in podman create/run - * apparmor: don't load default profile in rootless mode - * Cirrus: Enable AppArmor build and test - * Update ArchLinux installation instructions - * tutorials: describe how to use podman in updates-testing - * [skip ci] Cirrus: Container for tracking image use - * Cirrus: Use freshly built images - * remove sudo - * Vendor in latest containers/storage - * Show a better error message when podman info fails during a refresh - * enable podman-remote version - * Update transfer.md and commands.md to add missing commands. - * rootless: support port redirection from the host - * Mask unimplemeted commands for remote client - * Vendor in latest opencontainers/selinux - * podman-remote inspect - * Vendor in latest containers/storage - * rootless: fix --pid=host without --privileged - * Do not unmarshal into c.config.Spec - * podman-inspect: don't ignore errors - * Add openSUSE Kubic to install.md - * cirrus: Record start/end time of important things - * Cirrus: Consolidate VM image names in once place - * Update README for v1.0.0 - * Installing podman - * Ensure that wait exits on state transition - * Vendor in containers/storage - * Add --latest and --all to podman mount/umount - * Cleanup coverity scan issues - * Embed runtime struct in super localRuntime - * Collaberative podman-remote container exists - * Fix up `image sign` in PR 2108 - * add support for podman-remote history - * Rename localRuntime to runtime in cmd/podman - * podman remote integrations tests - * podman remote client -- add rmi - * Run integrations test with remote-client - * [skip ci] Hack: Fix get_ci_vm.sh w/ gcloud ssh/scp - * Update master branch with v1.0 changes from 1.0 branch - * Add local storage.conf example to troubleshoot - * config: store the runtime used to create each container - * oci: allow to define multiple OCI runtimes - * libpod: allow multiple oci runtimes - * Remove imageParts.{isTagged,registry,name,tag} - * Clarify comments about isRegistry a bit. - * Use imageParts.unnormalizedRef in GetImageBaseName - * FIXME? Introduce imageParts.suspiciousRefNameTagValuesForSearch - * Use imageParts.referenceWithRegistry in Image.getLocalImage - * Don't try to look up local images with an explicit :latest suffix - * Return a reference.Named from normalizedTag - * Use reference.TagNameOnly instead of manually adding imageParts.tag in normalizeTag - * Use imageParts.normalizedReference in normalizeTag - * Add imageParts.normalizedReference() - * Use imageparts.referenceWithRegistry in normalizeTag - * Remove no longer used imageParts.assemble() - * Use getPullRefPair / getSinglePullRefPairGoal in pullGoalFromPossiblyUnqualifiedName - * Use imageParts.referenceWithRegistry in pullGoalFromPossiblyUnqualifiedName - * Use imageParts.referenceWithRegistry in getPullRefPair - * Add imageParts.referenceWithRegistry - * Don't use imageParts.assemble when pulling from a qualified name - * Reorganize normalizeTag - * Simplify pullGoalFromPossiblyUnqualifiedName - * Remove imageParts.transport - * Simplify pullGoalFromPossiblyUnqualifiedName - * Inline imageParts.assembleWithTransport into callers - * Record the original reference.Named in imageParts - * Drop image.DecomposeString, make image.Parts private imageParts again - * Don't call image.DecomposeString in imageInListToContainerImage - * Add bridge support, for the varlink connection - * Add troubleshooting statement for homedirs mounted noexec - * Set default storage options from mounts.conf file. - * podman play kube: add containers to pod - * Add darwin support for remote-client - * vendor: update everything - * vendor make target - * rootless: create the userns immediately when creating a new pod - * rootless: join both userns and mount namespace with --pod - * spec: add nosuid,noexec,nodev to ro bind mount - * Use multi-arch images in test case scripts - * Add varlink support for prune - * Replace tab with spaces in MarshalIndent in libpod - * Remove one more usage of encoding/json in libpod - * Update vendor.conf for jsoniter vendor changes - * Move all libpod/ JSON references over to jsoniter - * Update json-iterator vendor to v1.1.5 - * Remove easyjson in preparation for switch to jsoniter - * remote-client support for images - * Move python code from contrib to it's own repo python-podman - * Use defaults if paths are not specified in storage.conf - * (Minor) Cirrus: Print timestamp at start - * fix up sigstore path - * Trivial readme updates - * podman: bump RLIMIT_NOFILE also without CAP_SYS_RESOURCE - * Fix handling of nil volumes - * sign: make all error messages lowercase - * sign: use filepath.Join instead of fmt.Sprintf - * createconfig: always cleanup a rootless container - * Fix 'image trust' from PR1899 - * libpod/image: Use ParseNormalizedNamed in RepoDigests - * apparmor: apply default profile at container initialization - * Fix up image sign and trust - * If you fail to open shm lock then attempt to create it - * List the long variant of each option before its shorter counterpart - * Use existing interface to request IP address during restore - * Added checkpoint/restore test for same IP - * Enable checkpoint test with established TCP connections - * .github/ISSUE_TEMPLATE: Suggest '/kind bug' and '/kind feature' - * pkg/hooks/exec: Include failed command in hook errors - * hooks/exec/runtimeconfigfilter: Log config changes - * hooks: Add pre-create hooks for runtime-config manipulation - * Add Validate completions - * Add a --workdir option to 'podman exec' - * Default --sig-proxy to true for 'podman start --attach' - * Test that 'podman start --sig-proxy' does not work without --attach - * [WIP]Support podman image sign - * vendor latest buildah - * Honor image environment variables with exec - * Minor: Remove redundant basename command in ooe.sh - * Rename libpod.Config back to ContainerConfig - * Add ability to build golang remote client - * vendor latest buildah - * Add the configuration file used to setup storage to podman info - * Address lingering review comments from SHM locking PR - * podman: set umask to 022 - * podman-login: adhere to user input - * Vendor in latest containers/buildah code - * Rootless with shmlocks was not working. - * Readd Python testing - * Update vendor of runc - * [skip ci] Docs: Add Bot Interactions section - * container runlabel NAME implementation - * Bump time for build_each_commit step - * Move lock init after tmp dir is populated properly - * DO NOT MERGE temporarily remove python tests - * When refreshing libpod, if SHM locks exist, remove them - * Ensure different error messages and creating/opening locks - * Update unit tests to use in-memory lock manager - * Remove runtime lockDir and add in-memory lock manager - * Convert pods to SHM locks - * Convert containers to SHM locking - * Add lock manager to libpod runtime - * Move to POSIX mutexes for SHM locks - * Disable lint on SHMLock struct - * Refactor locks package to build on non-Linux - * Add an SHM-backed Lock Manager implementation - * Add interface for libpod multiprocess locks - * Improve documentation and unit tests for SHM locks - * Propogate error codes from SHM lock creation and open - * Add mutex invariant to SHM semaphores. - * Initial skeleton of in-memory locks - * add container-init support - * If local storage file exists, then use it rather then defaults. - * vendor in new containers/storage - * Fix completions - * Touch up some troubleshooting nits - * Warn on overriding user-specified storage driver w/ DB - * Log container command before starting the container - * Use sprintf to generate port numbers while committing - * Add troubleshooting for sparse files - * Fix handling of symbolic links - * podman build is not using the default oci-runtime - * Re-enable checkpoint/restore CI tests on Fedora - * Fixes to handle /dev/shm correctly. - * rootless tests using stop is more reliable - * Allow alias for list, ls, ps to work - * Refactor: use idtools.ParseIDMap instead of bundling own version - * cirrus: Use updated images including new crui - * Switch all referencs to image.ContainerConfig to image.Config - * Allow users to specify a directory for additonal devices - * Change all 'can not' to 'cannot' for proper usage - * Invalid index for array - * Vendor in latest psgo code to fix race conditions - * test: add test for rootless export - * export: fix usage with rootless containers - * rootless: add function to join user and mount namespace - * libpod: always store the conmon pid file - * Use existing CRIU packages in CI setup - * skip test for blkio.weight when kernel does not support it - * Add Play - * Cirrus: Skip build all commits test on master - * prepare for move to validate on 1.11 only - * [skip ci] Gate: Update docs w/ safer local command - * Support podman image trust command - * Makefile: validate that each commit can at least build - * perf test a stress test to profile CPU load of podman - * all flakes must die - * Add information on --restart - * generate service object inline - * Cirrus: One IRC notice only - * docs/tutorials: add a basic network config - * display proper error when rmi -fa with infra containers - * add --get-login command to podman-login. - * Show image only once with images -q - * Add script to create CI VMs for debugging - * Cirrus: Migrate PAPR testing of F28 to Cirrus - * Skip checkpoint tests on Fedora <30 - * Cirrus: Add text editors to cache-images - * Bump gitvalidation epoch - * Bump to v0.12.2-dev - * Clean up some existing varlink endpoints - * mount: allow mount only when using vfs - -- Changelog for v1.0.0 (2018-1-11) - * Update release notes for v1.0 - * Remove clientintegration from Makefile - * Regenerate EasyJSON to fix JSON issues - * Update gitvalidation to avoid reverts w/o signoffs - * Cirrus: Post-Merge Testing for v1.0 Branch - * Move python code from contrib to it's own repo python-podman - * Use defaults if paths are not specified in storage.conf - * (Minor) Cirrus: Print timestamp at start - * fix up sigstore path - * Trivial readme updates - * podman: bump RLIMIT_NOFILE also without CAP_SYS_RESOURCE - * Fix handling of nil volumes - * sign: make all error messages lowercase - * sign: use filepath.Join instead of fmt.Sprintf - * createconfig: always cleanup a rootless container - * Fix 'image trust' from PR1899 - * libpod/image: Use ParseNormalizedNamed in RepoDigests - * apparmor: apply default profile at container initialization - * Fix up image sign and trust - * List the long variant of each option before its shorter counterpart - * Use existing interface to request IP address during restore - * Added checkpoint/restore test for same IP - * Enable checkpoint test with established TCP connections - * .github/ISSUE_TEMPLATE: Suggest '/kind bug' and '/kind feature' - * pkg/hooks/exec: Include failed command in hook errors - * hooks/exec/runtimeconfigfilter: Log config changes - * hooks: Add pre-create hooks for runtime-config manipulation - * Add Validate completions - * Add a --workdir option to 'podman exec' - * Default --sig-proxy to true for 'podman start --attach' - * Test that 'podman start --sig-proxy' does not work without --attach - * [WIP]Support podman image sign - * vendor latest buildah - * Honor image environment variables with exec - * Minor: Remove redundant basename command in ooe.sh - * Rename libpod.Config back to ContainerConfig - * Add ability to build golang remote client - * vendor latest buildah - * Add the configuration file used to setup storage to podman info - * podman: set umask to 022 - * podman-login: adhere to user input - * Vendor in latest containers/buildah code - * Readd Python testing - * Update vendor of runc - * [skip ci] Docs: Add Bot Interactions section - * container runlabel NAME implementation - * Bump time for build_each_commit step - * add container-init support - * If local storage file exists, then use it rather then defaults. - * vendor in new containers/storage - * Fix completions - * Touch up some troubleshooting nits - * Log container command before starting the container - * Use sprintf to generate port numbers while committing - * Add troubleshooting for sparse files - * Fix handling of symbolic links - * podman build is not using the default oci-runtime - * Re-enable checkpoint/restore CI tests on Fedora - * Fixes to handle /dev/shm correctly. - * rootless tests using stop is more reliable - * Allow alias for list, ls, ps to work - * Refactor: use idtools.ParseIDMap instead of bundling own version - * cirrus: Use updated images including new crui - * Switch all referencs to image.ContainerConfig to image.Config - * Allow users to specify a directory for additonal devices - * Change all 'can not' to 'cannot' for proper usage - * Invalid index for array - * Vendor in latest psgo code to fix race conditions - * test: add test for rootless export - * export: fix usage with rootless containers - * rootless: add function to join user and mount namespace - * libpod: always store the conmon pid file - * Use existing CRIU packages in CI setup - * skip test for blkio.weight when kernel does not support it - * Add Play - * Cirrus: Skip build all commits test on master - * prepare for move to validate on 1.11 only - * [skip ci] Gate: Update docs w/ safer local command - * Support podman image trust command - * Makefile: validate that each commit can at least build - * perf test a stress test to profile CPU load of podman - * all flakes must die - * Add information on --restart - * generate service object inline - * Cirrus: One IRC notice only - * docs/tutorials: add a basic network config - * display proper error when rmi -fa with infra containers - * add --get-login command to podman-login. - * Show image only once with images -q - * Add script to create CI VMs for debugging - * Cirrus: Migrate PAPR testing of F28 to Cirrus - * Skip checkpoint tests on Fedora <30 - * Cirrus: Add text editors to cache-images - * Clean up some existing varlink endpoints - * mount: allow mount only when using vfs - -- Changelog for v0.12.1.2 (2018-12-13) - * Add release notes for 0.12.1.2 - * runlabel should sub podman for docker|/usr/bin/docker - * condition fixed for adding volume to boltdb. - * e2e: add tests for systemd - * Add test for sharing resolv and hosts with netns - * Makefile tweaks to fix make shell - * failed containers with --rm should remove themselves - * Fix documentation links and flow - * Set Socket label for contianer - * Containers sharing a netns should share resolv/hosts - * Prevent a second lookup of user for image volumes - * fix typo in kubernetes - * No need to use `-i` in go build (with go 1.10 and above) - * rootless: fix restart when using fuse-overlayfs - * Cirrus: Update base-image build docs - * Add capabilities to generate kube - * disable F29 tests on PAPR - * Ensure storage options are properly initialized - * add more example usage to varlink endpoints - * Update for API change - * Vendor buildah after merging mtrmac/blob-info-caching-on-top-of-contents-caching - * Vendor c/image after merging c/image#536 - * Bump gitvalidation epoch - * Bump to v0.12.2-dev - -- Changelog for v0.12.1.1 (2018-12-07) - * Update release notes for v0.12.1.1 - * Fix errors where OCI hooks directory does not exist - * add timeout to pod stop - * Remove manual handling of insecure registries in (podman search) - * Fix reporting the registries.conf path on error - * Remove manual handling of insecure registries in doPullImage - * Remove the forceSecure parameter on the pull call stack - * Remove manual handling of insecure registries in PushImageToReference - * Factor out the registries.conf location code in pkg/registries - * Remove the forceSecure parameter of Image.PushImageTo* - * Minimally update for the DockerInsecureSkipTLSVerify type change - * Bump gitvalidation epoch - * Bump to v0.12.2-dev - * Fix build on non-Linux - * Remove some unused data structures and code - * Vendor buildah after merging https://github.com/containers/buildah/pull/1214 - * Update containers/image to 63a1cbdc5e6537056695cf0d627c0a33b334df53 - * Cirrus: Document and codify base-image production - * Cirrus: Use Makefile for image-building - * Refactor BooleanAction to mimic golang interface - * generate kube - -- Changelog for v0.12.1 (2018-12-06) - * Update release notes for 0.12.1 - * bind mount /etc/resolv.conf|hosts in pods - * Remove --sync flag from `podman rm` - * Add locking to Sync() on containers - * Add --sync flag to podman ps - * Add --sync option to podman rm - * Tests for podman volume commands - * Add "podman volume" command - * tutorial: add ostree dependency - * Pick registry to login from full image name as well - * Add ability to prune containers and images - * Invert tlsverify default in API - * set .54 version for f28 due to memory error - * Vendor in latest containers/storage - * pkg/lookup: Return ID-only pointers on ErrNo*Entries - * test for rmi with children - * libpod/container_internal_linux: Allow gids that aren't in the group file - * Don't initialize CNI when running as rootless - * correct algorithm for deleting all images - * Use runtime lockDir in BoltDB state - * test: update runc again - * vendor: update containers/storage - * create pod on the fly - * libpod/container_internal: Deprecate implicit hook directories - * Revert changes to GetDefaultStoreOptions - * Fix libpod static dir selection when graphroot changed - * podman pod exists - * Adding more varlink endpoints - * Ensure directory where we will make database exists - * Fix typo - * rootless: raise error if newuidmap/newgidmap are not installed - * Add better descriptions for validation errors in DB - * Fix gofmt and lint - * Make locks dir in unit tests - * Do not initialize locks dir in BoltDB - * Move rootless storage config into libpod - * Set default paths from DB if not explicitly overridden - * Add a struct indicating if some Runtime fields were set - * Make DB config validation an explicit step - * Move DB configuration up in runtime setup - * Add ability to retrieve runtime configuration from DB - * Add short-option handling to logs - * tests: always install runc on Ubuntu - * cirrus: update ubuntu image - * cirrus: make apt noninteractive - * Dockerfile, .cirrus.yml: update runc commit - * rootless: propagate XDG_RUNTIME_DIR to the OCI runtime - * Update ubuntu VM image w/ newer runc - * add pod short option to ps - * Add create test with --mount flag - * Only include container SizeRootFs when requested - * /dev/shm should be mounted even in rootless mode. - * disable checkpoint tests on f29 - * test, rootless: specify USER env variable - * Revert "downgrade runc due a rootless bug" - * Fix completions to work with podman run command - * hide kube command for now - * pypod create/run: ignore args for container command - * Add support for --all in pypodman ps command - * Fixes #1867 - * tests: fix NOTIFY_SOCKET test - * Fix golang formatting issues - * oci: propagate NOTIFY_SOCKET on runtime start - * test: fix test for NOTIFY_SOCKET - * Add test to ensure stopping a stopped container works - * Stopping a stopped container is not an error for Podman - * Disable mount options when running --privileged - * Vendor in latest containers/storage - * util: use fsnotify to wait for file - * vendor: update selinux - * rootless: store only subset of storage.conf - * rootless: fix cleanup - * network: allow slirp4netns mode also for root containers - * Added more checkpoint/restore test cases - * Fix podman container restore -a - * Update bash completion for checkpoint/restore - * Add '--tcp-established' to checkpoint/restore man page - * Added tcp-established to checkpoint/restore - * Remove unused CRIU_COMMIT variable - * Point CRIU_COMMIT to CRIU release 3.11 - * Updated CRIO_COMMIT to pull in new conmon for CRIU - * Use also a struct to pass options to Restore() - * _split_token(): handle None - * Use host's resolv.conf if no network namespace enabled - * rootless: add new netmode "slirp4netns" - * tests: change return type for PodmanAsUser to PodmanTestIntegration - * test: cleanup CNI network used by the tests - * exec: don't wait for pidfile when the runtime exited - * Remove mount options relatime from podman run --mount with shared - * Update test case name to podman run with --mount flag - * Add some tests for --ip flag with run and create command - * Add history and namespaceoptions to image inspect - * add podman container|image exists - * set root propagation based on volume properties - * Actually set version for podman module / pypodman - * implement --format for version command - * podman_tutorial.md typos: arguement -> argument; missing 'a' - * Load NAT modules to fix tests involving CRIU - * Vendor in latest containers/buildah - * Update checkpoint/restore man pages - * Added option to keep containers running after checkpointing - * Use a struct to pass options to Checkpoint() - * exec: always make explicit the tty value - * Allow users to expose ports from the pod to the host - * Improve speed of containers.list() - * output libpod container to kubernetes yaml - * rootless: create empty mounts.conf if it doesn't exist - * registries: check user registries file only in rootless mode - * rootless: create storage.conf when it doesn't exist - * rootless: create libpod.conf when it doesn't exist - * Don't use $HOST and $USER variables for remote - * Implement pypodman start command - * runlabel: use shlex for splitting commands - * Add a rule to compile system test in Makefile - * Fix no-new-privileges test - * The system test write with ginkgo - * Separate common used test functions and structs to test/utils - * Add version command to pypodman - * Bump gitvalidation epoch - * Bump to v0.11.2-dev - * Cirrus: Add documentation for system-testing - * Cirrus: Simplify optional system-test script - * Cirrus: Reveal magic, parallel system-testing - * libpod should know if the network is disabled - * Lint: Silence few given goconst lint warnings - * Lint: Extract constant unknownPackage - * Lint: Tests: add missing assertions - * Lint: Do not ignore errors from docker run command when selinux enabled - * Lint: InspectImage varlink api should return errors that occurred - * Lint: Exclude autogenerated files from lint test - * Lint: Update metalinter dependency - * Set --force-rm for podman build to true by default - * Vendor in latest containers/storage - -- Changelog for v0.11.1.1 (2018-11-15) - * Vendor in containers/storage - * Add release notes for 0.11.1.1 - * Increase pidWaitTimeout to 60s - * Cirrus: Add master branch testing status badge - * rootless: call IsRootless just once - * Bump golang to v1.10 in install.md - * Standardized container image for gofmt and lint - * Make list of approvers same as reviewers - * vendor: update ostree-go - * vendor.conf: fix typo - * Cleanup podman spec to not show git checkout is dirty - * Add space between num & unit in images output - * Update troubleshooting guide to deal with rootless path - * troubleshooting.md: add a recipe for rootless ping - * remove $-prefix from (most) shell examples - * docs: Fix duplicated entry for pod-container-unmount - * Better document rootless containers - * info: add rootless field - * Accurately update state if prepare() partially fails - * Do not hide errors when creating container with UserNSRoot - * rm -f now removes a paused container - * correct assignment of networkStatus - * podman_tutorial: cni build path has changed - * Bump gitvalidation epoch - * Bump to v0.11.2-dev - * Cirrus: Ignore any error from the IRC messenger - * rootless: default to fuse-overlayfs when available - -- Changelog for v0.11.1 (2018-11-08) - * Update release notes for 0.11.1 - * update seccomp.json - * Touch up --log* options and daemons in man pages - * Fix run --hostname test that started failing post-merge - * move defer'd function declaration ahead of prepare error return - * Don't fail if /etc/passwd or /etc/group does not exists - * Print error status code if we fail to parse it - * Properly set Running state when starting containers - * Fix misspelling - * Retrieve container PID from conmon - * If a container ceases to exist in runc, set exit status - * EXPERIMENTAL: Do not call out to runc for sync - * Actually save changes from post-stop sync - * rootless: mount /sys/fs/cgroup/systemd from the host - * rootless: don't bind mount /sys/fs/cgroup/systemd in systemd mode - * Add hostname to /etc/hosts - * Temporarily fix the Python tests to fix some PRs - * Remove conmon cgroup before pod cgroup for cgroupfs - * Fix cleanup for "Pause a bunch of running containers" - * --interactive shall keep STDIN attached even when not explicitly called out - * Do never override podman with docker - * Make kill, pause, and unpause parallel. - * Fix long image name handling - * Make restart parallel and add --all - * Add ChangeAction to parse sub-options from --change - * replace quay.io/baude to quay.io/libpod - * Change humanize to use MB vs MiB. - * allow ppc64le to pass libpod integration tests - * Cirrus-CI: Add option to run system-tests - * Cirrus: Skip rebuilding images unless instructed - * Cirrus: Disable image build job abort on push - * Cirrus: Add a readme - * Ubuntu VM image build: try update twice - * Cirrus: Enable updating F28 image - * rootless: do not add an additional /run to runroot - * rootless: avoid hang on failed slirp4netns - * Fix setting of version information - * runtime: do not allow runroot longer than 50 characters - * attach: fix attach when cuid is too long - * truncate command output in ps by default - * Update the runc commit used for testing - * make various changes to ps output - * Sync default config with libpod.conf - * Use two spaces to pad PS fields - * unmount: fix error logic - * get user and group information using securejoin and runc's user library - * CONTRIBUTING.md: add section about describing changes - * Change to exported name in ParseDevice - * Vendor in latest containers/storage - * fix bug in rm -fa parallel deletes - * Ensure test container in running state - * Add tests for selinux labels - * Add --max-workers and heuristics for parallel operations - * Increase security and performance when looking up groups - * run prepare in parallel - * downgrade runc due a rootless bug - * runlabel: run any command - * Eat our own dogfood - * vendor: update containers/storage - * Add support for /usr/local installation - * create: fix writing cidfile when using rootless - * Explain the device format in man pages - * read conmon output and convert to json in two steps - * Cirrus: Use images w/ buildah fix - * Add --all and --latest to checkpoint/restore - * Use the newly added getAllOrLatestContainers() function - * Use the new checkAllAndLatest() function - * Also factor out getAllOrLatestContainers() function - * Add checkAllAndLatest() function - * Downgrade code to support python3.4 - * Allow containers/storage to handle on SELinux labeling - * Use more reliable check for rootless for firewall init - * Vendor in latest containers/storage opencontainers/selinux - * Make podman ps fast - * Support auth file environment variable in podman build - * fix environment variable parsing - * tests: use existing CRIU version check - * Use the CRIU version check in checkpoint/restore - * Add helper function to read out CRIU version - * vendor in go-criu and dependencies - * oci: cleanup process status - * Handle http/https in registry given to login/out - * re-enable f29 testing - * correct stats err with non-running containers - * Use restoreArtifacts to save time in integration tests - * Make rm faster - * Fix man page to show info on storage - * Move rootless directory handling to the libpod/pkg/util directory - * Fix podman port -l - * Fix trivial missing markup in manpage - * Cirrus: Install CRIU in test images - * Cirrus: Use different CNI_COMMIT for Fedora - * Fix Cirrus/Packer VM image building - * Revert "Cirrus: Enable debugging delay on non-zero exit" - * Cirrus: IRC message when cirrus testing successful - * cirrus: Add simple IRC messenger - * fix NOTIFY_SOCKET in e2e testfix NOTIFY_SOCKET in e2e tests - * Bump gitvalidation epoch - * Bump to v0.10.2-dev - -- Changelog for v0.10.1.3 (2018-10-17) - * Update release notes for 0.10.1.3 - * Vendor in new new buildah/ci - * Fix podman in podman - * Bump gitvalidation epoch - * Bump to v0.10.2-dev - -- Changelog for v0.10.1.2 (2018-10-17) - * Update release notes for 0.10.1.2 - * Fix CGroup paths used for systemd CGroup mount - * Bump gitvalidation epoch - * Bump to v0.10.2-dev - -- Changelog for v0.10.1.1 (2018-10-16) - * Update release notes for 0.10.1.1 - * Mount proper cgroup for systemd to manage inside of the container. - * Cirrus: Enable debugging delay on non-zero exit - * Touchup fileo typo - * volume: resolve symlinks in paths - * volume: write the correct ID of the container in error messages - * vendor: update containers/buildah - * papr: relabel GOPATH/github.com/containers/podman - * tests: do not fail in the cleanup phase - * tests: do not make assumptions on the mount output - * papr_prepare: remove double process for starting up .papr.sh - * Add support for pod commands - * Support auth file environment variable & add change to man pages - * Generate a passwd file for users not in container - * Bump gitvalidation epoch - * Bump to v0.10.2-dev - -- Changelog for v0.10.1 (2018-10-11) - * Swap from map to channels for podman stop workers - * Add release notes for 0.10.1 - * Pass along syslog variable to podman cleanup processes - * Sort all command flags - * rootless: detect when user namespaces are not enabled - * Log an otherwise ignored error from joining a net ns - * Fix gofmt - * Add tests for --ip flag - * Update manpages for --ip flag - * Add --ip flag and plumbing into libpod - * Document --net as an alias of --network in podman run & create - * Update OCICNI vendor to 2d2983e4 - * Temporary commit to swap branches - * rootless: report more error messages from the startup phase - * rootless: fix an hang on older versions of setresuid/setresgid - * Update OCICNI vendor to e617a611 - * fix runlabel functions based on QA feedback - * Vendor latest containers/image - * Stop containers in parallel fashion - * wip - * remove hack/dind - * Vendor in latest github.com/containers/storage,image, buildah - * runlabel: execute /proc/self/exe and avoid recursion - * Re-add source-verify in cirrus-ci - * added links to buildah.io and podman.io to README.md - * Lower CPU/Memory usage by cirrus VMs - * skip userns tests on non-fedora distributions for now - * Remove Travis - * docker: Double quote array expansions to avoid re-splitting elements - * Ensure resolv.conf has the right label and path - * Remove no longer used libnetwork from vendor.conf - * Fix lint - * Drop libnetwork vendor and move the code into pkg/ - * Update libnetwork vendor to current master to fix CI - * Switch to using libnetwork's resolvconf package - * Add configuration for Cirrus-CI - * disable gce building of images - * re-add BR for golang compiler to contrib/spec/podman.spec.in - * completions: add checkpoint/restore completions - * tests: add checkpoint/restore test - * tutorial: add checkpoint/restore to tutorial - * docs: add checkpoint and restore man pages - * Add support to checkpoint/restore containers - * oci: split the stdout and stderr pipes - * oci: always set XDG_RUNTIME_DIR - * Fix pod status reporting for new Exited state - * Add ability for ubuntu to be tested - * selinux: drop superflous relabel - * rootless: always set XDG_RUNTIME_DIR - * Fix python tests - * Fix Wait() to allow Exited state as well as Stopped - * Fix cleanupRuntime to only save if container is valid - * Fix bug with exited state and container remove - * Address review comments and fix ps output - * Add ContainerStateExited and OCI delete() in cleanup() - * Need to allocate memory for hook struct - * Disable SELinux labeling if --privileged - * * Update documenation - * Implement pod varlink bindings - * Update docs to build a runc that works with systemd - * runtime: fix message which assumes the runtime is runc - * rootless: raise an error when trying to use cgroups - * Add --all flag to podman kill - * Add podman.io to README.md - * Vendor in the latest containers/storage, image and buildah - * Don't tmpcopyup on systemd cgroup - * Add container runlabel command - * run complex image names with short names - * Add buildah version and distribution to info - * Disable Fedora 29 and CentOS7 VM testing - * podman runs disabled containers and privileged containers as spc_t - * Update the OWNERS file so bot assigns sane reviewers - * rework CI tests to test on VMs - * Put openshift dockerfiles in test/install - * Bump gitvalidation epoch - * Bump to v0.9.4-dev - * contrib/python/*/Makefile: Fallback to unversioned 'python' - * Makefile: Drop PYTHON - * Makefile: Call contrib/python's clean regardless of HAS_PYTHON3 - -- Changelog for v0.9.3.1 (2018-09-25) - * Update release notes for 0.9.3.1 - * Disable problematic SELinux code causing runc issues - * Bump gitvalidation epoch - * Bump to v0.9.4-dev - -- Changelog for v0.9.3 (2018-09-21) - * Update release notes for 0.9.3 - * Add --mount option for `create` & `run` command - * Refactor Wait() to not require a timeout - * Updates from reviews - * Implement new subcommands - * Don't mount /dev/shm if the user told you --ipc=none - * rootless: error out if there are not enough UIDs/GIDs available - * Vendor in latest containers/buildah - * rootless: fix create with images not in the storage - * rootless: skip usage of filepath.Join - * create, rootless: join the userns of ns:PATH - * create, rootless: join the userns of container:CONTAINER - * spec: refactor ns modes to a common interface - * Don't output inodes created to run a container - * Add rpmbuild to the openshift fedora test image - * Add new field to libpod to indicate whether or not to use labelling - * Bind Mounts should be mounted read-only when in read-only mode - * test, rootless: enforce cgroupfs manager - * report when rootless - * add the gopath environment variable to the openshift dockerfile - * Vendor in latest opencontainers/runtime-tools - * Add python-varlink to the Fedora openshift image - * Add Dockerfile for openshift lint, gofmt, and validate testing - * Vendor in latest containers/buildah - * Don't crash if an image has no names - * Replace all usages of "install -D" with "install -d" - * Increase pidWaitTimeout to 1000ms - * Small updates to OCI spec generation - * Add new tests for ipc namespace sharing - * Hooks supports two directories, process default and override - * Bump gitvalidation epoch - * Bump to v0.9.3-dev - -- Changelog for v0.9.2.1 (2018-09-17) - * Update release notes for 0.9.2.1 - * Vendor in latest projectatomic/buildah - * Vndr latest containers/image - * Bump gitvalidation epoch - * Bump to v0.9.3-dev - -- Changelog for v0.9.2 (2018-09-14) - * Update release notes for 0.9.2 - * change search test to look for fedora and not fedora-minimal - * Don't mount /dev/* if user mounted /dev - * add registry information to varlink info - * libpod/image/pull: Return image-pulling errors from doPullImage - * Update gitvalidation epoch to avoid a bad commit - * Update README to reflect current development efforts - * rootless: do not raise an error if the entrypoint is specified - * Add Buildah Podman relationship to README.md - * Swap default mount propagation from private to rprivate - * Add a way to disable port reservation - * Add notes to check version on problem - * Do not set rlimits if we are rootless - * Up default Podman rlimits to avoid max open files - * Search registries with an empty query - * Vendor in latest containers/image - * Remove duplicate code between create.go and run.go - * Add --interval flag to podman wait - * Add `podman rm --volumes` flag - * Vendor in latest containers/storage to eliminage deadlock - * do not build with devicemapper - * run different cgroup tests depending on conditions - * dont make python when running make - * Explicitly set default CNI network name in libpod.conf - * Pass on securityOpts from podInfraContainer to container added to pod. - * Bump gitvalidation epoch - * Bump to v0.9.2-dev - -- Changelog for v0.9.1.1 (2018-09-10) - * Update release notes for 0.9.1.1 - * Replace existing iptables handler with firewall code - * Vendor CNI plugins firewall code - * Fix displaying size on size calculation error - * Bump gitvalidation epoch - * Bump to v0.9.2-dev - -- Changelog for v0.9.1 (2018-09-07) - * Update RELEASE_NOTES for 0.9.1 release - * Fix pod sharing for utsmode - * Respect user-added mounts over default spec mounts - * Ensure we do not overlap mounts in the spec - * Change references to cri-o to point at new repository - * fix docs for podman build - * use layer cache when building images - * Add first pass for baseline pod tests - * Change shm test to be less flaky. - * Update WaitForTimeOut to output OutputString to help with debugging. - * Fixups for baseline test script - * Fix nameing of Namespaces to be more consistent - * Start pod infra container when pod is created - * vendor containerd/cgroups - * Fix up libpod.conf man pages and referencese to it. - * Print errors from individual pull attempts - * Added GOPATH/bin to PATH install.md - * We should fail Podman with ExitCode 125 by default - * Add CRI logs parsing to podman logs - * rmi remove all not error when no images are present - * rootless: check uid with Geteuid() instead of Getuid() - * rootless, tests: add tests for the pod command - * rootless, create: support --pod - * rootless, run: support --pod - * rootless: create compatible pod infra container - * rootless: be in an userns to initialize the runtime - * commandNotFoundHandler: use stderr and exit code 1 - * Bump gitvalidation epoch - * Bump to v0.9.1-dev - * Update release notes for 0.8.5 - -- Changelog for v0.8.5 (2018-08-31) - * Add proper support for systemd inside of podman - * We are mistakenly seeing repos as registries. - * container: resolve rootfs symlinks - * Up time between checks for podman wait - * Turn on test debugging - * Add support for remote commands - * fixup A few language changes and subuid(5) - * Make the documentation of user namespace options in podman-run clearer - * pod create: restore help flag - * catch command-not-found errors - * don't print help message for usage errors - * Vendor in latest containers/storage and containers/image - * add conmon to copr spec - * docs: consistent format for example - * docs: consistent headings - * docs: make HISTORY consistent - * docs: fix headers - * varlink: fix --timeout usage - * run/create: reserve `-h` flag for hostname - * podman,varlink: inform user about --timeout 0 - * rootless: show an error when stats is used - * rootless: show an error when pause/unpause are used - * rootless: unexport GetUserNSForPid - * rootless, exec: use the new function to join the userns - * rootless: fix top - * rootless: add new function to join existing namespace - * Vendor in latest projectatomic/buildah - * Set nproc in containers unless explicitly overridden - * Do not set max open files by default if we are rootless - * Set default max open files in spec - * Resolve /etc/resolv.conf before reading - * document `--rm` semantics - * allow specification of entrypoint in the form of a slice - * Test RPM build and install for regressions - * rootless, search: do not create a new userns - * rootless, login, logout: do not create a new userns - * rootless, kill: do not create a new userns - * rootless, stop: do not create a new userns - * Ensure return errors match API docs - * Fix manpage to note how multiple filters are combined - * Fix handling of multiple filters in podman ps - * Fix Mount Propagation - * docs: add containers-mounts.conf(5) - * docs: use "containers-" prefix for registries and storage - * rootless: fix --pid=host - * rootless: fix --ipc=host - * spec: bind mount /sys only when userNS are enabled - * rootless, tests: add test for --uts=host - * Dockerfile.Fedora: install slirp4netns - * rootless: don't use kill --all - * rootless: exec handle processes that create an user namespace - * rootless: fix exec - * Move whale-says test to end of baseline - * Bump gitvalidation epoch - * Bump to v0.8.5-dev - -- Changelog for v0.8.4 (2018-08-24) - * Add release notes - * Regenerate easyjson after rebase - * Vendor easyjson code to fix build - * Swap from FFJSON to easyjson - * Make 'make clean' remove FFJSON generated code - * rootless: allow to override policy.json by the user - * add completion for --pod in run and create - * Fixed formatting and lowered verbosity of pod ps - * Do not try to enable AppArmor in rootless mode - * exposes tcp port only if no proto specified. - * rpm-spec: use skopeo-containers instead of containers-common - * Reveal information about container capabilities - * Vendor in latest projectatomic/buildah - * Refactor error checking in With*NSFromPod options - * Fixing network ns segfault - * Change pause container to infra container - * Support pause containers in varlink - * Added option to share kernel namespaces in libpod and podman - * Changed GetContainerStats to return ErrCtrStateInvalid - * Add GetPodStats to varlink - * rpm-spec: update distro-specific dependencies - * Add podman pod top - * Include pod stats and top in commands/completions - * Vendor changes to psgo - * Fix syntax description of --ulimit command - * Swap test image in exec test to fedora for useradd - * Add tests for exec --user - * Properly translate users into runc format for exec - * test: ad more tests for rootless containers - * rootless: fix --net host --privileged - * Fix a bug with hook ALWAYS matching with a process - * Fixed segfault in stats where container had netNS none or from container - * Enable pod stats with short ID and name - * Touch up cert-dir in man pages - * make dbuild fixed on ubuntu/debian - * vendor latest github.com/urfave/cli - * Add retry decorator for flakey tests - * Update error message from reviews - * Support Attach subcommand in pypodman - * Fix handling of devices - * tutorial: point to containers/skopeo - * point to containers/skopeo - * install.md: point to containers/libpod - * Bump gitvalidation epoch - * Bump to v0.8.4-dev - -- Changelog for v0.8.3 (2018-08-17) - * Make failure to retrieve individual ctrs/pods nonfatal - * Mention that systemd is the default cgroup manager - * Add dependency for python3-psutil module - * Vendor in latest buildah and imagebuilder - * Don't fail on size. - * Fix handling of socket connection refusal. - * podman: fix --uts=host - * podman pod stats - * Added helper function for libpod pod api calls - * CreatePod args now PodCreate structure - * Added reason to PodContainerError - * Change batchcontainer to shared - * Add Pod API to varlink. - * Change pod varlink API. - * Moved getPodStatus to pod API to be used in varlink - * Vendor in latest containers/psgo code - * switch projectatomic to containers - * Revert "spec: bind mount /sys only for rootless containers" - * Suport format param for varlink Commit - * Fix segfault in top when -l and no args are passed - * Document STORAGE_DRIVER and STORAGE_OPTS environment variable - * podman.spec: recommend slirp4netns - * Do not 'make all' on Travis for Linux - * Fix build on non-Linux OSes - * Create pod CGroups when using the systemd cgroup driver - * Switch systemd default CGroup parent to machine.slice - * spec: bind mount /sys only for rootless containers - * build, rootless: specify IsolationOCIRootless - * vendor: update buildah version - * Fix handling of hostname in --net=host - * Updated pod_api to reflect function spec - * Add create and pull commands - * rootless: not require userns for help/version - * pkg/apparmor: use a pipe instead of a tmp file - * pkg/apparmor: move data under Linux/apparmor buildtags - * pkg/apparmor: move all linux-code into apparmor_linux* - * podman in rootless mode will only work with cgroupfs at this point. - * when searching, survive errors for multiple registries - * Remove unused function in runtime.go - * Fix papr tests by forcing cgroupfs in CI - * Bump gitvalidation epoch - * Bump to v0.8.3-dev - -- Changelog for v0.8.2.1 (2018-08-11) - * Ensure pod inspect is locked and validity-checked - * Further fix tests - * Bump gitvalidation epoch - * Bump to v0.8.3-dev - * Fix python tests again - * Fix python tests to use cgroupfs - * Fix typo breaking tests - * Force cgroupfs for python tests - * Swap default CGroup manager to systemd - * Only use cgroupfs for containerized tests - * Temporarily force all tests to use cgroupfs driver - -- Changelog for v0.8.2 (2018-08-10) - * We need to sort mounts so that one mount does not over mount another. - * search name should include registry - * Split pod.go into 3 files - * Make errors during refresh nonfatal - * Add batch check to container stats lock - * removeContainer: fix deadlock - * Add FFJSON to build container - * Don't require .gopathok for individual FFJSON targets - * Add FFJSON generation to makefile - * Re-add FFJSON for container and pod structs - * Fixed a bug setting dependencies on the wrong container - * Always connect to the stdout and stderr of stream - * apparmor: respect "unconfined" setting - * oci.go: syslog: fix debug formatting - * add podman pod inspect - * Fix ambiguity in adding localhost to podman save - * Fix CGroupFS cgroup manager cgroup creation for pods - * Update Conmon commit for testing - * Pass newly-added --log-level flag to Conmon - * Cleanup man pages - * Improve ps handling of container start/stop time - * rootless: fix user lookup if USER= is not set - * enabled copr epel builds again - * Handle yum and dnf - * Test regressions against the RPM spec file - * Pass DESTDIR down to python Makefile - * Add dpkg support for returning oci/conmon versions - * Have info print conmon/oci runtime information - * Better pull error for fully-qualified images - * Stub varlink pod methods. - * Remove inotify work around - * Rename varlink socket and interface - * Change tarball filename in copr prepare and match short-commit length - * Add Runc and Conmon versions to Podman Version - * update copr spec, lets get it building again - * Add missing dependencies to build system - * Port to MacOS - * Make one runtime for the varlink service - * Bump gitvalidation epoch - * Bump to v0.8.2-dev - -- Changelog for v0.8.1 (2018-08-03) - * Vendor in latest github.com/projectatomic/buildah - * Update gitvalidation epoch - * Check for missing arguments in /proc/self/cmdline - * Added ps --pod option - * clarify pull error message - * rootless: do not set setgroups to deny when using newuidmap - * Man page fixes found by https://pagure.io/ManualPageScan - * Inline pullGoalNamesFromPossiblyUnqualifiedName into Runtime.pullGoalFromPossibly... - * Replace getPullRefName by Runtime.getPullRefPair - * Inline pullGoalNamesFromImageReference back into Runtime.pullGoalFromImageReference - * Introduce getSinglePullRefNameGoal - * Test Runtime.pullGoalFromPossiblyUnqualifiedName instead of pullGoalNameFrom... - * Test Runtime.pullGoalFromImageReference instead of pullGoalNamesFromImageReference - * Use REGISTRIES_CONFIG_PATH for all tests - * rootless: do not segfault if the parent already died - * RFC: Rename runtime.pullImage to runtime.pullImageFromHeuristicSource - * Introduce Runtime.pullImageFromReference, call it in Runtime.FromImageReference - * RFC: Remove unused transport name constants from libpod - * Replace Runtime.LoadFromArchive with Runtime.LoadFromArchiveReference - * Rename the "image" variable to "imageName" - * Fix the heuristic for docker-archive: sources in (podman pull) - * Split doPullImage from pullImage - * Remove the forceCompress parameter from getCopyOptions and DRO.GetSystemContext - * Remove the authFile parameter from getCopyOptions and DRO.GetSystemContext - * Remove the signaturePolicyPath parameter from getCopyOptions and DRO.GetSystemContext - * Add a *types.SystemContext parameter to getCopyOptions and DRO.GetSystemContext - * Move pullImage from Image to Runtime - * Do not re-parse the list of search registries just for an error message - * Eliminate duplicate determination whether to use search registries - * Eliminate the "DockerArchive means pull all refPairs" special case - * Introduce struct pullGoalNames - * Introduce struct pullGoal - * Use []pullRefPair instead of []*pullRefPair - * Use []pullRefName instead of []*pullRefName - * Introduce singlePullRefNameGoal - * Use an early return from refNamesFromPossiblyUnqualifiedName - * RFC: Rename Image.PushImage to Image.PushImageToHeuristicDestination - * Remove an unnecessary use of alltransports.ParseImageName - * RFC? Hard-code "format" string values instead of using libpod.* transport names - * Use PushImageToReference for (podman save) - * Call imageNameForSaveDestination while creating the references - * Exit early in the simple case in imageNameForSaveDestination - * Rename parameters of imageNameForSaveDestination - * Split imageNameForSaveDestination from saveCmd - * Split a single if statement into two. - * Move source handling before destination parsing - * Split Image.PushImageToReference from Image.PushImage - * Don't format to string and re-parse a DockerReference() - * Remove the :// end from DockerTransport - * Remove the TransportNames arrays - * Document the properties of DefaultTransport a bit better. - * Eliminate the "dest" variable. - * Use an early exit if a docker-archive: image has no repo tags - * Reorganize the tag loading in DockerArchive case - * Return early in refNamesFromImageReference instead of appending to pullNames - * Use srcRef.StringWithinTransport() instead of parsing imgName again - * Use a switch instead of if/if else/.../else - * Remove the error return value from getPullRefName - * Rename getPullListFromRef to refPairsFromImageReference - * Split refNamesFromImageReference from Runtime.getPullListFromRef - * Replace getPullRefPair with getPullRefName - * Include the rejected reference when parsing it fails in pullRefPairsFromRefNames - * Add --force to podman umount to force the unmounting of the rootfs - * Integration Test Improvements #3 - * Ensure container and pod refresh picks up a State - * Fix build on non-linux platforms - * Rework state testing to allow State structs to be empty - * Add additional comments on accessing state in API - * Do not fetch pod and ctr State on retrieval in Bolt - * network: add support for rootless network with slirp4netns - * varlink ImageRemove should always return image ID - * Add documentations on how to setup /etc/subuid and /etc/subgid - * Integration Test Improvements #2 - * avoid spewing fds do to restore of cached images - * Add load test for xz compressed images - * Speed up test results - * Show duration for each ginkgo test and test speed improvements - * vendor: update containers/storage - * Clean up pylint warnings and errors for podman - * podman rmi shouldn't delete named referenced images - * Bump gitvalidation epoch - * Bump to v0.8.1-dev - -- Changelog for v0.7.4 (2018-07-27) - * Add pod pause/unpause - * Fix up docker compatibility messages - * Update vendored version of runc,buildah,containers/image - * Refactored method of getting pods - * Fix godoc comment in pkg/netns - * Fix handling of Linux network namespaces - * Update containernetworking/plugins to current master - * Cleanup descriptions and help information - * Skip seccomp-dependent tests on non-Linux - * Use the Linux version BoltState.getContainerFromDB on all platforms. - * Split parseNetNSBoltData from BoltState.getContainerFromDB - * Use testify/require in a few places to avoid panics in tests - * Skip unit tests which require storage when not running as root - * Don't pollute the build output with failures to build checkseccomp - * Remove cmd/podman/user.go - * double papr timeout for all ci tests - * vendor latest containers/psgo - * Vendor latest container/storage to fix overlay mountopt - * Add pod kill - * Added pod restart - * Added pod.Restart() functionality to libpod. - * Add a mutex to BoltDB state to prevent lock issues - * Clear variables used to store options after parsing for every volume - * Clean up pylint warnings and errors - * podman: allow to specify the IPC namespace to join - * podman: allow to specify the UTS namespace to join - * podman: allow to specify the PID namespace to join - * podman: allow to specify the userns to join - * network: support ns: prefix to join existing namespace - * spec: allow container:NAME network mode - * Update comments in BoltDB and In-Memory states - * Add an E2E test to verify basic namespace functionality - * Add libpod namespace to config - * Add missing runtime.go lines to set namespace - * Address first round of review comments - * Set namespace for new pods/containers based on runtime - * Add --namespace flag to Podman - * Update documentation for the State interface - * Ensure pods are part of the set namespace when added - * Enforce namespace checks on container add - * Add tests for state namespacing - * Add namespaces to in memory state - * Untested implementation of namespaced BoltDB access - * Add constraint that dependencies must be in the same ns - * Add namespaces and initial constraints to database - * Add container and pod namespaces to configs - * Fix varlink API usage of psgo - * Update issue template to point build bugs at buildah - * We don't currently support --mac-address - * Vendor in latest containers/psgo code - * Update container Mounted() and Mountpoint() functions - * [WIP] Refactor and simplify python builds - * AppArmor: runtime check if it's enabled on the host - * Add format descriptors infor to podman top - * Fix error handling in pod start/stop. - * docs/podman-top: fix typo and whitespace - * Use the result of reference.Parse when checking for digests. - * Make refNamesFromPossiblyUnqualifiedName independent from Image - * Make Image.HasShaInInputName to an independent local function - * Split createNamesToPull into ref{Names,Pairs}FromPossiblyUnqualifiedName - * Rename local variables in Runtime.pullRefPairsFromRefNames - * Split Runtime.pullRefPairsFromRefNames from Image.createNamesToPull - * Rename nameToPull to pullRefName - * Rename pullStruct to pullRefPair - * Replace optional nameToPull.shaPullName with mandatory dstName - * Introduce nameToPull, move shaPullName in there - * Split normalizeTag from Image.TagImage - * Remove Image.isShortName - * Inline getTags into its only user - * Add unit tests for imageParts - * Add OnBuild and usernamespace test to baseline - * Bump gitvalidation epoch - * Bump to v0.7.4-dev - -- Changelog for v0.7.3 (2018-07-20) - * Update psgo vendor - * Podman load/tag/save prepend localhost when no repository is present - * Pod ps now uses pod.Status() - * Added pod start and stop - * rootless: support a per-user mounts.conf - * secrets: parse only one mounts configuration file - * rootless: allow a per-user registries.conf file - * pull: get registries using the registries pkg - * rootless: allow a per-user storage.conf file - * rootless, docs: document the libpod.conf file used in rootless mode - * Let containers/storage keep track of mounts - * podman-top: use containers/psgo - * Vendor in latest code for storage,image, buildah - * Abort a test on nil containers, so that future tests don't panic - * Fix TestPostDeleteHooks on macOS - * Use `...` for a regexp constant to improve readability - * oci: keep exposed ports busy and leak the fd into conmon - * Dockerfile: install nmap-ncat - * Update podman_tutorial.md - * Update transfer.md - * Add missing podman commands - * Update gitvalidation epoch - * Fix ps filter with key=value labels - * rootless: require subids to be present - * Downgrade setup.py - * Bump gitvalidation epoch - * Bump to v0.7.3-dev - -- Changelog for v0.7.2 (2018-07-13) - * Change logic for detecting conflicting flags in ps - * Update python directories to better support setup.py - * Fix ps --sort=size test - * remote python client for podman - * Only print container size JSON if --size was requested - * Don't print rootfs and rw sizes if they're empty - * Major fixes to podman ps --format=json output - * Ignore running containers in ps exit-code filters - * Record whether the container has exited - * rootless: correctly propagate the exit status from the container - * rootless: unshare mount namespace - * Need to wait for container to exit before completing run/start completes - * If proxy fails then then signal should be sent to the main process - * fix pull image that includes a sha - * Added full podman pod ps, with tests and man page - * Podman pod create/rm commands with man page and tests. - * Added created time to pod state - * Support multiple networks - * Log all output of logrus to syslog as well as stdout/stderr - * podman rmi should only untag image if parent of another - * Changed container status of Unknown from being printed as Dead to Error in Ps - * Fix podman build completions - * Refactor attach()/start() after podman changes - * create conmon sockets when getting their paths - * build: enable ostree in containers/storage when available - * podman/libpod: add default AppArmor profile - * runtime: delete unused function - * rootless: propagate errors from GetRootlessRuntimeDir() - * rootless: resolve the user home directory - * rootless: fix when argv[0] is not an absolute path - * Allow Init() on stopped containers - * urfave/cli: fix regression in short-opts parsing - * Add --volumes-from flag to podman run and create - * Vendor in latest buildah to add masks for /proc/keys and /proc/acpi - * Vendor in latest containers/storage - * Mask /proc/keys to protect information leak about keys on host - * ctime: Drop 32-/64-bit distinction on Linux - * Podman stats with no containers listed is the same as podman stats --all - * Refactor unittest for change in history API - * Bump gitvalidation epoch - * Bump to v0.7.2-dev - -- Changelog for v0.7.1 (2018-07-06) - * pkg/ctime: Factor libpod/finished* into a separate package - * Block use of /proc/acpi from inside containers - * remove buildah requirement for the libpod image library - * contrib/python/test/test_tunnel: Fix -nNT -> -nNTq - * Refactor podman/utils with a single container start and attach function - * Remove now-unneeded cleanupCgroup() for unsupported OS - * Remove per-container CGroup parents - * Fix nits and GOPATH in tutorial - * spec: Make addPrivilegedDevices and createBlockIO per-platform - * libpod/runtime_pod: Make removePod per-platform - * libpod/networking_unsupported: Remove JoinNetworkNameSpace - * .travis: Run gofmt and lint on OS X - * rootless: Merge rootless.go back into rootless_linux.go - * Makefile: Use a pattern rule for cross-compilation - * more changes to compile darwin - * Fix timeout issue with built-in volume test - * rootless: add /run/user/$UID to the lookup paths - * rootless: add function to retrieve the original UID - * rootless: always set XDG_RUNTIME_DIR - * rootless: set XDG_RUNTIME_DIR also for state and exec - * libpod/container: Replace containerState* with containerPlatformState - * urfave/cli: fix parsing of short opts - * docs: Follow man-pages(7) suggestions for SYNOPSIS - * Allow multiple mounts - * Makefile: Use 'git diff' to show gofmt changes - * Skip a test in Travis that has timeout issues - * vendor in selinux and buildah for darwin compilation - * add image user to inspect data - * changes to allow for darwin compilation - * Bump gitvalidation epoch - * Bump to v0.7.1-dev - -- Changelog for v0.6.5 (2018-06-29) - * Fix built-in volume issue with podman run/create - * Add `podman container cleanup` to CLI - * Allow multiple containers and all for umount - * Returning joining namespace error should not be fatal - * Start using github.com/seccomp/containers-golang - * Test to verify overlay quotas work, show container overhead on quota - * conmon no longer writes to syslog - * Fix broken f28/cloud instance - * Vendor latest projectatomic/buildah - * vendor in latest golang/x/sys - * vendor in latest docker package - * Remove the --registry flag from podman search - * utils: fix endless write of resize event - * Start prints UUID or container name that user inputs on success - * cmd/podman/utils.go: Cancel-able resize writes - * Fix podman hangs when detecting startup error in container attached mode - * travis: bump go to 1.9.x and 1.10.x - * podman-build --help: update description - * *: Replace Generator.Spec() with Generator.Config - * generator.New() requires an OS string input variable - * Vendor in latest buildah - * Vendor in latest runtime-tools - * docs: add documentation for rootless containers - * runtime: change rootless data storage default path - * rootless: use $HOME/.config/containers/libpod.conf - * test: add env variables to the debug output - * rootless: do not configure additional groups - * oci: set XDG_RUNTIME_DIR to the runtime from GetRootlessRuntimeDir() - * rootless: add management for the userNS - * container_internal: don't ignore error from cleanupNetwork() - * Mark containers invalid earlier during removal - * Add --authfile to podman search - * Vendor in latest containers/image - * add podman remote client - * Vendor in go-selinux again - * Update the vendoring of github.com/opencontainers/selinux - * Containers can dissappear - * Add podman-image and podman-container man page links - * Update ocicni vendor to pick up bugfixes - * make varlink optional for podman - * Bump gitvalidation epoch - * Bump to v0.6.5-dev - -- Changelog for v0.6.4 (2018-06-22) - * Add tests for podman refresh - * Point podman-refresh at the right manpage - * Add bash completions for podman refresh - * Add manpages for podman refresh - * Move podman refresh under the container subcommand - * Make CGroups cleanup optional on whether they exist - * Add podman refresh command - * Add Refresh() to ctrs to refresh state after db change - * Add information about the configuration files to the install docs - * Add unittests and fix bugs - * Fix docs on --sig-proxy to match current behaviour - * Podman history now prints out intermediate image IDs - * Add cap-add and cap-drop to build man page - * Option handling has become large and should be a shared function - * Fix image volumes access and mount problems on restart - * We are using err in defer function, needs to be defined name - * Update the version of conmon used in test - * install: need to install make on Fedora-like distros - * Vendor containers/storage for better error reporting on dups - * libpod: fix race with attach/start - * Implement SSH tunnels between client and podman server - * Add carriage return to log message when using --tty flag - * Errors from closing a netns on removal from DB are nonfatal - * Vendor in latest go-selinux - * Added --sort to ps - * Fix podman build -q - * Add extra debug so we can tell apart postdelete hooks - * hack/ostree_tag.sh: Fill in OSTree dependencies - * TLS verify is skipped per registry. - * Add missing functionality for podman build layers - * Add --all,-a flag to podman images - * Add MacAddress to inspect - * Update gitvalidation epoch - * top: make output tabular - * Add more network info ipv4/ipv6 and be more compatible with docker - * Do not run iptablesDNS workaround on IPv6 addresses - * Added --tls-verify functionality to podman search, with tests - * Bump gitvalidation epoch - * Bump to v0.6.4-dev - -- Changelog for v0.6.3 (2018-06-15) - * spec: remove dead code - * test: add test for running a rootless container - * container: specify path to error message - * podman: use a different store for the rootless case - * container: do not set any mapping when using a rootfs - * podman: do not use Chown in rootless mode - * network: do not attempt to create a network in rootless mode - * oci: do not set resources in rootless mode - * oci: do not use hooks in rootless mode - * oci: do not set the cgroup path in Rootless mode - * spec: change mount options for /dev/pts in rootless mode - * container: do not add shm in rootless mode - * oci: pass XDG_RUNTIME_DIR down to the OCI runtime - * podman: allow to override Tmpdir - * podman: provide a default UID mapping when non root - * podman: accept option --rootfs to use exploded images - * When setting a memory limit, also set a swap limit - * Fix cleaning up network namespaces on detached ctrs - * Vendor in latest projectatomic/buildah - * Temporarily turn of ps --last test until fixed - * Implement --latest for ps - * Correctly report errors retrieving containers in ps - * Doc changes to fix alignment on most of the docs - * Added --sort flag to podman image - * add podman container and image command - * Vendor in latest buildah code - * rmi: remove image if all tags are specified - * Aliases do not work with IsSet - * Touchups for registries.conf across a few man pages - * Remove container from state before cleaning up. - * hack/release.sh: Add a guard against -dev suffixes for argv[2] - * Bump gitvalidation epoch - * Bump to v0.6.3-dev - -- Changelog for v0.6.2 (2018-06-08) - * Test to make sure we are getting proper exit codes on podman run - * Propegate exit code on Exec calls and integrated test - * Vendor in latest buildah code - * Update epoch to fix validation problems - * Touch up whitespace issue in build man - * Add disable-content flag info to man page for build - * podman-run: clean up some formatting issues - * Add pointers for Integration Tests to docs - * Remove SELinux transition rule after conmon is started. - * Add --all flag even though it is a noop so scripts will work - * Add support for BuildImage - * Added a defer to an Unlock that immediately followed a Lock - * varlink build fixes - * podman-varlink: log timeouts - * bash completion: remove shebang - * install.md: fix typo - * Vendor in latest buildah code - * Update OWNERS file to be based on reality - * Add logo to transfer page - * libpod: Execute poststop hooks locally - * Add some test for podman run flag security-opt - * Add a function for e2e test to write json file - * Use go-selinux for selinux check - * Add flag to add annotations to a container - * Want to change the log level on buildah by default to warnf - * vendor in latest github.com/varlink/go - * hooks: Add debug logging for initial hook loading - * hooks/docs: Fix 1.0.0 Nvidia example (adding version, etc.) - * hooks/1.0.0/when_test: Fix "both, and" -> "both, or" name typo - * hooks/1.0.0: Fix 'annotation' -> 'annotations' in JSON - * hooks: Fail ReadDir if a configured hook executable is missing - * Cleanup transfer.md page, remove CRI-O content - * Vendor in latest containers/storage - * Bump gitvalidation epoch - * Bump to v0.6.2-dev - -- Changelog for v0.6.1 (2018-06-01) - * hack/release.sh: Bump spec in dev_version_commit - * hack/release.sh: No longer need to bump setup.py - * Provide examples for python podman API - * Bump Buildah vendor to pick up fix for tests - * Log podman build failures in papr - * Use Version from spec file in setup.py - * Attempt to use fedora 28 atomic host - * Fix lable handling - * runtime: add /usr/libexec/podman/conmon to the conmon paths - * varlink build - * Add OnBuild support for podman build - * return all inspect info for varlink containerinspect - * hooks/exec: Allow successful reaps for 0s post-kill timeouts - * hack/release.sh: Add a release script - * Implement container attach - * If user specifies UIDMapSlice without GIDMapSlice, set them equal - * fix panic with podman pull - * pkg/hooks/exec: Add a new package for local hook execution - * Remove --net flag and make it an alias for --network - * Catch does not exist error - * hooks: Rename Hooks() output to extensionStageHooks - * hooks: Allow local control of OCI stages via extensionStages - * We need to change the SELinux label of the conmon process to s0 - * Clear all caps, except the bounding set, when --user is specified. - * Makefile: Add stderr redirect to HAS_PYTHON3 definition - * Force update of API.md - * do not allow port related args to be used with --network=container: - * Update .gitignore for Varlink code and gopathok - * sort containers and images by create time - * Cleanup man pages - * add go generate varlink to copr spec - * Remove varlink's generated Go file - * Bump gitvalidation epoch - * Bump to v0.6.1-dev - -- Changelog for v0.5.4 (2018-05-25): - * Vendor in latest projectatomic/buildah - * Rename addFIPSsModeSecret to addFIPSModeSecret - * Make references to the Process part of Spec conditional - * save and load should support multi-tag for docker-archive - * Implement python podman create and start - * Spell check strings and comments - * hooks/1.0.0: Error on empty process.args instead of panicking - * Set Entrypoint from image only if not already set - * Update podman build to match buildah bud functionality - * Fix test_runner call of podman varlink - * Fix handling of command in images - * Add support for Zulu timestamp parsing - * Clarify using podman build with a URL, Git repo, or archive. - * Vendor in latest container/storage for devicemapper support - * set varlink timeout to 1 seconds - * podman create, start, getattachsocket - * use $GO env-var instead of hard-coded go binary - * tidy up the copr spec - * honor multiple change values - * hooks/README: Fix some Markdown typos (e.g. missing runc target) - * oci-hooks.5: Discuss directory precedence and monitoring - * finish changing the path for varlink - * Tighten the security on the podman varlink socket - * Implement podman.containers.commit() - -- Changelog for v0.5.3 (2018-05-18): - * remove hooks files reference and no varlink-python on f27 or epel - * contrib/spec/podman.spec.in: Drop README-hooks - * troubleshooting: Add console syntax highlighting - * Fix typo - * Refresh pods when refreshing podman state - * Add per-pod CGroups - * Add pod state - * hooks: Fix monitoring of multiple directories - * make sure hooks are renamed for copr spec - * Use container cleanup() functions when removing - * docs/podman.1: Link to hook documentation - * hooks/docs: Add oci-hooks.5 and per-package man page building - * Add Troubleshooting guide - * chrootuser: default to GID 0 when given a numeric --user - * Add python3 package to podman - * libpod: fix panic when using -t and the process fails to start - * Makefile: Use ?= for shell variables (ISODATE, etc.) - * Skip tests that are flaking, holding up merge queue - * Remove old varlink tests - * Allow push/save without image reference - * Vendor in latest containers/image - * Makefile: Respect GOBIN - * Fix podman inspect bash completions - * Update Tutorial with Fedora kit location - * Makefile: Drop find-godeps.sh for podman target - * Support pulling Dockerfile from http - * Refactor libpod python varlink bindings - * add more bash completions - * improve podman commit documentation and error messages - * Touch up logo links - * implement varlink commit - * fix segfault for podman push - * Add the Podman Logo - * logo: Remove unused directory - * hooks: Add package support for extension stages - * Gracefully handle containers removed from c/storage - * Add packaging for hooks/README.md - * Remove stop on error from Docker install switch in baseline tests - * docs: fix contrib/cni broken link - -- Changelog for v0.5.2 (2018-05-11): - * vendor/golang.org/x/text: Vendor collate and language - * hooks: Order injection by collated JSON filename - * libpod: Add HooksDirNotExistFatal - * hooks/read: Ignore IsNotExist for JSON files in ReadDir - * pkg/hooks: Version the hook structure and add 1.0.0 hooks - * Fix varlink remove image force - * Update Podman-specific readme - * Update main README - * vendor.conf: Pin containernetworking/plugins to 1fb94a42 - * Do not error trying to remove cgroups that don't exist - * Remove parent cgroup we create with cgroupfs - * Place Conmon and Container in separate CGroups - * Add --cgroup-manager flag to Podman binary - * Major fixes to systemd cgroup handling - * Skip systemd-style CGroups test - * Alter CGroup path handling for 'podman top' - * Add validation for CGroup parents. Pass CGroups path into runc - * vendor/github.com/docker/docker/hack: Remove unused directory - * varlink info - * vendor.conf: Bump containerd/cgroups to 77e62851 - * vendor.conf: Bump CNI to v0.6.0 - * Dont eat the pull error message for varlink - * podman push should honor registries.conf - * alphabetize the varlink methods, types, and errors in the docs - * Add missing newline to podman port - * Generate varlink API documentation automatically - * Allow streaming on some varlink container methods - * Remove extra close from attach resize channel - * Vendor in latest containers/storage fix for UserNS - * container.go: fix lint error - * Dockerfile.Fedora: use fedora:28 instead of fedora:27 - * Fix calculation of RunningFor in ps json output - * Should not error out if container no longer exists in oci - * Make invalid state nonfatal when cleaning up in run - * test/e2e/run_userns_test.go: new file - * podman, userNS: configure an intermediate mount namespace - * networking, userNS: configure the network namespace after create - * Begin wiring in USERNS Support into podman - -- Changelog for v0.5.1 (2018-05-04): - * Fix pulling from secure registry - * Optionally init() during container restart - * bashcompletion enhancements - * Add directory for systemd socket and service if not present - * varlink containers - * Make podman commit to localhost rather then docker.io - * Trivial refactor on volume addition - * When adding volumes to DB, handle nontrivial cases - * Add accessors for new image fields in container config - * Store user Volumes, Entrypoint, Command in database - * Further fix Godoc comments in options.go - * Update hooks to use config bool to detect volume mounts - * Fix Godoc comments in options.go - * Add config bool to indicate there are user volumes - * Print the Buildah comment from commit to given writer - * Do not print unnecessary Buildah details during commit - * remove options from create/run that we cannot support - * fix typos in the inspect json structs - * Fix podman logout --all flag - * podman should assign a host port to -p when omitted - * Vendor in latest buildah - * Fix misc stuff found by jhonce - * libpod.conf: Podman's conmon path on openSUSE - * Add iidfile parame to build and commit man pages - * do not commit default volumes from container - * correct varlink command in service file - * Vendor in latest containers/image - * Make ':' a restricted character for file names - * Add more validation to --volume flag for run and create - * Fix libseccomp not working in travis - * CONTRIBUTING: Document PR approval and link to OWNERS - * OWNERS: rename 'assignees' to 'approvers' - -- Changelog for v0.4.4 (2018-04-27): - * Use buildah commit and bud in podman - * README: Link to CONTRIBUTING.md - * Remove systemd-cat support - * Refactor unittest for varlink component - * Update .gitignore for python work - * Modify secrets pkg - * varlink images - * Retrieve IP addresses for container from DB - * Add --default-mounts-file hidden flag - * Add isolation note to build man page - * Modify man pages so they compile correctly in mandb - * Strip transport from image name when looking for local image - * readme: improve formatting, add links - * updated epoch for bad dco - * Only generate the varlink glue code if needed and from the vendor dir - * Latest revendoring deleted the cmd dir in varlink - * Remove more Errorf in favor of Wrapf - * Do not eat error messages from pullImage - * Updated varlink vendored code - * Add unit files to the copr spec - * packagers need the varlink generated file - * Makefile; make podman depend on varlink_generate - * Modify --user flag for podman create and run - * Add some podman search test with filter - * Fix podman search no-trunc test - * Dusty would prefer it to be part of the release. - * Add FIPS mode secret - * Initial varlink implementation - * Add restart test with timeout - * Improve restart latest container test - * Add start time check for restart test - * add libpod.conf man page - * Add seconds after epoch to copr rpms to tie break versioning - * enable no test cache - -- Changelog for v0.4.3 (2018-04-20): - * podman push without destination image - * Add make .git target - * Fix tests for podman run --attach - * Print ctr ID if neither STDOUT and STDERR are attached - * Add one test case for check diff in container and committed image - * Vendor in latest containers/image and contaners/storage - * Fix a typo - * It is OK to start an already running container (with no attach) - * Refactor logic for forceSecure in pull for readability - * Small logic fix for podman pull with tls-verify - * Allow podman start to attach to a running container - * regression: tls verify should be set on registries.conf if insecure - * ip validation game too strong - * - reverse host field order (ip goes first) - fix host string split to permit IPv6 - * Allow podman to exit exit codes of removed containers - * Modify diff and inspect docs - * Add oci-systemd-hook as a runtime dep to copr spec - * validate dns-search values prior to creation - * Change container.locked to batched - * Add a function for check if command exist - * Add WaitContainerReady for wait for docker registry ready - * Add several podman push tests - * podman pull should always try to pull - * Allow the use of -i/-a on any container - * Fix secrets patch - * Remove demos.sh file from test - -- Changelog for v0.4.2 (2018-04-13): - * Fix podman run --attach tests - * Fix another comparison of a Go interface against nil - * Allowing attaching stdin to non-interactive containers - * Add tests for podman attach - * Change attach to accept a struct containing streams - * Fix terminal attach - * Changes to attach to enable per-stream attaching - * HACK temporary fix for test suite - * Fix locking interaction in batched Exec() on container - * Fix leaking files in Ginkgo - * Force host UID/GID mapping when creating containers - * Do not lock all containers during pod kill - * Make pod stop lock one container at a time - * Do not lock all containers during pod start - * Containers transitioning to stop should not break stats - * Add -i to exec for compatibility reasons - * Unescape characters in inspect JSON format output - * Use buildah commit for podman commit - * Functionality changes to the following flags - * Vendor in latest containers/storage and containers/image - -- Changelog for v0.4.1 (2018-04-05): - * Remove image via storage if a buildah container is associated - * Add hooks support to podman - * Run images with no names - * Prevent a potential race when stopping containers - * Only allocate tty when -t - * Stopping a stopped container should not be an error - * Add conmon-pidfile flag to bash completions/manpages - * --entrypoint= should delete existing entrypoint - * Fix golint - * Remove explicit Init() calls in run and start - * Refactor dependency checks from init() into public API - * Do not require Init() before Start() - * Ensure dependencies are running before initializing containers - * Add container dependencies to Inspect output - * Add backend code for generic dependencies - * Vendor in latest containers/image - * Makefile: Fix typo podmon -> podman - * Correct a godoc comment - * Sleep for 5 seconds before pushing to registry in tests - * Change errorf to warnf in warning removing ctr storage - * Don't return an ImageConfig when creating storage - * More gracefully handle unexpected storage deletion - * Remove crictl from Dockerfile - * Eliminate raceyness of sig-proxy test - -- Changelog for v0.3.5 (2018-03-29): - * Allow sha256: prefix for input - * Add secrets patch to podman - * Fix tests - * Remove a loop in container graph - * Only start containers that are not running in pod start - * Change pod Start() to use container dependency graph - * Add tests for container graphs - * Initial implementation of container graph generation - * Error is already wrapped properly. - * Check for duplicate names when generating new container and pod names. - * podman: new option --conmon-pidfile= - * Ensure container dependencies are part of the same pod - * Prevent ctrs not in pods from depending on pod ctrs - * Disable --sig-proxy tests due to race conditions - * Remove dependency on kubernetes - * Vendor in lots of kubernetes stuff to shrink image size - * Fix some minor issues lint has been picking up - * cmd/podman/run.go: Error nicely when no image found - * podman exec should handle options --env foo - * Remove current SQLite DB driver - * Update containers/storage to pick up overlay driver fix - * First tag, untag THEN reload the image - * Add files section to podman man page - -- Changelog for v0.3.4 (2018-03-23): - * Bump version to v0.3.4 - * Make container env variable conditional - * Stage 4 Image cleanup - * Add CONTAINER environment variable - * Small manpage reword - * Document .containerenv in manpages. Move it to /run. - * Add .containerenv file - * Add script to determine dependency sizes - * If cidfile exists, do not proceed - * Removing tagged images change in behavior - * Use podman to test podman on FAH - * Migrate podman inspect and tag to image library - * Migrate podman images to image library - * Makefile: add changelog target - * Image library stage 4 - create and commit - * Add 'podman restart' asciinema - * Fix Travis tests for sig-proxy diff -Nru libpod-3.2.1+ds1/.cirrus.yml libpod-3.4.4+ds1/.cirrus.yml --- libpod-3.2.1+ds1/.cirrus.yml 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/.cirrus.yml 2021-12-26 00:45:51.000000000 +0000 @@ -6,7 +6,7 @@ #### Global variables used for all tasks #### # Name of the ultimate destination branch for this CI run, PR or post-merge. - DEST_BRANCH: "v3.2" + DEST_BRANCH: "v3.4" # Overrides default location (/tmp/cirrus) for repo clone GOPATH: &gopath "/var/tmp/go" GOBIN: "${GOPATH}/bin" @@ -27,20 +27,17 @@ FEDORA_NAME: "fedora-34" PRIOR_FEDORA_NAME: "fedora-33" UBUNTU_NAME: "ubuntu-2104" - PRIOR_UBUNTU_NAME: "ubuntu-2010" # Google-cloud VM Images - IMAGE_SUFFIX: "c5348179051806720" + IMAGE_SUFFIX: "c6431352024203264" FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}" - PRIOR_UBUNTU_CACHE_IMAGE_NAME: "prior-ubuntu-${IMAGE_SUFFIX}" # Container FQIN's FEDORA_CONTAINER_FQIN: "quay.io/libpod/fedora_podman:${IMAGE_SUFFIX}" PRIOR_FEDORA_CONTAINER_FQIN: "quay.io/libpod/prior-fedora_podman:${IMAGE_SUFFIX}" UBUNTU_CONTAINER_FQIN: "quay.io/libpod/ubuntu_podman:${IMAGE_SUFFIX}" - PRIOR_UBUNTU_CONTAINER_FQIN: "quay.io/libpod/prior-ubuntu_podman:${IMAGE_SUFFIX}" #### #### Control variables that determine what to run and how to run it. @@ -144,28 +141,23 @@ image_name: "${VM_IMAGE_NAME}" # from stdenvars matrix: &platform_axis # Ref: https://cirrus-ci.org/guide/writing-tasks/#matrix-modification - - env: &stdenvars + - env: &stdenvars DISTRO_NV: ${FEDORA_NAME} # Not used here, is used in other tasks VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME} CTR_FQIN: ${FEDORA_CONTAINER_FQIN} # ID for re-use of build output _BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - - env: + - env: &priorfedora_envvars DISTRO_NV: ${PRIOR_FEDORA_NAME} VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME} CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN} _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - - env: + - env: &ubuntu_envvars DISTRO_NV: ${UBUNTU_NAME} VM_IMAGE_NAME: ${UBUNTU_CACHE_IMAGE_NAME} CTR_FQIN: ${UBUNTU_CONTAINER_FQIN} _BUILD_CACHE_HANDLE: ${UBUNTU_NAME}-build-${CIRRUS_BUILD_ID} - - env: - DISTRO_NV: ${PRIOR_UBUNTU_NAME} - VM_IMAGE_NAME: ${PRIOR_UBUNTU_CACHE_IMAGE_NAME} - CTR_FQIN: ${PRIOR_UBUNTU_CONTAINER_FQIN} - _BUILD_CACHE_HANDLE: ${PRIOR_UBUNTU_NAME}-build-${CIRRUS_BUILD_ID} env: TEST_FLAVOR: build # Ref: https://cirrus-ci.org/guide/writing-tasks/#cache-instruction @@ -238,7 +230,12 @@ clone_script: *noop # Comes from cache setup_script: *setup main_script: *main - always: *runner_stats + always: &html_artifacts + <<: *runner_stats + # Required for `contrib/cirrus/logformatter` to work properly + html_artifacts: + path: ./*.html + type: text/html # Build the "libpod" API documentation `swagger.yaml` and @@ -370,8 +367,7 @@ always: *binary_artifacts -# This task is a stub: In the future it will be used to verify -# podman is compatible with the docker python-module. +# Verify podman is compatible with the docker python-module. docker-py_test_task: name: Docker-py Compat. alias: docker-py_test @@ -400,7 +396,15 @@ only_if: *not_docs depends_on: - validate - matrix: *platform_axis + matrix: + - env: *stdenvars + - env: *priorfedora_envvars + - env: *ubuntu_envvars + # Special-case: Rootless on latest Fedora (standard) VM + - name: "Rootless unit on $DISTRO_NV" + env: + <<: *stdenvars + PRIV_NAME: rootless gce_instance: *standardvm env: TEST_FLAVOR: unit @@ -414,10 +418,14 @@ apiv2_test_task: name: "APIv2 test on $DISTRO_NV" alias: apiv2_test + only_if: *not_docs skip: *tags depends_on: - validate gce_instance: *standardvm + # Test is normally pretty quick, about 10-minutes. If it hangs, + # don't make developers wait the full 1-hour timeout. + timeout_in: 20m env: <<: *stdenvars TEST_FLAVOR: apiv2 @@ -426,11 +434,7 @@ setup_script: *setup main_script: *main always: &logs_artifacts - <<: *runner_stats - # Required for `contrib/cirrus/logformatter` to work properly - html_artifacts: - path: ./*.html - type: text/html + <<: *html_artifacts package_versions_script: '$SCRIPT_BASE/logcollector.sh packages' df_script: '$SCRIPT_BASE/logcollector.sh df' audit_log_script: '$SCRIPT_BASE/logcollector.sh audit' @@ -598,7 +602,11 @@ CTR_FQIN: ${FEDORA_CONTAINER_FQIN} # ID for re-use of build output _BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID} - PODBIN_NAME: podman + matrix: + - env: + PODBIN_NAME: podman + - env: + PODBIN_NAME: remote gce_instance: *standardvm timeout_in: 45m clone_script: *noop @@ -625,17 +633,11 @@ main_script: *main always: *logs_artifacts -# FIXME: we may want to consider running this from nightly cron instead of CI. -# The tests are actually pretty quick (less than a minute) but they do rely -# on pulling images from quay.io, which means we're subject to network flakes. -# -# FIXME: how does this env matrix work, anyway? Does it spin up multiple VMs? -# We might just want to encode the version matrix in runner.sh instead upgrade_test_task: name: "Upgrade test: from $PODMAN_UPGRADE_FROM" alias: upgrade_test skip: *tags - only_if: *not_docs + only_if: $CIRRUS_CHANGE_TITLE !=~ '.*CI:DOCS.*' || $CIRRUS_CRON != '' depends_on: - local_system_test matrix: @@ -645,6 +647,8 @@ PODMAN_UPGRADE_FROM: v2.0.6 - env: PODMAN_UPGRADE_FROM: v2.1.1 + - env: + PODMAN_UPGRADE_FROM: v3.1.2 gce_instance: *standardvm env: TEST_FLAVOR: upgrade_test @@ -674,7 +678,6 @@ ${FEDORA_CACHE_IMAGE_NAME} ${PRIOR_FEDORA_CACHE_IMAGE_NAME} ${UBUNTU_CACHE_IMAGE_NAME} - ${PRIOR_UBUNTU_CACHE_IMAGE_NAME} BUILDID: "${CIRRUS_BUILD_ID}" REPOREF: "${CIRRUS_REPO_NAME}" GCPJSON: ENCRYPTED[3a198350077849c8df14b723c0f4c9fece9ebe6408d35982e7adf2105a33f8e0e166ed3ed614875a0887e1af2b8775f4] diff -Nru libpod-3.2.1+ds1/cmd/podman/auto-update.go libpod-3.4.4+ds1/cmd/podman/auto-update.go --- libpod-3.2.1+ds1/cmd/podman/auto-update.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/auto-update.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,15 @@ package main import ( + "encoding/json" "fmt" + "os" + "strings" "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" @@ -12,8 +17,13 @@ "github.com/spf13/cobra" ) +type cliAutoUpdateOptions struct { + entities.AutoUpdateOptions + format string +} + var ( - autoUpdateOptions = entities.AutoUpdateOptions{} + autoUpdateOptions = cliAutoUpdateOptions{} autoUpdateDescription = `Auto update containers according to their auto-update policy. Auto-update policies are specified with the "io.containers.autoupdate" label. @@ -21,6 +31,7 @@ or similar units that create new containers in order to run the updated images. Please refer to the podman-auto-update(1) man page for details.` autoUpdateCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "auto-update [options]", Short: "Auto update containers according to their auto-update policy", Long: autoUpdateDescription, @@ -33,7 +44,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: autoUpdateCommand, }) @@ -42,6 +52,12 @@ authfileFlagName := "authfile" flags.StringVar(&autoUpdateOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path to the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") _ = autoUpdateCommand.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + + flags.BoolVar(&autoUpdateOptions.DryRun, "dry-run", false, "Check for pending updates") + flags.BoolVar(&autoUpdateOptions.Rollback, "rollback", true, "Rollback to previous image if update fails") + + flags.StringVar(&autoUpdateOptions.format, "format", "", "Change the output format to JSON or a Go template") + _ = autoUpdateCommand.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(autoUpdateOutput{})) } func autoUpdate(cmd *cobra.Command, args []string) error { @@ -49,11 +65,83 @@ // Backwards compat. System tests expect this error string. return errors.Errorf("`%s` takes no arguments", cmd.CommandPath()) } - report, failures := registry.ContainerEngine().AutoUpdate(registry.GetContext(), autoUpdateOptions) - if report != nil { - for _, unit := range report.Units { - fmt.Println(unit) - } + + allReports, failures := registry.ContainerEngine().AutoUpdate(registry.GetContext(), autoUpdateOptions.AutoUpdateOptions) + if allReports == nil { + return errorhandling.JoinErrors(failures) + } + + if err := writeTemplate(allReports, autoUpdateOptions.format); err != nil { + failures = append(failures, err) } + return errorhandling.JoinErrors(failures) } + +type autoUpdateOutput struct { + Unit string + Container string + ContainerName string + ContainerID string + Image string + Policy string + Updated string +} + +func reportsToOutput(allReports []*entities.AutoUpdateReport) []autoUpdateOutput { + output := make([]autoUpdateOutput, len(allReports)) + for i, r := range allReports { + output[i] = autoUpdateOutput{ + Unit: r.SystemdUnit, + Container: fmt.Sprintf("%s (%s)", r.ContainerID[:12], r.ContainerName), + ContainerName: r.ContainerName, + ContainerID: r.ContainerID, + Image: r.ImageName, + Policy: r.Policy, + Updated: r.Updated, + } + } + return output +} + +func writeTemplate(allReports []*entities.AutoUpdateReport, inputFormat string) error { + var format string + var printHeader bool + + output := reportsToOutput(allReports) + switch inputFormat { + case "": + rows := []string{"{{.Unit}}", "{{.Container}}", "{{.Image}}", "{{.Policy}}", "{{.Updated}}"} + format = "{{range . }}" + strings.Join(rows, "\t") + "\n{{end -}}" + printHeader = true + case "json": + prettyJSON, err := json.MarshalIndent(output, "", " ") + if err != nil { + return err + } + fmt.Println(string(prettyJSON)) + return nil + default: + format = "{{range . }}" + inputFormat + "\n{{end -}}" + } + + tmpl, err := report.NewTemplate("auto-update").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + defer w.Flush() + + if printHeader { + headers := report.Headers(autoUpdateOutput{}, nil) + if err := tmpl.Execute(w, headers); err != nil { + return err + } + } + + return tmpl.Execute(w, output) +} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/completion.go libpod-3.4.4+ds1/cmd/podman/common/completion.go --- libpod-3.2.1+ds1/cmd/podman/common/completion.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/completion.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,11 +8,11 @@ "strings" "github.com/containers/common/pkg/config" + "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/network" - "github.com/containers/podman/v3/pkg/registries" "github.com/containers/podman/v3/pkg/rootless" systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/pkg/util" @@ -46,7 +46,9 @@ return nil, err } if !registry.IsRemote() && rootless.IsRootless() { - err := containerEngine.SetupRootless(registry.Context(), cmd) + _, noMoveProcess := cmd.Annotations[registry.NoMoveProcess] + + err := containerEngine.SetupRootless(registry.Context(), noMoveProcess) if err != nil { return nil, err } @@ -191,21 +193,14 @@ } else { // suggested "registry.fedoraproject.org/f29/httpd:latest" as // - "registry.fedoraproject.org/f29/httpd:latest" - // - "registry.fedoraproject.org/f29/httpd" // - "f29/httpd:latest" - // - "f29/httpd" // - "httpd:latest" - // - "httpd" paths := strings.Split(repo, "/") for i := range paths { suggestionWithTag := strings.Join(paths[i:], "/") if strings.HasPrefix(suggestionWithTag, toComplete) { suggestions = append(suggestions, suggestionWithTag) } - suggestionWithoutTag := strings.SplitN(strings.SplitN(suggestionWithTag, ":", 2)[0], "@", 2)[0] - if strings.HasPrefix(suggestionWithoutTag, toComplete) { - suggestions = append(suggestions, suggestionWithoutTag) - } } } } @@ -221,7 +216,7 @@ cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveNoFileComp } - secrets, err := engine.SecretList(registry.GetContext()) + secrets, err := engine.SecretList(registry.GetContext(), entities.SecretListRequest{}) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveNoFileComp @@ -236,7 +231,7 @@ } func getRegistries() ([]string, cobra.ShellCompDirective) { - regs, err := registries.GetRegistries() + regs, err := sysregistriesv2.UnqualifiedSearchRegistries(nil) if err != nil { cobra.CompErrorln(err.Error()) return nil, cobra.ShellCompDirectiveNoFileComp @@ -321,6 +316,18 @@ return slice } +func suffixCompSlice(suf string, slice []string) []string { + for i := range slice { + split := strings.SplitN(slice[i], "\t", 2) + if len(split) > 1 { + slice[i] = split[0] + suf + "\t" + split[1] + } else { + slice[i] = slice[i] + suf + } + } + return slice +} + func completeKeyValues(toComplete string, k keyValueCompletion) ([]string, cobra.ShellCompDirective) { suggestions := make([]string, 0, len(k)) directive := cobra.ShellCompDirectiveNoFileComp @@ -662,6 +669,42 @@ return suggestions, cobra.ShellCompDirectiveNoFileComp } +// AutocompleteScp returns a list of connections, images, or both, depending on the amount of arguments +func AutocompleteScp(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + if !validCurrentCmdLine(cmd, args, toComplete) { + return nil, cobra.ShellCompDirectiveNoFileComp + } + switch len(args) { + case 0: + split := strings.SplitN(toComplete, "::", 2) + if len(split) > 1 { + imageSuggestions, _ := getImages(cmd, split[1]) + return prefixSlice(split[0]+"::", imageSuggestions), cobra.ShellCompDirectiveNoFileComp + } + connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete) + imageSuggestions, _ := getImages(cmd, toComplete) + totalSuggestions := append(suffixCompSlice("::", connectionSuggestions), imageSuggestions...) + directive := cobra.ShellCompDirectiveNoFileComp + // if we have connections do not add a space after the completion + if len(connectionSuggestions) > 0 { + directive = cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace + } + return totalSuggestions, directive + case 1: + split := strings.SplitN(args[0], "::", 2) + if len(split) > 1 { + if len(split[1]) > 0 { + return nil, cobra.ShellCompDirectiveNoFileComp + } + imageSuggestions, _ := getImages(cmd, toComplete) + return imageSuggestions, cobra.ShellCompDirectiveNoFileComp + } + connectionSuggestions, _ := AutocompleteSystemConnections(cmd, args, toComplete) + return suffixCompSlice("::", connectionSuggestions), cobra.ShellCompDirectiveNoFileComp + } + return nil, cobra.ShellCompDirectiveNoFileComp +} + /* -------------- Flags ----------------- */ // AutocompleteDetachKeys - Autocomplete detach-keys options. @@ -935,40 +978,14 @@ f = f.Elem() } - // // the only supported type is struct + // the only supported type is struct if f.Kind() != reflect.Struct { return nil, cobra.ShellCompDirectiveNoFileComp } // last field get all names to suggest if i == len(fields)-1 { - suggestions := []string{} - for j := 0; j < f.NumField(); j++ { - fname := f.Type().Field(j).Name - suffix := "}}" - kind := f.Type().Field(j).Type.Kind() - if kind == reflect.Ptr { - // make sure to read the actual type when it is a pointer - kind = f.Type().Field(j).Type.Elem().Kind() - } - // when we have a nested struct do not append braces instead append a dot - if kind == reflect.Struct { - suffix = "." - } - if strings.HasPrefix(fname, fields[i]) { - // add field name with closing braces - suggestions = append(suggestions, fname+suffix) - } - } - - for j := 0; j < f.NumMethod(); j++ { - fname := f.Type().Method(j).Name - if strings.HasPrefix(fname, fields[i]) { - // add method name with closing braces - suggestions = append(suggestions, fname+"}}") - } - } - + suggestions := getStructFields(f, fields[i]) // add the current toComplete value in front so that the shell can complete this correctly toCompArr := strings.Split(toComplete, ".") toCompArr[len(toCompArr)-1] = "" @@ -982,6 +999,52 @@ } } +// getStructFields reads all struct field names and method names and returns them. +func getStructFields(f reflect.Value, prefix string) []string { + suggestions := []string{} + // follow the pointer first + if f.Kind() == reflect.Ptr { + f = f.Elem() + } + // we only support structs + if f.Kind() != reflect.Struct { + return nil + } + // loop over all field names + for j := 0; j < f.NumField(); j++ { + field := f.Type().Field(j) + fname := field.Name + suffix := "}}" + kind := field.Type.Kind() + if kind == reflect.Ptr { + // make sure to read the actual type when it is a pointer + kind = field.Type.Elem().Kind() + } + // when we have a nested struct do not append braces instead append a dot + if kind == reflect.Struct { + suffix = "." + } + if strings.HasPrefix(fname, prefix) { + // add field name with suffix + suggestions = append(suggestions, fname+suffix) + } + // if field is anonymous add the child fields as well + if field.Anonymous { + suggestions = append(suggestions, getStructFields(f.FieldByIndex([]int{j}), prefix)...) + } + } + + for j := 0; j < f.NumMethod(); j++ { + fname := f.Type().Method(j).Name + if strings.HasPrefix(fname, prefix) { + // add method name with closing braces + suggestions = append(suggestions, fname+"}}") + } + } + + return suggestions +} + // AutocompleteEventFilter - Autocomplete event filter flag options. // -> "container=", "event=", "image=", "pod=", "volume=", "type=" func AutocompleteEventFilter(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { @@ -1211,3 +1274,10 @@ } return completeKeyValues(toComplete, kv) } + +// AutocompleteCheckpointCompressType - Autocomplete checkpoint compress type options. +// -> "gzip", "none", "zstd" +func AutocompleteCheckpointCompressType(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + types := []string{"gzip", "none", "zstd"} + return types, cobra.ShellCompDirectiveNoFileComp +} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/completion_test.go libpod-3.4.4+ds1/cmd/podman/common/completion_test.go --- libpod-3.2.1+ds1/cmd/podman/common/completion_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/completion_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -17,6 +17,10 @@ Extras map[string]string } +type Anonymous struct { + Hello string +} + func (c Car) Type() string { return "" } @@ -30,7 +34,10 @@ Name string Age int Car *Car - }{} + *Anonymous + }{ + Anonymous: &Anonymous{}, + } testStruct.Car = &Car{} testStruct.Car.Extras = map[string]string{"test": "1"} @@ -73,12 +80,12 @@ { "fist level struct field name", "{{.", - []string{"{{.Name}}", "{{.Age}}", "{{.Car."}, + []string{"{{.Name}}", "{{.Age}}", "{{.Car.", "{{.Anonymous.", "{{.Hello}}"}, }, { "fist level struct field name", "{{ .", - []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car."}, + []string{"{{ .Name}}", "{{ .Age}}", "{{ .Car.", "{{ .Anonymous.", "{{ .Hello}}"}, }, { "fist level struct field name", diff -Nru libpod-3.2.1+ds1/cmd/podman/common/create.go libpod-3.4.4+ds1/cmd/podman/common/create.go --- libpod-3.2.1+ds1/cmd/podman/common/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,8 +5,10 @@ "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/completion" + commonFlag "github.com/containers/common/pkg/flag" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -14,655 +16,727 @@ var containerConfig = registry.PodmanConfig() -func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) { +func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions, isInfra bool) { createFlags := cmd.Flags() - annotationFlagName := "annotation" - createFlags.StringSliceVar( - &cf.Annotation, - annotationFlagName, []string{}, - "Add annotations to container (key:value)", - ) - _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) + if !isInfra { + annotationFlagName := "annotation" + createFlags.StringSliceVar( + &cf.Annotation, + annotationFlagName, []string{}, + "Add annotations to container (key:value)", + ) + _ = cmd.RegisterFlagCompletionFunc(annotationFlagName, completion.AutocompleteNone) - attachFlagName := "attach" - createFlags.StringSliceVarP( - &cf.Attach, - attachFlagName, "a", []string{}, - "Attach to STDIN, STDOUT or STDERR", - ) - _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) + attachFlagName := "attach" + createFlags.StringSliceVarP( + &cf.Attach, + attachFlagName, "a", []string{}, + "Attach to STDIN, STDOUT or STDERR", + ) + _ = cmd.RegisterFlagCompletionFunc(attachFlagName, AutocompleteCreateAttach) - authfileFlagName := "authfile" - createFlags.StringVar( - &cf.Authfile, - authfileFlagName, auth.GetDefaultAuthFile(), - "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", - ) - _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + authfileFlagName := "authfile" + createFlags.StringVar( + &cf.Authfile, + authfileFlagName, auth.GetDefaultAuthFile(), + "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override", + ) + _ = cmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) - blkioWeightFlagName := "blkio-weight" - createFlags.StringVar( - &cf.BlkIOWeight, - blkioWeightFlagName, "", - "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", - ) - _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) + blkioWeightFlagName := "blkio-weight" + createFlags.StringVar( + &cf.BlkIOWeight, + blkioWeightFlagName, "", + "Block IO weight (relative weight) accepts a weight value between 10 and 1000.", + ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightFlagName, completion.AutocompleteNone) - blkioWeightDeviceFlagName := "blkio-weight-device" - createFlags.StringSliceVar( - &cf.BlkIOWeightDevice, - blkioWeightDeviceFlagName, []string{}, - "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", - ) - _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) + blkioWeightDeviceFlagName := "blkio-weight-device" + createFlags.StringSliceVar( + &cf.BlkIOWeightDevice, + blkioWeightDeviceFlagName, []string{}, + "Block IO weight (relative device weight, format: `DEVICE_NAME:WEIGHT`)", + ) + _ = cmd.RegisterFlagCompletionFunc(blkioWeightDeviceFlagName, completion.AutocompleteDefault) - capAddFlagName := "cap-add" - createFlags.StringSliceVar( - &cf.CapAdd, - capAddFlagName, []string{}, - "Add capabilities to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) + capAddFlagName := "cap-add" + createFlags.StringSliceVar( + &cf.CapAdd, + capAddFlagName, []string{}, + "Add capabilities to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(capAddFlagName, completion.AutocompleteCapabilities) - capDropFlagName := "cap-drop" - createFlags.StringSliceVar( - &cf.CapDrop, - capDropFlagName, []string{}, - "Drop capabilities from the container", - ) - _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) + capDropFlagName := "cap-drop" + createFlags.StringSliceVar( + &cf.CapDrop, + capDropFlagName, []string{}, + "Drop capabilities from the container", + ) + _ = cmd.RegisterFlagCompletionFunc(capDropFlagName, completion.AutocompleteCapabilities) - cgroupnsFlagName := "cgroupns" - createFlags.String( - cgroupnsFlagName, "", - "cgroup namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) + cgroupnsFlagName := "cgroupns" + createFlags.String( + cgroupnsFlagName, "", + "cgroup namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupnsFlagName, AutocompleteNamespace) - cgroupsFlagName := "cgroups" - createFlags.StringVar( - &cf.CGroupsMode, - cgroupsFlagName, cgroupConfig(), - `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) + cgroupsFlagName := "cgroups" + createFlags.StringVar( + &cf.CGroupsMode, + cgroupsFlagName, cgroupConfig(), + `control container cgroup configuration ("enabled"|"disabled"|"no-conmon"|"split")`, + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupsFlagName, AutocompleteCgroupMode) - cgroupParentFlagName := "cgroup-parent" - createFlags.StringVar( - &cf.CGroupParent, - cgroupParentFlagName, "", - "Optional parent cgroup for the container", - ) - _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) + cpusFlagName := "cpus" + createFlags.Float64Var( + &cf.CPUS, + cpusFlagName, 0, + "Number of CPUs. The default is 0.000 which means no limit", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) - cidfileFlagName := "cidfile" - createFlags.StringVar( - &cf.CIDFile, - cidfileFlagName, "", - "Write the container ID to the file", - ) - _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + cpusetCpusFlagName := "cpuset-cpus" + createFlags.StringVar( + &cf.CPUSetCPUs, + cpusetCpusFlagName, "", + "CPUs in which to allow execution (0-3, 0,1)", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) - conmonPidfileFlagName := "conmon-pidfile" - createFlags.StringVar( - &cf.ConmonPIDFile, - conmonPidfileFlagName, "", - "Path to the file that will receive the PID of conmon", - ) - _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) + cpuPeriodFlagName := "cpu-period" + createFlags.Uint64Var( + &cf.CPUPeriod, + cpuPeriodFlagName, 0, + "Limit the CPU CFS (Completely Fair Scheduler) period", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) - cpuPeriodFlagName := "cpu-period" - createFlags.Uint64Var( - &cf.CPUPeriod, - cpuPeriodFlagName, 0, - "Limit the CPU CFS (Completely Fair Scheduler) period", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuPeriodFlagName, completion.AutocompleteNone) - - cpuQuotaFlagName := "cpu-quota" - createFlags.Int64Var( - &cf.CPUQuota, - cpuQuotaFlagName, 0, - "Limit the CPU CFS (Completely Fair Scheduler) quota", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) - - cpuRtPeriodFlagName := "cpu-rt-period" - createFlags.Uint64Var( - &cf.CPURTPeriod, - cpuRtPeriodFlagName, 0, - "Limit the CPU real-time period in microseconds", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) - - cpuRtRuntimeFlagName := "cpu-rt-runtime" - createFlags.Int64Var( - &cf.CPURTRuntime, - cpuRtRuntimeFlagName, 0, - "Limit the CPU real-time runtime in microseconds", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) - - cpuSharesFlagName := "cpu-shares" - createFlags.Uint64Var( - &cf.CPUShares, - cpuSharesFlagName, 0, - "CPU shares (relative weight)", - ) - _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) - - cpusFlagName := "cpus" - createFlags.Float64Var( - &cf.CPUS, - cpusFlagName, 0, - "Number of CPUs. The default is 0.000 which means no limit", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) + cpuQuotaFlagName := "cpu-quota" + createFlags.Int64Var( + &cf.CPUQuota, + cpuQuotaFlagName, 0, + "Limit the CPU CFS (Completely Fair Scheduler) quota", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuQuotaFlagName, completion.AutocompleteNone) - cpusetCpusFlagName := "cpuset-cpus" - createFlags.StringVar( - &cf.CPUSetCPUs, - cpusetCpusFlagName, "", - "CPUs in which to allow execution (0-3, 0,1)", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusetCpusFlagName, completion.AutocompleteNone) + cpuRtPeriodFlagName := "cpu-rt-period" + createFlags.Uint64Var( + &cf.CPURTPeriod, + cpuRtPeriodFlagName, 0, + "Limit the CPU real-time period in microseconds", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtPeriodFlagName, completion.AutocompleteNone) - cpusetMemsFlagName := "cpuset-mems" - createFlags.StringVar( - &cf.CPUSetMems, - cpusetMemsFlagName, "", - "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", - ) - _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) + cpuRtRuntimeFlagName := "cpu-rt-runtime" + createFlags.Int64Var( + &cf.CPURTRuntime, + cpuRtRuntimeFlagName, 0, + "Limit the CPU real-time runtime in microseconds", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuRtRuntimeFlagName, completion.AutocompleteNone) - deviceFlagName := "device" - createFlags.StringSliceVar( - &cf.Devices, - deviceFlagName, devices(), - "Add a host device to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) + cpuSharesFlagName := "cpu-shares" + createFlags.Uint64Var( + &cf.CPUShares, + cpuSharesFlagName, 0, + "CPU shares (relative weight)", + ) + _ = cmd.RegisterFlagCompletionFunc(cpuSharesFlagName, completion.AutocompleteNone) + cidfileFlagName := "cidfile" + createFlags.StringVar( + &cf.CIDFile, + cidfileFlagName, "", + "Write the container ID to the file", + ) + _ = cmd.RegisterFlagCompletionFunc(cidfileFlagName, completion.AutocompleteDefault) + cpusetMemsFlagName := "cpuset-mems" + createFlags.StringVar( + &cf.CPUSetMems, + cpusetMemsFlagName, "", + "Memory nodes (MEMs) in which to allow execution (0-3, 0,1). Only effective on NUMA systems.", + ) + _ = cmd.RegisterFlagCompletionFunc(cpusetMemsFlagName, completion.AutocompleteNone) - deviceCgroupRuleFlagName := "device-cgroup-rule" - createFlags.StringSliceVar( - &cf.DeviceCGroupRule, - deviceCgroupRuleFlagName, []string{}, - "Add a rule to the cgroup allowed devices list", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) + deviceFlagName := "device" + createFlags.StringSliceVar( + &cf.Devices, + deviceFlagName, devices(), + "Add a host device to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceFlagName, completion.AutocompleteDefault) - deviceReadBpsFlagName := "device-read-bps" - createFlags.StringSliceVar( - &cf.DeviceReadBPs, - deviceReadBpsFlagName, []string{}, - "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) + deviceCgroupRuleFlagName := "device-cgroup-rule" + createFlags.StringSliceVar( + &cf.DeviceCGroupRule, + deviceCgroupRuleFlagName, []string{}, + "Add a rule to the cgroup allowed devices list", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceCgroupRuleFlagName, completion.AutocompleteNone) - deviceReadIopsFlagName := "device-read-iops" - createFlags.StringSliceVar( - &cf.DeviceReadIOPs, - deviceReadIopsFlagName, []string{}, - "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) + deviceReadBpsFlagName := "device-read-bps" + createFlags.StringSliceVar( + &cf.DeviceReadBPs, + deviceReadBpsFlagName, []string{}, + "Limit read rate (bytes per second) from a device (e.g. --device-read-bps=/dev/sda:1mb)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadBpsFlagName, completion.AutocompleteDefault) - deviceWriteBpsFlagName := "device-write-bps" - createFlags.StringSliceVar( - &cf.DeviceWriteBPs, - deviceWriteBpsFlagName, []string{}, - "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) + deviceReadIopsFlagName := "device-read-iops" + createFlags.StringSliceVar( + &cf.DeviceReadIOPs, + deviceReadIopsFlagName, []string{}, + "Limit read rate (IO per second) from a device (e.g. --device-read-iops=/dev/sda:1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceReadIopsFlagName, completion.AutocompleteDefault) - deviceWriteIopsFlagName := "device-write-iops" - createFlags.StringSliceVar( - &cf.DeviceWriteIOPs, - deviceWriteIopsFlagName, []string{}, - "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) + deviceWriteBpsFlagName := "device-write-bps" + createFlags.StringSliceVar( + &cf.DeviceWriteBPs, + deviceWriteBpsFlagName, []string{}, + "Limit write rate (bytes per second) to a device (e.g. --device-write-bps=/dev/sda:1mb)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteBpsFlagName, completion.AutocompleteDefault) - createFlags.Bool( - "disable-content-trust", false, - "This is a Docker specific option and is a NOOP", - ) + deviceWriteIopsFlagName := "device-write-iops" + createFlags.StringSliceVar( + &cf.DeviceWriteIOPs, + deviceWriteIopsFlagName, []string{}, + "Limit write rate (IO per second) to a device (e.g. --device-write-iops=/dev/sda:1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(deviceWriteIopsFlagName, completion.AutocompleteDefault) - entrypointFlagName := "entrypoint" - createFlags.String(entrypointFlagName, "", - "Overwrite the default ENTRYPOINT of the image", - ) - _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) + createFlags.Bool( + "disable-content-trust", false, + "This is a Docker specific option and is a NOOP", + ) - envFlagName := "env" - createFlags.StringArrayP( - envFlagName, "e", env(), - "Set environment variables in container", - ) - _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + envFlagName := "env" + createFlags.StringArrayP( + envFlagName, "e", env(), + "Set environment variables in container", + ) + _ = cmd.RegisterFlagCompletionFunc(envFlagName, completion.AutocompleteNone) + + if !registry.IsRemote() { + createFlags.BoolVar( + &cf.EnvHost, + "env-host", false, "Use all current host environment variables in container", + ) + } + + envFileFlagName := "env-file" + createFlags.StringSliceVar( + &cf.EnvFile, + envFileFlagName, []string{}, + "Read in a file of environment variables", + ) + _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) + + exposeFlagName := "expose" + createFlags.StringSliceVar( + &cf.Expose, + exposeFlagName, []string{}, + "Expose a port or a range of ports", + ) + _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) + + groupAddFlagName := "group-add" + createFlags.StringSliceVar( + &cf.GroupAdd, + groupAddFlagName, []string{}, + "Add additional groups to the primary container process. 'keep-groups' allows container processes to use supplementary groups.", + ) + _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) + + healthCmdFlagName := "health-cmd" + createFlags.StringVar( + &cf.HealthCmd, + healthCmdFlagName, "", + "set a healthcheck command for the container ('none' disables the existing healthcheck)", + ) + _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) + + healthIntervalFlagName := "health-interval" + createFlags.StringVar( + &cf.HealthInterval, + healthIntervalFlagName, DefaultHealthCheckInterval, + "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", + ) + _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) + + healthRetriesFlagName := "health-retries" + createFlags.UintVar( + &cf.HealthRetries, + healthRetriesFlagName, DefaultHealthCheckRetries, + "the number of retries allowed before a healthcheck is considered to be unhealthy", + ) + _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) + + healthStartPeriodFlagName := "health-start-period" + createFlags.StringVar( + &cf.HealthStartPeriod, + healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, + "the initialization time needed for a container to bootstrap", + ) + _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) + + healthTimeoutFlagName := "health-timeout" + createFlags.StringVar( + &cf.HealthTimeout, + healthTimeoutFlagName, DefaultHealthCheckTimeout, + "the maximum time allowed to complete the healthcheck before an interval is considered failed", + ) + _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) - if !registry.IsRemote() { createFlags.BoolVar( - &cf.EnvHost, - "env-host", false, "Use all current host environment variables in container", + &cf.HTTPProxy, + "http-proxy", containerConfig.Containers.HTTPProxy, + "Set proxy environment variables in the container based on the host proxy vars", ) - } - envFileFlagName := "env-file" - createFlags.StringSliceVar( - &cf.EnvFile, - envFileFlagName, []string{}, - "Read in a file of environment variables", - ) - _ = cmd.RegisterFlagCompletionFunc(envFileFlagName, completion.AutocompleteDefault) + imageVolumeFlagName := "image-volume" + createFlags.StringVar( + &cf.ImageVolume, + imageVolumeFlagName, DefaultImageVolume, + `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, + ) + _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) - exposeFlagName := "expose" - createFlags.StringSliceVar( - &cf.Expose, - exposeFlagName, []string{}, - "Expose a port or a range of ports", - ) - _ = cmd.RegisterFlagCompletionFunc(exposeFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Init, + "init", false, + "Run an init binary inside the container that forwards signals and reaps processes", + ) - gidmapFlagName := "gidmap" - createFlags.StringSliceVar( - &cf.GIDMap, - gidmapFlagName, []string{}, - "GID map to use for the user namespace", - ) - _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) + initPathFlagName := "init-path" + createFlags.StringVar( + &cf.InitPath, + initPathFlagName, initPath(), + // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) + "Path to the container-init binary", + ) + _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) - groupAddFlagName := "group-add" - createFlags.StringSliceVar( - &cf.GroupAdd, - groupAddFlagName, []string{}, - "Add additional groups to the primary container process. 'keep-groups' allows container processes to use supplementary groups.", - ) - _ = cmd.RegisterFlagCompletionFunc(groupAddFlagName, completion.AutocompleteNone) + createFlags.BoolVarP( + &cf.Interactive, + "interactive", "i", false, + "Keep STDIN open even if not attached", + ) + ipcFlagName := "ipc" + createFlags.String( + ipcFlagName, "", + "IPC namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) - createFlags.Bool( - "help", false, "", - ) + kernelMemoryFlagName := "kernel-memory" + createFlags.StringVar( + &cf.KernelMemory, + kernelMemoryFlagName, "", + "Kernel memory limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + logDriverFlagName := "log-driver" + createFlags.StringVar( + &cf.LogDriver, + logDriverFlagName, logDriver(), + "Logging driver for the container", + ) + _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) - healthCmdFlagName := "health-cmd" - createFlags.StringVar( - &cf.HealthCmd, - healthCmdFlagName, "", - "set a healthcheck command for the container ('none' disables the existing healthcheck)", - ) - _ = cmd.RegisterFlagCompletionFunc(healthCmdFlagName, completion.AutocompleteNone) + logOptFlagName := "log-opt" + createFlags.StringSliceVar( + &cf.LogOptions, + logOptFlagName, []string{}, + "Logging driver options", + ) + _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) - healthIntervalFlagName := "health-interval" - createFlags.StringVar( - &cf.HealthInterval, - healthIntervalFlagName, DefaultHealthCheckInterval, - "set an interval for the healthchecks (a value of disable results in no automatic timer setup)", - ) - _ = cmd.RegisterFlagCompletionFunc(healthIntervalFlagName, completion.AutocompleteNone) + memoryFlagName := "memory" + createFlags.StringVarP( + &cf.Memory, + memoryFlagName, "m", "", + "Memory limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) - healthRetriesFlagName := "health-retries" - createFlags.UintVar( - &cf.HealthRetries, - healthRetriesFlagName, DefaultHealthCheckRetries, - "the number of retries allowed before a healthcheck is considered to be unhealthy", - ) - _ = cmd.RegisterFlagCompletionFunc(healthRetriesFlagName, completion.AutocompleteNone) + memoryReservationFlagName := "memory-reservation" + createFlags.StringVar( + &cf.MemoryReservation, + memoryReservationFlagName, "", + "Memory soft limit "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) - healthStartPeriodFlagName := "health-start-period" - createFlags.StringVar( - &cf.HealthStartPeriod, - healthStartPeriodFlagName, DefaultHealthCheckStartPeriod, - "the initialization time needed for a container to bootstrap", - ) - _ = cmd.RegisterFlagCompletionFunc(healthStartPeriodFlagName, completion.AutocompleteNone) + memorySwapFlagName := "memory-swap" + createFlags.StringVar( + &cf.MemorySwap, + memorySwapFlagName, "", + "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", + ) + _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) - healthTimeoutFlagName := "health-timeout" - createFlags.StringVar( - &cf.HealthTimeout, - healthTimeoutFlagName, DefaultHealthCheckTimeout, - "the maximum time allowed to complete the healthcheck before an interval is considered failed", - ) - _ = cmd.RegisterFlagCompletionFunc(healthTimeoutFlagName, completion.AutocompleteNone) + memorySwappinessFlagName := "memory-swappiness" + createFlags.Int64Var( + &cf.MemorySwappiness, + memorySwappinessFlagName, -1, + "Tune container memory swappiness (0 to 100, or -1 for system default)", + ) + _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) - hostnameFlagName := "hostname" - createFlags.StringVarP( - &cf.Hostname, - hostnameFlagName, "h", "", - "Set container hostname", - ) - _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.NoHealthCheck, + "no-healthcheck", false, + "Disable healthchecks on container", + ) + createFlags.BoolVar( + &cf.OOMKillDisable, + "oom-kill-disable", false, + "Disable OOM Killer", + ) - createFlags.BoolVar( - &cf.HTTPProxy, - "http-proxy", containerConfig.Containers.HTTPProxy, - "Set proxy environment variables in the container based on the host proxy vars", - ) + oomScoreAdjFlagName := "oom-score-adj" + createFlags.IntVar( + &cf.OOMScoreAdj, + oomScoreAdjFlagName, 0, + "Tune the host's OOM preferences (-1000 to 1000)", + ) + _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) - imageVolumeFlagName := "image-volume" - createFlags.StringVar( - &cf.ImageVolume, - imageVolumeFlagName, DefaultImageVolume, - `Tells podman how to handle the builtin image volumes ("bind"|"tmpfs"|"ignore")`, - ) - _ = cmd.RegisterFlagCompletionFunc(imageVolumeFlagName, AutocompleteImageVolume) + archFlagName := "arch" + createFlags.StringVar( + &cf.Arch, + archFlagName, "", + "use `ARCH` instead of the architecture of the machine for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch) - createFlags.BoolVar( - &cf.Init, - "init", false, - "Run an init binary inside the container that forwards signals and reaps processes", - ) + osFlagName := "os" + createFlags.StringVar( + &cf.OS, + osFlagName, "", + "use `OS` instead of the running OS for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) - initPathFlagName := "init-path" - createFlags.StringVar( - &cf.InitPath, - initPathFlagName, initPath(), - // Do not use the Value field for setting the default value to determine user input (i.e., non-empty string) - "Path to the container-init binary", - ) - _ = cmd.RegisterFlagCompletionFunc(initPathFlagName, completion.AutocompleteDefault) + variantFlagName := "variant" + createFlags.StringVar( + &cf.Variant, + variantFlagName, "", + "Use `VARIANT` instead of the running architecture variant for choosing images", + ) + _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) - createFlags.BoolVarP( - &cf.Interactive, - "interactive", "i", false, - "Keep STDIN open even if not attached", - ) + pidsLimitFlagName := "pids-limit" + createFlags.Int64( + pidsLimitFlagName, pidsLimit(), + "Tune container pids limit (set -1 for unlimited)", + ) + _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) - ipcFlagName := "ipc" - createFlags.String( - ipcFlagName, "", - "IPC namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(ipcFlagName, AutocompleteNamespace) + platformFlagName := "platform" + createFlags.StringVar( + &cf.Platform, + platformFlagName, "", + "Specify the platform for selecting the image. (Conflicts with --arch and --os)", + ) + _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) - kernelMemoryFlagName := "kernel-memory" - createFlags.StringVar( - &cf.KernelMemory, - kernelMemoryFlagName, "", - "Kernel memory limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone) + podFlagName := "pod" + createFlags.StringVar( + &cf.Pod, + podFlagName, "", + "Run container in an existing pod", + ) + _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) - labelFlagName := "label" - createFlags.StringArrayVarP( - &cf.Label, - labelFlagName, "l", []string{}, - "Set metadata on container", - ) - _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) + podIDFileFlagName := "pod-id-file" + createFlags.StringVar( + &cf.PodIDFile, + podIDFileFlagName, "", + "Read the pod ID from the file", + ) + _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + createFlags.BoolVar( + &cf.Privileged, + "privileged", false, + "Give extended privileges to container", + ) + createFlags.BoolVarP( + &cf.PublishAll, + "publish-all", "P", false, + "Publish all exposed ports to random ports on the host interface", + ) - labelFileFlagName := "label-file" - createFlags.StringSliceVar( - &cf.LabelFile, - labelFileFlagName, []string{}, - "Read in a line delimited file of labels", - ) - _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) + pullFlagName := "pull" + createFlags.StringVar( + &cf.Pull, + pullFlagName, policy(), + `Pull image before creating ("always"|"missing"|"never")`, + ) + _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) - logDriverFlagName := "log-driver" - createFlags.StringVar( - &cf.LogDriver, - logDriverFlagName, logDriver(), - "Logging driver for the container", - ) - _ = cmd.RegisterFlagCompletionFunc(logDriverFlagName, AutocompleteLogDriver) + createFlags.BoolVarP( + &cf.Quiet, + "quiet", "q", false, + "Suppress output information when pulling images", + ) + createFlags.BoolVar( + &cf.ReadOnly, + "read-only", false, + "Make containers root filesystem read-only", + ) + createFlags.BoolVar( + &cf.ReadOnlyTmpFS, + "read-only-tmpfs", true, + "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", + ) + requiresFlagName := "requires" + createFlags.StringSliceVar( + &cf.Requires, + requiresFlagName, []string{}, + "Add one or more requirement containers that must be started before this container will start", + ) + _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers) - logOptFlagName := "log-opt" - createFlags.StringSliceVar( - &cf.LogOptions, - logOptFlagName, []string{}, - "Logging driver options", - ) - _ = cmd.RegisterFlagCompletionFunc(logOptFlagName, AutocompleteLogOpt) + restartFlagName := "restart" + createFlags.StringVar( + &cf.Restart, + restartFlagName, "", + `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, + ) + _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) - memoryFlagName := "memory" - createFlags.StringVarP( - &cf.Memory, - memoryFlagName, "m", "", - "Memory limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) + createFlags.BoolVar( + &cf.Rm, + "rm", false, + "Remove container (and pod if created) after exit", + ) + createFlags.BoolVar( + &cf.RootFS, + "rootfs", false, + "The first argument is not an image but the rootfs to the exploded container", + ) - memoryReservationFlagName := "memory-reservation" - createFlags.StringVar( - &cf.MemoryReservation, - memoryReservationFlagName, "", - "Memory soft limit "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(memoryReservationFlagName, completion.AutocompleteNone) + sdnotifyFlagName := "sdnotify" + createFlags.StringVar( + &cf.SdNotifyMode, + sdnotifyFlagName, define.SdNotifyModeContainer, + `control sd-notify behavior ("container"|"conmon"|"ignore")`, + ) + _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) - memorySwapFlagName := "memory-swap" - createFlags.StringVar( - &cf.MemorySwap, - memorySwapFlagName, "", - "Swap limit equal to memory plus swap: '-1' to enable unlimited swap", - ) - _ = cmd.RegisterFlagCompletionFunc(memorySwapFlagName, completion.AutocompleteNone) + secretFlagName := "secret" + createFlags.StringArrayVar( + &cf.Secrets, + secretFlagName, []string{}, + "Add secret to container", + ) + _ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets) - memorySwappinessFlagName := "memory-swappiness" - createFlags.Int64Var( - &cf.MemorySwappiness, - memorySwappinessFlagName, -1, - "Tune container memory swappiness (0 to 100, or -1 for system default)", - ) - _ = cmd.RegisterFlagCompletionFunc(memorySwappinessFlagName, completion.AutocompleteNone) + securityOptFlagName := "security-opt" + createFlags.StringArrayVar( + &cf.SecurityOpt, + securityOptFlagName, []string{}, + "Security Options", + ) + _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) - nameFlagName := "name" - createFlags.StringVar( - &cf.Name, - nameFlagName, "", - "Assign a name to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + shmSizeFlagName := "shm-size" + createFlags.String( + shmSizeFlagName, shmSize(), + "Size of /dev/shm "+sizeWithUnitFormat, + ) + _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) - createFlags.BoolVar( - &cf.NoHealthCheck, - "no-healthcheck", false, - "Disable healthchecks on container", - ) - createFlags.BoolVar( - &cf.OOMKillDisable, - "oom-kill-disable", false, - "Disable OOM Killer", - ) - - oomScoreAdjFlagName := "oom-score-adj" - createFlags.IntVar( - &cf.OOMScoreAdj, - oomScoreAdjFlagName, 0, - "Tune the host's OOM preferences (-1000 to 1000)", - ) - _ = cmd.RegisterFlagCompletionFunc(oomScoreAdjFlagName, completion.AutocompleteNone) + stopSignalFlagName := "stop-signal" + createFlags.StringVar( + &cf.SignaturePolicy, + "signature-policy", "", + "`Pathname` of signature policy file (not usually used)", + ) + createFlags.StringVar( + &cf.StopSignal, + stopSignalFlagName, "", + "Signal to stop a container. Default is SIGTERM", + ) + _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) - archFlagName := "arch" - createFlags.StringVar( - &cf.Arch, - archFlagName, "", - "use `ARCH` instead of the architecture of the machine for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(archFlagName, completion.AutocompleteArch) + stopTimeoutFlagName := "stop-timeout" + createFlags.UintVar( + &cf.StopTimeout, + stopTimeoutFlagName, containerConfig.Engine.StopTimeout, + "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", + ) + _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) - osFlagName := "os" - createFlags.StringVar( - &cf.OS, - osFlagName, "", - "use `OS` instead of the running OS for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) + sysctlFlagName := "sysctl" + createFlags.StringSliceVar( + &cf.Sysctl, + sysctlFlagName, []string{}, + "Sysctl options", + ) + //TODO: Add function for sysctl completion. + _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) - variantFlagName := "variant" - createFlags.StringVar( - &cf.Variant, - variantFlagName, "", - "Use _VARIANT_ instead of the running architecture variant for choosing images", - ) - _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) + systemdFlagName := "systemd" + createFlags.StringVar( + &cf.Systemd, + systemdFlagName, "true", + `Run container in systemd mode ("true"|"false"|"always")`, + ) + _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) - pidFlagName := "pid" - createFlags.String( - pidFlagName, "", - "PID namespace to use", - ) - _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) + personalityFlagName := "personality" + createFlags.StringVar( + &cf.Personality, + personalityFlagName, "", + "Configure execution domain using personality (e.g., LINUX/LINUX32)", + ) + _ = cmd.RegisterFlagCompletionFunc(personalityFlagName, AutocompleteNamespace) - pidsLimitFlagName := "pids-limit" - createFlags.Int64( - pidsLimitFlagName, pidsLimit(), - "Tune container pids limit (set 0 for unlimited, -1 for server defaults)", - ) - _ = cmd.RegisterFlagCompletionFunc(pidsLimitFlagName, completion.AutocompleteNone) + timeoutFlagName := "timeout" + createFlags.UintVar( + &cf.Timeout, + timeoutFlagName, 0, + "Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.", + ) + _ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone) - platformFlagName := "platform" - createFlags.StringVar( - &cf.Platform, - platformFlagName, "", - "Specify the platform for selecting the image. (Conflicts with --arch and --os)", - ) - _ = cmd.RegisterFlagCompletionFunc(platformFlagName, completion.AutocompleteNone) + commonFlag.OptionalBoolFlag(createFlags, + &cf.TLSVerify, + "tls-verify", + "Require HTTPS and verify certificates when contacting registries for pulling images", + ) - podFlagName := "pod" - createFlags.StringVar( - &cf.Pod, - podFlagName, "", - "Run container in an existing pod", - ) - _ = cmd.RegisterFlagCompletionFunc(podFlagName, AutocompletePods) + tmpfsFlagName := "tmpfs" + createFlags.StringArrayVar( + &cf.TmpFS, + tmpfsFlagName, []string{}, + "Mount a temporary filesystem (`tmpfs`) into a container", + ) + _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) - podIDFileFlagName := "pod-id-file" - createFlags.StringVar( - &cf.PodIDFile, - podIDFileFlagName, "", - "Read the pod ID from the file", - ) - _ = cmd.RegisterFlagCompletionFunc(podIDFileFlagName, completion.AutocompleteDefault) + createFlags.BoolVarP( + &cf.TTY, + "tty", "t", false, + "Allocate a pseudo-TTY for container", + ) - createFlags.BoolVar( - &cf.Privileged, - "privileged", false, - "Give extended privileges to container", - ) - createFlags.BoolVarP( - &cf.PublishAll, - "publish-all", "P", false, - "Publish all exposed ports to random ports on the host interface", - ) + timezoneFlagName := "tz" + createFlags.StringVar( + &cf.Timezone, + timezoneFlagName, containerConfig.TZ(), + "Set timezone in container", + ) + _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion - pullFlagName := "pull" - createFlags.StringVar( - &cf.Pull, - pullFlagName, policy(), - `Pull image before creating ("always"|"missing"|"never")`, - ) - _ = cmd.RegisterFlagCompletionFunc(pullFlagName, AutocompletePullOption) - - createFlags.BoolVarP( - &cf.Quiet, - "quiet", "q", false, - "Suppress output information when pulling images", - ) - createFlags.BoolVar( - &cf.ReadOnly, - "read-only", false, - "Make containers root filesystem read-only", - ) - createFlags.BoolVar( - &cf.ReadOnlyTmpFS, - "read-only-tmpfs", true, - "When running containers in read-only mode mount a read-write tmpfs on /run, /tmp and /var/tmp", - ) - createFlags.BoolVar( - &cf.Replace, - "replace", false, - `If a container with the same name exists, replace it`, - ) + umaskFlagName := "umask" + createFlags.StringVar( + &cf.Umask, + umaskFlagName, containerConfig.Umask(), + "Set umask in container", + ) + _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) - requiresFlagName := "requires" - createFlags.StringSliceVar( - &cf.Requires, - requiresFlagName, []string{}, - "Add one or more requirement containers that must be started before this container will start", - ) - _ = cmd.RegisterFlagCompletionFunc(requiresFlagName, AutocompleteContainers) + ulimitFlagName := "ulimit" + createFlags.StringSliceVar( + &cf.Ulimit, + ulimitFlagName, ulimits(), + "Ulimit options", + ) + _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) - restartFlagName := "restart" - createFlags.StringVar( - &cf.Restart, - restartFlagName, "", - `Restart policy to apply when a container exits ("always"|"no"|"on-failure"|"unless-stopped")`, - ) - _ = cmd.RegisterFlagCompletionFunc(restartFlagName, AutocompleteRestartOption) + userFlagName := "user" + createFlags.StringVarP( + &cf.User, + userFlagName, "u", "", + "Username or UID (format: [:])", + ) + _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - createFlags.BoolVar( - &cf.Rm, - "rm", false, - "Remove container (and pod if created) after exit", - ) - createFlags.BoolVar( - &cf.RootFS, - "rootfs", false, - "The first argument is not an image but the rootfs to the exploded container", - ) + utsFlagName := "uts" + createFlags.String( + utsFlagName, "", + "UTS namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) - sdnotifyFlagName := "sdnotify" - createFlags.StringVar( - &cf.SdNotifyMode, - sdnotifyFlagName, define.SdNotifyModeContainer, - `control sd-notify behavior ("container"|"conmon"|"ignore")`, - ) - _ = cmd.RegisterFlagCompletionFunc(sdnotifyFlagName, AutocompleteSDNotify) + mountFlagName := "mount" + createFlags.StringArrayVar( + &cf.Mount, + mountFlagName, []string{}, + "Attach a filesystem mount to the container", + ) + _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) - secretFlagName := "secret" - createFlags.StringArrayVar( - &cf.Secrets, - secretFlagName, []string{}, - "Add secret to container", - ) - _ = cmd.RegisterFlagCompletionFunc(secretFlagName, AutocompleteSecrets) + volumeDesciption := "Bind mount a volume into the container" + if registry.IsRemote() { + volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" + } + volumeFlagName := "volume" + createFlags.StringArrayVarP( + &cf.Volume, + volumeFlagName, "v", volumes(), + volumeDesciption, + ) + _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) - securityOptFlagName := "security-opt" - createFlags.StringArrayVar( - &cf.SecurityOpt, - securityOptFlagName, []string{}, - "Security Options", - ) - _ = cmd.RegisterFlagCompletionFunc(securityOptFlagName, AutocompleteSecurityOption) + volumesFromFlagName := "volumes-from" + createFlags.StringArrayVar( + &cf.VolumesFrom, + volumesFromFlagName, []string{}, + "Mount volumes from the specified container(s)", + ) + _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) - shmSizeFlagName := "shm-size" - createFlags.String( - shmSizeFlagName, shmSize(), - "Size of /dev/shm "+sizeWithUnitFormat, - ) - _ = cmd.RegisterFlagCompletionFunc(shmSizeFlagName, completion.AutocompleteNone) + workdirFlagName := "workdir" + createFlags.StringVarP( + &cf.Workdir, + workdirFlagName, "w", "", + "Working directory inside the container", + ) + _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) - stopSignalFlagName := "stop-signal" - createFlags.StringVar( - &cf.SignaturePolicy, - "signature-policy", "", - "`Pathname` of signature policy file (not usually used)", - ) - createFlags.StringVar( - &cf.StopSignal, - stopSignalFlagName, "", - "Signal to stop a container. Default is SIGTERM", - ) - _ = cmd.RegisterFlagCompletionFunc(stopSignalFlagName, AutocompleteStopSignal) + seccompPolicyFlagName := "seccomp-policy" + createFlags.StringVar( + &cf.SeccompPolicy, + seccompPolicyFlagName, "default", + "Policy for selecting a seccomp profile (experimental)", + ) + _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) - stopTimeoutFlagName := "stop-timeout" - createFlags.UintVar( - &cf.StopTimeout, - stopTimeoutFlagName, containerConfig.Engine.StopTimeout, - "Timeout (in seconds) that containers stopped by user command have to exit. If exceeded, the container will be forcibly stopped via SIGKILL.", - ) - _ = cmd.RegisterFlagCompletionFunc(stopTimeoutFlagName, completion.AutocompleteNone) + cgroupConfFlagName := "cgroup-conf" + createFlags.StringSliceVar( + &cf.CgroupConf, + cgroupConfFlagName, []string{}, + "Configure cgroup v2 (key=value)", + ) + _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) - storageOptFlagName := "storage-opt" - createFlags.StringSliceVar( - &cf.StorageOpt, - storageOptFlagName, []string{}, - "Storage driver options per container", - ) - //FIXME: What should we suggest here? The flag is not in the man page. - _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone) + pidFileFlagName := "pidfile" + createFlags.StringVar( + &cf.PidFile, + pidFileFlagName, "", + "Write the container process ID to the file") + _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault) + + _ = createFlags.MarkHidden("signature-policy") + if registry.IsRemote() { + _ = createFlags.MarkHidden("env-host") + _ = createFlags.MarkHidden("http-proxy") + } + + createFlags.BoolVar( + &cf.Replace, + "replace", false, + `If a container with the same name exists, replace it`, + ) + } subgidnameFlagName := "subgidname" createFlags.StringVar( @@ -680,60 +754,13 @@ ) _ = cmd.RegisterFlagCompletionFunc(subuidnameFlagName, completion.AutocompleteSubuidName) - sysctlFlagName := "sysctl" + gidmapFlagName := "gidmap" createFlags.StringSliceVar( - &cf.Sysctl, - sysctlFlagName, []string{}, - "Sysctl options", - ) - //TODO: Add function for sysctl completion. - _ = cmd.RegisterFlagCompletionFunc(sysctlFlagName, completion.AutocompleteNone) - - systemdFlagName := "systemd" - createFlags.StringVar( - &cf.Systemd, - systemdFlagName, "true", - `Run container in systemd mode ("true"|"false"|"always")`, - ) - _ = cmd.RegisterFlagCompletionFunc(systemdFlagName, AutocompleteSystemdFlag) - - timeoutFlagName := "timeout" - createFlags.UintVar( - &cf.Timeout, - timeoutFlagName, 0, - "Maximum length of time a container is allowed to run. The container will be killed automatically after the time expires.", - ) - _ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone) - - tmpfsFlagName := "tmpfs" - createFlags.StringArrayVar( - &cf.TmpFS, - tmpfsFlagName, []string{}, - "Mount a temporary filesystem (`tmpfs`) into a container", - ) - _ = cmd.RegisterFlagCompletionFunc(tmpfsFlagName, completion.AutocompleteDefault) - - createFlags.BoolVarP( - &cf.TTY, - "tty", "t", false, - "Allocate a pseudo-TTY for container", - ) - - timezoneFlagName := "tz" - createFlags.StringVar( - &cf.Timezone, - timezoneFlagName, containerConfig.TZ(), - "Set timezone in container", - ) - _ = cmd.RegisterFlagCompletionFunc(timezoneFlagName, completion.AutocompleteNone) //TODO: add timezone completion - - umaskFlagName := "umask" - createFlags.StringVar( - &cf.Umask, - umaskFlagName, containerConfig.Umask(), - "Set umask in container", + &cf.GIDMap, + gidmapFlagName, []string{}, + "GID map to use for the user namespace", ) - _ = cmd.RegisterFlagCompletionFunc(umaskFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(gidmapFlagName, completion.AutocompleteNone) uidmapFlagName := "uidmap" createFlags.StringSliceVar( @@ -743,22 +770,6 @@ ) _ = cmd.RegisterFlagCompletionFunc(uidmapFlagName, completion.AutocompleteNone) - ulimitFlagName := "ulimit" - createFlags.StringSliceVar( - &cf.Ulimit, - ulimitFlagName, ulimits(), - "Ulimit options", - ) - _ = cmd.RegisterFlagCompletionFunc(ulimitFlagName, completion.AutocompleteNone) - - userFlagName := "user" - createFlags.StringVarP( - &cf.User, - userFlagName, "u", "", - "Username or UID (format: [:])", - ) - _ = cmd.RegisterFlagCompletionFunc(userFlagName, AutocompleteUserFlag) - usernsFlagName := "userns" createFlags.String( usernsFlagName, os.Getenv("PODMAN_USERNS"), @@ -766,75 +777,90 @@ ) _ = cmd.RegisterFlagCompletionFunc(usernsFlagName, AutocompleteUserNamespace) - utsFlagName := "uts" - createFlags.String( - utsFlagName, "", - "UTS namespace to use", + cgroupParentFlagName := "cgroup-parent" + createFlags.StringVar( + &cf.CGroupParent, + cgroupParentFlagName, "", + "Optional parent cgroup for the container", ) - _ = cmd.RegisterFlagCompletionFunc(utsFlagName, AutocompleteNamespace) + _ = cmd.RegisterFlagCompletionFunc(cgroupParentFlagName, completion.AutocompleteDefault) - mountFlagName := "mount" - createFlags.StringArrayVar( - &cf.Mount, - mountFlagName, []string{}, - "Attach a filesystem mount to the container", - ) - _ = cmd.RegisterFlagCompletionFunc(mountFlagName, AutocompleteMountFlag) - - volumeDesciption := "Bind mount a volume into the container" - if registry.IsRemote() { - volumeDesciption = "Bind mount a volume into the container. Volume src will be on the server machine, not the client" + conmonPidfileFlagName := "" + if !isInfra { + conmonPidfileFlagName = "conmon-pidfile" + } else { + conmonPidfileFlagName = "infra-conmon-pidfile" } - volumeFlagName := "volume" - createFlags.StringArrayVarP( - &cf.Volume, - volumeFlagName, "v", volumes(), - volumeDesciption, + createFlags.StringVar( + &cf.ConmonPIDFile, + conmonPidfileFlagName, "", + "Path to the file that will receive the PID of conmon", ) - _ = cmd.RegisterFlagCompletionFunc(volumeFlagName, AutocompleteVolumeFlag) + _ = cmd.RegisterFlagCompletionFunc(conmonPidfileFlagName, completion.AutocompleteDefault) + + entrypointFlagName := "" + if !isInfra { + entrypointFlagName = "entrypoint" + } else { + entrypointFlagName = "infra-command" + } - volumesFromFlagName := "volumes-from" - createFlags.StringArrayVar( - &cf.VolumesFrom, - volumesFromFlagName, []string{}, - "Mount volumes from the specified container(s)", + createFlags.String(entrypointFlagName, "", + "Overwrite the default ENTRYPOINT of the image", ) - _ = cmd.RegisterFlagCompletionFunc(volumesFromFlagName, AutocompleteContainers) + _ = cmd.RegisterFlagCompletionFunc(entrypointFlagName, completion.AutocompleteNone) - workdirFlagName := "workdir" + hostnameFlagName := "hostname" createFlags.StringVarP( - &cf.Workdir, - workdirFlagName, "w", "", - "Working directory inside the container", + &cf.Hostname, + hostnameFlagName, "h", "", + "Set container hostname", ) - _ = cmd.RegisterFlagCompletionFunc(workdirFlagName, completion.AutocompleteDefault) + _ = cmd.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) - seccompPolicyFlagName := "seccomp-policy" - createFlags.StringVar( - &cf.SeccompPolicy, - seccompPolicyFlagName, "default", - "Policy for selecting a seccomp profile (experimental)", + labelFlagName := "label" + createFlags.StringArrayVarP( + &cf.Label, + labelFlagName, "l", []string{}, + "Set metadata on container", ) - _ = cmd.RegisterFlagCompletionFunc(seccompPolicyFlagName, completion.AutocompleteDefault) + _ = cmd.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) - cgroupConfFlagName := "cgroup-conf" + labelFileFlagName := "label-file" createFlags.StringSliceVar( - &cf.CgroupConf, - cgroupConfFlagName, []string{}, - "Configure cgroup v2 (key=value)", + &cf.LabelFile, + labelFileFlagName, []string{}, + "Read in a line delimited file of labels", ) - _ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone) + _ = cmd.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) - pidFileFlagName := "pidfile" - createFlags.StringVar( - &cf.PidFile, - pidFileFlagName, "", - "Write the container process ID to the file") - _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault) - - _ = createFlags.MarkHidden("signature-policy") - if registry.IsRemote() { - _ = createFlags.MarkHidden("env-host") - _ = createFlags.MarkHidden("http-proxy") + nameFlagName := "" + if !isInfra { + nameFlagName = "name" + createFlags.StringVar( + &cf.Name, + nameFlagName, "", + "Assign a name to the container", + ) + } else { + nameFlagName = "infra-name" + createFlags.StringVar( + &cf.Name, + nameFlagName, "", + "Assign a name to the container", + ) } + _ = cmd.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) + + createFlags.Bool( + "help", false, "", + ) + + pidFlagName := "pid" + createFlags.StringVar( + &cf.PID, + pidFlagName, "", + "PID namespace to use", + ) + _ = cmd.RegisterFlagCompletionFunc(pidFlagName, AutocompleteNamespace) } diff -Nru libpod-3.2.1+ds1/cmd/podman/common/create_opts.go libpod-3.4.4+ds1/cmd/podman/common/create_opts.go --- libpod-3.2.1+ds1/cmd/podman/common/create_opts.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/create_opts.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,128 +8,18 @@ "strconv" "strings" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" + "github.com/docker/docker/api/types/mount" "github.com/pkg/errors" ) -type ContainerCLIOpts struct { - Annotation []string - Attach []string - Authfile string - BlkIOWeight string - BlkIOWeightDevice []string - CapAdd []string - CapDrop []string - CgroupNS string - CGroupsMode string - CGroupParent string - CIDFile string - ConmonPIDFile string - CPUPeriod uint64 - CPUQuota int64 - CPURTPeriod uint64 - CPURTRuntime int64 - CPUShares uint64 - CPUS float64 - CPUSetCPUs string - CPUSetMems string - Devices []string - DeviceCGroupRule []string - DeviceReadBPs []string - DeviceReadIOPs []string - DeviceWriteBPs []string - DeviceWriteIOPs []string - Entrypoint *string - Env []string - EnvHost bool - EnvFile []string - Expose []string - GIDMap []string - GroupAdd []string - HealthCmd string - HealthInterval string - HealthRetries uint - HealthStartPeriod string - HealthTimeout string - Hostname string - HTTPProxy bool - ImageVolume string - Init bool - InitPath string - Interactive bool - IPC string - KernelMemory string - Label []string - LabelFile []string - LogDriver string - LogOptions []string - Memory string - MemoryReservation string - MemorySwap string - MemorySwappiness int64 - Name string - NoHealthCheck bool - OOMKillDisable bool - OOMScoreAdj int - Arch string - OS string - Variant string - PID string - PIDsLimit *int64 - Platform string - Pod string - PodIDFile string - PreserveFDs uint - Privileged bool - PublishAll bool - Pull string - Quiet bool - ReadOnly bool - ReadOnlyTmpFS bool - Restart string - Replace bool - Requires []string - Rm bool - RootFS bool - Secrets []string - SecurityOpt []string - SdNotifyMode string - ShmSize string - SignaturePolicy string - StopSignal string - StopTimeout uint - StorageOpt []string - SubUIDName string - SubGIDName string - Sysctl []string - Systemd string - Timeout uint - TmpFS []string - TTY bool - Timezone string - Umask string - UIDMap []string - Ulimit []string - User string - UserNS string - UTS string - Mount []string - Volume []string - VolumesFrom []string - Workdir string - SeccompPolicy string - PidFile string - - Net *entities.NetOptions - - CgroupConf []string -} - func stringMaptoArray(m map[string]string) []string { a := make([]string, 0, len(m)) for k, v := range m { @@ -140,13 +30,13 @@ // ContainerCreateToContainerCLIOpts converts a compat input struct to cliopts so it can be converted to // a specgen spec. -func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, cgroupsManager string) (*ContainerCLIOpts, []string, error) { +func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.ContainerCreateOptions, []string, error) { var ( capAdd []string cappDrop []string entrypoint *string init bool - specPorts []specgen.PortMapping + specPorts []types.PortMapping ) if cc.HostConfig.Init != nil { @@ -205,18 +95,35 @@ expose = append(expose, fmt.Sprintf("%s/%s", p.Port(), p.Proto())) } - // mounts type=tmpfs/bind,source=,dest=,opt=val - // TODO options + // mounts type=tmpfs/bind,source=...,target=...=,opt=val mounts := make([]string, 0, len(cc.HostConfig.Mounts)) + var builder strings.Builder for _, m := range cc.HostConfig.Mounts { - mount := fmt.Sprintf("type=%s", m.Type) - if len(m.Source) > 0 { - mount += fmt.Sprintf(",source=%s", m.Source) - } - if len(m.Target) > 0 { - mount += fmt.Sprintf(",dst=%s", m.Target) + addField(&builder, "type", string(m.Type)) + addField(&builder, "source", m.Source) + addField(&builder, "target", m.Target) + if m.ReadOnly { + addField(&builder, "ro", "true") + } + addField(&builder, "consistency", string(m.Consistency)) + // Map any specialized mount options that intersect between *Options and cli options + switch m.Type { + case mount.TypeBind: + if m.BindOptions != nil { + addField(&builder, "bind-propagation", string(m.BindOptions.Propagation)) + addField(&builder, "bind-nonrecursive", strconv.FormatBool(m.BindOptions.NonRecursive)) + } + 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)) + } + case mount.TypeVolume: + // All current VolumeOpts are handled above + // See vendor/github.com/containers/common/pkg/parse/parse.go:ValidateVolumeOpts() } - mounts = append(mounts, mount) + mounts = append(mounts, builder.String()) + builder.Reset() } // dns @@ -236,7 +143,7 @@ if err != nil { return nil, nil, err } - tmpPort := specgen.PortMapping{ + tmpPort := types.PortMapping{ HostIP: pb.HostIP, ContainerPort: uint16(port.Int()), HostPort: uint16(hostport), @@ -248,7 +155,7 @@ } // netMode - nsmode, _, err := specgen.ParseNetworkNamespace(string(cc.HostConfig.NetworkMode)) + nsmode, networks, err := specgen.ParseNetworkNamespace(string(cc.HostConfig.NetworkMode), true) if err != nil { return nil, nil, err } @@ -321,7 +228,7 @@ netInfo.Aliases = aliases netInfo.CNINetworks = cniNetworks case len(cc.HostConfig.NetworkMode) > 0: - netInfo.CNINetworks = []string{string(cc.HostConfig.NetworkMode)} + netInfo.CNINetworks = networks } parsedTmp := make([]string, 0, len(cc.HostConfig.Tmpfs)) @@ -336,7 +243,7 @@ // Note: several options here are marked as "don't need". this is based // on speculation by Matt and I. We think that these come into play later // like with start. We believe this is just a difference in podman/compat - cliOpts := ContainerCLIOpts{ + cliOpts := entities.ContainerCreateOptions{ // Attach: nil, // don't need? Authfile: "", CapAdd: append(capAdd, cc.HostConfig.CapAdd...), @@ -353,51 +260,55 @@ CPUSetMems: cc.HostConfig.CpusetMems, // Detach: false, // don't need // DetachKeys: "", // don't need - Devices: devices, - DeviceCGroupRule: nil, - DeviceReadBPs: readBps, - DeviceReadIOPs: readIops, - DeviceWriteBPs: writeBps, - DeviceWriteIOPs: writeIops, - Entrypoint: entrypoint, - Env: cc.Config.Env, - Expose: expose, - GroupAdd: cc.HostConfig.GroupAdd, - Hostname: cc.Config.Hostname, - ImageVolume: "bind", - Init: init, - Interactive: cc.Config.OpenStdin, - IPC: string(cc.HostConfig.IpcMode), - Label: stringMaptoArray(cc.Config.Labels), - LogDriver: cc.HostConfig.LogConfig.Type, - LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), - Name: cc.Name, - OOMScoreAdj: cc.HostConfig.OomScoreAdj, - Arch: "", - OS: "", - Variant: "", - PID: string(cc.HostConfig.PidMode), - PIDsLimit: cc.HostConfig.PidsLimit, - Privileged: cc.HostConfig.Privileged, - PublishAll: cc.HostConfig.PublishAllPorts, - Quiet: false, - ReadOnly: cc.HostConfig.ReadonlyRootfs, - ReadOnlyTmpFS: true, // podman default - Rm: cc.HostConfig.AutoRemove, - SecurityOpt: cc.HostConfig.SecurityOpt, - StopSignal: cc.Config.StopSignal, - StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), - Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), - Systemd: "true", // podman default - TmpFS: parsedTmp, - TTY: cc.Config.Tty, - User: cc.Config.User, - UserNS: string(cc.HostConfig.UsernsMode), - UTS: string(cc.HostConfig.UTSMode), - Mount: mounts, - VolumesFrom: cc.HostConfig.VolumesFrom, - Workdir: cc.Config.WorkingDir, - Net: &netInfo, + Devices: devices, + DeviceCGroupRule: nil, + DeviceReadBPs: readBps, + DeviceReadIOPs: readIops, + DeviceWriteBPs: writeBps, + DeviceWriteIOPs: writeIops, + Entrypoint: entrypoint, + Env: cc.Config.Env, + Expose: expose, + GroupAdd: cc.HostConfig.GroupAdd, + Hostname: cc.Config.Hostname, + ImageVolume: "bind", + Init: init, + Interactive: cc.Config.OpenStdin, + IPC: string(cc.HostConfig.IpcMode), + Label: stringMaptoArray(cc.Config.Labels), + LogDriver: cc.HostConfig.LogConfig.Type, + LogOptions: stringMaptoArray(cc.HostConfig.LogConfig.Config), + Name: cc.Name, + OOMScoreAdj: cc.HostConfig.OomScoreAdj, + Arch: "", + OS: "", + Variant: "", + PID: string(cc.HostConfig.PidMode), + PIDsLimit: cc.HostConfig.PidsLimit, + Privileged: cc.HostConfig.Privileged, + PublishAll: cc.HostConfig.PublishAllPorts, + Quiet: false, + ReadOnly: cc.HostConfig.ReadonlyRootfs, + ReadOnlyTmpFS: true, // podman default + Rm: cc.HostConfig.AutoRemove, + SecurityOpt: cc.HostConfig.SecurityOpt, + StopSignal: cc.Config.StopSignal, + StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt), + Sysctl: stringMaptoArray(cc.HostConfig.Sysctls), + Systemd: "true", // podman default + TmpFS: parsedTmp, + TTY: cc.Config.Tty, + User: cc.Config.User, + UserNS: string(cc.HostConfig.UsernsMode), + UTS: string(cc.HostConfig.UTSMode), + Mount: mounts, + VolumesFrom: cc.HostConfig.VolumesFrom, + Workdir: cc.Config.WorkingDir, + Net: &netInfo, + HealthInterval: DefaultHealthCheckInterval, + HealthRetries: DefaultHealthCheckRetries, + HealthTimeout: DefaultHealthCheckTimeout, + HealthStartPeriod: DefaultHealthCheckStartPeriod, } if !rootless.IsRootless() { var ulimits []string @@ -507,7 +418,7 @@ cliOpts.Restart = policy } - if cc.HostConfig.MemorySwappiness != nil && (!rootless.IsRootless() || rootless.IsRootless() && cgroupsv2 && cgroupsManager == "systemd") { + if cc.HostConfig.MemorySwappiness != nil && (!rootless.IsRootless() || rootless.IsRootless() && cgroupsv2 && rtc.Engine.CgroupManager == "systemd") { cliOpts.MemorySwappiness = *cc.HostConfig.MemorySwappiness } else { cliOpts.MemorySwappiness = -1 @@ -516,11 +427,26 @@ cliOpts.OOMKillDisable = *cc.HostConfig.OomKillDisable } if cc.Config.Healthcheck != nil { - cliOpts.HealthCmd = strings.Join(cc.Config.Healthcheck.Test, " ") - cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() - cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) - cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() - cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String() + finCmd := "" + for _, str := range cc.Config.Healthcheck.Test { + finCmd = finCmd + str + " " + } + if len(finCmd) > 1 { + finCmd = finCmd[:len(finCmd)-1] + } + cliOpts.HealthCmd = finCmd + if cc.Config.Healthcheck.Interval > 0 { + cliOpts.HealthInterval = cc.Config.Healthcheck.Interval.String() + } + if cc.Config.Healthcheck.Retries > 0 { + cliOpts.HealthRetries = uint(cc.Config.Healthcheck.Retries) + } + if cc.Config.Healthcheck.StartPeriod > 0 { + cliOpts.HealthStartPeriod = cc.Config.Healthcheck.StartPeriod.String() + } + if cc.Config.Healthcheck.Timeout > 0 { + cliOpts.HealthTimeout = cc.Config.Healthcheck.Timeout.String() + } } // specgen assumes the image name is arg[0] @@ -598,3 +524,17 @@ } return "" } + +// addField is a helper function to populate mount options +func addField(b *strings.Builder, name string, value string) { + if value == "" { + return + } + + if b.Len() > 0 { + b.WriteRune(',') + } + b.WriteString(name) + b.WriteRune('=') + b.WriteString(value) +} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/createparse.go libpod-3.4.4+ds1/cmd/podman/common/createparse.go --- libpod-3.2.1+ds1/cmd/podman/common/createparse.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/createparse.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -package common - -import ( - "github.com/containers/common/pkg/config" - "github.com/pkg/errors" -) - -// validate determines if the flags and values given by the user are valid. things checked -// by validate must not need any state information on the flag (i.e. changed) -func (c *ContainerCLIOpts) validate() error { - var () - if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { - return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) - } - - if _, err := config.ParsePullPolicy(c.Pull); err != nil { - return err - } - - var imageVolType = map[string]string{ - "bind": "", - "tmpfs": "", - "ignore": "", - } - if _, ok := imageVolType[c.ImageVolume]; !ok { - return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume) - } - return nil -} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/netflags.go libpod-3.4.4+ds1/cmd/podman/common/netflags.go --- libpod-3.2.1+ds1/cmd/podman/common/netflags.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/netflags.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,15 +2,16 @@ import ( "net" - "strings" "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) func DefineNetFlags(cmd *cobra.Command) { @@ -85,12 +86,18 @@ ) } -func NetFlagsToNetOptions(cmd *cobra.Command) (*entities.NetOptions, error) { +// NetFlagsToNetOptions parses the network flags for the given cmd. +// The netnsFromConfig bool is used to indicate if the --network flag +// should always be parsed regardless if it was set on the cli. +func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet, netnsFromConfig bool) (*entities.NetOptions, error) { var ( err error ) - opts := entities.NetOptions{} - opts.AddHosts, err = cmd.Flags().GetStringSlice("add-host") + if opts == nil { + opts = &entities.NetOptions{} + } + + opts.AddHosts, err = flags.GetStringSlice("add-host") if err != nil { return nil, err } @@ -101,56 +108,50 @@ } } - if cmd.Flags().Changed("dns") { - servers, err := cmd.Flags().GetStringSlice("dns") - if err != nil { - return nil, err - } - for _, d := range servers { - if d == "none" { - opts.UseImageResolvConf = true - if len(servers) > 1 { - return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d) - } - break - } - dns := net.ParseIP(d) - if dns == nil { - return nil, errors.Errorf("%s is not an ip address", d) + servers, err := flags.GetStringSlice("dns") + if err != nil { + return nil, err + } + for _, d := range servers { + if d == "none" { + opts.UseImageResolvConf = true + if len(servers) > 1 { + return nil, errors.Errorf("%s is not allowed to be specified with other DNS ip addresses", d) } - opts.DNSServers = append(opts.DNSServers, dns) + break + } + dns := net.ParseIP(d) + if dns == nil { + return nil, errors.Errorf("%s is not an ip address", d) } + opts.DNSServers = append(opts.DNSServers, dns) } - if cmd.Flags().Changed("dns-opt") { - options, err := cmd.Flags().GetStringSlice("dns-opt") - if err != nil { - return nil, err - } - opts.DNSOptions = options + options, err := flags.GetStringSlice("dns-opt") + if err != nil { + return nil, err } + opts.DNSOptions = options - if cmd.Flags().Changed("dns-search") { - dnsSearches, err := cmd.Flags().GetStringSlice("dns-search") - if err != nil { - return nil, err - } - // Validate domains are good - for _, dom := range dnsSearches { - if dom == "." { - if len(dnsSearches) > 1 { - return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") - } - continue - } - if _, err := parse.ValidateDomain(dom); err != nil { - return nil, err + dnsSearches, err := flags.GetStringSlice("dns-search") + if err != nil { + return nil, err + } + // Validate domains are good + for _, dom := range dnsSearches { + if dom == "." { + if len(dnsSearches) > 1 { + return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'") } + continue + } + if _, err := parse.ValidateDomain(dom); err != nil { + return nil, err } - opts.DNSSearch = dnsSearches } + opts.DNSSearch = dnsSearches - m, err := cmd.Flags().GetString("mac-address") + m, err := flags.GetString("mac-address") if err != nil { return nil, err } @@ -162,18 +163,18 @@ opts.StaticMAC = &mac } - inputPorts, err := cmd.Flags().GetStringSlice("publish") + inputPorts, err := flags.GetStringSlice("publish") if err != nil { return nil, err } if len(inputPorts) > 0 { - opts.PublishPorts, err = createPortBindings(inputPorts) + opts.PublishPorts, err = specgenutil.CreatePortBindings(inputPorts) if err != nil { return nil, err } } - ip, err := cmd.Flags().GetString("ip") + ip, err := flags.GetString("ip") if err != nil { return nil, err } @@ -188,39 +189,38 @@ opts.StaticIP = &staticIP } - opts.NoHosts, err = cmd.Flags().GetBool("no-hosts") + opts.NoHosts, err = flags.GetBool("no-hosts") if err != nil { return nil, err } - if cmd.Flags().Changed("network") { - network, err := cmd.Flags().GetString("network") + // parse the --network value only when the flag is set or we need to use + // the netns config value, e.g. when --pod is not used + if netnsFromConfig || flags.Changed("network") { + network, err := flags.GetString("network") if err != nil { return nil, err } - parts := strings.SplitN(network, ":", 2) - - ns, cniNets, err := specgen.ParseNetworkNamespace(network) + ns, cniNets, options, err := specgen.ParseNetworkString(network) if err != nil { return nil, err } - if len(parts) > 1 { - opts.NetworkOptions = make(map[string][]string) - opts.NetworkOptions[parts[0]] = strings.Split(parts[1], ",") - cniNets = nil + if len(options) > 0 { + opts.NetworkOptions = options } opts.Network = ns opts.CNINetworks = cniNets } - aliases, err := cmd.Flags().GetStringSlice("network-alias") + aliases, err := flags.GetStringSlice("network-alias") if err != nil { return nil, err } if len(aliases) > 0 { opts.Aliases = aliases } - return &opts, err + + return opts, err } diff -Nru libpod-3.2.1+ds1/cmd/podman/common/ports.go libpod-3.4.4+ds1/cmd/podman/common/ports.go --- libpod-3.2.1+ds1/cmd/podman/common/ports.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/ports.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -package common - -import ( - "github.com/docker/go-connections/nat" - "github.com/pkg/errors" -) - -func verifyExpose(expose []string) error { - // add the expose ports from the user (--expose) - // can be single or a range - for _, expose := range expose { - // support two formats for expose, original format /[] or /[] - _, port := nat.SplitProtoPort(expose) - // parse the start and end port and create a sequence of ports to expose - // if expose a port, the start and end port are the same - _, _, err := nat.ParsePortRange(port) - if err != nil { - return errors.Wrapf(err, "invalid range format for --expose: %s", expose) - } - } - return nil -} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/specgen.go libpod-3.4.4+ds1/cmd/podman/common/specgen.go --- libpod-3.2.1+ds1/cmd/podman/common/specgen.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/specgen.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,846 +0,0 @@ -package common - -import ( - "fmt" - "os" - "strconv" - "strings" - "time" - - "github.com/containers/image/v5/manifest" - "github.com/containers/podman/v3/cmd/podman/parse" - "github.com/containers/podman/v3/libpod/define" - ann "github.com/containers/podman/v3/pkg/annotations" - envLib "github.com/containers/podman/v3/pkg/env" - ns "github.com/containers/podman/v3/pkg/namespaces" - "github.com/containers/podman/v3/pkg/specgen" - systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" - "github.com/containers/podman/v3/pkg/util" - "github.com/docker/go-units" - "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -func getCPULimits(c *ContainerCLIOpts) *specs.LinuxCPU { - cpu := &specs.LinuxCPU{} - hasLimits := false - - if c.CPUS > 0 { - period, quota := util.CoresToPeriodAndQuota(c.CPUS) - - cpu.Period = &period - cpu.Quota = "a - hasLimits = true - } - if c.CPUShares > 0 { - cpu.Shares = &c.CPUShares - hasLimits = true - } - if c.CPUPeriod > 0 { - cpu.Period = &c.CPUPeriod - hasLimits = true - } - if c.CPUSetCPUs != "" { - cpu.Cpus = c.CPUSetCPUs - hasLimits = true - } - if c.CPUSetMems != "" { - cpu.Mems = c.CPUSetMems - hasLimits = true - } - if c.CPUQuota > 0 { - cpu.Quota = &c.CPUQuota - hasLimits = true - } - if c.CPURTPeriod > 0 { - cpu.RealtimePeriod = &c.CPURTPeriod - hasLimits = true - } - if c.CPURTRuntime > 0 { - cpu.RealtimeRuntime = &c.CPURTRuntime - hasLimits = true - } - - if !hasLimits { - return nil - } - return cpu -} - -func getIOLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxBlockIO, error) { - var err error - io := &specs.LinuxBlockIO{} - hasLimits := false - if b := c.BlkIOWeight; len(b) > 0 { - u, err := strconv.ParseUint(b, 10, 16) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for blkio-weight") - } - nu := uint16(u) - io.Weight = &nu - hasLimits = true - } - - if len(c.BlkIOWeightDevice) > 0 { - if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceReadBPs; len(bps) > 0 { - if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if bps := c.DeviceWriteBPs; len(bps) > 0 { - if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceReadIOPs; len(iops) > 0 { - if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if iops := c.DeviceWriteIOPs; len(iops) > 0 { - if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { - return nil, err - } - hasLimits = true - } - - if !hasLimits { - return nil, nil - } - return io, nil -} - -func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.LinuxMemory, error) { - var err error - memory := &specs.LinuxMemory{} - hasLimits := false - if m := c.Memory; len(m) > 0 { - ml, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Limit = &ml - if c.MemorySwap == "" { - limit := 2 * ml - memory.Swap = &(limit) - } - hasLimits = true - } - if m := c.MemoryReservation; len(m) > 0 { - mr, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - memory.Reservation = &mr - hasLimits = true - } - if m := c.MemorySwap; len(m) > 0 { - var ms int64 - // only set memory swap if it was set - // -1 indicates unlimited - if m != "-1" { - ms, err = units.RAMInBytes(m) - memory.Swap = &ms - if err != nil { - return nil, errors.Wrapf(err, "invalid value for memory") - } - hasLimits = true - } - } - if m := c.KernelMemory; len(m) > 0 { - mk, err := units.RAMInBytes(m) - if err != nil { - return nil, errors.Wrapf(err, "invalid value for kernel-memory") - } - memory.Kernel = &mk - hasLimits = true - } - if c.MemorySwappiness >= 0 { - swappiness := uint64(c.MemorySwappiness) - memory.Swappiness = &swappiness - hasLimits = true - } - if c.OOMKillDisable { - memory.DisableOOMKiller = &c.OOMKillDisable - hasLimits = true - } - if !hasLimits { - return nil, nil - } - return memory, nil -} - -func setNamespaces(s *specgen.SpecGenerator, c *ContainerCLIOpts) error { - var err error - - if c.PID != "" { - s.PidNS, err = specgen.ParseNamespace(c.PID) - if err != nil { - return err - } - } - if c.IPC != "" { - s.IpcNS, err = specgen.ParseNamespace(c.IPC) - if err != nil { - return err - } - } - if c.UTS != "" { - s.UtsNS, err = specgen.ParseNamespace(c.UTS) - if err != nil { - return err - } - } - if c.CgroupNS != "" { - s.CgroupNS, err = specgen.ParseNamespace(c.CgroupNS) - if err != nil { - return err - } - } - // userns must be treated differently - if c.UserNS != "" { - s.UserNS, err = specgen.ParseUserNamespace(c.UserNS) - if err != nil { - return err - } - } - if c.Net != nil { - s.NetNS = c.Net.Network - } - return nil -} - -func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string) error { - var ( - err error - ) - - // validate flags as needed - if err := c.validate(); err != nil { - return err - } - - s.User = c.User - inputCommand := args[1:] - if len(c.HealthCmd) > 0 { - if c.NoHealthCheck { - return errors.New("Cannot specify both --no-healthcheck and --health-cmd") - } - s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) - if err != nil { - return err - } - } else if c.NoHealthCheck { - s.HealthConfig = &manifest.Schema2HealthConfig{ - Test: []string{"NONE"}, - } - } - - userNS := ns.UsernsMode(c.UserNS) - s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) - if err != nil { - return err - } - // If some mappings are specified, assume a private user namespace - if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { - s.UserNS.NSMode = specgen.Private - } else { - s.UserNS.NSMode = specgen.NamespaceMode(userNS) - } - - s.Terminal = c.TTY - - if err := verifyExpose(c.Expose); err != nil { - return err - } - // We are not handling the Expose flag yet. - // s.PortsExpose = c.Expose - s.PortMappings = c.Net.PublishPorts - s.PublishExposedPorts = c.PublishAll - s.Pod = c.Pod - - if len(c.PodIDFile) > 0 { - if len(s.Pod) > 0 { - return errors.New("Cannot specify both --pod and --pod-id-file") - } - podID, err := ReadPodIDFile(c.PodIDFile) - if err != nil { - return err - } - s.Pod = podID - } - - expose, err := createExpose(c.Expose) - if err != nil { - return err - } - s.Expose = expose - - if err := setNamespaces(s, c); err != nil { - return err - } - - if sig := c.StopSignal; len(sig) > 0 { - stopSignal, err := util.ParseSignal(sig) - if err != nil { - return err - } - s.StopSignal = &stopSignal - } - - // ENVIRONMENT VARIABLES - // - // Precedence order (higher index wins): - // 1) containers.conf (EnvHost, EnvHTTP, Env) 2) image data, 3 User EnvHost/EnvHTTP, 4) env-file, 5) env - // containers.conf handled and image data handled on the server side - // user specified EnvHost and EnvHTTP handled on Server Side relative to Server - // env-file and env handled on client side - var env map[string]string - - // First transform the os env into a map. We need it for the labels later in - // any case. - osEnv, err := envLib.ParseSlice(os.Environ()) - if err != nil { - return errors.Wrap(err, "error parsing host environment variables") - } - - s.EnvHost = c.EnvHost - s.HTTPProxy = c.HTTPProxy - - // env-file overrides any previous variables - for _, f := range c.EnvFile { - fileEnv, err := envLib.ParseFile(f) - if err != nil { - return err - } - // File env is overridden by env. - env = envLib.Join(env, fileEnv) - } - - parsedEnv, err := envLib.ParseSlice(c.Env) - if err != nil { - return err - } - - s.Env = envLib.Join(env, parsedEnv) - - // LABEL VARIABLES - labels, err := parse.GetAllLabels(c.LabelFile, c.Label) - if err != nil { - return errors.Wrapf(err, "unable to process labels") - } - - if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { - labels[systemdDefine.EnvVariable] = systemdUnit - } - - s.Labels = labels - - // ANNOTATIONS - annotations := make(map[string]string) - - // First, add our default annotations - annotations[ann.TTY] = "false" - if c.TTY { - annotations[ann.TTY] = "true" - } - - // Last, add user annotations - for _, annotation := range c.Annotation { - splitAnnotation := strings.SplitN(annotation, "=", 2) - if len(splitAnnotation) < 2 { - return errors.Errorf("Annotations must be formatted KEY=VALUE") - } - annotations[splitAnnotation[0]] = splitAnnotation[1] - } - s.Annotations = annotations - - s.WorkDir = c.Workdir - if c.Entrypoint != nil { - entrypoint := []string{} - if ep := *c.Entrypoint; len(ep) > 0 { - // Check if entrypoint specified is json - if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil { - entrypoint = append(entrypoint, ep) - } - } - s.Entrypoint = entrypoint - } - - // Include the command used to create the container. - s.ContainerCreateCommand = os.Args - - if len(inputCommand) > 0 { - s.Command = inputCommand - } - - // SHM Size - if c.ShmSize != "" { - shmSize, err := units.FromHumanSize(c.ShmSize) - if err != nil { - return errors.Wrapf(err, "unable to translate --shm-size") - } - s.ShmSize = &shmSize - } - s.CNINetworks = c.Net.CNINetworks - - // Network aliases - if len(c.Net.Aliases) > 0 { - // build a map of aliases where key=cniName - aliases := make(map[string][]string, len(s.CNINetworks)) - for _, cniNetwork := range s.CNINetworks { - aliases[cniNetwork] = c.Net.Aliases - } - s.Aliases = aliases - } - - s.HostAdd = c.Net.AddHosts - s.UseImageResolvConf = c.Net.UseImageResolvConf - s.DNSServers = c.Net.DNSServers - s.DNSSearch = c.Net.DNSSearch - s.DNSOptions = c.Net.DNSOptions - s.StaticIP = c.Net.StaticIP - s.StaticMAC = c.Net.StaticMAC - s.NetworkOptions = c.Net.NetworkOptions - s.UseImageHosts = c.Net.NoHosts - - s.ImageVolumeMode = c.ImageVolume - if s.ImageVolumeMode == "bind" { - s.ImageVolumeMode = "anonymous" - } - - s.Systemd = c.Systemd - s.SdNotifyMode = c.SdNotifyMode - if s.ResourceLimits == nil { - s.ResourceLimits = &specs.LinuxResources{} - } - s.ResourceLimits.Memory, err = getMemoryLimits(s, c) - if err != nil { - return err - } - s.ResourceLimits.BlockIO, err = getIOLimits(s, c) - if err != nil { - return err - } - if c.PIDsLimit != nil { - pids := specs.LinuxPids{ - Limit: *c.PIDsLimit, - } - - s.ResourceLimits.Pids = &pids - } - s.ResourceLimits.CPU = getCPULimits(c) - - unifieds := make(map[string]string) - for _, unified := range c.CgroupConf { - splitUnified := strings.SplitN(unified, "=", 2) - if len(splitUnified) < 2 { - return errors.Errorf("--cgroup-conf must be formatted KEY=VALUE") - } - unifieds[splitUnified[0]] = splitUnified[1] - } - if len(unifieds) > 0 { - s.ResourceLimits.Unified = unifieds - } - - if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil { - s.ResourceLimits = nil - } - - if s.LogConfiguration == nil { - s.LogConfiguration = &specgen.LogConfig{} - } - - if ld := c.LogDriver; len(ld) > 0 { - s.LogConfiguration.Driver = ld - } - s.CgroupParent = c.CGroupParent - s.CgroupsMode = c.CGroupsMode - s.Groups = c.GroupAdd - - s.Hostname = c.Hostname - sysctl := map[string]string{} - if ctl := c.Sysctl; len(ctl) > 0 { - sysctl, err = util.ValidateSysctls(ctl) - if err != nil { - return err - } - } - s.Sysctl = sysctl - - s.CapAdd = c.CapAdd - s.CapDrop = c.CapDrop - s.Privileged = c.Privileged - s.ReadOnlyFilesystem = c.ReadOnly - s.ConmonPidFile = c.ConmonPIDFile - - s.DependencyContainers = c.Requires - - // TODO - // outside of specgen and oci though - // defaults to true, check spec/storage - // s.readonly = c.ReadOnlyTmpFS - // TODO convert to map? - // check if key=value and convert - sysmap := make(map[string]string) - for _, ctl := range c.Sysctl { - splitCtl := strings.SplitN(ctl, "=", 2) - if len(splitCtl) < 2 { - return errors.Errorf("invalid sysctl value %q", ctl) - } - sysmap[splitCtl[0]] = splitCtl[1] - } - s.Sysctl = sysmap - - if c.CIDFile != "" { - s.Annotations[define.InspectAnnotationCIDFile] = c.CIDFile - } - - for _, opt := range c.SecurityOpt { - if opt == "no-new-privileges" { - s.ContainerSecurityConfig.NoNewPrivileges = true - } else { - con := strings.SplitN(opt, "=", 2) - if len(con) != 2 { - return fmt.Errorf("invalid --security-opt 1: %q", opt) - } - - switch con[0] { - case "apparmor": - s.ContainerSecurityConfig.ApparmorProfile = con[1] - s.Annotations[define.InspectAnnotationApparmor] = con[1] - case "label": - // TODO selinux opts and label opts are the same thing - s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) - s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") - case "mask": - s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...) - case "proc-opts": - s.ProcOpts = strings.Split(con[1], ",") - case "seccomp": - s.SeccompProfilePath = con[1] - s.Annotations[define.InspectAnnotationSeccomp] = con[1] - // this option is for docker compatibility, it is the same as unmask=ALL - case "systempaths": - if con[1] == "unconfined" { - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...) - } else { - return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1]) - } - case "unmask": - s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...) - default: - return fmt.Errorf("invalid --security-opt 2: %q", opt) - } - } - } - - s.SeccompPolicy = c.SeccompPolicy - - s.VolumesFrom = c.VolumesFrom - - // Only add read-only tmpfs mounts in case that we are read-only and the - // read-only tmpfs flag has been set. - mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) - if err != nil { - return err - } - s.Mounts = mounts - s.Volumes = volumes - s.OverlayVolumes = overlayVolumes - s.ImageVolumes = imageVolumes - - for _, dev := range c.Devices { - s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) - } - - s.Init = c.Init - s.InitPath = c.InitPath - s.Stdin = c.Interactive - // quiet - // DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), - - // Rlimits/Ulimits - for _, u := range c.Ulimit { - if u == "host" { - s.Rlimits = nil - break - } - ul, err := units.ParseUlimit(u) - if err != nil { - return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) - } - rl := specs.POSIXRlimit{ - Type: ul.Name, - Hard: uint64(ul.Hard), - Soft: uint64(ul.Soft), - } - s.Rlimits = append(s.Rlimits, rl) - } - - logOpts := make(map[string]string) - for _, o := range c.LogOptions { - split := strings.SplitN(o, "=", 2) - if len(split) < 2 { - return errors.Errorf("invalid log option %q", o) - } - switch strings.ToLower(split[0]) { - case "driver": - s.LogConfiguration.Driver = split[1] - case "path": - s.LogConfiguration.Path = split[1] - case "max-size": - logSize, err := units.FromHumanSize(split[1]) - if err != nil { - return err - } - s.LogConfiguration.Size = logSize - default: - logOpts[split[0]] = split[1] - } - } - s.LogConfiguration.Options = logOpts - s.Name = c.Name - s.PreserveFDs = c.PreserveFDs - - s.OOMScoreAdj = &c.OOMScoreAdj - if c.Restart != "" { - splitRestart := strings.Split(c.Restart, ":") - switch len(splitRestart) { - case 1: - // No retries specified - case 2: - if strings.ToLower(splitRestart[0]) != "on-failure" { - return errors.Errorf("restart policy retries can only be specified with on-failure restart policy") - } - retries, err := strconv.Atoi(splitRestart[1]) - if err != nil { - return errors.Wrapf(err, "error parsing restart policy retry count") - } - if retries < 0 { - return errors.Errorf("must specify restart policy retry count as a number greater than 0") - } - var retriesUint = uint(retries) - s.RestartRetries = &retriesUint - default: - return errors.Errorf("invalid restart policy: may specify retries at most once") - } - s.RestartPolicy = splitRestart[0] - } - - s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets) - if err != nil { - return err - } - s.Remove = c.Rm - s.StopTimeout = &c.StopTimeout - s.Timeout = c.Timeout - s.Timezone = c.Timezone - s.Umask = c.Umask - s.PidFile = c.PidFile - s.Volatile = c.Rm - - return nil -} - -func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { - // Every healthcheck requires a command - if len(inCmd) == 0 { - return nil, errors.New("Must define a healthcheck command for all healthchecks") - } - - // first try to parse option value as JSON array of strings... - cmd := []string{} - - if inCmd == "none" { - cmd = []string{"NONE"} - } else { - err := json.Unmarshal([]byte(inCmd), &cmd) - if err != nil { - // ...otherwise pass it to "/bin/sh -c" inside the container - cmd = []string{"CMD-SHELL", inCmd} - } - } - hc := manifest.Schema2HealthConfig{ - Test: cmd, - } - - if interval == "disable" { - interval = "0" - } - intervalDuration, err := time.ParseDuration(interval) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-interval") - } - - hc.Interval = intervalDuration - - if retries < 1 { - return nil, errors.New("healthcheck-retries must be greater than 0") - } - hc.Retries = int(retries) - timeoutDuration, err := time.ParseDuration(timeout) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-timeout") - } - if timeoutDuration < time.Duration(1) { - return nil, errors.New("healthcheck-timeout must be at least 1 second") - } - hc.Timeout = timeoutDuration - - startPeriodDuration, err := time.ParseDuration(startPeriod) - if err != nil { - return nil, errors.Wrapf(err, "invalid healthcheck-start-period") - } - if startPeriodDuration < time.Duration(0) { - return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") - } - hc.StartPeriod = startPeriodDuration - - return &hc, nil -} - -func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { - for _, val := range weightDevs { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return fmt.Errorf("bad format for device path: %s", val) - } - weight, err := strconv.ParseUint(split[1], 10, 0) - if err != nil { - return fmt.Errorf("invalid weight for device: %s", val) - } - if weight > 0 && (weight < 10 || weight > 1000) { - return fmt.Errorf("invalid weight for device: %s", val) - } - w := uint16(weight) - s.WeightDevice[split[0]] = specs.LinuxWeightDevice{ - Weight: &w, - LeafWeight: nil, - } - } - return nil -} - -func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range bpsDevices { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := units.RAMInBytes(split[1]) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :[]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - if rate < 0 { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :[]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)} - } - return td, nil -} - -func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { - td := make(map[string]specs.LinuxThrottleDevice) - for _, val := range iopsDevices { - split := strings.SplitN(val, ":", 2) - if len(split) != 2 { - return nil, fmt.Errorf("bad format: %s", val) - } - if !strings.HasPrefix(split[0], "/dev/") { - return nil, fmt.Errorf("bad format for device path: %s", val) - } - rate, err := strconv.ParseUint(split[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :. Number must be a positive integer", val) - } - td[split[0]] = specs.LinuxThrottleDevice{Rate: rate} - } - return td, nil -} - -func parseSecrets(secrets []string) ([]string, map[string]string, error) { - secretParseError := errors.New("error parsing secret") - var mount []string - envs := make(map[string]string) - for _, val := range secrets { - source := "" - secretType := "" - target := "" - split := strings.Split(val, ",") - - // --secret mysecret - if len(split) == 1 { - source = val - mount = append(mount, source) - continue - } - // --secret mysecret,opt=opt - if !strings.Contains(split[0], "=") { - source = split[0] - split = split[1:] - } - // TODO: implement other secret options - for _, val := range split { - kv := strings.SplitN(val, "=", 2) - if len(kv) < 2 { - return nil, nil, errors.Wrapf(secretParseError, "option %s must be in form option=value", val) - } - switch kv[0] { - case "source": - source = kv[1] - case "type": - if secretType != "" { - return nil, nil, errors.Wrap(secretParseError, "cannot set more tha one secret type") - } - if kv[1] != "mount" && kv[1] != "env" { - return nil, nil, errors.Wrapf(secretParseError, "type %s is invalid", kv[1]) - } - secretType = kv[1] - case "target": - target = kv[1] - default: - return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val) - } - } - - if secretType == "" { - secretType = "mount" - } - if source == "" { - return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) - } - if secretType == "mount" { - if target != "" { - return nil, nil, errors.Wrapf(secretParseError, "target option is invalid for mounted secrets") - } - mount = append(mount, source) - } - if secretType == "env" { - if target == "" { - target = source - } - envs[target] = source - } - } - return mount, envs, nil -} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/util.go libpod-3.4.4+ds1/cmd/podman/common/util.go --- libpod-3.2.1+ds1/cmd/podman/common/util.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/util.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,274 +0,0 @@ -package common - -import ( - "io/ioutil" - "net" - "strconv" - "strings" - - "github.com/containers/podman/v3/pkg/specgen" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -// ReadPodIDFile reads the specified file and returns its content (i.e., first -// line). -func ReadPodIDFile(path string) (string, error) { - content, err := ioutil.ReadFile(path) - if err != nil { - return "", errors.Wrap(err, "error reading pod ID file") - } - return strings.Split(string(content), "\n")[0], nil -} - -// ReadPodIDFiles reads the specified files and returns their content (i.e., -// first line). -func ReadPodIDFiles(files []string) ([]string, error) { - ids := []string{} - for _, file := range files { - id, err := ReadPodIDFile(file) - if err != nil { - return nil, err - } - ids = append(ids, id) - } - return ids, nil -} - -// ParseFilters transforms one filter format to another and validates input -func ParseFilters(filter []string) (map[string][]string, error) { - // TODO Remove once filter refactor is finished and url.Values done. - filters := map[string][]string{} - for _, f := range filter { - t := strings.SplitN(f, "=", 2) - filters = make(map[string][]string) - if len(t) < 2 { - return map[string][]string{}, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) - } - filters[t[0]] = append(filters[t[0]], t[1]) - } - return filters, nil -} - -// createExpose parses user-provided exposed port definitions and converts them -// into SpecGen format. -// TODO: The SpecGen format should really handle ranges more sanely - we could -// be massively inflating what is sent over the wire with a large range. -func createExpose(expose []string) (map[uint16]string, error) { - toReturn := make(map[uint16]string) - - for _, e := range expose { - // Check for protocol - proto := "tcp" - splitProto := strings.Split(e, "/") - if len(splitProto) > 2 { - return nil, errors.Errorf("invalid expose format - protocol can only be specified once") - } else if len(splitProto) == 2 { - proto = splitProto[1] - } - - // Check for a range - start, len, err := parseAndValidateRange(splitProto[0]) - if err != nil { - return nil, err - } - - var index uint16 - for index = 0; index < len; index++ { - portNum := start + index - protocols, ok := toReturn[portNum] - if !ok { - toReturn[portNum] = proto - } else { - newProto := strings.Join(append(strings.Split(protocols, ","), strings.Split(proto, ",")...), ",") - toReturn[portNum] = newProto - } - } - } - - return toReturn, nil -} - -// createPortBindings iterates ports mappings into SpecGen format. -func createPortBindings(ports []string) ([]specgen.PortMapping, error) { - // --publish is formatted as follows: - // [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol] - toReturn := make([]specgen.PortMapping, 0, len(ports)) - - for _, p := range ports { - var ( - ctrPort string - proto, hostIP, hostPort *string - ) - - splitProto := strings.Split(p, "/") - switch len(splitProto) { - case 1: - // No protocol was provided - case 2: - proto = &(splitProto[1]) - default: - return nil, errors.Errorf("invalid port format - protocol can only be specified once") - } - - remainder := splitProto[0] - haveV6 := false - - // Check for an IPv6 address in brackets - splitV6 := strings.Split(remainder, "]") - switch len(splitV6) { - case 1: - // Do nothing, proceed as before - case 2: - // We potentially have an IPv6 address - haveV6 = true - if !strings.HasPrefix(splitV6[0], "[") { - return nil, errors.Errorf("invalid port format - IPv6 addresses must be enclosed by []") - } - if !strings.HasPrefix(splitV6[1], ":") { - return nil, errors.Errorf("invalid port format - IPv6 address must be followed by a colon (':')") - } - ipNoPrefix := strings.TrimPrefix(splitV6[0], "[") - hostIP = &ipNoPrefix - remainder = strings.TrimPrefix(splitV6[1], ":") - default: - return nil, errors.Errorf("invalid port format - at most one IPv6 address can be specified in a --publish") - } - - splitPort := strings.Split(remainder, ":") - switch len(splitPort) { - case 1: - if haveV6 { - return nil, errors.Errorf("invalid port format - must provide host and destination port if specifying an IP") - } - ctrPort = splitPort[0] - case 2: - hostPort = &(splitPort[0]) - ctrPort = splitPort[1] - case 3: - if haveV6 { - return nil, errors.Errorf("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") - } - hostIP = &(splitPort[0]) - hostPort = &(splitPort[1]) - ctrPort = splitPort[2] - default: - return nil, errors.Errorf("invalid port format - format is [[hostIP:]hostPort:]containerPort") - } - - newPort, err := parseSplitPort(hostIP, hostPort, ctrPort, proto) - if err != nil { - return nil, err - } - - toReturn = append(toReturn, newPort) - } - - return toReturn, nil -} - -// parseSplitPort parses individual components of the --publish flag to produce -// a single port mapping in SpecGen format. -func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) (specgen.PortMapping, error) { - newPort := specgen.PortMapping{} - if ctrPort == "" { - return newPort, errors.Errorf("must provide a non-empty container port to publish") - } - ctrStart, ctrLen, err := parseAndValidateRange(ctrPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing container port") - } - newPort.ContainerPort = ctrStart - newPort.Range = ctrLen - - if protocol != nil { - if *protocol == "" { - return newPort, errors.Errorf("must provide a non-empty protocol to publish") - } - newPort.Protocol = *protocol - } - if hostIP != nil { - if *hostIP == "" { - return newPort, errors.Errorf("must provide a non-empty container host IP to publish") - } else if *hostIP != "0.0.0.0" { - // If hostIP is 0.0.0.0, leave it unset - CNI treats - // 0.0.0.0 and empty differently, Docker does not. - testIP := net.ParseIP(*hostIP) - if testIP == nil { - return newPort, errors.Errorf("cannot parse %q as an IP address", *hostIP) - } - newPort.HostIP = testIP.String() - } - } - if hostPort != nil { - if *hostPort == "" { - // Set 0 as a placeholder. The server side of Specgen - // will find a random, open, unused port to use. - newPort.HostPort = 0 - } else { - hostStart, hostLen, err := parseAndValidateRange(*hostPort) - if err != nil { - return newPort, errors.Wrapf(err, "error parsing host port") - } - if hostLen != ctrLen { - return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) - } - newPort.HostPort = hostStart - } - } - - hport := newPort.HostPort - logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol) - - return newPort, nil -} - -// Parse and validate a port range. -// Returns start port, length of range, error. -func parseAndValidateRange(portRange string) (uint16, uint16, error) { - splitRange := strings.Split(portRange, "-") - if len(splitRange) > 2 { - return 0, 0, errors.Errorf("invalid port format - port ranges are formatted as startPort-stopPort") - } - - if splitRange[0] == "" { - return 0, 0, errors.Errorf("port numbers cannot be negative") - } - - startPort, err := parseAndValidatePort(splitRange[0]) - if err != nil { - return 0, 0, err - } - - var rangeLen uint16 = 1 - if len(splitRange) == 2 { - if splitRange[1] == "" { - return 0, 0, errors.Errorf("must provide ending number for port range") - } - endPort, err := parseAndValidatePort(splitRange[1]) - if err != nil { - return 0, 0, err - } - if endPort <= startPort { - return 0, 0, errors.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort) - } - // Our range is the total number of ports - // involved, so we need to add 1 (8080:8081 is - // 2 ports, for example, not 1) - rangeLen = endPort - startPort + 1 - } - - return startPort, rangeLen, nil -} - -// Turn a single string into a valid U16 port. -func parseAndValidatePort(port string) (uint16, error) { - num, err := strconv.Atoi(port) - if err != nil { - return 0, errors.Wrapf(err, "invalid port number") - } - if num < 1 || num > 65535 { - return 0, errors.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num) - } - return uint16(num), nil -} diff -Nru libpod-3.2.1+ds1/cmd/podman/common/volumes.go libpod-3.4.4+ds1/cmd/podman/common/volumes.go --- libpod-3.2.1+ds1/cmd/podman/common/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/common/volumes.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,630 +0,0 @@ -package common - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/containers/common/pkg/parse" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/specgen" - "github.com/containers/podman/v3/pkg/util" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" -) - -var ( - errDuplicateDest = errors.Errorf("duplicate mount destination") - optionArgError = errors.Errorf("must provide an argument for option") - noDestError = errors.Errorf("must set volume destination") - errInvalidSyntax = errors.Errorf("incorrect mount format: should be --mount type=,[src=,]target=[,options]") -) - -// Parse all volume-related options in the create config into a set of mounts -// and named volumes to add to the container. -// Handles --volumes, --mount, and --tmpfs flags. -// Does not handle image volumes, init, and --volumes-from flags. -// Can also add tmpfs mounts from read-only tmpfs. -// TODO: handle options parsing/processing via containers/storage/pkg/mount -func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { - // Get mounts from the --mounts flag. - unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Next --volumes flag. - volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Next --tmpfs flag. - tmpfsMounts, err := getTmpfsMounts(tmpfsFlag) - if err != nil { - return nil, nil, nil, nil, err - } - - // Unify mounts from --mount, --volume, --tmpfs. - // Start with --volume. - for dest, mount := range volumeMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = mount - } - for dest, volume := range volumeVolumes { - if _, ok := unifiedVolumes[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedVolumes[dest] = volume - } - // Now --tmpfs - for dest, tmpfs := range tmpfsMounts { - if _, ok := unifiedMounts[dest]; ok { - return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) - } - unifiedMounts[dest] = tmpfs - } - - // If requested, add tmpfs filesystems for read-only containers. - if addReadOnlyTmpfs { - readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} - options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} - for _, dest := range readonlyTmpfs { - if _, ok := unifiedMounts[dest]; ok { - continue - } - if _, ok := unifiedVolumes[dest]; ok { - continue - } - unifiedMounts[dest] = spec.Mount{ - Destination: dest, - Type: define.TypeTmpfs, - Source: "tmpfs", - Options: options, - } - } - } - - // Check for conflicts between named volumes, overlay & image volumes, - // and mounts - allMounts := make(map[string]bool) - testAndSet := func(dest string) error { - if _, ok := allMounts[dest]; ok { - return errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) - } - allMounts[dest] = true - return nil - } - for dest := range unifiedMounts { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range unifiedVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range overlayVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - for dest := range unifiedImageVolumes { - if err := testAndSet(dest); err != nil { - return nil, nil, nil, nil, err - } - } - - // Final step: maps to arrays - finalMounts := make([]spec.Mount, 0, len(unifiedMounts)) - for _, mount := range unifiedMounts { - if mount.Type == define.TypeBind { - absSrc, err := filepath.Abs(mount.Source) - if err != nil { - return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) - } - mount.Source = absSrc - } - finalMounts = append(finalMounts, mount) - } - finalVolumes := make([]*specgen.NamedVolume, 0, len(unifiedVolumes)) - for _, volume := range unifiedVolumes { - finalVolumes = append(finalVolumes, volume) - } - finalOverlayVolume := make([]*specgen.OverlayVolume, 0) - for _, volume := range overlayVolumes { - finalOverlayVolume = append(finalOverlayVolume, volume) - } - finalImageVolumes := make([]*specgen.ImageVolume, 0, len(unifiedImageVolumes)) - for _, volume := range unifiedImageVolumes { - finalImageVolumes = append(finalImageVolumes, volume) - } - - return finalMounts, finalVolumes, finalOverlayVolume, finalImageVolumes, nil -} - -// findMountType parses the input and extracts the type of the mount type and -// the remaining non-type tokens. -func findMountType(input string) (mountType string, tokens []string, err error) { - // Split by comma, iterate over the slice and look for - // "type=$mountType". Everything else is appended to tokens. - found := false - for _, s := range strings.Split(input, ",") { - kv := strings.Split(s, "=") - if found || !(len(kv) == 2 && kv[0] == "type") { - tokens = append(tokens, s) - continue - } - mountType = kv[1] - found = true - } - if !found { - err = errInvalidSyntax - } - return -} - -// getMounts takes user-provided input from the --mount flag and creates OCI -// spec mounts and Libpod named volumes. -// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... -// podman run --mount type=tmpfs,target=/dev/shm ... -// podman run --mount type=volume,source=test-volume, ... -func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { - finalMounts := make(map[string]spec.Mount) - finalNamedVolumes := make(map[string]*specgen.NamedVolume) - finalImageVolumes := make(map[string]*specgen.ImageVolume) - - for _, mount := range mountFlag { - // TODO: Docker defaults to "volume" if no mount type is specified. - mountType, tokens, err := findMountType(mount) - if err != nil { - return nil, nil, nil, err - } - switch mountType { - case define.TypeBind: - mount, err := getBindMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case define.TypeTmpfs: - mount, err := getTmpfsMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case define.TypeDevpts: - mount, err := getDevptsMount(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalMounts[mount.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) - } - finalMounts[mount.Destination] = mount - case "image": - volume, err := getImageVolume(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalImageVolumes[volume.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Destination) - } - finalImageVolumes[volume.Destination] = volume - case "volume": - volume, err := getNamedVolume(tokens) - if err != nil { - return nil, nil, nil, err - } - if _, ok := finalNamedVolumes[volume.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) - } - finalNamedVolumes[volume.Dest] = volume - default: - return nil, nil, nil, errors.Errorf("invalid filesystem type %q", mountType) - } - } - - return finalMounts, finalNamedVolumes, finalImageVolumes, nil -} - -// Parse a single bind mount entry from the --mount flag. -func getBindMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeBind, - } - - var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "bind-nonrecursive": - newMount.Options = append(newMount.Options, "bind") - case "readonly", "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'readonly', 'ro', or 'rw' options more than once") - } - setRORW = true - // Can be formatted as one of: - // readonly - // readonly=[true|false] - // ro - // ro=[true|false] - // rw - // rw=[true|false] - if kv[0] == "readonly" { - kv[0] = "ro" - } - switch len(kv) { - case 1: - newMount.Options = append(newMount.Options, kv[0]) - case 2: - switch strings.ToLower(kv[1]) { - case "true": - newMount.Options = append(newMount.Options, kv[0]) - case "false": - // Set the opposite only for rw - // ro's opposite is the default - if kv[0] == "rw" { - newMount.Options = append(newMount.Options, "ro") - } - default: - return newMount, errors.Wrapf(optionArgError, "'readonly', 'ro', or 'rw' must be set to true or false, instead received %q", kv[1]) - } - default: - return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) - } - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": - newMount.Options = append(newMount.Options, kv[0]) - case "bind-propagation": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, kv[1]) - case "src", "source": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if len(kv[1]) == 0 { - return newMount, errors.Wrapf(optionArgError, "host directory cannot be empty") - } - newMount.Source = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "relabel": - if setRelabel { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once") - } - setRelabel = true - if len(kv) != 2 { - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - switch kv[1] { - case "private": - newMount.Options = append(newMount.Options, "z") - case "shared": - newMount.Options = append(newMount.Options, "Z") - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) - } - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - if !setSource { - newMount.Source = newMount.Destination - } - - options, err := parse.ValidateVolumeOpts(newMount.Options) - if err != nil { - return newMount, err - } - newMount.Options = options - return newMount, nil -} - -// Parse a single tmpfs mount entry from the --mount flag -func getTmpfsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeTmpfs, - Source: define.TypeTmpfs, - } - - var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "tmpcopyup", "notmpcopyup": - if setTmpcopyup { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once") - } - setTmpcopyup = true - newMount.Options = append(newMount.Options, kv[0]) - case "ro", "rw": - if setRORW { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newMount.Options = append(newMount.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newMount.Options = append(newMount.Options, kv[0]) - case "nodev", "dev": - if setDev { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newMount.Options = append(newMount.Options, kv[0]) - case "noexec", "exec": - if setExec { - return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newMount.Options = append(newMount.Options, kv[0]) - case "tmpfs-mode": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) - case "tmpfs-size": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) - case "src", "source": - return newMount, errors.Errorf("source is not supported with tmpfs mounts") - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single devpts mount entry from the --mount flag -func getDevptsMount(args []string) (spec.Mount, error) { - newMount := spec.Mount{ - Type: define.TypeDevpts, - Source: define.TypeDevpts, - } - - var setDest bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "target", "dst", "destination": - if len(kv) == 1 { - return newMount, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return newMount, err - } - newMount.Destination = filepath.Clean(kv[1]) - setDest = true - default: - return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setDest { - return newMount, noDestError - } - - return newMount, nil -} - -// Parse a single volume mount entry from the --mount flag. -// Note that the volume-label option for named volumes is currently NOT supported. -// TODO: add support for --volume-label -func getNamedVolume(args []string) (*specgen.NamedVolume, error) { - newVolume := new(specgen.NamedVolume) - - var setSource, setDest, setRORW, setSuid, setDev, setExec bool - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "ro", "rw": - if setRORW { - return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") - } - setRORW = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nosuid", "suid": - if setSuid { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") - } - setSuid = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "nodev", "dev": - if setDev { - return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") - } - setDev = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "noexec", "exec": - if setExec { - return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") - } - setExec = true - newVolume.Options = append(newVolume.Options, kv[0]) - case "volume-label": - return nil, errors.Errorf("the --volume-label option is not presently implemented") - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Name = kv[1] - setSource = true - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Dest = filepath.Clean(kv[1]) - setDest = true - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if !setSource { - return nil, errors.Errorf("must set source volume") - } - if !setDest { - return nil, noDestError - } - - return newVolume, nil -} - -// Parse the arguments into an image volume. An image volume is a volume based -// on a container image. The container image is first mounted on the host and -// is then bind-mounted into the container. An ImageVolume is always mounted -// read only. -func getImageVolume(args []string) (*specgen.ImageVolume, error) { - newVolume := new(specgen.ImageVolume) - - for _, val := range args { - kv := strings.SplitN(val, "=", 2) - switch kv[0] { - case "src", "source": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - newVolume.Source = kv[1] - case "target", "dst", "destination": - if len(kv) == 1 { - return nil, errors.Wrapf(optionArgError, kv[0]) - } - if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { - return nil, err - } - newVolume.Destination = filepath.Clean(kv[1]) - case "rw", "readwrite": - switch kv[1] { - case "true": - newVolume.ReadWrite = true - case "false": - // Nothing to do. RO is default. - default: - return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) - } - case "consistency": - // Often used on MACs and mistakenly on Linux platforms. - // Since Docker ignores this option so shall we. - continue - default: - return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) - } - } - - if len(newVolume.Source)*len(newVolume.Destination) == 0 { - return nil, errors.Errorf("must set source and destination for image volume") - } - - return newVolume, nil -} - -// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts -func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { - m := make(map[string]spec.Mount) - for _, i := range tmpfsFlag { - // Default options if nothing passed - var options []string - spliti := strings.Split(i, ":") - destPath := spliti[0] - if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil { - return nil, err - } - if len(spliti) > 1 { - options = strings.Split(spliti[1], ",") - } - - if _, ok := m[destPath]; ok { - return nil, errors.Wrapf(errDuplicateDest, destPath) - } - - mount := spec.Mount{ - Destination: filepath.Clean(destPath), - Type: string(define.TypeTmpfs), - Options: options, - Source: string(define.TypeTmpfs), - } - m[destPath] = mount - } - return m, nil -} diff -Nru libpod-3.2.1+ds1/cmd/podman/completion/completion.go libpod-3.4.4+ds1/cmd/podman/completion/completion.go --- libpod-3.2.1+ds1/cmd/podman/completion/completion.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/completion/completion.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,7 +8,6 @@ commonComp "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -39,7 +38,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: completionCmd, }) flags := completionCmd.Flags() @@ -67,7 +65,7 @@ var err error switch args[0] { case "bash": - err = cmd.Root().GenBashCompletion(w) + err = cmd.Root().GenBashCompletionV2(w, !noDesc) case "zsh": if noDesc { err = cmd.Root().GenZshCompletionNoDesc(w) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/attach.go libpod-3.4.4+ds1/cmd/podman/containers/attach.go --- libpod-3.2.1+ds1/cmd/podman/containers/attach.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/attach.go 2021-12-26 00:45:51.000000000 +0000 @@ -55,14 +55,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: attachCommand, }) attachFlags(attachCommand) validate.AddLatestFlag(attachCommand, &attachOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerAttachCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/checkpoint.go libpod-3.4.4+ds1/cmd/podman/containers/checkpoint.go --- libpod-3.2.1+ds1/cmd/podman/containers/checkpoint.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/checkpoint.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ import ( "context" "fmt" + "strings" "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/common" @@ -11,6 +12,7 @@ "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -36,13 +38,10 @@ } ) -var ( - checkpointOptions entities.CheckpointOptions -) +var checkpointOptions entities.CheckpointOptions func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: checkpointCommand, Parent: containerCmd, }) @@ -61,11 +60,32 @@ flags.BoolVarP(&checkpointOptions.PreCheckPoint, "pre-checkpoint", "P", false, "Dump container's memory information only, leave the container running") flags.BoolVar(&checkpointOptions.WithPrevious, "with-previous", false, "Checkpoint container with pre-checkpoint images") + flags.StringP("compress", "c", "zstd", "Select compression algorithm (gzip, none, zstd) for checkpoint archive.") + _ = checkpointCommand.RegisterFlagCompletionFunc("compress", common.AutocompleteCheckpointCompressType) + validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest) } func checkpoint(cmd *cobra.Command, args []string) error { var errs utils.OutputErrors + if cmd.Flags().Changed("compress") { + if checkpointOptions.Export == "" { + return errors.Errorf("--compress can only be used with --export") + } + compress, _ := cmd.Flags().GetString("compress") + switch strings.ToLower(compress) { + case "none": + checkpointOptions.Compression = archive.Uncompressed + case "gzip": + checkpointOptions.Compression = archive.Gzip + case "zstd": + checkpointOptions.Compression = archive.Zstd + default: + return errors.Errorf("Selected compression algorithm (%q) not supported. Please select one from: gzip, none, zstd", compress) + } + } else { + checkpointOptions.Compression = archive.Zstd + } if rootless.IsRootless() { return errors.New("checkpointing a container requires root") } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/cleanup.go libpod-3.4.4+ds1/cmd/podman/containers/cleanup.go --- libpod-3.2.1+ds1/cmd/podman/containers/cleanup.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/cleanup.go 2021-12-26 00:45:51.000000000 +0000 @@ -21,10 +21,11 @@ Cleans up mount points and network stacks on one or more containers from the host. The container name or ID can be used. This command is used internally when running containers, but can also be used if container cleanup has failed when a container exits. ` cleanupCommand = &cobra.Command{ - Use: "cleanup [options] CONTAINER [CONTAINER...]", - Short: "Cleanup network and mountpoints of one or more containers", - Long: cleanupDescription, - RunE: cleanup, + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "cleanup [options] CONTAINER [CONTAINER...]", + Short: "Cleanup network and mountpoints of one or more containers", + Long: cleanupDescription, + RunE: cleanup, Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, @@ -41,7 +42,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Parent: containerCmd, Command: cleanupCommand, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/commit.go libpod-3.4.4+ds1/cmd/podman/containers/commit.go --- libpod-3.2.1+ds1/cmd/podman/containers/commit.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/commit.go 2021-12-26 00:45:51.000000000 +0000 @@ -82,13 +82,11 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: commitCommand, }) commitFlags(commitCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCommitCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/container.go libpod-3.4.4+ds1/cmd/podman/containers/container.go --- libpod-3.2.1+ds1/cmd/podman/containers/container.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/container.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/util" "github.com/spf13/cobra" ) @@ -26,7 +25,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/cp.go libpod-3.4.4+ds1/cmd/podman/containers/cp.go --- libpod-3.2.1+ds1/cmd/podman/containers/cp.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/cp.go 2021-12-26 00:45:51.000000000 +0000 @@ -28,13 +28,13 @@ You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. If "-" is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. The CONTAINER can be a running or stopped container. The SRC_PATH or DEST_PATH can be a file or a directory. ` cpCommand = &cobra.Command{ - Use: "cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + Use: "cp [options] [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", Short: "Copy files/folders between a container and the local filesystem", Long: cpDescription, Args: cobra.ExactArgs(2), RunE: cp, ValidArgsFunction: common.AutocompleteCpCommand, - Example: "podman cp [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", + Example: "podman cp [options] [CONTAINER:]SRC_PATH [CONTAINER:]DEST_PATH", } containerCpCommand = &cobra.Command{ @@ -50,25 +50,25 @@ var ( cpOpts entities.ContainerCpOptions + chown bool ) func cpFlags(cmd *cobra.Command) { flags := cmd.Flags() flags.BoolVar(&cpOpts.Extract, "extract", false, "Deprecated...") flags.BoolVar(&cpOpts.Pause, "pause", true, "Deprecated") + flags.BoolVarP(&chown, "archive", "a", true, `Chown copied files to the primary uid/gid of the destination container.`) _ = flags.MarkHidden("extract") _ = flags.MarkHidden("pause") } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: cpCommand, }) cpFlags(cpCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCpCommand, Parent: containerCmd, }) @@ -82,7 +82,9 @@ return err } - if len(sourceContainerStr) > 0 { + if len(sourceContainerStr) > 0 && len(destContainerStr) > 0 { + return copyContainerToContainer(sourceContainerStr, sourcePath, destContainerStr, destPath) + } else if len(sourceContainerStr) > 0 { return copyFromContainer(sourceContainerStr, sourcePath, destPath) } @@ -115,6 +117,84 @@ return errorhandling.JoinErrors(copyErrors) } +func copyContainerToContainer(sourceContainer string, sourcePath string, destContainer string, destPath string) error { + if err := containerMustExist(sourceContainer); err != nil { + return err + } + + if err := containerMustExist(destContainer); err != nil { + return err + } + + sourceContainerInfo, err := registry.ContainerEngine().ContainerStat(registry.GetContext(), sourceContainer, sourcePath) + if err != nil { + return errors.Wrapf(err, "%q could not be found on container %s", sourcePath, sourceContainer) + } + + destContainerBaseName, destContainerInfo, destResolvedToParentDir, err := resolvePathOnDestinationContainer(destContainer, destPath, false) + if err != nil { + return err + } + + if sourceContainerInfo.IsDir && !destContainerInfo.IsDir { + return errors.New("destination must be a directory when copying a directory") + } + + sourceContainerTarget := sourceContainerInfo.LinkTarget + destContainerTarget := destContainerInfo.LinkTarget + if !destContainerInfo.IsDir { + destContainerTarget = filepath.Dir(destPath) + } + + // If we copy a directory via the "." notation and the container path + // does not exist, we need to make sure that the destination on the + // container gets created; otherwise the contents of the source + // directory will be written to the destination's parent directory. + // + // Hence, whenever "." is the source and the destination does not + // exist, we copy the source's parent and let the copier package create + // the destination via the Rename option. + if destResolvedToParentDir && sourceContainerInfo.IsDir && strings.HasSuffix(sourcePath, ".") { + sourceContainerTarget = filepath.Dir(sourceContainerTarget) + } + + reader, writer := io.Pipe() + + sourceContainerCopy := func() error { + defer writer.Close() + copyFunc, err := registry.ContainerEngine().ContainerCopyToArchive(registry.GetContext(), sourceContainer, sourceContainerTarget, writer) + if err != nil { + return err + } + if err := copyFunc(); err != nil { + return errors.Wrap(err, "error copying from container") + } + return nil + } + + destContainerCopy := func() error { + defer reader.Close() + + copyOptions := entities.CopyOptions{Chown: chown} + if (!sourceContainerInfo.IsDir && !destContainerInfo.IsDir) || destResolvedToParentDir { + // If we're having a file-to-file copy, make sure to + // rename accordingly. + copyOptions.Rename = map[string]string{filepath.Base(sourceContainerTarget): destContainerBaseName} + } + + copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), destContainer, destContainerTarget, reader, copyOptions) + if err != nil { + return err + } + if err := copyFunc(); err != nil { + return errors.Wrap(err, "error copying to container") + } + return nil + } + + return doCopy(sourceContainerCopy, destContainerCopy) +} + // copyFromContainer copies from the containerPath on the container to hostPath. func copyFromContainer(container string, containerPath string, hostPath string) error { if err := containerMustExist(container); err != nil { @@ -133,6 +213,7 @@ } var hostBaseName string + var resolvedToHostParentDir bool hostInfo, hostInfoErr := copy.ResolveHostPath(hostPath) if hostInfoErr != nil { if strings.HasSuffix(hostPath, "/") { @@ -148,6 +229,7 @@ // it'll be created while copying. Hence, we use it as the // base path. hostBaseName = filepath.Base(hostPath) + resolvedToHostParentDir = true } else { // If the specified path exists on the host, we must use its // base path as it may have changed due to symlink evaluations. @@ -175,10 +257,14 @@ // we copy the source's parent and let the copier package create the // destination via the Rename option. containerTarget := containerInfo.LinkTarget - if hostInfoErr != nil && containerInfo.IsDir && strings.HasSuffix(containerTarget, ".") { + if resolvedToHostParentDir && containerInfo.IsDir && strings.HasSuffix(containerTarget, ".") { containerTarget = filepath.Dir(containerTarget) } + if !isStdout && containerInfo.IsDir && !hostInfo.IsDir { + return errors.New("destination must be a directory when copying a directory") + } + reader, writer := io.Pipe() hostCopy := func() error { defer reader.Close() @@ -212,7 +298,7 @@ ChownFiles: &idPair, IgnoreDevices: true, } - if (!containerInfo.IsDir && !hostInfo.IsDir) || hostInfoErr != nil { + if (!containerInfo.IsDir && !hostInfo.IsDir) || resolvedToHostParentDir { // If we're having a file-to-file copy, make sure to // rename accordingly. putOptions.Rename = map[string]string{filepath.Base(containerTarget): hostBaseName} @@ -259,42 +345,9 @@ return errors.Wrapf(err, "%q could not be found on the host", hostPath) } - // If the path on the container does not exist. We need to make sure - // that it's parent directory exists. The destination may be created - // while copying. - var containerBaseName string - containerInfo, containerInfoErr := registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath) - if containerInfoErr != nil { - if strings.HasSuffix(containerPath, "/") { - return errors.Wrapf(containerInfoErr, "%q could not be found on container %s", containerPath, container) - } - if isStdin { - return errors.New("destination must be a directory when copying from stdin") - } - // NOTE: containerInfo may actually be set. That happens when - // the container path is a symlink into nirvana. In that case, - // we must use the symlinked path instead. - path := containerPath - if containerInfo != nil { - containerBaseName = filepath.Base(containerInfo.LinkTarget) - path = containerInfo.LinkTarget - } else { - containerBaseName = filepath.Base(containerPath) - } - - parentDir, err := containerParentDir(container, path) - if err != nil { - return errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container) - } - containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir) - if err != nil { - return errors.Wrapf(err, "%q could not be found on container %s", containerPath, container) - } - } else { - // If the specified path exists on the container, we must use - // its base path as it may have changed due to symlink - // evaluations. - containerBaseName = filepath.Base(containerInfo.LinkTarget) + containerBaseName, containerInfo, containerResolvedToParentDir, err := resolvePathOnDestinationContainer(container, containerPath, isStdin) + if err != nil { + return err } // If we copy a directory via the "." notation and the container path @@ -306,7 +359,7 @@ // exist, we copy the source's parent and let the copier package create // the destination via the Rename option. hostTarget := hostInfo.LinkTarget - if containerInfoErr != nil && hostInfo.IsDir && strings.HasSuffix(hostTarget, ".") { + if containerResolvedToParentDir && hostInfo.IsDir && strings.HasSuffix(hostTarget, ".") { hostTarget = filepath.Dir(hostTarget) } @@ -336,6 +389,10 @@ stdinFile = tmpFile.Name() } + if hostInfo.IsDir && !containerInfo.IsDir { + return errors.New("destination must be a directory when copying a directory") + } + reader, writer := io.Pipe() hostCopy := func() error { defer writer.Close() @@ -354,7 +411,7 @@ // copy the base directory. KeepDirectoryNames: hostInfo.IsDir && filepath.Base(hostTarget) != ".", } - if (!hostInfo.IsDir && !containerInfo.IsDir) || containerInfoErr != nil { + if (!hostInfo.IsDir && !containerInfo.IsDir) || containerResolvedToParentDir { // If we're having a file-to-file copy, make sure to // rename accordingly. getOptions.Rename = map[string]string{filepath.Base(hostTarget): containerBaseName} @@ -372,7 +429,7 @@ target = filepath.Dir(target) } - copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader) + copyFunc, err := registry.ContainerEngine().ContainerCopyFromArchive(registry.GetContext(), container, target, reader, entities.CopyOptions{Chown: chown}) if err != nil { return err } @@ -385,6 +442,52 @@ return doCopy(hostCopy, containerCopy) } +// resolvePathOnDestinationContainer resolves the specified path on the +// container. If the path does not exist, it attempts to use the parent +// directory. +func resolvePathOnDestinationContainer(container string, containerPath string, isStdin bool) (baseName string, containerInfo *entities.ContainerStatReport, resolvedToParentDir bool, err error) { + containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, containerPath) + if err == nil { + baseName = filepath.Base(containerInfo.LinkTarget) + return + } + + if strings.HasSuffix(containerPath, "/") { + err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container) + return + } + if isStdin { + err = errors.New("destination must be a directory when copying from stdin") + return + } + + // NOTE: containerInfo may actually be set. That happens when + // the container path is a symlink into nirvana. In that case, + // we must use the symlinked path instead. + path := containerPath + if containerInfo != nil { + baseName = filepath.Base(containerInfo.LinkTarget) + path = containerInfo.LinkTarget + } else { + baseName = filepath.Base(containerPath) + } + + parentDir, err := containerParentDir(container, path) + if err != nil { + err = errors.Wrapf(err, "could not determine parent dir of %q on container %s", path, container) + return + } + + containerInfo, err = registry.ContainerEngine().ContainerStat(registry.GetContext(), container, parentDir) + if err != nil { + err = errors.Wrapf(err, "%q could not be found on container %s", containerPath, container) + return + } + + resolvedToParentDir = true + return baseName, containerInfo, resolvedToParentDir, nil +} + // containerParentDir returns the parent directory of the specified path on the // container. If the path is relative, it will be resolved relative to the // container's working directory (or "/" if the work dir isn't set). diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/create.go libpod-3.4.4+ds1/cmd/podman/containers/create.go --- libpod-3.2.1+ds1/cmd/podman/containers/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,18 +7,19 @@ "strconv" "strings" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" - storageTransport "github.com/containers/image/v5/storage" "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" - "github.com/containers/storage" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -39,7 +40,7 @@ } containerCreateCommand = &cobra.Command{ - Args: cobra.MinimumNArgs(1), + Args: createCommand.Args, Use: createCommand.Use, Short: createCommand.Short, Long: createCommand.Long, @@ -52,33 +53,46 @@ ) var ( - cliVals common.ContainerCLIOpts + InitContainerType string + cliVals entities.ContainerCreateOptions ) func createFlags(cmd *cobra.Command) { flags := cmd.Flags() + initContainerFlagName := "init-ctr" + flags.StringVar( + &InitContainerType, + initContainerFlagName, "", + "Make this a pod init container.", + ) + flags.SetInterspersed(false) - common.DefineCreateFlags(cmd, &cliVals) + common.DefineCreateFlags(cmd, &cliVals, false) common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) if registry.IsRemote() { - _ = flags.MarkHidden("conmon-pidfile") + if cliVals.IsInfra { + _ = flags.MarkHidden("infra-conmon-pidfile") + } else { + _ = flags.MarkHidden("conmon-pidfile") + } + _ = flags.MarkHidden("pidfile") } + + _ = cmd.RegisterFlagCompletionFunc(initContainerFlagName, completion.AutocompleteDefault) } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: createCommand, }) createFlags(createCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerCreateCommand, Parent: containerCmd, }) @@ -89,27 +103,39 @@ var ( err error ) - cliVals.Net, err = common.NetFlagsToNetOptions(cmd) + flags := cmd.Flags() + cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, cliVals.Pod == "" && cliVals.PodIDFile == "") if err != nil { return err } - if err := createInit(cmd); err != nil { - return err + // Check if initctr is used with --pod and the value is correct + if initctr := InitContainerType; cmd.Flags().Changed("init-ctr") { + if !cmd.Flags().Changed("pod") { + return errors.New("must specify pod value with init-ctr") + } + if !util.StringInSlice(initctr, []string{define.AlwaysInitContainer, define.OneShotInitContainer}) { + return errors.Errorf("init-ctr value must be '%s' or '%s'", define.AlwaysInitContainer, define.OneShotInitContainer) + } + cliVals.InitContainerType = initctr } + cliVals, err = CreateInit(cmd, cliVals, false) + if err != nil { + return err + } imageName := args[0] rawImageName := "" if !cliVals.RootFS { rawImageName = args[0] - name, err := pullImage(args[0]) + name, err := PullImage(args[0], cliVals) if err != nil { return err } imageName = name } s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) - if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { + if err := specgenutil.FillOutSpecGen(s, &cliVals, args); err != nil { return err } s.RawImageName = rawImageName @@ -150,118 +176,109 @@ return removeContainers([]string{name}, rmOptions, false) } -func createInit(c *cobra.Command) error { - if c.Flag("shm-size").Changed { - cliVals.ShmSize = c.Flag("shm-size").Value.String() - } - - if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && (cliVals.Net.Network.NSMode == specgen.NoNetwork || cliVals.Net.Network.IsContainer()) { - return errors.Errorf("conflicting options: dns and the network mode.") - } - - if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed { - return errors.Errorf("--cpu-period and --cpus cannot be set together") - } - if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed { - return errors.Errorf("--cpu-quota and --cpus cannot be set together") - } - - noHosts, err := c.Flags().GetBool("no-hosts") - if err != nil { - return err - } - if noHosts && c.Flag("add-host").Changed { - return errors.Errorf("--no-hosts and --add-host cannot be set together") - } - cliVals.UserNS = c.Flag("userns").Value.String() +func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra bool) (entities.ContainerCreateOptions, error) { + vals.UserNS = c.Flag("userns").Value.String() // if user did not modify --userns flag and did turn on // uid/gid mappings, set userns flag to "private" - if !c.Flag("userns").Changed && cliVals.UserNS == "host" { - if len(cliVals.UIDMap) > 0 || - len(cliVals.GIDMap) > 0 || - cliVals.SubUIDName != "" || - cliVals.SubGIDName != "" { - cliVals.UserNS = "private" + if !c.Flag("userns").Changed && vals.UserNS == "host" { + if len(vals.UIDMap) > 0 || + len(vals.GIDMap) > 0 || + vals.SubUIDName != "" || + vals.SubGIDName != "" { + vals.UserNS = "private" } } - cliVals.IPC = c.Flag("ipc").Value.String() - cliVals.UTS = c.Flag("uts").Value.String() - cliVals.PID = c.Flag("pid").Value.String() - cliVals.CgroupNS = c.Flag("cgroupns").Value.String() - if c.Flag("entrypoint").Changed { - val := c.Flag("entrypoint").Value.String() - cliVals.Entrypoint = &val - } - - if c.Flags().Changed("group-add") { - groups := []string{} - for _, g := range cliVals.GroupAdd { - if g == "keep-groups" { - if len(cliVals.GroupAdd) > 1 { - return errors.New("the '--group-add keep-groups' option is not allowed with any other --group-add options") - } - if registry.IsRemote() { - return errors.New("the '--group-add keep-groups' option is not supported in remote mode") + if !isInfra { + if c.Flag("shm-size").Changed { + vals.ShmSize = c.Flag("shm-size").Value.String() + } + if c.Flag("cpu-period").Changed && c.Flag("cpus").Changed { + return vals, errors.Errorf("--cpu-period and --cpus cannot be set together") + } + if c.Flag("cpu-quota").Changed && c.Flag("cpus").Changed { + return vals, errors.Errorf("--cpu-quota and --cpus cannot be set together") + } + vals.IPC = c.Flag("ipc").Value.String() + vals.UTS = c.Flag("uts").Value.String() + vals.PID = c.Flag("pid").Value.String() + vals.CgroupNS = c.Flag("cgroupns").Value.String() + + if c.Flags().Changed("group-add") { + groups := []string{} + for _, g := range cliVals.GroupAdd { + if g == "keep-groups" { + if len(cliVals.GroupAdd) > 1 { + return vals, errors.New("the '--group-add keep-groups' option is not allowed with any other --group-add options") + } + if registry.IsRemote() { + return vals, errors.New("the '--group-add keep-groups' option is not supported in remote mode") + } + vals.Annotation = append(vals.Annotation, "run.oci.keep_original_groups=1") + } else { + groups = append(groups, g) } - cliVals.Annotation = append(cliVals.Annotation, "run.oci.keep_original_groups=1") - } else { - groups = append(groups, g) } + vals.GroupAdd = groups } - cliVals.GroupAdd = groups - } - if c.Flags().Changed("pids-limit") { - val := c.Flag("pids-limit").Value.String() - pidsLimit, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return err + if c.Flags().Changed("pids-limit") { + val := c.Flag("pids-limit").Value.String() + // Convert -1 to 0, so that -1 maps to unlimited pids limit + if val == "-1" { + val = "0" + } + pidsLimit, err := strconv.ParseInt(val, 10, 32) + if err != nil { + return vals, err + } + vals.PIDsLimit = &pidsLimit } - cliVals.PIDsLimit = &pidsLimit - } - if c.Flags().Changed("env") { - env, err := c.Flags().GetStringArray("env") - if err != nil { - return errors.Wrapf(err, "retrieve env flag") + if c.Flags().Changed("env") { + env, err := c.Flags().GetStringArray("env") + if err != nil { + return vals, errors.Wrapf(err, "retrieve env flag") + } + vals.Env = env + } + if c.Flag("cgroups").Changed && vals.CGroupsMode == "split" && registry.IsRemote() { + return vals, errors.Errorf("the option --cgroups=%q is not supported in remote mode", vals.CGroupsMode) } - cliVals.Env = env + + if c.Flag("pod").Changed && !strings.HasPrefix(c.Flag("pod").Value.String(), "new:") && c.Flag("userns").Changed { + return vals, errors.Errorf("--userns and --pod cannot be set together") + } + } + if (c.Flag("dns").Changed || c.Flag("dns-opt").Changed || c.Flag("dns-search").Changed) && vals.Net != nil && (vals.Net.Network.NSMode == specgen.NoNetwork || vals.Net.Network.IsContainer()) { + return vals, errors.Errorf("conflicting options: dns and the network mode: " + string(vals.Net.Network.NSMode)) + } + noHosts, err := c.Flags().GetBool("no-hosts") + if err != nil { + return vals, err + } + if noHosts && c.Flag("add-host").Changed { + return vals, errors.Errorf("--no-hosts and --add-host cannot be set together") } - if c.Flag("cgroups").Changed && cliVals.CGroupsMode == "split" && registry.IsRemote() { - return errors.Errorf("the option --cgroups=%q is not supported in remote mode", cliVals.CGroupsMode) + if !isInfra && c.Flag("entrypoint").Changed { + val := c.Flag("entrypoint").Value.String() + vals.Entrypoint = &val + } else if isInfra && c.Flag("infra-command").Changed { + } // Docker-compatibility: the "-h" flag for run/create is reserved for // the hostname (see https://github.com/containers/podman/issues/1367). - return nil + return vals, nil } -// TODO: we should let the backend take care of the pull policy (which it -// does!). The code below is at risk of causing regression and code divergence. -func pullImage(imageName string) (string, error) { +func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (string, error) { pullPolicy, err := config.ValidatePullPolicy(cliVals.Pull) if err != nil { return "", err } - // Check if the image is missing and hence if we need to pull it. - imageMissing := true - imageRef, err := alltransports.ParseImageName(imageName) - switch { - case err != nil: - // Assume we specified a local image without the explicit storage transport. - fallthrough - - case imageRef.Transport().Name() == storageTransport.Transport.Name(): - br, err := registry.ImageEngine().Exists(registry.GetContext(), imageName) - if err != nil { - return "", err - } - imageMissing = !br.Value - } - if cliVals.Platform != "" || cliVals.Arch != "" || cliVals.OS != "" { if cliVals.Platform != "" { if cliVals.Arch != "" || cliVals.OS != "" { @@ -273,31 +290,34 @@ cliVals.Arch = split[1] } } + } - if pullPolicy != config.PullPolicyAlways { - logrus.Info("--platform --arch and --os causes the pull policy to be \"always\"") - pullPolicy = config.PullPolicyAlways - } + skipTLSVerify := types.OptionalBoolUndefined + if cliVals.TLSVerify.Present() { + skipTLSVerify = types.NewOptionalBool(!cliVals.TLSVerify.Value()) } - if imageMissing || pullPolicy == config.PullPolicyAlways { - if pullPolicy == config.PullPolicyNever { - return "", errors.Wrap(storage.ErrImageUnknown, imageName) - } - pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ - Authfile: cliVals.Authfile, - Quiet: cliVals.Quiet, - Arch: cliVals.Arch, - OS: cliVals.OS, - Variant: cliVals.Variant, - SignaturePolicy: cliVals.SignaturePolicy, - PullPolicy: pullPolicy, - }) - if pullErr != nil { - return "", pullErr - } + pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{ + Authfile: cliVals.Authfile, + Quiet: cliVals.Quiet, + Arch: cliVals.Arch, + OS: cliVals.OS, + Variant: cliVals.Variant, + SignaturePolicy: cliVals.SignaturePolicy, + PullPolicy: pullPolicy, + SkipTLSVerify: skipTLSVerify, + }) + if pullErr != nil { + return "", pullErr + } + + // Return the input name such that the image resolves to correct + // repo/tag in the backend (see #8082). Unless we're referring to + // the image via a transport. + if _, err := alltransports.ParseImageName(imageName); err == nil { imageName = pullReport.Images[0] } + return imageName, nil } @@ -312,17 +332,54 @@ if len(podName) < 1 { return nil, errors.Errorf("new pod name must be at least one character") } + + var err error + uns := specgen.Namespace{NSMode: specgen.Default} + if cliVals.UserNS != "" { + uns, err = specgen.ParseNamespace(cliVals.UserNS) + if err != nil { + return nil, err + } + } createOptions := entities.PodCreateOptions{ Name: podName, Infra: true, Net: netOpts, CreateCommand: os.Args, Hostname: s.ContainerBasicConfig.Hostname, + Cpus: cliVals.CPUS, + CpusetCpus: cliVals.CPUSetCPUs, + Pid: cliVals.PID, + Userns: uns, } // Unset config values we passed to the pod to prevent them being used twice for the container and pod. s.ContainerBasicConfig.Hostname = "" s.ContainerNetworkConfig = specgen.ContainerNetworkConfig{} s.Pod = podName - return registry.ContainerEngine().PodCreate(context.Background(), createOptions) + podSpec := entities.PodSpec{} + podGen := specgen.NewPodSpecGenerator() + podSpec.PodSpecGen = *podGen + podGen, err = entities.ToPodSpecGen(*&podSpec.PodSpecGen, &createOptions) + if err != nil { + return nil, err + } + + infraOpts := entities.ContainerCreateOptions{ImageVolume: "bind", Net: netOpts, Quiet: true} + rawImageName := config.DefaultInfraImage + name, err := PullImage(rawImageName, infraOpts) + if err != nil { + fmt.Println(err) + } + imageName := name + podGen.InfraImage = imageName + podGen.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false) + podGen.InfraContainerSpec.RawImageName = rawImageName + podGen.InfraContainerSpec.NetworkOptions = podGen.NetworkOptions + err = specgenutil.FillOutSpecGen(podGen.InfraContainerSpec, &infraOpts, []string{}) + if err != nil { + return nil, err + } + podSpec.PodSpecGen = *podGen + return registry.ContainerEngine().PodCreate(context.Background(), podSpec) } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/diff.go libpod-3.4.4+ds1/cmd/podman/containers/diff.go --- libpod-3.2.1+ds1/cmd/podman/containers/diff.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,11 @@ package containers import ( - "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/diff" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -13,11 +14,11 @@ var ( // podman container _diff_ diffCmd = &cobra.Command{ - Use: "diff [options] CONTAINER", - Args: validate.IDOrLatestArgs, + Use: "diff [options] CONTAINER [CONTAINER]", + Args: diff.ValidateContainerDiffArgs, Short: "Inspect changes to the container's file systems", - Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer.`, - RunE: diff, + Long: `Displays changes to the container filesystem's'. The container will be compared to its parent layer or the second argument when given.`, + RunE: diffRun, ValidArgsFunction: common.AutocompleteContainers, Example: `podman container diff myCtr podman container diff -l --format json myCtr`, @@ -27,48 +28,28 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: diffCmd, Parent: containerCmd, }) diffOpts = &entities.DiffOptions{} flags := diffCmd.Flags() + + // FIXME: Why does this exists? It is not used anywhere. flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkHidden("archive") formatFlagName := "format" - flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") + flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)") _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } -func diff(cmd *cobra.Command, args []string) error { +func diffRun(cmd *cobra.Command, args []string) error { if len(args) == 0 && !diffOpts.Latest { return errors.New("container must be specified: podman container diff [options [...]] ID-NAME") } - - var id string - if len(args) > 0 { - id = args[0] - } - results, err := registry.ContainerEngine().ContainerDiff(registry.GetContext(), id, *diffOpts) - if err != nil { - return err - } - - switch { - case report.IsJSON(diffOpts.Format): - return common.ChangesToJSON(results) - case diffOpts.Format == "": - return common.ChangesToTable(results) - default: - return errors.New("only supported value for '--format' is 'json'") - } -} - -func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error { - diffOpts = &options - return diff(cmd, args) + diffOpts.Type = define.DiffContainer + return diff.Diff(cmd, args, *diffOpts) } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/exec.go libpod-3.4.4+ds1/cmd/podman/containers/exec.go --- libpod-3.2.1+ds1/cmd/podman/containers/exec.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/exec.go 2021-12-26 00:45:51.000000000 +0000 @@ -21,24 +21,22 @@ execDescription = `Execute the specified command inside a running container. ` execCommand = &cobra.Command{ - Use: "exec [options] CONTAINER [COMMAND [ARG...]]", - Short: "Run a process in a running container", - Long: execDescription, - RunE: exec, - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteExecCommand, + Use: "exec [options] CONTAINER [COMMAND [ARG...]]", + Short: "Run a process in a running container", + Long: execDescription, + RunE: exec, + ValidArgsFunction: common.AutocompleteExecCommand, Example: `podman exec -it ctrID ls podman exec -it -w /tmp myCtr pwd podman exec --user root ctrID ls`, } containerExecCommand = &cobra.Command{ - Use: execCommand.Use, - Short: execCommand.Short, - Long: execCommand.Long, - RunE: execCommand.RunE, - DisableFlagsInUseLine: true, - ValidArgsFunction: execCommand.ValidArgsFunction, + Use: execCommand.Use, + Short: execCommand.Short, + Long: execCommand.Long, + RunE: execCommand.RunE, + ValidArgsFunction: execCommand.ValidArgsFunction, Example: `podman container exec -it ctrID ls podman container exec -it -w /tmp myCtr pwd podman container exec --user root ctrID ls`, @@ -92,14 +90,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: execCommand, }) execFlags(execCommand) validate.AddLatestFlag(execCommand, &execOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerExecCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/exists.go libpod-3.4.4+ds1/cmd/podman/containers/exists.go --- libpod-3.2.1+ds1/cmd/podman/containers/exists.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/exists.go 2021-12-26 00:45:51.000000000 +0000 @@ -18,16 +18,14 @@ Long: containerExistsDescription, Example: `podman container exists --external containerID podman container exists myctr || podman run --name myctr [etc...]`, - RunE: exists, - Args: cobra.ExactArgs(1), - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteContainers, + RunE: exists, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainers, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: existsCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/export.go libpod-3.4.4+ds1/cmd/podman/containers/export.go --- libpod-3.2.1+ds1/cmd/podman/containers/export.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/export.go 2021-12-26 00:45:51.000000000 +0000 @@ -55,13 +55,11 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: exportCommand, }) exportFlags(exportCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerExportCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/init.go libpod-3.4.4+ds1/cmd/podman/containers/init.go --- libpod-3.2.1+ds1/cmd/podman/containers/init.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/init.go 2021-12-26 00:45:51.000000000 +0000 @@ -52,7 +52,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: initCommand, }) flags := initCommand.Flags() @@ -60,7 +59,6 @@ validate.AddLatestFlag(initCommand, &initOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Parent: containerCmd, Command: containerInitCommand, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/inspect.go libpod-3.4.4+ds1/cmd/podman/containers/inspect.go --- libpod-3.2.1+ds1/cmd/podman/containers/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -26,7 +26,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/kill.go libpod-3.4.4+ds1/cmd/podman/containers/kill.go --- libpod-3.2.1+ds1/cmd/podman/containers/kill.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/kill.go 2021-12-26 00:45:51.000000000 +0000 @@ -67,14 +67,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: killCommand, }) killFlags(killCommand) validate.AddLatestFlag(killCommand, &killOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerKillCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/list.go libpod-3.4.4+ds1/cmd/podman/containers/list.go --- libpod-3.2.1+ds1/cmd/podman/containers/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,7 +4,6 @@ "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -26,7 +25,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: listCmd, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/logs.go libpod-3.4.4+ds1/cmd/podman/containers/logs.go --- libpod-3.2.1+ds1/cmd/podman/containers/logs.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/logs.go 2021-12-26 00:45:51.000000000 +0000 @@ -19,6 +19,8 @@ entities.ContainerLogsOptions SinceRaw string + + UntilRaw string } var ( @@ -77,7 +79,6 @@ // logs registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: logsCommand, }) logsFlags(logsCommand) @@ -85,7 +86,6 @@ // container logs registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerLogsCommand, Parent: containerCmd, }) @@ -103,6 +103,10 @@ flags.StringVar(&logsOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP") _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) + untilFlagName := "until" + flags.StringVar(&logsOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP") + _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone) + tailFlagName := "tail" flags.Int64Var(&logsOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs. Defaults to -1, which prints all lines") _ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone) @@ -116,12 +120,20 @@ func logs(_ *cobra.Command, args []string) error { if logsOptions.SinceRaw != "" { // parse time, error out if something is wrong - since, err := util.ParseInputTime(logsOptions.SinceRaw) + since, err := util.ParseInputTime(logsOptions.SinceRaw, true) if err != nil { return errors.Wrapf(err, "error parsing --since %q", logsOptions.SinceRaw) } logsOptions.Since = since } + if logsOptions.UntilRaw != "" { + // parse time, error out if something is wrong + until, err := util.ParseInputTime(logsOptions.UntilRaw, false) + if err != nil { + return errors.Wrapf(err, "error parsing --until %q", logsOptions.UntilRaw) + } + logsOptions.Until = until + } logsOptions.StdoutWriter = os.Stdout logsOptions.StderrWriter = os.Stderr return registry.ContainerEngine().ContainerLogs(registry.GetContext(), args, logsOptions.ContainerLogsOptions) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/mount.go libpod-3.4.4+ds1/cmd/podman/containers/mount.go --- libpod-3.2.1+ds1/cmd/podman/containers/mount.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/mount.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,8 +3,6 @@ import ( "fmt" "os" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" @@ -25,6 +23,11 @@ ` mountCommand = &cobra.Command{ + Annotations: map[string]string{ + registry.UnshareNSRequired: "", + registry.ParentNSRequired: "", + registry.EngineMode: registry.ABIMode, + }, Use: "mount [options] [CONTAINER...]", Short: "Mount a working container's root filesystem", Long: mountDescription, @@ -32,20 +35,16 @@ Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, true, false) }, - Annotations: map[string]string{ - registry.UnshareNSRequired: "", - registry.ParentNSRequired: "", - }, ValidArgsFunction: common.AutocompleteContainers, } containerMountCommand = &cobra.Command{ + Annotations: mountCommand.Annotations, Use: mountCommand.Use, Short: mountCommand.Short, Long: mountCommand.Long, RunE: mountCommand.RunE, Args: mountCommand.Args, - Annotations: mountCommand.Annotations, ValidArgsFunction: mountCommand.ValidArgsFunction, } ) @@ -68,14 +67,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: mountCommand, }) mountFlags(mountCommand) validate.AddLatestFlag(mountCommand, &mountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: containerMountCommand, Parent: containerCmd, }) @@ -118,12 +115,16 @@ mrs = append(mrs, mountReporter{r}) } - format := "{{range . }}{{.ID}}\t{{.Path}}\n{{end}}" - tmpl, err := template.New("mounts").Parse(format) + format := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}" + tmpl, err := report.NewTemplate("mounts").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() return tmpl.Execute(w, mrs) } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/pause.go libpod-3.4.4+ds1/cmd/podman/containers/pause.go --- libpod-3.2.1+ds1/cmd/podman/containers/pause.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/pause.go 2021-12-26 00:45:51.000000000 +0000 @@ -48,14 +48,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pauseCommand, }) flags := pauseCommand.Flags() pauseFlags(flags) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerPauseCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/port.go libpod-3.4.4+ds1/cmd/podman/containers/port.go --- libpod-3.2.1+ds1/cmd/podman/containers/port.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/port.go 2021-12-26 00:45:51.000000000 +0000 @@ -56,14 +56,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: portCommand, }) portFlags(portCommand.Flags()) validate.AddLatestFlag(portCommand, &portOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerPortCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/prune.go libpod-3.4.4+ds1/cmd/podman/containers/prune.go --- libpod-3.2.1+ds1/cmd/podman/containers/prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,6 +13,7 @@ "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -35,7 +36,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pruneCommand, Parent: containerCmd, }) @@ -64,7 +64,7 @@ } } - pruneOptions.Filters, err = common.ParseFilters(filter) + pruneOptions.Filters, err = specgenutil.ParseFilters(filter) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/ps.go libpod-3.4.4+ds1/cmd/podman/containers/ps.go --- libpod-3.2.1+ds1/cmd/podman/containers/ps.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/ps.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,15 +6,12 @@ "sort" "strconv" "strings" - "text/tabwriter" - "text/template" "time" tm "github.com/buger/goterm" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" @@ -60,14 +57,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: psCommand, }) listFlagSet(psCommand) validate.AddLatestFlag(psCommand, &listOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: psContainerCommand, Parent: containerCmd, }) @@ -226,25 +221,29 @@ } hdrs, format := createPsOut() + + noHeading, _ := cmd.Flags().GetBool("noheading") if cmd.Flags().Changed("format") { + noHeading = noHeading || !report.HasTable(listOpts.Format) format = report.NormalizeFormat(listOpts.Format) - format = parse.EnforceRange(format) + format = report.EnforceRange(format) } ns := strings.NewReplacer(".Namespaces.", ".") format = ns.Replace(format) - tmpl, err := template.New("listContainers"). - Funcs(template.FuncMap(report.DefaultFuncs)). - Parse(format) + tmpl, err := report.NewTemplate("list").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() headers := func() error { return nil } - noHeading, _ := cmd.Flags().GetBool("noheading") - if !(noHeading || listOpts.Quiet || cmd.Flags().Changed("format")) { + if !noHeading { headers = func() error { return tmpl.Execute(w, hdrs) } @@ -301,9 +300,11 @@ "IPC": "ipc", "MNT": "mnt", "NET": "net", + "Networks": "networks", "PIDNS": "pidns", "Pod": "pod id", "PodName": "podname", // undo camelcase space break + "RunningFor": "running for", "UTS": "uts", "User": "userns", }) @@ -322,7 +323,7 @@ row += "\t{{.Size}}" } } - return hdrs, "{{range .}}" + row + "\n{{end}}" + return hdrs, "{{range .}}" + row + "\n{{end -}}" } type psReporter struct { @@ -374,6 +375,10 @@ // Status is a synonym for State() func (l psReporter) Status() string { + hc := l.ListContainer.Status + if hc != "" { + return l.State() + " (" + hc + ")" + } return l.State() } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/rename.go libpod-3.4.4+ds1/cmd/podman/containers/rename.go --- libpod-3.2.1+ds1/cmd/podman/containers/rename.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/rename.go 2021-12-26 00:45:51.000000000 +0000 @@ -33,12 +33,10 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: renameCommand, }) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRenameCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/restart.go libpod-3.4.4+ds1/cmd/podman/containers/restart.go --- libpod-3.2.1+ds1/cmd/podman/containers/restart.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/restart.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,6 +39,7 @@ Short: restartCommand.Short, Long: restartCommand.Long, RunE: restartCommand.RunE, + Args: restartCommand.Args, ValidArgsFunction: restartCommand.ValidArgsFunction, Example: `podman container restart ctrID podman container restart --latest @@ -66,14 +67,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: restartCommand, }) restartFlags(restartCommand) validate.AddLatestFlag(restartCommand, &restartOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRestartCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/restore.go libpod-3.4.4+ds1/cmd/podman/containers/restore.go --- libpod-3.2.1+ds1/cmd/podman/containers/restore.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/restore.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -36,13 +37,10 @@ } ) -var ( - restoreOptions entities.RestoreOptions -) +var restoreOptions entities.RestoreOptions func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: restoreCommand, Parent: containerCmd, }) @@ -67,10 +65,20 @@ flags.BoolVar(&restoreOptions.IgnoreStaticIP, "ignore-static-ip", false, "Ignore IP address set via --static-ip") flags.BoolVar(&restoreOptions.IgnoreStaticMAC, "ignore-static-mac", false, "Ignore MAC address set via --mac-address") flags.BoolVar(&restoreOptions.IgnoreVolumes, "ignore-volumes", false, "Do not export volumes associated with container") + + flags.StringSliceP( + "publish", "p", []string{}, + "Publish a container's port, or a range of ports, to the host (default [])", + ) + _ = restoreCommand.RegisterFlagCompletionFunc("publish", completion.AutocompleteNone) + + flags.StringVar(&restoreOptions.Pod, "pod", "", "Restore container into existing Pod (only works with --import)") + _ = restoreCommand.RegisterFlagCompletionFunc("pod", common.AutocompletePodsRunning) + validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest) } -func restore(_ *cobra.Command, args []string) error { +func restore(cmd *cobra.Command, args []string) error { var errs utils.OutputErrors if rootless.IsRootless() { return errors.New("restoring a container requires root") @@ -87,10 +95,24 @@ if restoreOptions.Import == "" && restoreOptions.Name != "" { return errors.Errorf("--name can only be used with --import") } + if restoreOptions.Import == "" && restoreOptions.Pod != "" { + return errors.Errorf("--pod can only be used with --import") + } if restoreOptions.Name != "" && restoreOptions.TCPEstablished { return errors.Errorf("--tcp-established cannot be used with --name") } + inputPorts, err := cmd.Flags().GetStringSlice("publish") + if err != nil { + return err + } + if len(inputPorts) > 0 { + restoreOptions.PublishPorts, err = specgenutil.CreatePortBindings(inputPorts) + if err != nil { + return err + } + } + argLen := len(args) if restoreOptions.Import != "" { if restoreOptions.All || restoreOptions.Latest { diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/rm.go libpod-3.4.4+ds1/cmd/podman/containers/rm.go --- libpod-3.2.1+ds1/cmd/podman/containers/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -38,13 +38,11 @@ } containerRmCommand = &cobra.Command{ - Use: rmCommand.Use, - Short: rmCommand.Short, - Long: rmCommand.Long, - RunE: rmCommand.RunE, - Args: func(cmd *cobra.Command, args []string) error { - return validate.CheckAllLatestAndCIDFile(cmd, args, false, true) - }, + Use: rmCommand.Use, + Short: rmCommand.Short, + Long: rmCommand.Long, + RunE: rmCommand.RunE, + Args: rmCommand.Args, ValidArgsFunction: rmCommand.ValidArgsFunction, Example: `podman container rm imageID podman container rm mywebserver myflaskserver 860a4b23 @@ -79,14 +77,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCommand, }) rmFlags(rmCommand) validate.AddLatestFlag(rmCommand, &rmOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRmCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/run.go libpod-3.4.4+ds1/cmd/podman/containers/run.go --- libpod-3.2.1+ds1/cmd/podman/containers/run.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/run.go 2021-12-26 00:45:51.000000000 +0000 @@ -14,6 +14,7 @@ "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -60,7 +61,7 @@ flags := cmd.Flags() flags.SetInterspersed(false) - common.DefineCreateFlags(cmd, &cliVals) + common.DefineCreateFlags(cmd, &cliVals, false) common.DefineNetFlags(cmd) flags.SetNormalizeFunc(utils.AliasFlags) @@ -77,6 +78,11 @@ flags.StringVar(&runOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-`, where `` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`") _ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys) + gpuFlagName := "gpus" + flags.String(gpuFlagName, "", "This is a Docker specific option and is a NOOP") + _ = cmd.RegisterFlagCompletionFunc(gpuFlagName, completion.AutocompleteNone) + _ = flags.MarkHidden("gpus") + if registry.IsRemote() { _ = flags.MarkHidden("preserve-fds") _ = flags.MarkHidden("conmon-pidfile") @@ -86,14 +92,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: runCommand, }) runFlags(runCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerRunCommand, Parent: containerCmd, }) @@ -103,10 +107,6 @@ func run(cmd *cobra.Command, args []string) error { var err error - cliVals.Net, err = common.NetFlagsToNetOptions(cmd) - if err != nil { - return err - } // TODO: Breaking change should be made fatal in next major Release if cliVals.TTY && cliVals.Interactive && !terminal.IsTerminal(int(os.Stdin.Fd())) { @@ -119,11 +119,17 @@ } } + flags := cmd.Flags() + cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, cliVals.Pod == "" && cliVals.PodIDFile == "") + if err != nil { + return err + } runOpts.CIDFile = cliVals.CIDFile runOpts.Rm = cliVals.Rm - if err := createInit(cmd); err != nil { + if cliVals, err = CreateInit(cmd, cliVals, false); err != nil { return err } + for fd := 3; fd < int(3+runOpts.PreserveFDs); fd++ { if !rootless.IsFdInherited(fd) { return errors.Errorf("file descriptor %d is not available - the preserve-fds option requires that file descriptors must be passed", fd) @@ -134,7 +140,7 @@ rawImageName := "" if !cliVals.RootFS { rawImageName = args[0] - name, err := pullImage(args[0]) + name, err := PullImage(args[0], cliVals) if err != nil { return err } @@ -175,7 +181,7 @@ } cliVals.PreserveFDs = runOpts.PreserveFDs s := specgen.NewSpecGenerator(imageName, cliVals.RootFS) - if err := common.FillOutSpecGen(s, &cliVals, args); err != nil { + if err := specgenutil.FillOutSpecGen(s, &cliVals, args); err != nil { return err } s.RawImageName = rawImageName @@ -204,5 +210,8 @@ logrus.Errorf("%s", errorhandling.JoinErrors(rmErrors)) } } + if cmd.Flag("gpus").Changed { + logrus.Info("--gpus is a Docker specific option and is a NOOP") + } return nil } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/runlabel.go libpod-3.4.4+ds1/cmd/podman/containers/runlabel.go --- libpod-3.2.1+ds1/cmd/podman/containers/runlabel.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/runlabel.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,6 +25,7 @@ runlabelOptions = runlabelOptionsWrapper{} runlabelDescription = "Executes a command as described by a container image label." runlabelCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "runlabel [options] LABEL IMAGE [ARG...]", Short: "Execute the command described by an image label", Long: runlabelDescription, @@ -39,7 +40,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: runlabelCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/start.go libpod-3.4.4+ds1/cmd/podman/containers/start.go --- libpod-3.2.1+ds1/cmd/podman/containers/start.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/start.go 2021-12-26 00:45:51.000000000 +0000 @@ -70,14 +70,12 @@ } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: startCommand, }) startFlags(startCommand) validate.AddLatestFlag(startCommand, &startOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStartCommand, Parent: containerCmd, }) @@ -89,6 +87,9 @@ if len(args) == 0 && !startOptions.Latest && !startOptions.All { return errors.New("start requires at least one argument") } + if startOptions.All && startOptions.Latest { + return errors.Errorf("--all and --latest cannot be used together") + } if len(args) > 0 && startOptions.Latest { return errors.Errorf("--latest and containers cannot be used together") } diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/stats.go libpod-3.4.4+ds1/cmd/podman/containers/stats.go --- libpod-3.2.1+ds1/cmd/podman/containers/stats.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/stats.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,23 +3,18 @@ import ( "fmt" "os" - "text/tabwriter" - "text/template" tm "github.com/buger/goterm" + "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/utils" "github.com/docker/go-units" "github.com/pkg/errors" - "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -58,6 +53,7 @@ Latest bool NoReset bool NoStream bool + Interval int } var ( @@ -75,18 +71,19 @@ flags.BoolVar(&statsOptions.NoReset, "no-reset", false, "Disable resetting the screen between intervals") flags.BoolVar(&statsOptions.NoStream, "no-stream", false, "Disable streaming stats and only pull the first result, default setting is false") + intervalFlagName := "interval" + flags.IntVarP(&statsOptions.Interval, intervalFlagName, "i", 5, "Time in seconds between stats reports") + _ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone) } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: statsCommand, }) statFlags(statsCommand) validate.AddLatestFlag(statsCommand, &statsOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStatsCommand, Parent: containerCmd, }) @@ -114,21 +111,12 @@ } func stats(cmd *cobra.Command, args []string) error { - if rootless.IsRootless() { - unified, err := cgroups.IsCgroup2UnifiedMode() - if err != nil { - return err - } - if !unified { - return errors.New("stats is not supported in rootless mode without cgroups v2") - } - } - // Convert to the entities options. We should not leak CLI-only // options into the backend and separate concerns. opts := entities.ContainerStatsOptions{ - Latest: statsOptions.Latest, - Stream: !statsOptions.NoStream, + Latest: statsOptions.Latest, + Stream: !statsOptions.NoStream, + Interval: statsOptions.Interval, } statsChan, err := registry.ContainerEngine().ContainerStats(registry.Context(), args, opts) if err != nil { @@ -139,7 +127,7 @@ return report.Error } if err := outputStats(report.Stats); err != nil { - logrus.Error(err) + return err } } return nil @@ -148,7 +136,9 @@ func outputStats(reports []define.ContainerStats) error { headers := report.Headers(define.ContainerStats{}, map[string]string{ "ID": "ID", + "UpTime": "CPU TIME", "CPUPerc": "CPU %", + "AVGCPU": "Avg CPU %", "MemUsage": "MEM USAGE / LIMIT", "MemUsageBytes": "MEM USAGE / LIMIT", "MemPerc": "MEM %", @@ -168,17 +158,21 @@ if report.IsJSON(statsOptions.Format) { return outputJSON(stats) } - format := "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\n" + format := "{{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDS}}\t{{.UpTime}}\t{{.AVGCPU}}\n" if len(statsOptions.Format) > 0 { format = report.NormalizeFormat(statsOptions.Format) } - format = parse.EnforceRange(format) + format = report.EnforceRange(format) + + tmpl, err := report.NewTemplate("stats").Parse(format) + if err != nil { + return err + } - tmpl, err := template.New("stats").Parse(format) + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() if len(statsOptions.Format) < 1 { @@ -204,6 +198,14 @@ return floatToPercentString(s.CPU) } +func (s *containerStats) AVGCPU() string { + return floatToPercentString(s.AvgCPU) +} + +func (s *containerStats) Up() string { + return (s.UpTime.String()) +} + func (s *containerStats) MemPerc() string { return floatToPercentString(s.ContainerStats.MemPerc) } @@ -259,7 +261,9 @@ type jstat struct { Id string `json:"id"` // nolint Name string `json:"name"` + CPUTime string `json:"cpu_time"` CpuPercent string `json:"cpu_percent"` // nolint + AverageCPU string `json:"avg_cpu"` MemUsage string `json:"mem_usage"` MemPerc string `json:"mem_percent"` NetIO string `json:"net_io"` @@ -271,7 +275,9 @@ jstats = append(jstats, jstat{ Id: j.ID(), Name: j.Name, + CPUTime: j.Up(), CpuPercent: j.CPUPerc(), + AverageCPU: j.AVGCPU(), MemUsage: j.MemUsage(), MemPerc: j.MemPerc(), NetIO: j.NetIO(), diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/stop.go libpod-3.4.4+ds1/cmd/podman/containers/stop.go --- libpod-3.2.1+ds1/cmd/podman/containers/stop.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/stop.go 2021-12-26 00:45:51.000000000 +0000 @@ -78,14 +78,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: stopCommand, }) stopFlags(stopCommand) validate.AddLatestFlag(stopCommand, &stopOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerStopCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/top.go libpod-3.4.4+ds1/cmd/podman/containers/top.go --- libpod-3.2.1+ds1/cmd/podman/containers/top.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/top.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,8 +5,8 @@ "fmt" "os" "strings" - "text/tabwriter" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" @@ -58,7 +58,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: topCommand, }) topFlags(topCommand.Flags()) @@ -71,7 +70,6 @@ } registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerTopCommand, Parent: containerCmd, }) @@ -79,7 +77,7 @@ validate.AddLatestFlag(containerTopCommand, &topOptions.Latest) } -func top(cmd *cobra.Command, args []string) error { +func top(_ *cobra.Command, args []string) error { if topOptions.ListDescriptors { descriptors, err := util.GetContainerPidInformationDescriptors() if err != nil { @@ -105,7 +103,11 @@ return err } - w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + for _, proc := range topResponse.Value { if _, err := fmt.Fprintln(w, proc); err != nil { return err diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/unmount.go libpod-3.4.4+ds1/cmd/podman/containers/unmount.go --- libpod-3.2.1+ds1/cmd/podman/containers/unmount.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/unmount.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,11 +20,12 @@ An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ - Use: "unmount [options] CONTAINER [CONTAINER...]", - Aliases: []string{"umount"}, - Short: "Unmounts working container's root filesystem", - Long: description, - RunE: unmount, + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "unmount [options] CONTAINER [CONTAINER...]", + Aliases: []string{"umount"}, + Short: "Unmounts working container's root filesystem", + Long: description, + RunE: unmount, Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, @@ -35,11 +36,12 @@ } containerUnmountCommand = &cobra.Command{ - Use: unmountCommand.Use, - Short: unmountCommand.Short, - Aliases: unmountCommand.Aliases, - Long: unmountCommand.Long, - RunE: unmountCommand.RunE, + Annotations: unmountCommand.Annotations, + Use: unmountCommand.Use, + Short: unmountCommand.Short, + Aliases: unmountCommand.Aliases, + Long: unmountCommand.Long, + RunE: unmountCommand.RunE, Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, @@ -61,14 +63,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: unmountCommand, }) unmountFlags(unmountCommand.Flags()) validate.AddLatestFlag(unmountCommand, &unmountOpts.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: containerUnmountCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/unpause.go libpod-3.4.4+ds1/cmd/podman/containers/unpause.go --- libpod-3.2.1+ds1/cmd/podman/containers/unpause.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/unpause.go 2021-12-26 00:45:51.000000000 +0000 @@ -45,14 +45,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: unpauseCommand, }) flags := unpauseCommand.Flags() unpauseFlags(flags) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerUnpauseCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/containers/wait.go libpod-3.4.4+ds1/cmd/podman/containers/wait.go --- libpod-3.2.1+ds1/cmd/podman/containers/wait.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/containers/wait.go 2021-12-26 00:45:51.000000000 +0000 @@ -60,14 +60,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: waitCommand, }) waitFlags(waitCommand) validate.AddLatestFlag(waitCommand, &waitOptions.Latest) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: containerWaitCommand, Parent: containerCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/diff/diff.go libpod-3.4.4+ds1/cmd/podman/diff/diff.go --- libpod-3.2.1+ds1/cmd/podman/diff/diff.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/diff/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,79 @@ +package diff + +import ( + "encoding/json" + "fmt" + "os" + + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/storage/pkg/archive" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error { + results, err := registry.ContainerEngine().Diff(registry.GetContext(), args, options) + if err != nil { + return err + } + + switch { + case report.IsJSON(options.Format): + return changesToJSON(results) + case options.Format == "": + return changesToTable(results) + default: + return errors.New("only supported value for '--format' is 'json'") + } +} + +type ChangesReportJSON struct { + Changed []string `json:"changed,omitempty"` + Added []string `json:"added,omitempty"` + Deleted []string `json:"deleted,omitempty"` +} + +func changesToJSON(diffs *entities.DiffReport) error { + body := ChangesReportJSON{} + for _, row := range diffs.Changes { + switch row.Kind { + case archive.ChangeAdd: + body.Added = append(body.Added, row.Path) + case archive.ChangeDelete: + body.Deleted = append(body.Deleted, row.Path) + case archive.ChangeModify: + body.Changed = append(body.Changed, row.Path) + default: + return errors.Errorf("output kind %q not recognized", row.Kind) + } + } + + // Pull in configured json library + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + return enc.Encode(body) +} + +func changesToTable(diffs *entities.DiffReport) error { + for _, row := range diffs.Changes { + fmt.Fprintln(os.Stdout, row.String()) + } + return nil +} + +// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag +func ValidateContainerDiffArgs(cmd *cobra.Command, args []string) error { + given, _ := cmd.Flags().GetBool("latest") + if len(args) > 0 && !given { + return cobra.RangeArgs(1, 2)(cmd, args) + } + if len(args) > 0 && given { + return errors.New("--latest and containers cannot be used together") + } + if len(args) == 0 && !given { + return errors.Errorf("%q requires a name, id, or the \"--latest\" flag", cmd.CommandPath()) + } + return nil +} diff -Nru libpod-3.2.1+ds1/cmd/podman/diff.go libpod-3.4.4+ds1/cmd/podman/diff.go --- libpod-3.2.1+ds1/cmd/podman/diff.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,13 +1,11 @@ package main import ( - "fmt" - "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/containers" - "github.com/containers/podman/v3/cmd/podman/images" + "github.com/containers/podman/v3/cmd/podman/diff" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -16,13 +14,13 @@ var ( // Command: podman _diff_ Object_ID - diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.` + diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer or the second argument when given.` diffCmd = &cobra.Command{ - Use: "diff [options] {CONTAINER|IMAGE}", - Args: validate.IDOrLatestArgs, + Use: "diff [options] {CONTAINER|IMAGE} [{CONTAINER|IMAGE}]", + Args: diff.ValidateContainerDiffArgs, Short: "Display the changes to the object's file system", Long: diffDescription, - RunE: diff, + RunE: diffRun, ValidArgsFunction: common.AutocompleteContainersAndImages, Example: `podman diff imageID podman diff ctrID @@ -34,40 +32,21 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: diffCmd, }) flags := diffCmd.Flags() + // FIXME: Why does this exists? It is not used anywhere. flags.BoolVar(&diffOpts.Archive, "archive", true, "Save the diff as a tar archive") _ = flags.MarkHidden("archive") formatFlagName := "format" - flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") + flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)") _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) validate.AddLatestFlag(diffCmd, &diffOpts.Latest) } -func diff(cmd *cobra.Command, args []string) error { - // Latest implies looking for a container - if diffOpts.Latest { - return containers.Diff(cmd, args, diffOpts) - } - - options := entities.ContainerExistsOptions{ - External: true, - } - if found, err := registry.ContainerEngine().ContainerExists(registry.GetContext(), args[0], options); err != nil { - return err - } else if found.Value { - return containers.Diff(cmd, args, diffOpts) - } - - if found, err := registry.ImageEngine().Exists(registry.GetContext(), args[0]); err != nil { - return err - } else if found.Value { - return images.Diff(cmd, args, diffOpts) - } - - return fmt.Errorf("%s not found on system", args[0]) +func diffRun(cmd *cobra.Command, args []string) error { + diffOpts.Type = define.DiffAll + return diff.Diff(cmd, args, diffOpts) } diff -Nru libpod-3.2.1+ds1/cmd/podman/generate/generate.go libpod-3.4.4+ds1/cmd/podman/generate/generate.go --- libpod-3.2.1+ds1/cmd/podman/generate/generate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/generate/generate.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/util" "github.com/spf13/cobra" ) @@ -12,7 +11,7 @@ // Command: podman _generate_ generateCmd = &cobra.Command{ Use: "generate", - Short: "Generate structured data based on containers, pods or volumes.", + Short: "Generate structured data based on containers, pods or volumes", Long: "Generate structured data (e.g., Kubernetes YAML or systemd units) based on containers, pods or volumes.", RunE: validate.SubCommandExists, } @@ -21,7 +20,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: generateCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/generate/kube.go libpod-3.4.4+ds1/cmd/podman/generate/kube.go --- libpod-3.2.1+ds1/cmd/podman/generate/kube.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/generate/kube.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,6 +2,7 @@ import ( "fmt" + "io" "io/ioutil" "os" @@ -38,7 +39,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: kubeCmd, Parent: generateCmd, }) @@ -62,6 +62,10 @@ if err != nil { return err } + if r, ok := report.Reader.(io.ReadCloser); ok { + defer r.Close() + } + if cmd.Flags().Changed("filename") { if _, err := os.Stat(kubeFile); err == nil { return errors.Errorf("cannot write to %q; file exists", kubeFile) diff -Nru libpod-3.2.1+ds1/cmd/podman/generate/systemd.go libpod-3.4.4+ds1/cmd/podman/generate/systemd.go --- libpod-3.2.1+ds1/cmd/podman/generate/systemd.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/generate/systemd.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,15 +12,22 @@ "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/pkg/domain/entities" + systemDefine "github.com/containers/podman/v3/pkg/systemd/define" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) +const ( + restartPolicyFlagName = "restart-policy" + timeFlagName = "time" +) + var ( files bool format string systemdTimeout uint + systemdRestart string systemdOptions = entities.GenerateSystemdOptions{} systemdDescription = `Generate systemd units for a pod or container. The generated units can later be controlled via systemctl(1).` @@ -40,7 +47,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: systemdCmd, Parent: generateCmd, }) @@ -48,10 +54,9 @@ flags.BoolVarP(&systemdOptions.Name, "name", "n", false, "Use container/pod names instead of IDs") flags.BoolVarP(&files, "files", "f", false, "Generate .service files instead of printing to stdout") - timeFlagName := "time" flags.UintVarP(&systemdTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Stop timeout override") _ = systemdCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) - flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container instead of starting an existing one") + flags.BoolVarP(&systemdOptions.New, "new", "", false, "Create a new container or pod instead of starting an existing one") flags.BoolVarP(&systemdOptions.NoHeader, "no-header", "", false, "Skip header generation") containerPrefixFlagName := "container-prefix" @@ -66,8 +71,7 @@ flags.StringVar(&systemdOptions.Separator, separatorFlagName, "-", "Systemd unit name separator between name/id and prefix") _ = systemdCmd.RegisterFlagCompletionFunc(separatorFlagName, completion.AutocompleteNone) - restartPolicyFlagName := "restart-policy" - flags.StringVar(&systemdOptions.RestartPolicy, restartPolicyFlagName, "on-failure", "Systemd restart-policy") + flags.StringVar(&systemdRestart, restartPolicyFlagName, systemDefine.DefaultRestartPolicy, "Systemd restart-policy") _ = systemdCmd.RegisterFlagCompletionFunc(restartPolicyFlagName, common.AutocompleteSystemdRestartOptions) formatFlagName := "format" @@ -78,9 +82,12 @@ } func systemd(cmd *cobra.Command, args []string) error { - if cmd.Flags().Changed("time") { + if cmd.Flags().Changed(timeFlagName) { systemdOptions.StopTimeout = &systemdTimeout } + if cmd.Flags().Changed(restartPolicyFlagName) { + systemdOptions.RestartPolicy = &systemdRestart + } if registry.IsRemote() { logrus.Warnln("The generated units should be placed on your remote system") diff -Nru libpod-3.2.1+ds1/cmd/podman/healthcheck/healthcheck.go libpod-3.4.4+ds1/cmd/podman/healthcheck/healthcheck.go --- libpod-3.2.1+ds1/cmd/podman/healthcheck/healthcheck.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/healthcheck/healthcheck.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,12 +3,10 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( - // Command: healthcheck healthCmd = &cobra.Command{ Use: "healthcheck", Short: "Manage health checks on containers", @@ -19,7 +17,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: healthCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/healthcheck/run.go libpod-3.4.4+ds1/cmd/podman/healthcheck/run.go --- libpod-3.2.1+ds1/cmd/podman/healthcheck/run.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/healthcheck/run.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,28 +6,26 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( - healthcheckRunDescription = "run the health check of a container" - healthcheckrunCommand = &cobra.Command{ - Use: "run CONTAINER", - Short: "run the health check of a container", - Long: healthcheckRunDescription, - Example: `podman healthcheck run mywebapp`, - RunE: run, - Args: cobra.ExactArgs(1), - ValidArgsFunction: common.AutocompleteContainersRunning, - DisableFlagsInUseLine: true, + runCmd = &cobra.Command{ + Use: "run CONTAINER", + Short: "run the health check of a container", + Long: "run the health check of a container", + Example: `podman healthcheck run mywebapp`, + RunE: run, + Args: cobra.ExactArgs(1), + ValidArgsFunction: common.AutocompleteContainersRunning, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: healthcheckrunCommand, + Command: runCmd, Parent: healthCmd, }) } @@ -37,9 +35,9 @@ if err != nil { return err } - if response.Status == "unhealthy" { + if response.Status == define.HealthCheckUnhealthy { registry.SetExitCode(1) + fmt.Println(response.Status) } - fmt.Println(response.Status) return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/images/build.go libpod-3.4.4+ds1/cmd/podman/images/build.go --- libpod-3.2.1+ds1/cmd/podman/images/build.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/build.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ buildahDefine "github.com/containers/buildah/define" buildahCLI "github.com/containers/buildah/pkg/cli" "github.com/containers/buildah/pkg/parse" + buildahUtil "github.com/containers/buildah/pkg/util" "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" @@ -67,6 +68,18 @@ podman image build --layers --force-rm --tag imageName .`, } + buildxBuildCmd = &cobra.Command{ + Args: buildCmd.Args, + Use: buildCmd.Use, + Short: buildCmd.Short, + Long: buildCmd.Long, + RunE: buildCmd.RunE, + ValidArgsFunction: buildCmd.ValidArgsFunction, + Example: `podman buildx build . + podman buildx build --creds=username:password -t imageName -f Containerfile.simple . + podman buildx build --layers --force-rm --tag imageName .`, + } + buildOpts = buildFlagsWrapper{} ) @@ -82,23 +95,33 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: buildCmd, }) buildFlags(buildCmd) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageBuildCmd, Parent: imageCmd, }) buildFlags(imageBuildCmd) + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: buildxBuildCmd, + Parent: buildxCmd, + }) + buildFlags(buildxBuildCmd) } func buildFlags(cmd *cobra.Command) { flags := cmd.Flags() + // buildx build --load ignored, but added for compliance + flags.Bool("load", false, "buildx --load") + _ = flags.MarkHidden("load") + + // buildx build --progress ignored, but added for compliance + flags.String("progress", "auto", "buildx --progress") + _ = flags.MarkHidden("progress") + // Podman flags flags.BoolVarP(&buildOpts.SquashAll, "squash-all", "", false, "Squash all layers into a single layer") @@ -337,6 +360,12 @@ } } + cleanTmpFile := false + flags.Authfile, cleanTmpFile = buildahUtil.MirrorToTempFileIfPathIsDescriptor(flags.Authfile) + if cleanTmpFile { + defer os.Remove(flags.Authfile) + } + args := make(map[string]string) if c.Flag("build-arg").Changed { for _, arg := range flags.BuildArg { @@ -454,7 +483,7 @@ runtimeFlags = append(runtimeFlags, "--systemd-cgroup") } - imageOS, arch, err := parse.PlatformFromOptions(c) + platforms, err := parse.PlatformsFromOptions(c) if err != nil { return nil, err } @@ -468,7 +497,6 @@ AddCapabilities: flags.CapAdd, AdditionalTags: tags, Annotations: flags.Annotation, - Architecture: arch, Args: args, BlobDirectory: flags.BlobCache, CNIConfigDir: flags.CNIConfigDir, @@ -494,11 +522,11 @@ MaxPullPushRetries: 3, NamespaceOptions: nsValues, NoCache: flags.NoCache, - OS: imageOS, OciDecryptConfig: decConfig, Out: stdout, Output: output, OutputFormat: format, + Platforms: platforms, PullPolicy: pullPolicy, PullPushRetryDelay: 2 * time.Second, Quiet: flags.Quiet, @@ -506,6 +534,7 @@ ReportWriter: reporter, Runtime: containerConfig.RuntimePath, RuntimeArgs: runtimeFlags, + RusageLogFile: flags.RusageLogFile, SignBy: flags.SignBy, SignaturePolicyPath: flags.SignaturePolicy, Squash: flags.Squash, diff -Nru libpod-3.2.1+ds1/cmd/podman/images/buildx.go libpod-3.4.4+ds1/cmd/podman/images/buildx.go --- libpod-3.2.1+ds1/cmd/podman/images/buildx.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/buildx.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,29 @@ +package images + +import ( + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/spf13/cobra" +) + +var ( + // Command: podman _buildx_ + // This is a hidden command, which was added to make converting + // from Docker to Podman easier. + // For now podman buildx build just calls into podman build + // If we are adding new buildx features, we will add them by default + // to podman build. + buildxCmd = &cobra.Command{ + Use: "buildx", + Short: "Build images", + Long: "Build images", + RunE: validate.SubCommandExists, + Hidden: true, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: buildxCmd, + }) +} diff -Nru libpod-3.2.1+ds1/cmd/podman/images/diff.go libpod-3.4.4+ds1/cmd/podman/images/diff.go --- libpod-3.2.1+ds1/cmd/podman/images/diff.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,11 +1,11 @@ package images import ( - "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/diff" "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -13,11 +13,11 @@ var ( // podman container _inspect_ diffCmd = &cobra.Command{ - Use: "diff [options] IMAGE", - Args: cobra.ExactArgs(1), + Use: "diff [options] IMAGE [IMAGE]", + Args: cobra.RangeArgs(1, 2), Short: "Inspect changes to the image's file systems", - Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer.`, - RunE: diff, + Long: `Displays changes to the image's filesystem. The image will be compared to its parent layer or the second argument when given.`, + RunE: diffRun, ValidArgsFunction: common.AutocompleteImages, Example: `podman image diff myImage podman image diff --format json redis:alpine`, @@ -27,7 +27,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: diffCmd, Parent: imageCmd, }) @@ -40,31 +39,11 @@ _ = flags.MarkDeprecated("archive", "Provided for backwards compatibility, has no impact on output.") formatFlagName := "format" - flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format") + flags.StringVar(&diffOpts.Format, formatFlagName, "", "Change the output format (json)") _ = diffCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(nil)) } -func diff(cmd *cobra.Command, args []string) error { - if diffOpts.Latest { - return errors.New("image diff does not support --latest") - } - - results, err := registry.ImageEngine().Diff(registry.GetContext(), args[0], *diffOpts) - if err != nil { - return err - } - - switch { - case report.IsJSON(diffOpts.Format): - return common.ChangesToJSON(results) - case diffOpts.Format == "": - return common.ChangesToTable(results) - default: - return errors.New("only supported value for '--format' is 'json'") - } -} - -func Diff(cmd *cobra.Command, args []string, options entities.DiffOptions) error { - diffOpts = &options - return diff(cmd, args) +func diffRun(cmd *cobra.Command, args []string) error { + diffOpts.Type = define.DiffImage + return diff.Diff(cmd, args, *diffOpts) } diff -Nru libpod-3.2.1+ds1/cmd/podman/images/exists.go libpod-3.4.4+ds1/cmd/podman/images/exists.go --- libpod-3.2.1+ds1/cmd/podman/images/exists.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/exists.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -17,13 +16,11 @@ ValidArgsFunction: common.AutocompleteImages, Example: `podman image exists ID podman image exists IMAGE && podman pull IMAGE`, - DisableFlagsInUseLine: true, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: existsCmd, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/history.go libpod-3.4.4+ds1/cmd/podman/images/history.go --- libpod-3.2.1+ds1/cmd/podman/images/history.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/history.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,14 +5,11 @@ "fmt" "os" "strings" - "text/tabwriter" - "text/template" "time" "unicode" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/docker/go-units" @@ -56,13 +53,11 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: historyCmd, }) historyFlags(historyCmd) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageHistoryCmd, Parent: imageCmd, }) @@ -127,13 +122,17 @@ case opts.quiet: row = "{{.ID}}\n" } - format := parse.EnforceRange(row) + format := report.EnforceRange(row) - tmpl, err := template.New("report").Parse(format) + tmpl, err := report.NewTemplate("history").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() if !opts.quiet && !cmd.Flags().Changed("format") { diff -Nru libpod-3.2.1+ds1/cmd/podman/images/image.go libpod-3.4.4+ds1/cmd/podman/images/image.go --- libpod-3.2.1+ds1/cmd/podman/images/image.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/image.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -22,7 +21,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/images/images.go libpod-3.4.4+ds1/cmd/podman/images/images.go --- libpod-3.2.1+ds1/cmd/podman/images/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/images.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -package images - -import ( - "strings" - - "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/spf13/cobra" -) - -var ( - // podman _images_ Alias for podman image _list_ - imagesCmd = &cobra.Command{ - Use: strings.Replace(listCmd.Use, "list", "images", 1), - Args: listCmd.Args, - Short: listCmd.Short, - Long: listCmd.Long, - RunE: listCmd.RunE, - ValidArgsFunction: listCmd.ValidArgsFunction, - Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1), - DisableFlagsInUseLine: true, - } -) - -func init() { - registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: imagesCmd, - }) - - imageListFlagSet(imagesCmd) -} diff -Nru libpod-3.2.1+ds1/cmd/podman/images/import.go libpod-3.4.4+ds1/cmd/podman/images/import.go --- libpod-3.2.1+ds1/cmd/podman/images/import.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/import.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,9 @@ import ( "context" "fmt" + "io" + "io/ioutil" + "os" "strings" "github.com/containers/common/pkg/completion" @@ -27,7 +30,7 @@ RunE: importCon, Args: cobra.RangeArgs(1, 2), ValidArgsFunction: common.AutocompleteDefaultOneArg, - Example: `podman import http://example.com/ctr.tar url-image + Example: `podman import https://example.com/ctr.tar url-image cat ctr.tar | podman -q import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman import -`, } @@ -39,7 +42,7 @@ RunE: importCommand.RunE, Args: importCommand.Args, ValidArgsFunction: importCommand.ValidArgsFunction, - Example: `podman image import http://example.com/ctr.tar url-image + Example: `podman image import https://example.com/ctr.tar url-image cat ctr.tar | podman -q image import --message "importing the ctr.tar tarball" - image-imported cat ctr.tar | podman image import -`, } @@ -51,13 +54,11 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: importCommand, }) importFlags(importCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageImportCommand, Parent: imageCmd, }) @@ -99,6 +100,22 @@ default: return errors.Errorf("too many arguments. Usage TARBALL [REFERENCE]") } + + if source == "-" { + outFile, err := ioutil.TempFile("", "podman") + if err != nil { + return errors.Errorf("error creating file %v", err) + } + defer os.Remove(outFile.Name()) + defer outFile.Close() + + _, err = io.Copy(outFile, os.Stdin) + if err != nil { + return errors.Errorf("error copying file %v", err) + } + source = outFile.Name() + } + errFileName := parse.ValidateFileName(source) errURL := parse.ValidURL(source) if errURL == nil { diff -Nru libpod-3.2.1+ds1/cmd/podman/images/inspect.go libpod-3.4.4+ds1/cmd/podman/images/inspect.go --- libpod-3.2.1+ds1/cmd/podman/images/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -17,16 +17,15 @@ Long: `Displays the low-level information of an image identified by name or ID.`, RunE: inspectExec, ValidArgsFunction: common.AutocompleteImages, - Example: `podman inspect alpine - podman inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine - podman inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, + Example: `podman image inspect alpine + podman image inspect --format "imageId: {{.Id}} size: {{.Size}}" alpine + podman image inspect --format "image: {{.ImageName}} driver: {{.Driver}}" myctr`, } inspectOpts *entities.InspectOptions ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/list.go libpod-3.4.4+ds1/cmd/podman/images/list.go --- libpod-3.2.1+ds1/cmd/podman/images/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,8 +5,6 @@ "os" "sort" "strings" - "text/tabwriter" - "text/template" "time" "unicode" @@ -14,7 +12,6 @@ "github.com/containers/common/pkg/report" "github.com/containers/image/v5/docker/reference" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/docker/go-units" @@ -34,8 +31,7 @@ } var ( - // Command: podman image _list_ - listCmd = &cobra.Command{ + imageListCmd = &cobra.Command{ Use: "list [options] [IMAGE]", Aliases: []string{"ls"}, Args: cobra.MaximumNArgs(1), @@ -46,7 +42,18 @@ Example: `podman image list --format json podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}" podman image list --filter dangling=true`, - DisableFlagsInUseLine: true, + } + + imagesCmd = &cobra.Command{ + Use: "images [options] [IMAGE]", + Args: imageListCmd.Args, + Short: imageListCmd.Short, + Long: imageListCmd.Long, + RunE: imageListCmd.RunE, + ValidArgsFunction: imageListCmd.ValidArgsFunction, + Example: `podman images --format json + podman images --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}" + podman images --filter dangling=true`, } // Options to pull data @@ -65,11 +72,15 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: listCmd, + Command: imageListCmd, Parent: imageCmd, }) - imageListFlagSet(listCmd) + imageListFlagSet(imageListCmd) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: imagesCmd, + }) + imageListFlagSet(imagesCmd) } func imageListFlagSet(cmd *cobra.Command) { @@ -126,7 +137,7 @@ case listFlag.quiet: return writeID(imgs) default: - if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) { listFlag.noHeading = true } return writeTemplate(imgs) @@ -181,20 +192,23 @@ "ReadOnly": "R/O", }) - var row string + var format string if listFlag.format == "" { - row = lsFormatFromFlags(listFlag) + format = lsFormatFromFlags(listFlag) } else { - row = report.NormalizeFormat(listFlag.format) + format = report.NormalizeFormat(listFlag.format) + format = report.EnforceRange(format) } - format := parse.EnforceRange(row) - tmpl, err := template.New("list").Parse(format) + tmpl, err := report.NewTemplate("list").Parse(format) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } defer w.Flush() if !listFlag.noHeading { @@ -323,7 +337,7 @@ row = append(row, "{{.ReadOnly}}") } - return strings.Join(row, "\t") + "\n" + return "{{range . }}" + strings.Join(row, "\t") + "\n{{end -}}" } type imageReporter struct { diff -Nru libpod-3.2.1+ds1/cmd/podman/images/load.go libpod-3.4.4+ds1/cmd/podman/images/load.go --- libpod-3.2.1+ds1/cmd/podman/images/load.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/load.go 2021-12-26 00:45:51.000000000 +0000 @@ -45,12 +45,10 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: loadCommand, }) loadFlags(loadCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageLoadCommand, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/mount.go libpod-3.4.4+ds1/cmd/podman/images/mount.go --- libpod-3.2.1+ds1/cmd/podman/images/mount.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/mount.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,8 +3,6 @@ import ( "fmt" "os" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" @@ -24,6 +22,11 @@ ` mountCommand = &cobra.Command{ + Annotations: map[string]string{ + registry.UnshareNSRequired: "", + registry.ParentNSRequired: "", + registry.EngineMode: registry.ABIMode, + }, Use: "mount [options] [IMAGE...]", Short: "Mount an image's root filesystem", Long: mountDescription, @@ -33,10 +36,6 @@ podman image mount imgID1 imgID2 imgID3 podman image mount podman image mount --all`, - Annotations: map[string]string{ - registry.UnshareNSRequired: "", - registry.ParentNSRequired: "", - }, } ) @@ -56,7 +55,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: mountCommand, Parent: imageCmd, }) @@ -99,13 +97,18 @@ mrs = append(mrs, mountReporter{r}) } - row := "{{range . }}{{.ID}}\t{{.Path}}\n{{end}}" - tmpl, err := template.New("mounts").Parse(row) + row := "{{range . }}{{.ID}}\t{{.Path}}\n{{end -}}" + tmpl, err := report.NewTemplate("mounts").Parse(row) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() + return tmpl.Execute(w, mrs) } diff -Nru libpod-3.2.1+ds1/cmd/podman/images/prune.go libpod-3.4.4+ds1/cmd/podman/images/prune.go --- libpod-3.2.1+ds1/cmd/podman/images/prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -34,7 +35,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pruneCmd, Parent: imageCmd, }) @@ -60,7 +60,7 @@ return nil } } - filterMap, err := common.ParseFilters(filter) + filterMap, err := specgenutil.ParseFilters(filter) if err != nil { return err } @@ -80,7 +80,7 @@ func createPruneWarningMessage(pruneOpts entities.ImagePruneOptions) string { question := "Are you sure you want to continue? [y/N] " if pruneOpts.All { - return "WARNING! This will remove all images without at least one container associated to them.\n" + question + return "WARNING! This command removes all images without at least one container associated with them.\n" + question } - return "WARNING! This will remove all dangling images.\n" + question + return "WARNING! This command removes all dangling images.\n" + question } diff -Nru libpod-3.2.1+ds1/cmd/podman/images/pull.go libpod-3.4.4+ds1/cmd/podman/images/pull.go --- libpod-3.2.1+ds1/cmd/podman/images/pull.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/pull.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" @@ -32,8 +33,8 @@ // Command: podman pull pullCmd = &cobra.Command{ - Use: "pull [options] IMAGE", - Args: cobra.ExactArgs(1), + Use: "pull [options] IMAGE [IMAGE...]", + Args: cobra.MinimumNArgs(1), Short: "Pull an image from a registry", Long: pullDescription, RunE: imagePull, @@ -60,14 +61,12 @@ func init() { // pull registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pullCmd, }) pullFlags(pullCmd) // images pull registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imagesPullCmd, Parent: imageCmd, }) @@ -93,7 +92,7 @@ _ = cmd.RegisterFlagCompletionFunc(osFlagName, completion.AutocompleteOS) variantFlagName := "variant" - flags.StringVar(&pullOptions.Variant, variantFlagName, "", " use VARIANT instead of the running architecture variant for choosing images") + flags.StringVar(&pullOptions.Variant, variantFlagName, "", "Use VARIANT instead of the running architecture variant for choosing images") _ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone) platformFlagName := "platform" @@ -156,17 +155,16 @@ } // Let's do all the remaining Yoga in the API to prevent us from // scattering logic across (too) many parts of the code. - pullReport, err := registry.ImageEngine().Pull(registry.GetContext(), args[0], pullOptions.ImagePullOptions) - if err != nil { - return err - } - - if len(pullReport.Images) > 1 { - fmt.Println("Pulled Images:") - } - for _, img := range pullReport.Images { - fmt.Println(img) + var errs utils.OutputErrors + for _, arg := range args { + pullReport, err := registry.ImageEngine().Pull(registry.GetContext(), arg, pullOptions.ImagePullOptions) + if err != nil { + errs = append(errs, err) + continue + } + for _, img := range pullReport.Images { + fmt.Println(img) + } } - - return nil + return errs.PrintErrors() } diff -Nru libpod-3.2.1+ds1/cmd/podman/images/push.go libpod-3.4.4+ds1/cmd/podman/images/push.go --- libpod-3.2.1+ds1/cmd/podman/images/push.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/push.go 2021-12-26 00:45:51.000000000 +0000 @@ -57,14 +57,12 @@ func init() { // push registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pushCmd, }) pushFlags(pushCmd) // images push registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imagePushCmd, Parent: imageCmd, }) @@ -98,7 +96,7 @@ _ = cmd.RegisterFlagCompletionFunc(digestfileFlagName, completion.AutocompleteDefault) formatFlagName := "format" - flags.StringVarP(&pushOptions.Format, formatFlagName, "f", "", "Manifest type (oci, v2s2, or v2s1) to use when pushing an image using the 'dir' transport (default is manifest type of source)") + flags.StringVarP(&pushOptions.Format, formatFlagName, "f", "", "Manifest type (oci, v2s2, or v2s1) to use in the destination (default is manifest type of source, with fallbacks)") _ = cmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteManifestFormat) flags.BoolVarP(&pushOptions.Quiet, "quiet", "q", false, "Suppress output information when pushing images") diff -Nru libpod-3.2.1+ds1/cmd/podman/images/rm.go libpod-3.4.4+ds1/cmd/podman/images/rm.go --- libpod-3.2.1+ds1/cmd/podman/images/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,17 +25,33 @@ podman image rm c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`, } + rmiCmd = &cobra.Command{ + Use: "rmi [options] IMAGE [IMAGE...]", + Args: rmCmd.Args, + Short: rmCmd.Short, + Long: rmCmd.Long, + RunE: rmCmd.RunE, + ValidArgsFunction: rmCmd.ValidArgsFunction, + Example: `podman rmi imageID + podman rmi --force alpine + podman rmi c4dfb1609ee2 93fd78260bd1 c0ed59d05ff7`, + } + imageOpts = entities.ImageRemoveOptions{} ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCmd, Parent: imageCmd, }) imageRemoveFlagSet(rmCmd.Flags()) + + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: rmiCmd, + }) + imageRemoveFlagSet(rmiCmd.Flags()) } func imageRemoveFlagSet(flags *pflag.FlagSet) { diff -Nru libpod-3.2.1+ds1/cmd/podman/images/rmi.go libpod-3.4.4+ds1/cmd/podman/images/rmi.go --- libpod-3.2.1+ds1/cmd/podman/images/rmi.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/rmi.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -package images - -import ( - "strings" - - "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/spf13/cobra" -) - -var ( - rmiCmd = &cobra.Command{ - Use: strings.Replace(rmCmd.Use, "rm ", "rmi ", 1), - Args: rmCmd.Args, - Short: rmCmd.Short, - Long: rmCmd.Long, - RunE: rmCmd.RunE, - ValidArgsFunction: rmCmd.ValidArgsFunction, - Example: strings.Replace(rmCmd.Example, "podman image rm", "podman rmi", -1), - } -) - -func init() { - registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: rmiCmd, - }) - imageRemoveFlagSet(rmiCmd.Flags()) -} diff -Nru libpod-3.2.1+ds1/cmd/podman/images/save.go libpod-3.4.4+ds1/cmd/podman/images/save.go --- libpod-3.2.1+ds1/cmd/podman/images/save.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/save.go 2021-12-26 00:45:51.000000000 +0000 @@ -48,6 +48,7 @@ podman save --format docker-dir -o ubuntu-dir ubuntu podman save > alpine-all.tar alpine:latest`, } + imageSaveCommand = &cobra.Command{ Args: saveCommand.Args, Use: saveCommand.Use, @@ -67,13 +68,11 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: saveCommand, }) saveFlags(saveCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageSaveCommand, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/scp.go libpod-3.4.4+ds1/cmd/podman/images/scp.go --- libpod-3.2.1+ds1/cmd/podman/images/scp.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/scp.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,348 @@ +package images + +import ( + "context" + "fmt" + "io/ioutil" + urlP "net/url" + "os" + "strconv" + "strings" + + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/parse" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/system/connection" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/docker/distribution/reference" + scpD "github.com/dtylman/scp" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "golang.org/x/crypto/ssh" +) + +var ( + saveScpDescription = `Securely copy an image from one host to another.` + imageScpCommand = &cobra.Command{ + Use: "scp [options] IMAGE [HOST::]", + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Long: saveScpDescription, + Short: "securely copy images", + RunE: scp, + Args: cobra.RangeArgs(1, 2), + ValidArgsFunction: common.AutocompleteScp, + Example: `podman image scp myimage:latest otherhost::`, + } +) + +var ( + scpOpts entities.ImageScpOptions +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: imageScpCommand, + Parent: imageCmd, + }) + scpFlags(imageScpCommand) +} + +func scpFlags(cmd *cobra.Command) { + flags := cmd.Flags() + flags.BoolVarP(&scpOpts.Save.Quiet, "quiet", "q", false, "Suppress the output") +} + +func scp(cmd *cobra.Command, args []string) (finalErr error) { + var ( + // TODO add tag support for images + err error + ) + if scpOpts.Save.Quiet { // set quiet for both load and save + scpOpts.Load.Quiet = true + } + f, err := ioutil.TempFile("", "podman") // open temp file for load/save output + if err != nil { + return err + } + defer os.Remove(f.Name()) + + scpOpts.Save.Output = f.Name() + scpOpts.Load.Input = scpOpts.Save.Output + if err := parse.ValidateFileName(saveOpts.Output); err != nil { + return err + } + confR, err := config.NewConfig("") // create a hand made config for the remote engine since we might use remote and native at once + if err != nil { + return errors.Wrapf(err, "could not make config") + } + abiEng, err := registry.NewImageEngine(cmd, args) // abi native engine + if err != nil { + return err + } + + cfg, err := config.ReadCustomConfig() // get ready to set ssh destination if necessary + if err != nil { + return err + } + serv, err := parseArgs(args, cfg) // parses connection data and "which way" we are loading and saving + if err != nil { + return err + } + // TODO: Add podman remote support + confR.Engine = config.EngineConfig{Remote: true, CgroupManager: "cgroupfs", ServiceDestinations: serv} // pass the service dest (either remote or something else) to engine + switch { + case scpOpts.FromRemote: // if we want to load FROM the remote + err = saveToRemote(scpOpts.SourceImageName, scpOpts.Save.Output, "", scpOpts.URI[0], scpOpts.Iden[0]) + if err != nil { + return err + } + if scpOpts.ToRemote { // we want to load remote -> remote + rep, err := loadToRemote(scpOpts.Save.Output, "", scpOpts.URI[1], scpOpts.Iden[1]) + if err != nil { + return err + } + fmt.Println(rep) + break + } + report, err := abiEng.Load(context.Background(), scpOpts.Load) + if err != nil { + return err + } + fmt.Println("Loaded image(s): " + strings.Join(report.Names, ",")) + case scpOpts.ToRemote: // remote host load + scpOpts.Save.Format = "oci-archive" + abiErr := abiEng.Save(context.Background(), scpOpts.SourceImageName, []string{}, scpOpts.Save) // save the image locally before loading it on remote, local, or ssh + if abiErr != nil { + errors.Wrapf(abiErr, "could not save image as specified") + } + rep, err := loadToRemote(scpOpts.Save.Output, "", scpOpts.URI[0], scpOpts.Iden[0]) + if err != nil { + return err + } + fmt.Println(rep) + // TODO: Add podman remote support + default: // else native load + if scpOpts.Tag != "" { + return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported") + } + scpOpts.Save.Format = "oci-archive" + abiErr := abiEng.Save(context.Background(), scpOpts.SourceImageName, []string{}, scpOpts.Save) // save the image locally before loading it on remote, local, or ssh + if abiErr != nil { + errors.Wrapf(abiErr, "could not save image as specified") + } + rep, err := abiEng.Load(context.Background(), scpOpts.Load) + if err != nil { + return err + } + fmt.Println("Loaded image(s): " + strings.Join(rep.Names, ",")) + } + return nil +} + +// loadToRemote takes image and remote connection information. it connects to the specified client +// and copies the saved image dir over to the remote host and then loads it onto the machine +// returns a string containing output or an error +func loadToRemote(localFile string, tag string, url *urlP.URL, iden string) (string, error) { + dial, remoteFile, err := createConnection(url, iden) + if err != nil { + return "", err + } + defer dial.Close() + + n, err := scpD.CopyTo(dial, localFile, remoteFile) + if err != nil { + errOut := (strconv.Itoa(int(n)) + " Bytes copied before error") + return " ", errors.Wrapf(err, errOut) + } + run := "" + if tag != "" { + return "", errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported") + } + podman := os.Args[0] + run = podman + " image load --input=" + remoteFile + ";rm " + remoteFile // run ssh image load of the file copied via scp + out, err := connection.ExecRemoteCommand(dial, run) + if err != nil { + return "", err + } + return strings.TrimSuffix(out, "\n"), nil +} + +// saveToRemote takes image information and remote connection information. it connects to the specified client +// and saves the specified image on the remote machine and then copies it to the specified local location +// returns an error if one occurs. +func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden string) error { + dial, remoteFile, err := createConnection(uri, iden) + + if err != nil { + return err + } + defer dial.Close() + + if tag != "" { + return errors.Wrapf(define.ErrInvalidArg, "Renaming of an image is currently not supported") + } + podman := os.Args[0] + run := podman + " image save " + image + " --format=oci-archive --output=" + remoteFile // run ssh image load of the file copied via scp. Files are reverse in this case... + _, err = connection.ExecRemoteCommand(dial, run) + if err != nil { + return nil + } + n, err := scpD.CopyFrom(dial, remoteFile, localFile) + connection.ExecRemoteCommand(dial, "rm "+remoteFile) + if err != nil { + errOut := (strconv.Itoa(int(n)) + " Bytes copied before error") + return errors.Wrapf(err, errOut) + } + return nil +} + +// makeRemoteFile creates the necessary remote file on the host to +// save or load the image to. returns a string with the file name or an error +func makeRemoteFile(dial *ssh.Client) (string, error) { + run := "mktemp" + remoteFile, err := connection.ExecRemoteCommand(dial, run) + if err != nil { + return "", err + } + remoteFile = strings.TrimSuffix(remoteFile, "\n") + if err != nil { + return "", err + } + return remoteFile, nil +} + +// createConnections takes a boolean determining which ssh client to dial +// and returns the dials client, its newly opened remote file, and an error if applicable. +func createConnection(url *urlP.URL, iden string) (*ssh.Client, string, error) { + cfg, err := connection.ValidateAndConfigure(url, iden) + if err != nil { + return nil, "", err + } + dialAdd, err := ssh.Dial("tcp", url.Host, cfg) // dial the client + if err != nil { + return nil, "", errors.Wrapf(err, "failed to connect") + } + file, err := makeRemoteFile(dialAdd) + if err != nil { + return nil, "", err + } + + return dialAdd, file, nil +} + +// validateImageName makes sure that the image given is valid and no injections are occurring +// we simply use this for error checking, bot setting the image +func validateImageName(input string) error { + // ParseNormalizedNamed transforms a shortname image into its + // full name reference so busybox => docker.io/library/busybox + // we want to keep our shortnames, so only return an error if + // we cannot parse what th euser has given us + _, err := reference.ParseNormalizedNamed(input) + return err +} + +// remoteArgLength is a helper function to simplify the extracting of host argument data +// returns an int which contains the length of a specified index in a host::image string +func remoteArgLength(input string, side int) int { + return len((strings.Split(input, "::"))[side]) +} + +// parseArgs returns the valid connection data based off of the information provided by the user +// args is an array of the command arguments and cfg is tooling configuration used to get service destinations +// returned is serv and an error if applicable. serv is a map of service destinations with the connection name as the index +// this connection name is intended to be used as EngineConfig.ServiceDestinations +// this function modifies the global scpOpt entities: FromRemote, ToRemote, Connections, and SourceImageName +func parseArgs(args []string, cfg *config.Config) (map[string]config.Destination, error) { + serv := map[string]config.Destination{} + cliConnections := []string{} + switch len(args) { + case 1: + if strings.Contains(args[0], "::") { + scpOpts.FromRemote = true + cliConnections = append(cliConnections, args[0]) + } else { + err := validateImageName(args[0]) + if err != nil { + return nil, err + } + scpOpts.SourceImageName = args[0] + } + case 2: + if strings.Contains(args[0], "::") { + if !(strings.Contains(args[1], "::")) && remoteArgLength(args[0], 1) == 0 { // if an image is specified, this mean we are loading to our client + cliConnections = append(cliConnections, args[0]) + scpOpts.ToRemote = true + scpOpts.SourceImageName = args[1] + } else if strings.Contains(args[1], "::") { // both remote clients + scpOpts.FromRemote = true + scpOpts.ToRemote = true + if remoteArgLength(args[0], 1) == 0 { // is save->load w/ one image name + cliConnections = append(cliConnections, args[0]) + cliConnections = append(cliConnections, args[1]) + } else if remoteArgLength(args[0], 1) > 0 && remoteArgLength(args[1], 1) > 0 { + //in the future, this function could, instead of rejecting renames, also set a DestImageName field + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify an image rename") + } else { // else its a load save (order of args) + cliConnections = append(cliConnections, args[1]) + cliConnections = append(cliConnections, args[0]) + } + } else { + //in the future, this function could, instead of rejecting renames, also set a DestImageName field + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify an image rename") + } + } else if strings.Contains(args[1], "::") { // if we are given image host:: + if remoteArgLength(args[1], 1) > 0 { + //in the future, this function could, instead of rejecting renames, also set a DestImageName field + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify an image rename") + } + err := validateImageName(args[0]) + if err != nil { + return nil, err + } + scpOpts.SourceImageName = args[0] + scpOpts.ToRemote = true + cliConnections = append(cliConnections, args[1]) + } else { + //in the future, this function could, instead of rejecting renames, also set a DestImageName field + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot specify an image rename") + } + } + var url string + var iden string + for i, val := range cliConnections { + splitEnv := strings.SplitN(val, "::", 2) + scpOpts.Connections = append(scpOpts.Connections, splitEnv[0]) + if len(splitEnv[1]) != 0 { + err := validateImageName(splitEnv[1]) + if err != nil { + return nil, err + } + scpOpts.SourceImageName = splitEnv[1] + //TODO: actually use the new name given by the user + } + conn, found := cfg.Engine.ServiceDestinations[scpOpts.Connections[i]] + if found { + url = conn.URI + iden = conn.Identity + } else { // no match, warn user and do a manual connection. + url = "ssh://" + scpOpts.Connections[i] + iden = "" + logrus.Warnf("Unknown connection name given. Please use system connection add to specify the default remote socket location") + } + urlT, err := urlP.Parse(url) // create an actual url to pass to exec command + if err != nil { + return nil, err + } + if urlT.User.Username() == "" { + if urlT.User, err = connection.GetUserInfo(urlT); err != nil { + return nil, err + } + } + scpOpts.URI = append(scpOpts.URI, urlT) + scpOpts.Iden = append(scpOpts.Iden, iden) + } + return serv, nil +} diff -Nru libpod-3.2.1+ds1/cmd/podman/images/search.go libpod-3.4.4+ds1/cmd/podman/images/search.go --- libpod-3.2.1+ds1/cmd/podman/images/search.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/search.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,14 +3,11 @@ import ( "fmt" "os" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/auth" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/image/v5/types" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" @@ -38,7 +35,6 @@ Users can limit the number of results, and filter the output based on certain conditions.` - // Command: podman search searchCmd = &cobra.Command{ Use: "search [options] TERM", Short: "Search registry for image", @@ -51,14 +47,12 @@ podman search --format "table {{.Index}} {{.Name}}" registry.fedoraproject.org/fedora`, } - // Command: podman image search imageSearchCmd = &cobra.Command{ Use: searchCmd.Use, Short: searchCmd.Short, Long: searchCmd.Long, RunE: searchCmd.RunE, Args: searchCmd.Args, - Annotations: searchCmd.Annotations, ValidArgsFunction: searchCmd.ValidArgsFunction, Example: `podman image search --filter=is-official --limit 3 alpine podman image search registry.fedoraproject.org/ # only works with v2 registries @@ -67,16 +61,12 @@ ) func init() { - // search registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: searchCmd, }) searchFlags(searchCmd) - // images search registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageSearchCmd, Parent: imageCmd, }) @@ -163,18 +153,22 @@ case report.IsJSON(searchOptions.Format): return printArbitraryJSON(searchReport) case cmd.Flags().Changed("format"): - renderHeaders = parse.HasTable(searchOptions.Format) + renderHeaders = report.HasTable(searchOptions.Format) row = report.NormalizeFormat(searchOptions.Format) default: row = "{{.Index}}\t{{.Name}}\t{{.Description}}\t{{.Stars}}\t{{.Official}}\t{{.Automated}}\n" } - format := parse.EnforceRange(row) + format := report.EnforceRange(row) - tmpl, err := template.New("search").Parse(format) + tmpl, err := report.NewTemplate("search").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() if renderHeaders { diff -Nru libpod-3.2.1+ds1/cmd/podman/images/sign.go libpod-3.4.4+ds1/cmd/podman/images/sign.go --- libpod-3.2.1+ds1/cmd/podman/images/sign.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/sign.go 2021-12-26 00:45:51.000000000 +0000 @@ -14,6 +14,7 @@ var ( signDescription = "Create a signature file that can be used later to verify the image." signCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "sign [options] IMAGE [IMAGE...]", Short: "Sign an image", Long: signDescription, @@ -31,7 +32,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: signCommand, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/tag.go libpod-3.4.4+ds1/cmd/podman/images/tag.go --- libpod-3.2.1+ds1/cmd/podman/images/tag.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/tag.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,26 +10,24 @@ var ( tagDescription = "Adds one or more additional names to locally-stored image." tagCommand = &cobra.Command{ - Use: "tag IMAGE TARGET_NAME [TARGET_NAME...]", - Short: "Add an additional name to a local image", - Long: tagDescription, - RunE: tag, - Args: cobra.MinimumNArgs(2), - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteImages, + Use: "tag IMAGE TARGET_NAME [TARGET_NAME...]", + Short: "Add an additional name to a local image", + Long: tagDescription, + RunE: tag, + Args: cobra.MinimumNArgs(2), + ValidArgsFunction: common.AutocompleteImages, Example: `podman tag 0e3bbc2 fedora:latest podman tag imageID:latest myNewImage:newTag podman tag httpd myregistryhost:5000/fedora/httpd:v2`, } imageTagCommand = &cobra.Command{ - Args: tagCommand.Args, - DisableFlagsInUseLine: true, - Use: tagCommand.Use, - Short: tagCommand.Short, - Long: tagCommand.Long, - RunE: tagCommand.RunE, - ValidArgsFunction: tagCommand.ValidArgsFunction, + Args: tagCommand.Args, + Use: tagCommand.Use, + Short: tagCommand.Short, + Long: tagCommand.Long, + RunE: tagCommand.RunE, + ValidArgsFunction: tagCommand.ValidArgsFunction, Example: `podman image tag 0e3bbc2 fedora:latest podman image tag imageID:latest myNewImage:newTag podman image tag httpd myregistryhost:5000/fedora/httpd:v2`, @@ -38,11 +36,9 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: tagCommand, }) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: imageTagCommand, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/tree.go libpod-3.4.4+ds1/cmd/podman/images/tree.go --- libpod-3.2.1+ds1/cmd/podman/images/tree.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/tree.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,7 +25,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: treeCmd, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/trust.go libpod-3.4.4+ds1/cmd/podman/images/trust.go --- libpod-3.2.1+ds1/cmd/podman/images/trust.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/trust.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -11,16 +10,16 @@ trustDescription = `Manages which registries you trust as a source of container images based on their location. The location is determined by the transport and the registry host of the image. Using this container image docker://quay.io/podman/stable as an example, docker is the transport and quay.io is the registry host.` trustCmd = &cobra.Command{ - Use: "trust", - Short: "Manage container image trust policy", - Long: trustDescription, - RunE: validate.SubCommandExists, + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "trust", + Short: "Manage container image trust policy", + Long: trustDescription, + RunE: validate.SubCommandExists, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: trustCmd, Parent: imageCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/trust_set.go libpod-3.4.4+ds1/cmd/podman/images/trust_set.go --- libpod-3.2.1+ds1/cmd/podman/images/trust_set.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/trust_set.go 2021-12-26 00:45:51.000000000 +0000 @@ -16,6 +16,7 @@ var ( setTrustDescription = "Set default trust policy or add a new trust policy for a registry" setTrustCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "set [options] REGISTRY", Short: "Set default trust policy or a new trust policy for a registry", Long: setTrustDescription, @@ -32,7 +33,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: setTrustCommand, Parent: trustCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/trust_show.go libpod-3.4.4+ds1/cmd/podman/images/trust_show.go --- libpod-3.2.1+ds1/cmd/podman/images/trust_show.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/trust_show.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,9 +3,8 @@ import ( "fmt" "os" - "text/tabwriter" - "text/template" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" @@ -15,6 +14,7 @@ var ( showTrustDescription = "Display trust policy for the system" showTrustCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "show [options] [REGISTRY]", Short: "Display trust policy for the system", Long: showTrustDescription, @@ -31,7 +31,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: showTrustCommand, Parent: trustCmd, }) @@ -45,16 +44,16 @@ } func showTrust(cmd *cobra.Command, args []string) error { - report, err := registry.ImageEngine().ShowTrust(registry.Context(), args, showTrustOptions) + trust, err := registry.ImageEngine().ShowTrust(registry.Context(), args, showTrustOptions) if err != nil { return err } if showTrustOptions.Raw { - fmt.Println(string(report.Raw)) + fmt.Println(string(trust.Raw)) return nil } if showTrustOptions.JSON { - b, err := json.MarshalIndent(report.Policies, "", " ") + b, err := json.MarshalIndent(trust.Policies, "", " ") if err != nil { return err } @@ -62,14 +61,18 @@ return nil } - row := "{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n" - format := "{{range . }}" + row + "{{end}}" - tmpl, err := template.New("listContainers").Parse(format) + format := "{{range . }}{{.RepoName}}\t{{.Type}}\t{{.GPGId}}\t{{.SignatureStore}}\n{{end -}}" + tmpl, err := report.NewTemplate("list").Parse(format) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) - if err := tmpl.Execute(w, report.Policies); err != nil { + + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + + if err := tmpl.Execute(w, trust.Policies); err != nil { return err } if err := w.Flush(); err != nil { diff -Nru libpod-3.2.1+ds1/cmd/podman/images/unmount.go libpod-3.4.4+ds1/cmd/podman/images/unmount.go --- libpod-3.2.1+ds1/cmd/podman/images/unmount.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/unmount.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,6 +20,7 @@ An unmount can be forced with the --force flag. ` unmountCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "unmount [options] IMAGE [IMAGE...]", Aliases: []string{"umount"}, Short: "Unmount an image's root filesystem", @@ -43,7 +44,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Parent: imageCmd, Command: unmountCommand, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/images/untag.go libpod-3.4.4+ds1/cmd/podman/images/untag.go --- libpod-3.2.1+ds1/cmd/podman/images/untag.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/images/untag.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,27 +8,25 @@ ) var ( - untagCommand = &cobra.Command{ - Use: "untag IMAGE [IMAGE...]", - Short: "Remove a name from a local image", - Long: "Removes one or more names from a locally-stored image.", - RunE: untag, - Args: cobra.MinimumNArgs(1), - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteImages, + untagCmd = &cobra.Command{ + Use: "untag IMAGE [IMAGE...]", + Short: "Remove a name from a local image", + Long: "Removes one or more names from a locally-stored image.", + RunE: untag, + Args: cobra.MinimumNArgs(1), + ValidArgsFunction: common.AutocompleteImages, Example: `podman untag 0e3bbc2 podman untag imageID:latest otherImageName:latest podman untag httpd myregistryhost:5000/fedora/httpd:v2`, } - imageUntagCommand = &cobra.Command{ - Args: untagCommand.Args, - DisableFlagsInUseLine: true, - Use: untagCommand.Use, - Short: untagCommand.Short, - Long: untagCommand.Long, - RunE: untagCommand.RunE, - ValidArgsFunction: untagCommand.ValidArgsFunction, + imageUntagCmd = &cobra.Command{ + Args: untagCmd.Args, + Use: untagCmd.Use, + Short: untagCmd.Short, + Long: untagCmd.Long, + RunE: untagCmd.RunE, + ValidArgsFunction: untagCmd.ValidArgsFunction, Example: `podman image untag 0e3bbc2 podman image untag imageID:latest otherImageName:latest podman image untag httpd myregistryhost:5000/fedora/httpd:v2`, @@ -37,12 +35,10 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: untagCommand, + Command: untagCmd, }) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - Command: imageUntagCommand, + Command: imageUntagCmd, Parent: imageCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/inspect/inspect.go libpod-3.4.4+ds1/cmd/podman/inspect/inspect.go --- libpod-3.2.1+ds1/cmd/podman/inspect/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/inspect/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,7 +7,6 @@ "os" "regexp" "strings" - "text/tabwriter" "text/template" "github.com/containers/common/pkg/completion" @@ -217,7 +216,7 @@ err = printJSON(data) default: row := inspectNormalize(i.options.Format) - row = "{{range . }}" + report.NormalizeFormat(row) + "{{end}}" + row = "{{range . }}" + report.NormalizeFormat(row) + "{{end -}}" err = printTmpl(tmpType, row, data) } if err != nil { @@ -250,8 +249,14 @@ if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) - return t.Execute(w, data) + + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + err = t.Execute(w, data) + w.Flush() + return err } func (i *inspector) inspectAll(ctx context.Context, namesOrIDs []string) ([]interface{}, []error, error) { diff -Nru libpod-3.2.1+ds1/cmd/podman/inspect.go libpod-3.4.4+ds1/cmd/podman/inspect.go --- libpod-3.2.1+ds1/cmd/podman/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -36,7 +36,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, }) inspectOpts = inspect.AddInspectFlagSet(inspectCmd) diff -Nru libpod-3.2.1+ds1/cmd/podman/login.go libpod-3.4.4+ds1/cmd/podman/login.go --- libpod-3.2.1+ds1/cmd/podman/login.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/login.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,8 +9,6 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/registries" "github.com/spf13/cobra" ) @@ -39,7 +37,6 @@ // store credentials locally while the remote client will pass them // over the wire to the endpoint. registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: loginCommand, }) flags := loginCommand.Flags() @@ -55,6 +52,7 @@ loginOptions.Stdin = os.Stdin loginOptions.Stdout = os.Stdout loginOptions.AcceptUnspecifiedRegistry = true + loginOptions.AcceptRepositories = true } // Implementation of podman-login. @@ -65,12 +63,29 @@ skipTLS = types.NewOptionalBool(!loginOptions.tlsVerify) } - sysCtx := types.SystemContext{ + sysCtx := &types.SystemContext{ AuthFilePath: loginOptions.AuthFile, DockerCertPath: loginOptions.CertDir, DockerInsecureSkipTLSVerify: skipTLS, - SystemRegistriesConfPath: registries.SystemRegistriesConfPath(), } + setRegistriesConfPath(sysCtx) loginOptions.GetLoginSet = cmd.Flag("get-login").Changed - return auth.Login(context.Background(), &sysCtx, &loginOptions.LoginOptions, args) + return auth.Login(context.Background(), sysCtx, &loginOptions.LoginOptions, args) +} + +// setRegistriesConfPath sets the registries.conf path for the specified context. +// NOTE: this is a verbatim copy from c/common/libimage which we're not using +// to prevent leaking c/storage into this file. Maybe this should go into c/image? +func setRegistriesConfPath(systemContext *types.SystemContext) { + if systemContext.SystemRegistriesConfPath != "" { + return + } + if envOverride, ok := os.LookupEnv("CONTAINERS_REGISTRIES_CONF"); ok { + systemContext.SystemRegistriesConfPath = envOverride + return + } + if envOverride, ok := os.LookupEnv("REGISTRIES_CONFIG_PATH"); ok { + systemContext.SystemRegistriesConfPath = envOverride + return + } } diff -Nru libpod-3.2.1+ds1/cmd/podman/logout.go libpod-3.4.4+ds1/cmd/podman/logout.go --- libpod-3.2.1+ds1/cmd/podman/logout.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/logout.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,8 +8,6 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/registries" "github.com/spf13/cobra" ) @@ -33,7 +31,6 @@ // store credentials locally while the remote client will pass them // over the wire to the endpoint. registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: logoutCommand, }) flags := logoutCommand.Flags() @@ -46,13 +43,14 @@ logoutOptions.Stdout = os.Stdout logoutOptions.AcceptUnspecifiedRegistry = true + logoutOptions.AcceptRepositories = true } // Implementation of podman-logout. func logout(cmd *cobra.Command, args []string) error { - sysCtx := types.SystemContext{ - AuthFilePath: logoutOptions.AuthFile, - SystemRegistriesConfPath: registries.SystemRegistriesConfPath(), + sysCtx := &types.SystemContext{ + AuthFilePath: logoutOptions.AuthFile, } - return auth.Logout(&sysCtx, &logoutOptions, args) + setRegistriesConfPath(sysCtx) + return auth.Logout(sysCtx, &logoutOptions, args) } diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/init.go libpod-3.4.4+ds1/cmd/podman/machine/init.go --- libpod-3.2.1+ds1/cmd/podman/machine/init.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/init.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,11 +1,10 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine import ( "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/pkg/errors" @@ -25,30 +24,30 @@ ) var ( - initOpts = machine.InitOptions{} - defaultMachineName string = "podman-machine-default" + initOpts = machine.InitOptions{} + defaultMachineName = "podman-machine-default" ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: initCmd, Parent: machineCmd, }) flags := initCmd.Flags() + cfg := registry.PodmanConfig() cpusFlagName := "cpus" flags.Uint64Var( &initOpts.CPUS, - cpusFlagName, 1, - "Number of CPUs. The default is 1.", + cpusFlagName, cfg.Machine.CPUs, + "Number of CPUs", ) _ = initCmd.RegisterFlagCompletionFunc(cpusFlagName, completion.AutocompleteNone) diskSizeFlagName := "disk-size" flags.Uint64Var( &initOpts.DiskSize, - diskSizeFlagName, 10, + diskSizeFlagName, cfg.Machine.DiskSize, "Disk size in GB", ) @@ -57,13 +56,13 @@ memoryFlagName := "memory" flags.Uint64VarP( &initOpts.Memory, - memoryFlagName, "m", 2048, - "Memory (in MB)", + memoryFlagName, "m", cfg.Machine.Memory, + "Memory in MB", ) _ = initCmd.RegisterFlagCompletionFunc(memoryFlagName, completion.AutocompleteNone) ImagePathFlagName := "image-path" - flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, "", "Path to qcow image") + flags.StringVar(&initOpts.ImagePath, ImagePathFlagName, cfg.Machine.Image, "Path to qcow image") _ = initCmd.RegisterFlagCompletionFunc(ImagePathFlagName, completion.AutocompleteDefault) IgnitionPathFlagName := "ignition-path" diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/list.go libpod-3.4.4+ds1/cmd/podman/machine/list.go --- libpod-3.2.1+ds1/cmd/podman/machine/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,21 +1,17 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine import ( "os" "sort" - "text/tabwriter" - "text/template" "time" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/report" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/docker/go-units" @@ -25,15 +21,15 @@ var ( lsCmd = &cobra.Command{ - Use: "list [options]", - Aliases: []string{"ls"}, - Short: "List machines", - Long: "List managed virtual machines.", - RunE: list, - Args: validate.NoArgs, + Use: "list [options]", + Aliases: []string{"ls"}, + Short: "List machines", + Long: "List managed virtual machines.", + RunE: list, + Args: validate.NoArgs, + ValidArgsFunction: completion.AutocompleteNone, Example: `podman machine list, podman machine ls`, - ValidArgsFunction: completion.AutocompleteNone, } listFlag = listFlagType{} ) @@ -44,22 +40,24 @@ } type machineReporter struct { - Name string - Created string - LastUp string - VMType string + Name string + Created string + LastUp string + VMType string + CPUs uint64 + Memory string + DiskSize string } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: lsCmd, Parent: machineCmd, }) flags := lsCmd.Flags() formatFlagName := "format" - flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n", "Format volume output using Go template") + flags.StringVar(&listFlag.format, formatFlagName, "{{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\t{{.CPUs}}\t{{.Memory}}\t{{.DiskSize}}\n", "Format volume output using Go template") _ = lsCmd.RegisterFlagCompletionFunc(formatFlagName, completion.AutocompleteNone) flags.BoolVar(&listFlag.noHeading, "noheading", false, "Do not print headers") } @@ -90,21 +88,28 @@ func outputTemplate(cmd *cobra.Command, responses []*machineReporter) error { headers := report.Headers(machineReporter{}, map[string]string{ - "LastUp": "LAST UP", - "VmType": "VM TYPE", + "LastUp": "LAST UP", + "VmType": "VM TYPE", + "CPUs": "CPUS", + "Memory": "MEMORY", + "DiskSize": "DISK SIZE", }) row := report.NormalizeFormat(listFlag.format) - format := parse.EnforceRange(row) + format := report.EnforceRange(row) + + tmpl, err := report.NewTemplate("list").Parse(format) + if err != nil { + return err + } - tmpl, err := template.New("list machines").Parse(format) + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0) defer w.Flush() - if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) { listFlag.noHeading = true } @@ -137,6 +142,9 @@ } response.Created = units.HumanDuration(time.Since(vm.CreatedAt)) + " ago" response.VMType = vm.VMType + response.CPUs = vm.CPUs + response.Memory = units.HumanSize(float64(vm.Memory) * units.MiB) + response.DiskSize = units.HumanSize(float64(vm.DiskSize) * units.GiB) humanResponses = append(humanResponses, response) } diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/machine.go libpod-3.4.4+ds1/cmd/podman/machine/machine.go --- libpod-3.2.1+ds1/cmd/podman/machine/machine.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/machine.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine @@ -7,7 +7,6 @@ "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/spf13/cobra" @@ -30,7 +29,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: machineCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/machine_unsupported.go libpod-3.4.4+ds1/cmd/podman/machine/machine_unsupported.go --- libpod-3.2.1+ds1/cmd/podman/machine/machine_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/machine_unsupported.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,4 +2,5 @@ package machine +// init do not register _podman machine_ command on unsupported platforms func init() {} diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/rm.go libpod-3.4.4+ds1/cmd/podman/machine/rm.go --- libpod-3.2.1+ds1/cmd/podman/machine/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine @@ -9,7 +9,6 @@ "strings" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/spf13/cobra" @@ -33,7 +32,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCmd, Parent: machineCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/ssh.go libpod-3.4.4+ds1/cmd/podman/machine/ssh.go --- libpod-3.2.1+ds1/cmd/podman/machine/ssh.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/ssh.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,12 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine import ( + "net/url" + + "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/pkg/errors" @@ -30,7 +32,6 @@ func init() { sshCmd.Flags().SetInterspersed(false) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: sshCmd, Parent: machineCmd, }) @@ -46,6 +47,14 @@ // Set the VM to default vmName := defaultMachineName + + // If we're not given a VM name, use the remote username from the connection config + if len(args) == 0 { + sshOpts.Username, err = remoteConnectionUsername() + if err != nil { + return err + } + } // If len is greater than 0, it means we may have been // provided the VM name. If so, we check. The VM name, // if provided, must be in args[0]. @@ -59,16 +68,25 @@ if validVM { vmName = args[0] } else { + sshOpts.Username, err = remoteConnectionUsername() + if err != nil { + return err + } sshOpts.Args = append(sshOpts.Args, args[0]) } } } + // If len is greater than 1, it means we might have been // given a vmname and args or just args if len(args) > 1 { if validVM { sshOpts.Args = args[1:] } else { + sshOpts.Username, err = remoteConnectionUsername() + if err != nil { + return err + } sshOpts.Args = args } } @@ -82,3 +100,20 @@ } return vm.SSH(vmName, sshOpts) } + +func remoteConnectionUsername() (string, error) { + cfg, err := config.ReadCustomConfig() + if err != nil { + return "", err + } + dest, _, err := cfg.ActiveDestination() + if err != nil { + return "", err + } + uri, err := url.Parse(dest) + if err != nil { + return "", err + } + username := uri.User.String() + return username, nil +} diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/start.go libpod-3.4.4+ds1/cmd/podman/machine/start.go --- libpod-3.2.1+ds1/cmd/podman/machine/start.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/start.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,11 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine import ( + "fmt" + "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/pkg/errors" @@ -25,7 +26,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: startCmd, Parent: machineCmd, }) @@ -60,5 +60,9 @@ if err != nil { return err } - return vm.Start(vmName, machine.StartOptions{}) + if err := vm.Start(vmName, machine.StartOptions{}); err != nil { + return err + } + fmt.Printf("Machine %q started successfully\n", vmName) + return nil } diff -Nru libpod-3.2.1+ds1/cmd/podman/machine/stop.go libpod-3.4.4+ds1/cmd/podman/machine/stop.go --- libpod-3.2.1+ds1/cmd/podman/machine/stop.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/machine/stop.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,9 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine import ( "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/machine" "github.com/containers/podman/v3/pkg/machine/qemu" "github.com/spf13/cobra" @@ -24,7 +23,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: stopCmd, Parent: machineCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/main.go libpod-3.4.4+ds1/cmd/podman/main.go --- libpod-3.2.1+ds1/cmd/podman/main.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/main.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ import ( "fmt" "os" + "strings" _ "github.com/containers/podman/v3/cmd/podman/completion" _ "github.com/containers/podman/v3/cmd/podman/containers" @@ -42,38 +43,41 @@ func parseCommands() *cobra.Command { cfg := registry.PodmanConfig() for _, c := range registry.Commands { - for _, m := range c.Mode { - if cfg.EngineMode == m { - // Command cannot be run rootless - _, found := c.Command.Annotations[registry.UnshareNSRequired] - if found { - if rootless.IsRootless() && found && os.Getuid() != 0 { - c.Command.RunE = func(cmd *cobra.Command, args []string) error { - return fmt.Errorf("cannot run command %q in rootless mode, must execute `podman unshare` first", cmd.CommandPath()) - } - } - } else { - _, found = c.Command.Annotations[registry.ParentNSRequired] - if rootless.IsRootless() && found { - c.Command.RunE = func(cmd *cobra.Command, args []string) error { - return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath()) - } - } + if supported, found := c.Command.Annotations[registry.EngineMode]; found { + if !strings.Contains(cfg.EngineMode.String(), supported) { + continue + } + } + + // Command cannot be run rootless + _, found := c.Command.Annotations[registry.UnshareNSRequired] + if found { + if rootless.IsRootless() && os.Getuid() != 0 { + c.Command.RunE = func(cmd *cobra.Command, args []string) error { + return fmt.Errorf("cannot run command %q in rootless mode, must execute `podman unshare` first", cmd.CommandPath()) } - parent := rootCmd - if c.Parent != nil { - parent = c.Parent + } + } else { + _, found = c.Command.Annotations[registry.ParentNSRequired] + if rootless.IsRootless() && found { + c.Command.RunE = func(cmd *cobra.Command, args []string) error { + return fmt.Errorf("cannot run command %q in rootless mode", cmd.CommandPath()) } - parent.AddCommand(c.Command) - - // - templates need to be set here, as PersistentPreRunE() is - // not called when --help is used. - // - rootCmd uses cobra default template not ours - c.Command.SetHelpTemplate(helpTemplate) - c.Command.SetUsageTemplate(usageTemplate) - c.Command.DisableFlagsInUseLine = true } } + + parent := rootCmd + if c.Parent != nil { + parent = c.Parent + } + parent.AddCommand(c.Command) + + // - templates need to be set here, as PersistentPreRunE() is + // not called when --help is used. + // - rootCmd uses cobra default template not ours + c.Command.SetHelpTemplate(helpTemplate) + c.Command.SetUsageTemplate(usageTemplate) + c.Command.DisableFlagsInUseLine = true } if err := terminal.SetConsole(); err != nil { logrus.Error(err) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/add.go libpod-3.4.4+ds1/cmd/podman/manifest/add.go --- libpod-3.2.1+ds1/cmd/podman/manifest/add.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/add.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,7 +39,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: addCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/annotate.go libpod-3.4.4+ds1/cmd/podman/manifest/annotate.go --- libpod-3.2.1+ds1/cmd/podman/manifest/annotate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/annotate.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,6 +15,7 @@ var ( manifestAnnotateOpts = entities.ManifestAnnotateOptions{} annotateCmd = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "annotate [options] LIST IMAGE", Short: "Add or update information about an entry in a manifest list or image index", Long: "Adds or updates information about an entry in a manifest list or image index.", @@ -27,7 +28,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: annotateCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/create.go libpod-3.4.4+ds1/cmd/podman/manifest/create.go --- libpod-3.2.1+ds1/cmd/podman/manifest/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,21 +13,21 @@ var ( manifestCreateOpts = entities.ManifestCreateOptions{} createCmd = &cobra.Command{ - Use: "create [options] LIST [IMAGE]", + Use: "create [options] LIST [IMAGE...]", Short: "Create manifest list or image index", Long: "Creates manifest lists or image indexes.", RunE: create, ValidArgsFunction: common.AutocompleteImages, Example: `podman manifest create mylist:v1.11 podman manifest create mylist:v1.11 arch-specific-image-to-add + podman manifest create mylist:v1.11 arch-specific-image-to-add another-arch-specific-image-to-add podman manifest create --all mylist:v1.11 transport:tagged-image-to-add`, - Args: cobra.RangeArgs(1, 2), + Args: cobra.MinimumNArgs(1), } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: createCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/exists.go libpod-3.4.4+ds1/cmd/podman/manifest/exists.go --- libpod-3.2.1+ds1/cmd/podman/manifest/exists.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/exists.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,7 +20,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: existsCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/inspect.go libpod-3.4.4+ds1/cmd/podman/manifest/inspect.go --- libpod-3.2.1+ds1/cmd/podman/manifest/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,26 +6,23 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( inspectCmd = &cobra.Command{ - Use: "inspect IMAGE", - Short: "Display the contents of a manifest list or image index", - Long: "Display the contents of a manifest list or image index.", - RunE: inspect, - ValidArgsFunction: common.AutocompleteImages, - Example: "podman manifest inspect localhost/list", - Args: cobra.ExactArgs(1), - DisableFlagsInUseLine: true, + Use: "inspect IMAGE", + Short: "Display the contents of a manifest list or image index", + Long: "Display the contents of a manifest list or image index.", + RunE: inspect, + ValidArgsFunction: common.AutocompleteImages, + Example: "podman manifest inspect localhost/list", + Args: cobra.ExactArgs(1), } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/manifest.go libpod-3.4.4+ds1/cmd/podman/manifest/manifest.go --- libpod-3.2.1+ds1/cmd/podman/manifest/manifest.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/manifest.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -19,13 +18,13 @@ podman manifest inspect localhost/list podman manifest annotate --annotation left=right mylist:v1.11 image:v1.11-amd64 podman manifest push mylist:v1.11 docker://quay.io/myuser/image:v1.11 - podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, + podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736 + podman manifest rm mylist:v1.11`, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: manifestCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/push.go libpod-3.4.4+ds1/cmd/podman/manifest/push.go --- libpod-3.2.1+ds1/cmd/podman/manifest/push.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/push.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,7 +39,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pushCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/remove.go libpod-3.4.4+ds1/cmd/podman/manifest/remove.go --- libpod-3.2.1+ds1/cmd/podman/manifest/remove.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/remove.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,27 +6,24 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" "github.com/spf13/cobra" ) var ( removeCmd = &cobra.Command{ - Use: "remove LIST IMAGE", - Short: "Remove an entry from a manifest list or image index", - Long: "Removes an image from a manifest list or image index.", - RunE: remove, - ValidArgsFunction: common.AutocompleteImages, - Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, - Args: cobra.ExactArgs(2), - DisableFlagsInUseLine: true, + Use: "remove LIST IMAGE", + Short: "Remove an entry from a manifest list or image index", + Long: "Removes an image from a manifest list or image index.", + RunE: remove, + ValidArgsFunction: common.AutocompleteImages, + Example: `podman manifest remove mylist:v1.11 sha256:15352d97781ffdf357bf3459c037be3efac4133dc9070c2dce7eca7c05c3e736`, + Args: cobra.ExactArgs(2), } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: removeCmd, Parent: manifestCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/manifest/rm.go libpod-3.4.4+ds1/cmd/podman/manifest/rm.go --- libpod-3.2.1+ds1/cmd/podman/manifest/rm.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/manifest/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,48 @@ +package manifest + +import ( + "context" + "fmt" + + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/errorhandling" + "github.com/spf13/cobra" +) + +var ( + rmCmd = &cobra.Command{ + Use: "rm LIST", + Short: "Remove manifest list or image index from local storage", + Long: "Remove manifest list or image index from local storage.", + RunE: rm, + ValidArgsFunction: common.AutocompleteImages, + Example: `podman manifest rm mylist:v1.11`, + Args: cobra.ExactArgs(1), + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: rmCmd, + Parent: manifestCmd, + }) +} + +func rm(cmd *cobra.Command, args []string) error { + report, rmErrors := registry.ImageEngine().ManifestRm(context.Background(), args) + if report != nil { + for _, u := range report.Untagged { + fmt.Println("Untagged: " + u) + } + for _, d := range report.Deleted { + // Make sure an image was deleted (and not just untagged); else print it + if len(d) > 0 { + fmt.Println("Deleted: " + d) + } + } + registry.SetExitCode(report.ExitCode) + } + + return errorhandling.JoinErrors(rmErrors) +} diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/connect.go libpod-3.4.4+ds1/cmd/podman/networks/connect.go --- libpod-3.2.1+ds1/cmd/podman/networks/connect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/connect.go 2021-12-26 00:45:51.000000000 +0000 @@ -34,7 +34,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkConnectCommand, Parent: networkCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/create.go libpod-3.4.4+ds1/cmd/podman/networks/create.go --- libpod-3.2.1+ds1/cmd/podman/networks/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "github.com/spf13/cobra" ) @@ -56,7 +57,8 @@ macvlanFlagName := "macvlan" flags.StringVar(&networkCreateOptions.MacVLAN, macvlanFlagName, "", "create a Macvlan connection based on this device") - _ = cmd.RegisterFlagCompletionFunc(macvlanFlagName, completion.AutocompleteNone) + // This option is deprecated + flags.MarkHidden(macvlanFlagName) labelFlagName := "label" flags.StringArrayVar(&labels, labelFlagName, nil, "set metadata on a network") @@ -75,7 +77,6 @@ } func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkCreateCommand, Parent: networkCmd, }) @@ -101,6 +102,11 @@ if err != nil { return errors.Wrapf(err, "unable to process options") } + + if networkCreateOptions.MacVLAN != "" { + logrus.Warn("The --macvlan option is deprecated, use `--driver macvlan --opt parent=` instead") + } + response, err := registry.ContainerEngine().NetworkCreate(registry.Context(), name, networkCreateOptions) if err != nil { return err diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/disconnect.go libpod-3.4.4+ds1/cmd/podman/networks/disconnect.go --- libpod-3.2.1+ds1/cmd/podman/networks/disconnect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/disconnect.go 2021-12-26 00:45:51.000000000 +0000 @@ -31,7 +31,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkDisconnectCommand, Parent: networkCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/exists.go libpod-3.4.4+ds1/cmd/podman/networks/exists.go --- libpod-3.2.1+ds1/cmd/podman/networks/exists.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/exists.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -22,7 +21,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkExistsCommand, Parent: networkCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/inspect.go libpod-3.4.4+ds1/cmd/podman/networks/inspect.go --- libpod-3.2.1+ds1/cmd/podman/networks/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,7 +12,7 @@ networkinspectDescription = `Inspect network` networkinspectCommand = &cobra.Command{ Use: "inspect [options] NETWORK [NETWORK...]", - Short: "network inspect", + Short: "Displays the raw CNI network configuration for one or more networks.", Long: networkinspectDescription, RunE: networkInspect, Example: `podman network inspect podman`, @@ -24,7 +24,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkinspectCommand, Parent: networkCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/list.go libpod-3.4.4+ds1/cmd/podman/networks/list.go --- libpod-3.2.1+ds1/cmd/podman/networks/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,8 +4,6 @@ "fmt" "os" "strings" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" @@ -54,7 +52,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networklistCommand, Parent: networkCmd, }) @@ -134,11 +131,15 @@ } format = report.EnforceRange(row) - tmpl, err := template.New("listNetworks").Parse(format) + tmpl, err := report.NewTemplate("list").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() noHeading, _ := cmd.Flags().GetBool("noheading") diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/network.go libpod-3.4.4+ds1/cmd/podman/networks/network.go --- libpod-3.2.1+ds1/cmd/podman/networks/network.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -22,7 +21,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/prune.go libpod-3.4.4+ds1/cmd/podman/networks/prune.go --- libpod-3.2.1+ds1/cmd/podman/networks/prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -43,7 +44,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkPruneCommand, Parent: networkCmd, }) @@ -68,7 +68,7 @@ return nil } } - networkPruneOptions.Filters, err = common.ParseFilters(filter) + networkPruneOptions.Filters, err = specgenutil.ParseFilters(filter) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/reload.go libpod-3.4.4+ds1/cmd/podman/networks/reload.go --- libpod-3.2.1+ds1/cmd/podman/networks/reload.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/reload.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,10 +15,11 @@ var ( networkReloadDescription = `reload container networks, recreating firewall rules` networkReloadCommand = &cobra.Command{ - Use: "reload [options] [CONTAINER...]", - Short: "Reload firewall rules for one or more containers", - Long: networkReloadDescription, - RunE: networkReload, + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "reload [options] [CONTAINER...]", + Short: "Reload firewall rules for one or more containers", + Long: networkReloadDescription, + RunE: networkReload, Args: func(cmd *cobra.Command, args []string) error { return validate.CheckAllLatestAndCIDFile(cmd, args, false, false) }, @@ -39,7 +40,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: networkReloadCommand, Parent: networkCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/networks/rm.go libpod-3.4.4+ds1/cmd/podman/networks/rm.go --- libpod-3.2.1+ds1/cmd/podman/networks/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/networks/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -38,7 +38,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: networkrmCommand, Parent: networkCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/parse/net.go libpod-3.4.4+ds1/cmd/podman/parse/net.go --- libpod-3.2.1+ds1/cmd/podman/parse/net.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/parse/net.go 2021-12-26 00:45:51.000000000 +0000 @@ -180,9 +180,12 @@ // ValidURL checks a string urlStr is a url or not func ValidURL(urlStr string) error { - _, err := url.ParseRequestURI(urlStr) + url, err := url.ParseRequestURI(urlStr) if err != nil { - return errors.Wrapf(err, "invalid url path: %q", urlStr) + return errors.Wrapf(err, "invalid url %q", urlStr) + } + if url.Scheme == "" { + return errors.Errorf("invalid url %q: missing scheme", urlStr) } return nil } diff -Nru libpod-3.2.1+ds1/cmd/podman/parse/template.go libpod-3.4.4+ds1/cmd/podman/parse/template.go --- libpod-3.2.1+ds1/cmd/podman/parse/template.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/parse/template.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -package parse - -import ( - "regexp" - "strings" -) - -var rangeRegex = regexp.MustCompile(`{{\s*range\s*\.\s*}}.*{{\s*end\s*}}`) - -// TODO move to github.com/containers/common/pkg/report -// EnforceRange ensures that the format string contains a range -func EnforceRange(format string) string { - if !rangeRegex.MatchString(format) { - return "{{range .}}" + format + "{{end}}" - } - return format -} - -// EnforceRange ensures that the format string contains a range -func HasTable(format string) bool { - return strings.HasPrefix(format, "table ") -} diff -Nru libpod-3.2.1+ds1/cmd/podman/parse/template_test.go libpod-3.4.4+ds1/cmd/podman/parse/template_test.go --- libpod-3.2.1+ds1/cmd/podman/parse/template_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/parse/template_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -package parse - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEnforceRange(t *testing.T) { - tests := []struct { - input string - expected string - }{ - {"{{range .}}{{.ID}}{{end}}", "{{range .}}{{.ID}}{{end}}"}, - {"{{.ID}}", "{{range .}}{{.ID}}{{end}}"}, - {"{{ range . }}{{ .ID }}{{ end }}", "{{ range . }}{{ .ID }}{{ end }}"}, - // EnforceRange does not verify syntax or semantics, that will happen later - {"{{range .}}{{.ID}}", "{{range .}}{{range .}}{{.ID}}{{end}}"}, - {".ID", "{{range .}}.ID{{end}}"}, - } - - for _, tc := range tests { - tc := tc - label := "TestEnforceRange_" + tc.input - t.Run(label, func(t *testing.T) { - t.Parallel() - assert.Equal(t, tc.expected, EnforceRange(tc.input)) - }) - } -} diff -Nru libpod-3.2.1+ds1/cmd/podman/play/kube.go libpod-3.4.4+ds1/cmd/podman/play/kube.go --- libpod-3.2.1+ds1/cmd/podman/play/kube.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/play/kube.go 2021-12-26 00:45:51.000000000 +0000 @@ -51,7 +51,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: kubeCmd, Parent: playCmd, }) @@ -87,6 +86,9 @@ flags.StringVar(&kubeOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override") _ = kubeCmd.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault) + downFlagName := "down" + flags.BoolVar(&kubeOptions.Down, downFlagName, false, "Stop pods defined in the YAML file") + if !registry.IsRemote() { certDirFlagName := "cert-dir" flags.StringVar(&kubeOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys") @@ -101,6 +103,9 @@ configmapFlagName := "configmap" flags.StringSliceVar(&kubeOptions.ConfigMaps, configmapFlagName, []string{}, "`Pathname` of a YAML file containing a kubernetes configmap") _ = kubeCmd.RegisterFlagCompletionFunc(configmapFlagName, completion.AutocompleteDefault) + + buildFlagName := "build" + flags.BoolVar(&kubeOptions.Build, buildFlagName, false, "Build all images in a YAML (given Containerfiles exist)") } _ = flags.MarkHidden("signature-policy") } @@ -142,12 +147,55 @@ } kubeOptions.StaticMACs = append(kubeOptions.StaticMACs, m) } + if kubeOptions.Down { + return teardown(yamlfile) + } + return playkube(yamlfile) +} - report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions) +func teardown(yamlfile string) error { + var ( + podStopErrors utils.OutputErrors + podRmErrors utils.OutputErrors + ) + options := new(entities.PlayKubeDownOptions) + reports, err := registry.ContainerEngine().PlayKubeDown(registry.GetContext(), yamlfile, *options) if err != nil { return err } + // Output stopped pods + fmt.Println("Pods stopped:") + for _, stopped := range reports.StopReport { + if len(stopped.Errs) == 0 { + fmt.Println(stopped.Id) + } else { + podStopErrors = append(podStopErrors, stopped.Errs...) + } + } + // Dump any stop errors + lastStopError := podStopErrors.PrintErrors() + if lastStopError != nil { + fmt.Fprintf(os.Stderr, "Error: %s\n", lastStopError) + } + + // Output rm'd pods + fmt.Println("Pods removed:") + for _, removed := range reports.RmReport { + if removed.Err == nil { + fmt.Println(removed.Id) + } else { + podRmErrors = append(podRmErrors, removed.Err) + } + } + return podRmErrors.PrintErrors() +} + +func playkube(yamlfile string) error { + report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), yamlfile, kubeOptions.PlayKubeOptions) + if err != nil { + return err + } // Print volumes report for i, volume := range report.Volumes { if i == 0 { diff -Nru libpod-3.2.1+ds1/cmd/podman/play/play.go libpod-3.4.4+ds1/cmd/podman/play/play.go --- libpod-3.2.1+ds1/cmd/podman/play/play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/play/play.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -11,7 +10,7 @@ // Command: podman _play_ playCmd = &cobra.Command{ Use: "play", - Short: "Play containers, pods or volumes from a structured file.", + Short: "Play containers, pods or volumes from a structured file", Long: "Play structured data (e.g., Kubernetes YAML) based on containers, pods or volumes.", RunE: validate.SubCommandExists, } @@ -19,7 +18,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: playCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/create.go libpod-3.4.4+ds1/cmd/podman/pods/create.go --- libpod-3.2.1+ds1/cmd/podman/pods/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,17 +5,25 @@ "fmt" "io/ioutil" "os" + "runtime" + "sort" + "strconv" "strings" "github.com/containers/common/pkg/completion" + "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/sysinfo" "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/containers" "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" + "github.com/docker/docker/pkg/parsers" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -39,6 +47,7 @@ var ( createOptions entities.PodCreateOptions + infraOptions entities.ContainerCreateOptions labels, labelFile []string podIDFile string replace bool @@ -47,48 +56,24 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: createCommand, Parent: podCmd, }) flags := createCommand.Flags() flags.SetInterspersed(false) - + infraOptions.IsInfra = true + common.DefineCreateFlags(createCommand, &infraOptions, true) common.DefineNetFlags(createCommand) - cgroupParentflagName := "cgroup-parent" - flags.StringVar(&createOptions.CGroupParent, cgroupParentflagName, "", "Set parent cgroup for the pod") - _ = createCommand.RegisterFlagCompletionFunc(cgroupParentflagName, completion.AutocompleteDefault) - flags.BoolVar(&createOptions.Infra, "infra", true, "Create an infra container associated with the pod to share namespaces with") - infraConmonPidfileFlagName := "infra-conmon-pidfile" - flags.StringVar(&createOptions.InfraConmonPidFile, infraConmonPidfileFlagName, "", "Path to the file that will receive the POD of the infra container's conmon") - _ = createCommand.RegisterFlagCompletionFunc(infraConmonPidfileFlagName, completion.AutocompleteDefault) - - infraImageFlagName := "infra-image" - flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") - _ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages) - - infraCommandFlagName := "infra-command" - flags.String(infraCommandFlagName, containerConfig.Engine.InfraCommand, "The command to run on the infra container when the pod is started") - _ = createCommand.RegisterFlagCompletionFunc(infraCommandFlagName, completion.AutocompleteNone) - - labelFileFlagName := "label-file" - flags.StringSliceVar(&labelFile, labelFileFlagName, []string{}, "Read in a line delimited file of labels") - _ = createCommand.RegisterFlagCompletionFunc(labelFileFlagName, completion.AutocompleteDefault) - - labelFlagName := "label" - flags.StringSliceVarP(&labels, labelFlagName, "l", []string{}, "Set metadata on pod (default [])") - _ = createCommand.RegisterFlagCompletionFunc(labelFlagName, completion.AutocompleteNone) - nameFlagName := "name" flags.StringVarP(&createOptions.Name, nameFlagName, "n", "", "Assign a name to the pod") _ = createCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone) - hostnameFlagName := "hostname" - flags.StringVarP(&createOptions.Hostname, hostnameFlagName, "", "", "Set a hostname to the pod") - _ = createCommand.RegisterFlagCompletionFunc(hostnameFlagName, completion.AutocompleteNone) + infraImageFlagName := "infra-image" + flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod") + _ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages) podIDFileFlagName := "pod-id-file" flags.StringVar(&podIDFile, podIDFileFlagName, "", "Write the pod ID to the file") @@ -112,43 +97,71 @@ func create(cmd *cobra.Command, args []string) error { var ( - err error - podIDFD *os.File + err error + podIDFD *os.File + imageName string + rawImageName string ) + labelFile = infraOptions.LabelFile + labels = infraOptions.Label createOptions.Labels, err = parse.GetAllLabels(labelFile, labels) if err != nil { return errors.Wrapf(err, "unable to process labels") } + imageName = config.DefaultInfraImage + img := imageName if !createOptions.Infra { - logrus.Debugf("Not creating an infra container") - if cmd.Flag("infra-conmon-pidfile").Changed { - return errors.New("cannot set infra-conmon-pid without an infra container") + if cmd.Flag("no-hosts").Changed { + return fmt.Errorf("cannot specify no-hosts without an infra container") } - if cmd.Flag("infra-command").Changed { - return errors.New("cannot set infra-command without an infra container") - } - if cmd.Flag("infra-image").Changed { - return errors.New("cannot set infra-image without an infra container") + flags := cmd.Flags() + createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra) + if err != nil { + return err } + logrus.Debugf("Not creating an infra container") createOptions.InfraImage = "" + if createOptions.InfraName != "" { + return errors.New("cannot set infra-name without an infra container") + } if cmd.Flag("share").Changed && share != "none" && share != "" { return fmt.Errorf("cannot set share(%s) namespaces without an infra container", cmd.Flag("share").Value) } createOptions.Share = nil } else { + // reassign certain optios for lbpod api, these need to be populated in spec + createOptions.InfraConmonPidFile = infraOptions.ConmonPIDFile + createOptions.InfraName = infraOptions.Name + createOptions.Hostname = infraOptions.Hostname + createOptions.Cpus = infraOptions.CPUS + createOptions.CpusetCpus = infraOptions.CPUSetCPUs + createOptions.Pid = infraOptions.PID + flags := cmd.Flags() + infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra) + if err != nil { + return err + } + infraOptions, err = containers.CreateInit(cmd, infraOptions, true) + if err != nil { + return err + } + createOptions.Net = infraOptions.Net createOptions.Share = strings.Split(share, ",") if cmd.Flag("infra-command").Changed { // Only send content to server side if user changed defaults - createOptions.InfraCommand, err = cmd.Flags().GetString("infra-command") + cmdIn, err := cmd.Flags().GetString("infra-command") + infraOptions.Entrypoint = &cmdIn + createOptions.InfraCommand = cmdIn if err != nil { return err } } if cmd.Flag("infra-image").Changed { // Only send content to server side if user changed defaults - createOptions.InfraImage, err = cmd.Flags().GetString("infra-image") + img, err = cmd.Flags().GetString("infra-image") + imageName = img if err != nil { return err } @@ -167,10 +180,6 @@ defer errorhandling.SyncQuiet(podIDFD) } - createOptions.Net, err = common.NetFlagsToNetOptions(cmd) - if err != nil { - return err - } if len(createOptions.Net.PublishPorts) > 0 { if !createOptions.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") @@ -185,10 +194,84 @@ } } - response, err := registry.ContainerEngine().PodCreate(context.Background(), createOptions) + numCPU := sysinfo.NumCPU() + if numCPU == 0 { + numCPU = runtime.NumCPU() + } + if createOptions.Cpus > float64(numCPU) { + createOptions.Cpus = float64(numCPU) + } + copy := createOptions.CpusetCpus + cpuSet := createOptions.Cpus + if cpuSet == 0 { + cpuSet = float64(sysinfo.NumCPU()) + } + ret, err := parsers.ParseUintList(copy) + copy = "" + if err != nil { + errors.Wrapf(err, "could not parse list") + } + var vals []int + for ind, val := range ret { + if val { + vals = append(vals, ind) + } + } + sort.Ints(vals) + for ind, core := range vals { + if core > int(cpuSet) { + if copy == "" { + copy = "0-" + strconv.Itoa(int(cpuSet)) + createOptions.CpusetCpus = copy + break + } else { + createOptions.CpusetCpus = copy + break + } + } else if ind != 0 { + copy += "," + strconv.Itoa(core) + } else { + copy = "" + strconv.Itoa(core) + } + } + podSpec := specgen.NewPodSpecGenerator() + podSpec, err = entities.ToPodSpecGen(*podSpec, &createOptions) if err != nil { return err } + if createOptions.Infra { + rawImageName = img + if !infraOptions.RootFS { + curr := infraOptions.Quiet + infraOptions.Quiet = true + name, err := containers.PullImage(imageName, infraOptions) + if err != nil { + fmt.Println(err) + } + imageName = name + infraOptions.Quiet = curr + } + podSpec.InfraImage = imageName + if infraOptions.Entrypoint != nil { + createOptions.InfraCommand = *infraOptions.Entrypoint + } + infraOptions.CPUS = createOptions.Cpus + infraOptions.CPUSetCPUs = createOptions.CpusetCpus + infraOptions.PID = createOptions.Pid + podSpec.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false) + podSpec.InfraContainerSpec.RawImageName = rawImageName + podSpec.InfraContainerSpec.NetworkOptions = podSpec.NetworkOptions + err = specgenutil.FillOutSpecGen(podSpec.InfraContainerSpec, &infraOptions, []string{}) + if err != nil { + return err + } + } + PodSpec := entities.PodSpec{PodSpecGen: *podSpec} + response, err := registry.ContainerEngine().PodCreate(context.Background(), PodSpec) + if err != nil { + return err + } + if len(podIDFile) > 0 { if err = ioutil.WriteFile(podIDFile, []byte(response.Id), 0644); err != nil { return errors.Wrapf(err, "failed to write pod ID to file") diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/exists.go libpod-3.4.4+ds1/cmd/podman/pods/exists.go --- libpod-3.2.1+ds1/cmd/podman/pods/exists.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/exists.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,7 +5,6 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -21,13 +20,11 @@ ValidArgsFunction: common.AutocompletePods, Example: `podman pod exists podID podman pod exists mypod || podman pod create --name mypod`, - DisableFlagsInUseLine: true, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: existsCommand, Parent: podCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/inspect.go libpod-3.4.4+ds1/cmd/podman/pods/inspect.go --- libpod-3.2.1+ds1/cmd/podman/pods/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,8 +3,6 @@ import ( "context" "os" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" @@ -37,7 +35,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: podCmd, }) @@ -74,11 +71,16 @@ row := report.NormalizeFormat(inspectOptions.Format) - t, err := template.New("pod inspect").Parse(row) + t, err := report.NewTemplate("inspect").Parse(row) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) - return t.Execute(w, *responses) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + err = t.Execute(w, *responses) + w.Flush() + return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/kill.go libpod-3.4.4+ds1/cmd/podman/pods/kill.go --- libpod-3.2.1+ds1/cmd/podman/pods/kill.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/kill.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,7 +37,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: killCommand, Parent: podCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/logs.go libpod-3.4.4+ds1/cmd/podman/pods/logs.go --- libpod-3.2.1+ds1/cmd/podman/pods/logs.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/logs.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,121 @@ +package pods + +import ( + "os" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/util" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// logsOptionsWrapper wraps entities.LogsOptions and prevents leaking +// CLI-only fields into the API types. +type logsOptionsWrapper struct { + entities.PodLogsOptions + + SinceRaw string + + UntilRaw string +} + +var ( + logsPodOptions logsOptionsWrapper + logsPodDescription = `Displays logs for pod with one or more containers.` + podLogsCommand = &cobra.Command{ + Use: "logs [options] POD", + Short: "Fetch logs for pod with one or more containers", + Long: logsPodDescription, + // We dont want users to invoke latest and pod together + Args: func(cmd *cobra.Command, args []string) error { + switch { + case registry.IsRemote() && logsPodOptions.Latest: + return errors.New(cmd.Name() + " does not support 'latest' when run remotely") + case len(args) > 1: + return errors.New("requires exactly 1 arg") + case logsPodOptions.Latest && len(args) > 0: + return errors.New("--latest and pods cannot be used together") + case !logsPodOptions.Latest && len(args) < 1: + return errors.New("specify at least one pod name or ID to log") + } + return nil + }, + RunE: logs, + ValidArgsFunction: common.AutocompletePods, + Example: `podman pod logs podID + podman pod logs -c ctrname podName + podman pod logs --tail 2 mywebserver + podman pod logs --follow=true --since 10m podID + podman pod logs mywebserver`, + } +) + +func init() { + // pod logs + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: podLogsCommand, + Parent: podCmd, + }) + logsFlags(podLogsCommand) + validate.AddLatestFlag(podLogsCommand, &logsPodOptions.Latest) +} + +func logsFlags(cmd *cobra.Command) { + flags := cmd.Flags() + + flags.BoolVar(&logsPodOptions.Details, "details", false, "Show extra details provided to the logs") + flags.BoolVarP(&logsPodOptions.Follow, "follow", "f", false, "Follow log output.") + + containerNameFlag := "container" + flags.StringVarP(&logsPodOptions.ContainerName, containerNameFlag, "c", "", "Filter logs by container name or id which belongs to pod") + _ = cmd.RegisterFlagCompletionFunc(containerNameFlag, common.AutocompleteContainers) + + sinceFlagName := "since" + flags.StringVar(&logsPodOptions.SinceRaw, sinceFlagName, "", "Show logs since TIMESTAMP") + _ = cmd.RegisterFlagCompletionFunc(sinceFlagName, completion.AutocompleteNone) + + untilFlagName := "until" + flags.StringVar(&logsPodOptions.UntilRaw, untilFlagName, "", "Show logs until TIMESTAMP") + _ = cmd.RegisterFlagCompletionFunc(untilFlagName, completion.AutocompleteNone) + + tailFlagName := "tail" + flags.Int64Var(&logsPodOptions.Tail, tailFlagName, -1, "Output the specified number of LINES at the end of the logs.") + _ = cmd.RegisterFlagCompletionFunc(tailFlagName, completion.AutocompleteNone) + + flags.BoolVarP(&logsPodOptions.Timestamps, "timestamps", "t", false, "Output the timestamps in the log") + flags.SetInterspersed(false) + _ = flags.MarkHidden("details") +} + +func logs(_ *cobra.Command, args []string) error { + if logsPodOptions.SinceRaw != "" { + // parse time, error out if something is wrong + since, err := util.ParseInputTime(logsPodOptions.SinceRaw, true) + if err != nil { + return errors.Wrapf(err, "error parsing --since %q", logsPodOptions.SinceRaw) + } + logsPodOptions.Since = since + } + if logsPodOptions.UntilRaw != "" { + // parse time, error out if something is wrong + until, err := util.ParseInputTime(logsPodOptions.UntilRaw, false) + if err != nil { + return errors.Wrapf(err, "error parsing --until %q", logsPodOptions.UntilRaw) + } + logsPodOptions.Until = until + } + + // Remote can only process one container at a time + if registry.IsRemote() && logsPodOptions.ContainerName == "" { + return errors.Wrapf(define.ErrInvalidArg, "-c or --container cannot be empty") + } + + logsPodOptions.StdoutWriter = os.Stdout + logsPodOptions.StderrWriter = os.Stderr + return registry.ContainerEngine().PodLogs(registry.GetContext(), args[0], logsPodOptions.PodLogsOptions) +} diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/pause.go libpod-3.4.4+ds1/cmd/podman/pods/pause.go --- libpod-3.2.1+ds1/cmd/podman/pods/pause.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/pause.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,7 +37,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pauseCommand, Parent: podCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/pod.go libpod-3.4.4+ds1/cmd/podman/pods/pod.go --- libpod-3.2.1+ds1/cmd/podman/pods/pod.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/pod.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/util" "github.com/spf13/cobra" ) @@ -24,7 +23,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: podCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/prune.go libpod-3.4.4+ds1/cmd/podman/pods/prune.go --- libpod-3.2.1+ds1/cmd/podman/pods/prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -35,7 +35,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pruneCommand, Parent: podCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/ps.go libpod-3.4.4+ds1/cmd/podman/pods/ps.go --- libpod-3.2.1+ds1/cmd/podman/pods/ps.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/ps.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,14 +6,11 @@ "os" "sort" "strings" - "text/tabwriter" - "text/template" "time" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" @@ -45,7 +42,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: psCmd, Parent: podCmd, }) @@ -61,7 +57,7 @@ formatFlagName := "format" flags.StringVar(&psInput.Format, formatFlagName, "", "Pretty-print pods to JSON or using a Go template") - _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{})) + _ = psCmd.RegisterFlagCompletionFunc(formatFlagName, common.AutocompleteFormat(ListPodReporter{ListPodsReport: &entities.ListPodsReport{}})) flags.Bool("noheading", false, "Do not print headers") flags.BoolVar(&psInput.Namespace, "namespace", false, "Display namespace information of the pod") @@ -128,24 +124,33 @@ "NumberOfContainers": "# OF CONTAINERS", "Created": "CREATED", "InfraID": "INFRA ID", + "ContainerIds": "IDS", + "ContainerNames": "NAMES", + "ContainerStatuses": "STATUS", + "Cgroup": "CGROUP", + "Namespace": "NAMESPACES", }) renderHeaders := true row := podPsFormat() if cmd.Flags().Changed("format") { - renderHeaders = parse.HasTable(psInput.Format) + renderHeaders = report.HasTable(psInput.Format) row = report.NormalizeFormat(psInput.Format) } noHeading, _ := cmd.Flags().GetBool("noheading") if noHeading { renderHeaders = false } - format := parse.EnforceRange(row) + format := report.EnforceRange(row) - tmpl, err := template.New("listPods").Parse(format) + tmpl, err := report.NewTemplate("list").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) defer w.Flush() if renderHeaders { diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/restart.go libpod-3.4.4+ds1/cmd/podman/pods/restart.go --- libpod-3.2.1+ds1/cmd/podman/pods/restart.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/restart.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,7 +37,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: restartCommand, Parent: podCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/rm.go libpod-3.4.4+ds1/cmd/podman/pods/rm.go --- libpod-3.2.1+ds1/cmd/podman/pods/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -45,7 +46,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCommand, Parent: podCmd, }) @@ -67,7 +67,7 @@ } func rm(_ *cobra.Command, args []string) error { - ids, err := common.ReadPodIDFiles(rmOptions.PodIDFiles) + ids, err := specgenutil.ReadPodIDFiles(rmOptions.PodIDFiles) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/start.go libpod-3.4.4+ds1/cmd/podman/pods/start.go --- libpod-3.2.1+ds1/cmd/podman/pods/start.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/start.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -45,7 +46,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: startCommand, Parent: podCmd, }) @@ -65,7 +65,7 @@ errs utils.OutputErrors ) - ids, err := common.ReadPodIDFiles(startOptions.PodIDFiles) + ids, err := specgenutil.ReadPodIDFiles(startOptions.PodIDFiles) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/stats.go libpod-3.4.4+ds1/cmd/podman/pods/stats.go --- libpod-3.2.1+ds1/cmd/podman/pods/stats.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/stats.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,14 +4,11 @@ "context" "fmt" "os" - "text/tabwriter" - "text/template" "time" "github.com/buger/goterm" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" @@ -48,7 +45,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: statsCmd, Parent: podCmd, }) @@ -124,8 +120,12 @@ return nil } -func printPodStatsLines(stats []*entities.PodStatsReport) { - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) +func printPodStatsLines(stats []*entities.PodStatsReport) error { + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + outFormat := "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n" fmt.Fprintf(w, outFormat, "POD", "CID", "NAME", "CPU %", "MEM USAGE/ LIMIT", "MEM %", "NET IO", "BLOCK IO", "PIDS") if len(stats) == 0 { @@ -135,7 +135,7 @@ fmt.Fprintf(w, outFormat, i.Pod, i.CID, i.Name, i.CPU, i.MemUsage, i.Mem, i.NetIO, i.BlockIO, i.PIDS) } } - w.Flush() + return w.Flush() } func printFormattedPodStatsLines(headerNames []map[string]string, row string, stats []*entities.PodStatsReport) error { @@ -143,13 +143,17 @@ return nil } - row = parse.EnforceRange(row) + row = report.EnforceRange(row) + + tmpl, err := report.NewTemplate("stats").Parse(row) + if err != nil { + return err + } - tmpl, err := template.New("pod stats").Parse(row) + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) defer w.Flush() if err := tmpl.Execute(w, headerNames); err != nil { diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/stop.go libpod-3.4.4+ds1/cmd/podman/pods/stop.go --- libpod-3.2.1+ds1/cmd/podman/pods/stop.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/stop.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/containers/podman/v3/cmd/podman/utils" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/spf13/cobra" ) @@ -46,7 +47,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: stopCommand, Parent: podCmd, }) @@ -79,7 +79,7 @@ stopOptions.Timeout = int(stopOptions.TimeoutCLI) } - ids, err := common.ReadPodIDFiles(stopOptions.PodIDFiles) + ids, err := specgenutil.ReadPodIDFiles(stopOptions.PodIDFiles) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/top.go libpod-3.4.4+ds1/cmd/podman/pods/top.go --- libpod-3.2.1+ds1/cmd/podman/pods/top.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/top.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,8 +5,8 @@ "fmt" "os" "strings" - "text/tabwriter" + "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" @@ -39,7 +39,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: topCommand, Parent: podCmd, }) @@ -83,7 +82,11 @@ return err } - w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } + for _, proc := range topResponse.Value { if _, err := fmt.Fprintln(w, proc); err != nil { return err diff -Nru libpod-3.2.1+ds1/cmd/podman/pods/unpause.go libpod-3.4.4+ds1/cmd/podman/pods/unpause.go --- libpod-3.2.1+ds1/cmd/podman/pods/unpause.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/pods/unpause.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,7 +39,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: unpauseCommand, Parent: podCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/README.md libpod-3.4.4+ds1/cmd/podman/README.md --- libpod-3.2.1+ds1/cmd/podman/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -40,9 +40,6 @@ func init() { // Subscribe command to podman registry.Commands = append(registry.Commands, registry.CliCommand{ - // _podman manifest_ will support both ABIMode and TunnelMode - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - // The definition for this command Command: manifestCmd, }) } @@ -83,9 +80,6 @@ func init() { // Subscribe inspect sub command to manifest command registry.Commands = append(registry.Commands, registry.CliCommand{ - // _podman manifest inspect_ will support both ABIMode and TunnelMode - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, - // The definition for this command Command: inspectCmd, // The parent command to proceed this command on the CLI Parent: manifestCmd, diff -Nru libpod-3.2.1+ds1/cmd/podman/registry/config.go libpod-3.4.4+ds1/cmd/podman/registry/config.go --- libpod-3.2.1+ds1/cmd/podman/registry/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/registry/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,14 +15,28 @@ ) const ( - ParentNSRequired = "ParentNSRequired" + // NoMoveProcess used as cobra.Annotation when command doesn't need Podman to be moved to a separate cgroup + NoMoveProcess = "NoMoveProcess" + + // ParentNSRequired used as cobra.Annotation when command requires root access + ParentNSRequired = "ParentNSRequired" + + // UnshareNSRequired used as cobra.Annotation when command requires modified user namespace UnshareNSRequired = "UnshareNSRequired" + + // EngineMode used as cobra.Annotation when command supports a limited number of Engines + EngineMode = "EngineMode" ) var ( podmanOptions entities.PodmanConfig podmanSync sync.Once abiSupport = false + + // ABIMode used in cobra.Annotations registry.EngineMode when command only supports ABIMode + ABIMode = entities.ABIMode.String() + // TunnelMode used in in cobra.Annotations registry.EngineMode when command only supports TunnelMode + TunnelMode = entities.TunnelMode.String() ) // PodmanConfig returns an entities.PodmanConfig built up from diff -Nru libpod-3.2.1+ds1/cmd/podman/registry/registry.go libpod-3.4.4+ds1/cmd/podman/registry/registry.go --- libpod-3.2.1+ds1/cmd/podman/registry/registry.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/registry/registry.go 2021-12-26 00:45:51.000000000 +0000 @@ -19,17 +19,14 @@ const DefaultRootAPIAddress = "unix:" + DefaultRootAPIPath type CliCommand struct { - Mode []entities.EngineMode Command *cobra.Command Parent *cobra.Command } -const ExecErrorCodeGeneric = 125 - var ( cliCtx context.Context containerEngine entities.ContainerEngine - exitCode = ExecErrorCodeGeneric + exitCode = 0 imageEngine entities.ImageEngine // Commands holds the cobra.Commands to present to the user, including diff -Nru libpod-3.2.1+ds1/cmd/podman/root.go libpod-3.4.4+ds1/cmd/podman/root.go --- libpod-3.2.1+ds1/cmd/podman/root.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/root.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ "path/filepath" "runtime" "runtime/pprof" + "strconv" "strings" "github.com/containers/common/pkg/completion" @@ -88,14 +89,15 @@ func Execute() { if err := rootCmd.ExecuteContext(registry.GetContextWithOptions()); err != nil { + if registry.GetExitCode() == 0 { + registry.SetExitCode(define.ExecErrorCodeGeneric) + } + if registry.IsRemote() { + if strings.Contains(err.Error(), "unable to connect to Podman") { + fmt.Fprintln(os.Stderr, "Cannot connect to Podman. Please verify your connection to the Linux system using `podman system connection list`, or try `podman machine init` and `podman machine start` to manage a new Linux VM") + } + } fmt.Fprintln(os.Stderr, formatError(err)) - } else if registry.GetExitCode() == registry.ExecErrorCodeGeneric { - // The exitCode modified from registry.ExecErrorCodeGeneric, - // indicates an application - // running inside of a container failed, as opposed to the - // podman command failed. Must exit with that exit code - // otherwise command exited correctly. - registry.SetExitCode(0) } os.Exit(registry.GetExitCode()) } @@ -194,6 +196,17 @@ return err } } + if cmd.Flag("memory-profile").Changed { + // Same value as the default in github.com/pkg/profile. + runtime.MemProfileRate = 4096 + if rate := os.Getenv("MemProfileRate"); rate != "" { + r, err := strconv.Atoi(rate) + if err != nil { + return err + } + runtime.MemProfileRate = r + } + } if cfg.MaxWorks <= 0 { return errors.Errorf("maximum workers must be set to a positive number (got %d)", cfg.MaxWorks) @@ -208,7 +221,8 @@ // 3) command doesn't require Parent Namespace _, found := cmd.Annotations[registry.ParentNSRequired] if !registry.IsRemote() && rootless.IsRootless() && !found { - err := registry.ContainerEngine().SetupRootless(registry.Context(), cmd) + _, noMoveProcess := cmd.Annotations[registry.NoMoveProcess] + err := registry.ContainerEngine().SetupRootless(registry.Context(), noMoveProcess) if err != nil { return err } @@ -223,14 +237,29 @@ return nil } - if !registry.IsRemote() { - if cmd.Flag("cpu-profile").Changed { - pprof.StopCPUProfile() + registry.ImageEngine().Shutdown(registry.Context()) + registry.ContainerEngine().Shutdown(registry.Context()) + + if registry.IsRemote() { + return nil + } + + // CPU and memory profiling. + if cmd.Flag("cpu-profile").Changed { + pprof.StopCPUProfile() + } + if cmd.Flag("memory-profile").Changed { + f, err := os.Create(registry.PodmanConfig().MemoryProfile) + if err != nil { + return errors.Wrap(err, "creating memory profile") + } + defer f.Close() + runtime.GC() // get up-to-date GC statistics + if err := pprof.WriteHeapProfile(f); err != nil { + return errors.Wrap(err, "writing memory profile") } } - registry.ImageEngine().Shutdown(registry.Context()) - registry.ContainerEngine().Shutdown(registry.Context()) return nil } @@ -293,7 +322,8 @@ pFlags.StringVar(&cfg.Engine.CgroupManager, cgroupManagerFlagName, cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") _ = cmd.RegisterFlagCompletionFunc(cgroupManagerFlagName, common.AutocompleteCgroupManager) - pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results") + pFlags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu-profiling results") + pFlags.StringVar(&opts.MemoryProfile, "memory-profile", "", "Path for the memory-profiling results") conmonFlagName := "conmon" pFlags.StringVar(&opts.ConmonPath, conmonFlagName, "", "Path of the conmon binary") @@ -342,10 +372,6 @@ pFlags.StringVar(&opts.StorageDriver, storageDriverFlagName, "", "Select which storage driver is used to manage storage of images and containers (default is overlay)") _ = cmd.RegisterFlagCompletionFunc(storageDriverFlagName, completion.AutocompleteNone) //TODO: what can we recommend here? - storageOptFlagName := "storage-opt" - pFlags.StringArrayVar(&opts.StorageOpts, storageOptFlagName, []string{}, "Used to pass an option to the storage driver") - _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone) - tmpdirFlagName := "tmpdir" pFlags.StringVar(&opts.Engine.TmpDir, tmpdirFlagName, "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n") _ = cmd.RegisterFlagCompletionFunc(tmpdirFlagName, completion.AutocompleteDefault) @@ -357,6 +383,7 @@ "cpu-profile", "default-mounts-file", "max-workers", + "memory-profile", "registries-conf", "trace", } { @@ -365,6 +392,10 @@ } } } + storageOptFlagName := "storage-opt" + pFlags.StringArrayVar(&opts.StorageOpts, storageOptFlagName, []string{}, "Used to pass an option to the storage driver") + _ = cmd.RegisterFlagCompletionFunc(storageOptFlagName, completion.AutocompleteNone) + // Override default --help information of `--help` global flag var dummyHelp bool pFlags.BoolVar(&dummyHelp, "help", false, "Help for podman") diff -Nru libpod-3.2.1+ds1/cmd/podman/secrets/create.go libpod-3.4.4+ds1/cmd/podman/secrets/create.go --- libpod-3.2.1+ds1/cmd/podman/secrets/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/secrets/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -35,7 +35,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: createCmd, Parent: secretCmd, }) @@ -43,8 +42,14 @@ flags := createCmd.Flags() driverFlagName := "driver" - flags.StringVar(&createOpts.Driver, driverFlagName, "file", "Specify secret driver") + optsFlagName := "driver-opts" + + cfg := registry.PodmanConfig() + + flags.StringVar(&createOpts.Driver, driverFlagName, cfg.Secrets.Driver, "Specify secret driver") + flags.StringToStringVar(&createOpts.DriverOpts, optsFlagName, cfg.Secrets.Opts, "Specify driver specific options") _ = createCmd.RegisterFlagCompletionFunc(driverFlagName, completion.AutocompleteNone) + _ = createCmd.RegisterFlagCompletionFunc(optsFlagName, completion.AutocompleteNone) envFlagName := "env" flags.BoolVar(&env, envFlagName, false, "Read secret data from environment variable") diff -Nru libpod-3.2.1+ds1/cmd/podman/secrets/inspect.go libpod-3.4.4+ds1/cmd/podman/secrets/inspect.go --- libpod-3.2.1+ds1/cmd/podman/secrets/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/secrets/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,13 +4,10 @@ "context" "encoding/json" "fmt" - "html/template" "os" - "text/tabwriter" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/pkg/errors" @@ -33,7 +30,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCmd, Parent: secretCmd, }) @@ -53,13 +49,17 @@ if cmd.Flags().Changed("format") { row := report.NormalizeFormat(format) - formatted := parse.EnforceRange(row) + formatted := report.EnforceRange(row) - tmpl, err := template.New("inspect secret").Parse(formatted) + tmpl, err := report.NewTemplate("inspect").Parse(formatted) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0) defer w.Flush() tmpl.Execute(w, inspected) } else { diff -Nru libpod-3.2.1+ds1/cmd/podman/secrets/list.go libpod-3.4.4+ds1/cmd/podman/secrets/list.go --- libpod-3.2.1+ds1/cmd/podman/secrets/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/secrets/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,15 +2,12 @@ import ( "context" - "html/template" "os" - "text/tabwriter" "time" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" @@ -39,7 +36,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: lsCmd, Parent: secretCmd, }) @@ -52,7 +48,7 @@ } func ls(cmd *cobra.Command, args []string) error { - responses, err := registry.ContainerEngine().SecretList(context.Background()) + responses, err := registry.ContainerEngine().SecretList(context.Background(), entities.SecretListRequest{}) if err != nil { return err } @@ -76,16 +72,20 @@ }) row := report.NormalizeFormat(listFlag.format) - format := parse.EnforceRange(row) + format := report.EnforceRange(row) - tmpl, err := template.New("list secret").Parse(format) + tmpl, err := report.NewTemplate("list").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0) defer w.Flush() - if cmd.Flags().Changed("format") && !parse.HasTable(listFlag.format) { + if cmd.Flags().Changed("format") && !report.HasTable(listFlag.format) { listFlag.noHeading = true } diff -Nru libpod-3.2.1+ds1/cmd/podman/secrets/rm.go libpod-3.4.4+ds1/cmd/podman/secrets/rm.go --- libpod-3.2.1+ds1/cmd/podman/secrets/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/secrets/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -24,7 +24,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCmd, Parent: secretCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/secrets/secret.go libpod-3.4.4+ds1/cmd/podman/secrets/secret.go --- libpod-3.2.1+ds1/cmd/podman/secrets/secret.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/secrets/secret.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -19,7 +18,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: secretCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/shell_completion_test.go libpod-3.4.4+ds1/cmd/podman/shell_completion_test.go --- libpod-3.2.1+ds1/cmd/podman/shell_completion_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/shell_completion_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -33,7 +33,9 @@ func checkCommand(t *testing.T, cmd *cobra.Command) { if cmd.HasSubCommands() { for _, childCmd := range cmd.Commands() { - checkCommand(t, childCmd) + if !childCmd.Hidden { + checkCommand(t, childCmd) + } } // if not check if completion for that command is provided diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection/add.go libpod-3.4.4+ds1/cmd/podman/system/connection/add.go --- libpod-3.2.1+ds1/cmd/podman/system/connection/add.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection/add.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,6 @@ package connection import ( - "bytes" "encoding/json" "fmt" "net" @@ -9,13 +8,13 @@ "os" "os/user" "regexp" + "time" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/system" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/terminal" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -24,22 +23,24 @@ "golang.org/x/crypto/ssh/agent" ) -const schemaPattern = "^[A-Za-z][A-Za-z0-9+.-]*:" - var ( addCmd = &cobra.Command{ Use: "add [options] NAME DESTINATION", Args: cobra.ExactArgs(2), Short: "Record destination for the Podman service", Long: `Add destination to podman configuration. - "destination" is of the form [user@]hostname or - an URI of the form ssh://[user@]hostname[:port] + "destination" is one of the form: + [user@]hostname (will default to ssh) + ssh://[user@]hostname[:port][/path] (will obtain socket path from service, if not given.) + tcp://hostname:port (not secured) + unix://path (absolute path required) `, RunE: add, ValidArgsFunction: completion.AutocompleteNone, Example: `podman system connection add laptop server.fubar.com podman system connection add --identity ~/.ssh/dev_rsa testing ssh://root@server.fubar.com:2222 podman system connection add --identity ~/.ssh/dev_rsa --port 22 production root@server.fubar.com + podman system connection add debug tcp://localhost:8080 `, } @@ -53,7 +54,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: addCmd, Parent: system.ConnectionCmd, }) @@ -76,41 +76,78 @@ } func add(cmd *cobra.Command, args []string) error { - // Default to ssh: schema if none given + // Default to ssh schema if none given dest := args[1] - if match, err := regexp.Match(schemaPattern, []byte(dest)); err != nil { + if match, err := regexp.Match("^[A-Za-z][A-Za-z0-9+.-]*://", []byte(dest)); err != nil { return errors.Wrapf(err, "invalid destination") } else if !match { dest = "ssh://" + dest } - uri, err := url.Parse(dest) if err != nil { return err } - if uri.User.Username() == "" { - if uri.User, err = getUserInfo(uri); err != nil { - return err - } - } - if cmd.Flags().Changed("socket-path") { uri.Path = cmd.Flag("socket-path").Value.String() } - if cmd.Flags().Changed("port") { - uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").Value.String()) - } + switch uri.Scheme { + case "ssh": + if uri.User.Username() == "" { + if uri.User, err = GetUserInfo(uri); err != nil { + return err + } + } - if uri.Port() == "" { - uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").DefValue) - } + if cmd.Flags().Changed("port") { + uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").Value.String()) + } - if uri.Path == "" || uri.Path == "/" { - if uri.Path, err = getUDS(cmd, uri); err != nil { + if uri.Port() == "" { + uri.Host = net.JoinHostPort(uri.Hostname(), cmd.Flag("port").DefValue) + } + iden := "" + if cmd.Flags().Changed("identity") { + iden = cOpts.Identity + } + if uri.Path == "" || uri.Path == "/" { + if uri.Path, err = getUDS(cmd, uri, iden); err != nil { + return err + } + } + case "unix": + if cmd.Flags().Changed("identity") { + return errors.New("--identity option not supported for unix scheme") + } + + if cmd.Flags().Changed("socket-path") { + uri.Path = cmd.Flag("socket-path").Value.String() + } + + info, err := os.Stat(uri.Path) + switch { + case errors.Is(err, os.ErrNotExist): + logrus.Warnf("%q does not exists", uri.Path) + case errors.Is(err, os.ErrPermission): + logrus.Warnf("You do not have permission to read %q", uri.Path) + case err != nil: return err + case info.Mode()&os.ModeSocket == 0: + return fmt.Errorf("%q exists and is not a unix domain socket", uri.Path) + } + case "tcp": + if cmd.Flags().Changed("socket-path") { + return errors.New("--socket-path option not supported for tcp scheme") + } + if cmd.Flags().Changed("identity") { + return errors.New("--identity option not supported for tcp scheme") + } + if uri.Port() == "" { + return errors.New("tcp scheme requires a port either via --port or in destination URL") } + default: + logrus.Warnf("%q unknown scheme, no validation provided", uri.Scheme) } cfg, err := config.ReadCustomConfig() @@ -143,7 +180,7 @@ return cfg.Write() } -func getUserInfo(uri *url.URL) (*url.Userinfo, error) { +func GetUserInfo(uri *url.URL) (*url.Userinfo, error) { var ( usr *user.User err error @@ -167,30 +204,74 @@ return url.User(usr.Username), nil } -func getUDS(cmd *cobra.Command, uri *url.URL) (string, error) { - var signers []ssh.Signer +func getUDS(cmd *cobra.Command, uri *url.URL, iden string) (string, error) { + cfg, err := ValidateAndConfigure(uri, iden) + if err != nil { + return "", errors.Wrapf(err, "failed to validate") + } + dial, err := ssh.Dial("tcp", uri.Host, cfg) + if err != nil { + return "", errors.Wrapf(err, "failed to connect") + } + defer dial.Close() + + session, err := dial.NewSession() + if err != nil { + return "", errors.Wrapf(err, "failed to create new ssh session on %q", uri.Host) + } + defer session.Close() + + // Override podman binary for testing etc + podman := "podman" + if v, found := os.LookupEnv("PODMAN_BINARY"); found { + podman = v + } + run := podman + " info --format=json" + out, err := ExecRemoteCommand(dial, run) + if err != nil { + return "", err + } + infoJSON, err := json.Marshal(out) + if err != nil { + return "", err + } + var info define.Info + if err := json.Unmarshal(infoJSON, &info); err != nil { + return "", errors.Wrapf(err, "failed to parse 'podman info' results") + } + + if info.Host.RemoteSocket == nil || len(info.Host.RemoteSocket.Path) == 0 { + return "", errors.Errorf("remote podman %q failed to report its UDS socket", uri.Host) + } + return info.Host.RemoteSocket.Path, nil +} + +// ValidateAndConfigure will take a ssh url and an identity key (rsa and the like) and ensure the information given is valid +// iden iden can be blank to mean no identity key +// once the function validates the information it creates and returns an ssh.ClientConfig +func ValidateAndConfigure(uri *url.URL, iden string) (*ssh.ClientConfig, error) { + var signers []ssh.Signer passwd, passwdSet := uri.User.Password() - if cmd.Flags().Changed("identity") { - value := cmd.Flag("identity").Value.String() + if iden != "" { // iden might be blank if coming from image scp or if no validation is needed + value := iden s, err := terminal.PublicKey(value, []byte(passwd)) if err != nil { - return "", errors.Wrapf(err, "failed to read identity %q", value) + return nil, errors.Wrapf(err, "failed to read identity %q", value) } signers = append(signers, s) logrus.Debugf("SSH Ident Key %q %s %s", value, ssh.FingerprintSHA256(s.PublicKey()), s.PublicKey().Type()) } - - if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { + if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { // validate ssh information, specifically the unix file socket used by the ssh agent. logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock) c, err := net.Dial("unix", sock) if err != nil { - return "", err + return nil, err } agentSigners, err := agent.NewClient(c).Signers() if err != nil { - return "", err + return nil, err } signers = append(signers, agentSigners...) @@ -201,11 +282,9 @@ } } } - - var authMethods []ssh.AuthMethod + var authMethods []ssh.AuthMethod // now we validate and check for the authorization methods, most notaibly public key authorization if len(signers) > 0 { var dedup = make(map[string]ssh.Signer) - // Dedup signers based on fingerprint, ssh-agent keys override CONTAINER_SSHKEY for _, s := range signers { fp := ssh.FingerprintSHA256(s.PublicKey()) if _, found := dedup[fp]; found { @@ -218,60 +297,28 @@ for _, s := range dedup { uniq = append(uniq, s) } - authMethods = append(authMethods, ssh.PublicKeysCallback(func() ([]ssh.Signer, error) { return uniq, nil })) } - - if passwdSet { + if passwdSet { // if password authentication is given and valid, add to the list authMethods = append(authMethods, ssh.Password(passwd)) } - if len(authMethods) == 0 { authMethods = append(authMethods, ssh.PasswordCallback(func() (string, error) { pass, err := terminal.ReadPassword(fmt.Sprintf("%s's login password:", uri.User.Username())) return string(pass), err })) } - + tick, err := time.ParseDuration("40s") + if err != nil { + return nil, err + } cfg := &ssh.ClientConfig{ User: uri.User.Username(), Auth: authMethods, HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Timeout: tick, } - dial, err := ssh.Dial("tcp", uri.Host, cfg) - if err != nil { - return "", errors.Wrapf(err, "failed to connect") - } - defer dial.Close() - - session, err := dial.NewSession() - if err != nil { - return "", errors.Wrapf(err, "failed to create new ssh session on %q", uri.Host) - } - defer session.Close() - - // Override podman binary for testing etc - podman := "podman" - if v, found := os.LookupEnv("PODMAN_BINARY"); found { - podman = v - } - run := podman + " info --format=json" - - var buffer bytes.Buffer - session.Stdout = &buffer - if err := session.Run(run); err != nil { - return "", err - } - - var info define.Info - if err := json.Unmarshal(buffer.Bytes(), &info); err != nil { - return "", errors.Wrapf(err, "failed to parse 'podman info' results") - } - - if info.Host.RemoteSocket == nil || len(info.Host.RemoteSocket.Path) == 0 { - return "", errors.Errorf("remote podman %q failed to report its UDS socket", uri.Host) - } - return info.Host.RemoteSocket.Path, nil + return cfg, nil } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection/default.go libpod-3.4.4+ds1/cmd/podman/system/connection/default.go --- libpod-3.2.1+ds1/cmd/podman/system/connection/default.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection/default.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,27 +7,24 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/system" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( // Skip creating engines since this command will obtain connection information to said engines dfltCmd = &cobra.Command{ - Use: "default NAME", - Args: cobra.ExactArgs(1), - Short: "Set named destination as default", - Long: `Set named destination as default for the Podman service`, - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteSystemConnections, - RunE: defaultRunE, - Example: `podman system connection default testing`, + Use: "default NAME", + Args: cobra.ExactArgs(1), + Short: "Set named destination as default", + Long: `Set named destination as default for the Podman service`, + ValidArgsFunction: common.AutocompleteSystemConnections, + RunE: defaultRunE, + Example: `podman system connection default testing`, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: dfltCmd, Parent: system.ConnectionCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection/list.go libpod-3.4.4+ds1/cmd/podman/system/connection/list.go --- libpod-3.2.1+ds1/cmd/podman/system/connection/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,29 +1,30 @@ package connection import ( + "fmt" "os" - "text/tabwriter" - "text/template" + "sort" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/report" + "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/system" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( listCmd = &cobra.Command{ - Use: "list", - Aliases: []string{"ls"}, - Args: validate.NoArgs, - Short: "List destination for the Podman service(s)", - Long: `List destination information for the Podman service(s) in podman configuration`, - DisableFlagsInUseLine: true, + Use: "list [options]", + Aliases: []string{"ls"}, + Args: validate.NoArgs, + Short: "List destination for the Podman service(s)", + Long: `List destination information for the Podman service(s) in podman configuration`, Example: `podman system connection list - podman system connection ls`, + podman system connection ls + podman system connection ls --format=json`, ValidArgsFunction: completion.AutocompleteNone, RunE: list, TraverseChildren: false, @@ -32,10 +33,12 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: listCmd, Parent: system.ConnectionCmd, }) + + listCmd.Flags().String("format", "", "Custom Go template for printing connections") + _ = listCmd.RegisterFlagCompletionFunc("format", common.AutocompleteFormat(namedDestination{})) } type namedDestination struct { @@ -43,7 +46,7 @@ config.Destination } -func list(_ *cobra.Command, _ []string) error { +func list(cmd *cobra.Command, _ []string) error { cfg, err := config.ReadCustomConfig() if err != nil { return err @@ -75,16 +78,40 @@ rows = append(rows, r) } - // TODO: Allow user to override format - format := "{{range . }}{{.Name}}\t{{.Identity}}\t{{.URI}}\n{{end}}" - tmpl, err := template.New("connection").Parse(format) + sort.Slice(rows, func(i, j int) bool { + return rows[i].Name < rows[j].Name + }) + + format := "{{.Name}}\t{{.Identity}}\t{{.URI}}\n" + switch { + case report.IsJSON(cmd.Flag("format").Value.String()): + buf, err := registry.JSONLibrary().MarshalIndent(rows, "", " ") + if err == nil { + fmt.Println(string(buf)) + } + return err + default: + if cmd.Flag("format").Changed { + format = cmd.Flag("format").Value.String() + format = report.NormalizeFormat(format) + } + } + format = report.EnforceRange(format) + + tmpl, err := report.NewTemplate("list").Parse(format) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } defer w.Flush() - _ = tmpl.Execute(w, hdrs) + isTable := report.HasTable(cmd.Flag("format").Value.String()) + if !cmd.Flag("format").Changed || isTable { + _ = tmpl.Execute(w, hdrs) + } return tmpl.Execute(w, rows) } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection/remove.go libpod-3.4.4+ds1/cmd/podman/system/connection/remove.go --- libpod-3.2.1+ds1/cmd/podman/system/connection/remove.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection/remove.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,21 +5,19 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/system" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( // Skip creating engines since this command will obtain connection information to said engines rmCmd = &cobra.Command{ - Use: "remove NAME", - Args: cobra.ExactArgs(1), - Aliases: []string{"rm"}, - Long: `Delete named destination from podman configuration`, - Short: "Delete named destination", - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteSystemConnections, - RunE: rm, + Use: "remove NAME", + Args: cobra.ExactArgs(1), + Aliases: []string{"rm"}, + Long: `Delete named destination from podman configuration`, + Short: "Delete named destination", + ValidArgsFunction: common.AutocompleteSystemConnections, + RunE: rm, Example: `podman system connection remove devl podman system connection rm devl`, } @@ -27,7 +25,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCmd, Parent: system.ConnectionCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection/rename.go libpod-3.4.4+ds1/cmd/podman/system/connection/rename.go --- libpod-3.2.1+ds1/cmd/podman/system/connection/rename.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection/rename.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,21 +7,19 @@ "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/system" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) var ( // Skip creating engines since this command will obtain connection information to said engines renameCmd = &cobra.Command{ - Use: "rename OLD NEW", - Aliases: []string{"mv"}, - Args: cobra.ExactArgs(2), - Short: "Rename \"old\" to \"new\"", - Long: `Rename destination for the Podman service from "old" to "new"`, - DisableFlagsInUseLine: true, - ValidArgsFunction: common.AutocompleteSystemConnections, - RunE: rename, + Use: "rename OLD NEW", + Aliases: []string{"mv"}, + Args: cobra.ExactArgs(2), + Short: "Rename \"old\" to \"new\"", + Long: `Rename destination for the Podman service from "old" to "new"`, + ValidArgsFunction: common.AutocompleteSystemConnections, + RunE: rename, Example: `podman system connection rename laptop devl, podman system connection mv laptop devl`, } @@ -29,7 +27,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: renameCmd, Parent: system.ConnectionCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection/shared.go libpod-3.4.4+ds1/cmd/podman/system/connection/shared.go --- libpod-3.2.1+ds1/cmd/podman/system/connection/shared.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection/shared.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,28 @@ +package connection + +import ( + "bytes" + + "github.com/pkg/errors" + "golang.org/x/crypto/ssh" +) + +// ExecRemoteCommand takes a ssh client connection and a command to run and executes the +// command on the specified client. The function returns the Stdout from the client or the Stderr +func ExecRemoteCommand(dial *ssh.Client, run string) (string, error) { + sess, err := dial.NewSession() // new ssh client session + if err != nil { + return "", err + } + defer sess.Close() + + var buffer bytes.Buffer + var bufferErr bytes.Buffer + sess.Stdout = &buffer // output from client funneled into buffer + sess.Stderr = &bufferErr // err form client funneled into buffer + if err := sess.Run(run); err != nil { // run the command on the ssh client + return "", errors.Wrapf(err, bufferErr.String()) + } + out := buffer.String() // output from command + return out, nil +} diff -Nru libpod-3.2.1+ds1/cmd/podman/system/connection.go libpod-3.4.4+ds1/cmd/podman/system/connection.go --- libpod-3.2.1+ds1/cmd/podman/system/connection.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/connection.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -14,20 +13,18 @@ } ConnectionCmd = &cobra.Command{ - Use: "connection", - Short: "Manage remote ssh destinations", - Long: `Manage ssh destination information in podman configuration`, - DisableFlagsInUseLine: true, - PersistentPreRunE: noOp, - RunE: validate.SubCommandExists, - PersistentPostRunE: noOp, - TraverseChildren: false, + Use: "connection", + Short: "Manage remote ssh destinations", + Long: `Manage ssh destination information in podman configuration`, + PersistentPreRunE: noOp, + RunE: validate.SubCommandExists, + PersistentPostRunE: noOp, + TraverseChildren: false, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: ConnectionCmd, Parent: systemCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/system/df.go libpod-3.4.4+ds1/cmd/podman/system/df.go --- libpod-3.2.1+ds1/cmd/podman/system/df.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/df.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,13 +4,10 @@ "fmt" "os" "strings" - "text/tabwriter" - "text/template" "time" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/pkg/domain/entities" @@ -40,7 +37,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: dfSystemCommand, Parent: systemCmd, }) @@ -58,7 +54,10 @@ return err } - w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } if dfOptions.Verbose { return printVerbose(w, cmd, reports) @@ -66,7 +65,7 @@ return printSummary(w, cmd, reports) } -func printSummary(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error { +func printSummary(w *report.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error { var ( dfSummaries []*dfSummary active int @@ -144,7 +143,7 @@ return writeTemplate(w, cmd, hdrs, row, dfSummaries) } -func printVerbose(w *tabwriter.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error { +func printVerbose(w *report.Writer, cmd *cobra.Command, reports *entities.SystemDfReport) error { defer w.Flush() fmt.Fprint(w, "Images space usage:\n\n") @@ -192,11 +191,11 @@ return writeTemplate(w, cmd, hdrs, volumeRow, dfVolumes) } -func writeTemplate(w *tabwriter.Writer, cmd *cobra.Command, hdrs []map[string]string, format string, output interface{}) error { +func writeTemplate(w *report.Writer, cmd *cobra.Command, hdrs []map[string]string, format string, output interface{}) error { defer w.Flush() - format = parse.EnforceRange(format) - tmpl, err := template.New("df").Parse(format) + format = report.EnforceRange(format) + tmpl, err := report.NewTemplate("df").Parse(format) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/events.go libpod-3.4.4+ds1/cmd/podman/system/events.go --- libpod-3.2.1+ds1/cmd/podman/system/events.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/events.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,7 +4,6 @@ "context" "fmt" "os" - "text/template" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" @@ -41,7 +40,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: eventsCommand, }) flags := eventsCommand.Flags() @@ -76,7 +74,7 @@ errChannel := make(chan error) var ( - tmpl *template.Template + tmpl *report.Template doJSON bool ) @@ -84,7 +82,7 @@ doJSON = report.IsJSON(eventFormat) if !doJSON { var err error - tmpl, err = template.New("events").Parse(eventFormat) + tmpl, err = report.NewTemplate("events").Parse(eventFormat) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/info.go libpod-3.4.4+ds1/cmd/podman/system/info.go --- libpod-3.2.1+ds1/cmd/podman/system/info.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/info.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "fmt" "os" - "text/template" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" @@ -11,7 +10,6 @@ "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/ghodss/yaml" "github.com/spf13/cobra" ) @@ -49,13 +47,11 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: infoCommand, }) infoFlags(infoCommand) registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: systemInfoCommand, Parent: systemCmd, }) @@ -88,7 +84,7 @@ } fmt.Println(string(b)) case cmd.Flags().Changed("format"): - tmpl, err := template.New("info").Parse(inFormat) + tmpl, err := report.NewTemplate("info").Parse(inFormat) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/migrate.go libpod-3.4.4+ds1/cmd/podman/system/migrate.go --- libpod-3.2.1+ds1/cmd/podman/system/migrate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/migrate.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra" "github.com/spf13/cobra" @@ -22,6 +23,10 @@ ` migrateCommand = &cobra.Command{ + Annotations: map[string]string{ + registry.EngineMode: registry.ABIMode, + registry.NoMoveProcess: registry.NoMoveProcess, + }, Use: "migrate [options]", Args: validate.NoArgs, Short: "Migrate containers", @@ -37,7 +42,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: migrateCommand, Parent: systemCmd, }) @@ -57,14 +61,14 @@ engine, err := infra.NewSystemEngine(entities.MigrateMode, registry.PodmanConfig()) if err != nil { fmt.Println(err) - os.Exit(125) + os.Exit(define.ExecErrorCodeGeneric) } defer engine.Shutdown(registry.Context()) err = engine.Migrate(registry.Context(), cmd.Flags(), registry.PodmanConfig(), migrateOptions) if err != nil { fmt.Println(err) - os.Exit(125) + os.Exit(define.ExecErrorCodeGeneric) } os.Exit(0) } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/prune.go libpod-3.4.4+ds1/cmd/podman/system/prune.go --- libpod-3.2.1+ds1/cmd/podman/system/prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -41,7 +41,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pruneCommand, Parent: systemCmd, }) @@ -114,15 +113,15 @@ func createPruneWarningMessage(pruneOpts entities.SystemPruneOptions) string { if pruneOpts.All { - return `WARNING! This will remove: + return `WARNING! This command removes: - all stopped containers - all networks not used by at least one container%s - - all images without at least one container associated to them + - all images without at least one container associated with them - all build cache %s` } - return `WARNING! This will remove: + return `WARNING! This command removes: - all stopped containers - all networks not used by at least one container%s - all dangling images diff -Nru libpod-3.2.1+ds1/cmd/podman/system/renumber.go libpod-3.4.4+ds1/cmd/podman/system/renumber.go --- libpod-3.2.1+ds1/cmd/podman/system/renumber.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/renumber.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra" "github.com/spf13/cobra" @@ -23,19 +24,18 @@ ` renumberCommand = &cobra.Command{ - Use: "renumber", - Args: validate.NoArgs, - DisableFlagsInUseLine: true, - Short: "Migrate lock numbers", - Long: renumberDescription, - Run: renumber, - ValidArgsFunction: completion.AutocompleteNone, + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "renumber", + Args: validate.NoArgs, + Short: "Migrate lock numbers", + Long: renumberDescription, + Run: renumber, + ValidArgsFunction: completion.AutocompleteNone, } ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: renumberCommand, Parent: systemCmd, }) @@ -48,14 +48,14 @@ engine, err := infra.NewSystemEngine(entities.RenumberMode, registry.PodmanConfig()) if err != nil { fmt.Println(err) - os.Exit(125) + os.Exit(define.ExecErrorCodeGeneric) } defer engine.Shutdown(registry.Context()) err = engine.Renumber(registry.Context(), cmd.Flags(), registry.PodmanConfig()) if err != nil { fmt.Println(err) - os.Exit(125) + os.Exit(define.ExecErrorCodeGeneric) } os.Exit(0) } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/reset.go libpod-3.4.4+ds1/cmd/podman/system/reset.go --- libpod-3.2.1+ds1/cmd/podman/system/reset.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/reset.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra" "github.com/sirupsen/logrus" @@ -23,6 +24,7 @@ All containers will be stopped and removed, and all images, volumes and container content will be removed. ` systemResetCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "reset [options]", Args: validate.NoArgs, Short: "Reset podman storage", @@ -36,7 +38,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: systemResetCommand, Parent: systemCmd, }) @@ -45,16 +46,29 @@ } func reset(cmd *cobra.Command, args []string) { + // Get all the external containers in use + listCtn, _ := registry.ContainerEngine().ContainerListExternal(registry.Context()) + listCtnIds := make([]string, 0, len(listCtn)) + for _, externalCtn := range listCtn { + listCtnIds = append(listCtnIds, externalCtn.ID) + } // Prompt for confirmation if --force is not set if !forceFlag { reader := bufio.NewReader(os.Stdin) - fmt.Print(` + fmt.Println(` WARNING! This will remove: - all containers - all pods - all images - - all build cache -Are you sure you want to continue? [y/N] `) + - all build cache`) + if len(listCtn) > 0 { + fmt.Println(`WARNING! The following external containers will be purged:`) + // print first 12 characters of ID and first configured name alias + for _, externalCtn := range listCtn { + fmt.Printf(" - %s (%s)\n", externalCtn.ID[0:12], externalCtn.Names[0]) + } + } + fmt.Print(`Are you sure you want to continue? [y/N] `) answer, err := reader.ReadString('\n') if err != nil { logrus.Error(err) @@ -65,6 +79,8 @@ } } + // Purge all the external containers with storage + registry.ContainerEngine().ContainerRm(registry.Context(), listCtnIds, entities.RmOptions{Force: true, All: true, Ignore: true, Volumes: true}) // Shutdown all running engines, `reset` will hijack repository registry.ContainerEngine().Shutdown(registry.Context()) registry.ImageEngine().Shutdown(registry.Context()) @@ -72,13 +88,13 @@ engine, err := infra.NewSystemEngine(entities.ResetMode, registry.PodmanConfig()) if err != nil { logrus.Error(err) - os.Exit(125) + os.Exit(define.ExecErrorCodeGeneric) } defer engine.Shutdown(registry.Context()) if err := engine.Reset(registry.Context()); err != nil { logrus.Error(err) - os.Exit(125) + os.Exit(define.ExecErrorCodeGeneric) } os.Exit(0) } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/service_abi.go libpod-3.4.4+ds1/cmd/podman/system/service_abi.go --- libpod-3.2.1+ds1/cmd/podman/system/service_abi.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/service_abi.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ api "github.com/containers/podman/v3/pkg/api/server" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra" + "github.com/containers/podman/v3/pkg/servicereaper" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -71,8 +72,10 @@ return err } + servicereaper.Start() + infra.StartWatcher(rt) - server, err := api.NewServerWithSettings(rt, opts.Timeout, listener) + server, err := api.NewServerWithSettings(rt, listener, api.Options{Timeout: opts.Timeout, CorsHeaders: opts.CorsHeaders}) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/service.go libpod-3.4.4+ds1/cmd/podman/system/service.go --- libpod-3.2.1+ds1/cmd/podman/system/service.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/service.go 2021-12-26 00:45:51.000000000 +0000 @@ -28,6 +28,7 @@ ` srvCmd = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, Use: "service [options] [URI]", Args: cobra.MaximumNArgs(1), Short: "Run API service", @@ -38,13 +39,13 @@ } srvArgs = struct { - Timeout int64 + Timeout int64 + CorsHeaders string }{} ) func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: srvCmd, Parent: systemCmd, }) @@ -54,6 +55,8 @@ timeFlagName := "time" flags.Int64VarP(&srvArgs.Timeout, timeFlagName, "t", 5, "Time until the service session expires in seconds. Use 0 to disable the timeout") _ = srvCmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone) + flags.StringVarP(&srvArgs.CorsHeaders, "cors", "", "", "Set CORS Headers") + _ = srvCmd.RegisterFlagCompletionFunc("cors", completion.AutocompleteNone) flags.SetNormalizeFunc(aliasTimeoutFlag) } @@ -71,7 +74,6 @@ return err } logrus.Infof("using API endpoint: '%s'", apiURI) - // Clean up any old existing unix domain socket if len(apiURI) > 0 { uri, err := url.Parse(apiURI) @@ -90,8 +92,9 @@ } opts := entities.ServiceOptions{ - URI: apiURI, - Command: cmd, + URI: apiURI, + Command: cmd, + CorsHeaders: srvArgs.CorsHeaders, } opts.Timeout = time.Duration(srvArgs.Timeout) * time.Second diff -Nru libpod-3.2.1+ds1/cmd/podman/system/system.go libpod-3.4.4+ds1/cmd/podman/system/system.go --- libpod-3.2.1+ds1/cmd/podman/system/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -22,7 +21,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: systemCmd, }) } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/unshare.go libpod-3.4.4+ds1/cmd/podman/system/unshare.go --- libpod-3.2.1+ds1/cmd/podman/system/unshare.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/unshare.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,6 +2,7 @@ import ( "os" + "os/exec" "github.com/containers/common/pkg/completion" "github.com/containers/podman/v3/cmd/podman/registry" @@ -15,12 +16,12 @@ unshareOptions = entities.SystemUnshareOptions{} unshareDescription = "Runs a command in a modified user namespace." unshareCommand = &cobra.Command{ - Use: "unshare [options] [COMMAND [ARG...]]", - DisableFlagsInUseLine: true, - Short: "Run a command in a modified user namespace", - Long: unshareDescription, - RunE: unshare, - ValidArgsFunction: completion.AutocompleteDefault, + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "unshare [options] [COMMAND [ARG...]]", + Short: "Run a command in a modified user namespace", + Long: unshareDescription, + RunE: unshare, + ValidArgsFunction: completion.AutocompleteDefault, Example: `podman unshare id podman unshare cat /proc/self/uid_map, podman unshare podman-script.sh`, @@ -29,7 +30,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode}, Command: unshareCommand, }) flags := unshareCommand.Flags() @@ -51,5 +51,23 @@ args = []string{shell} } - return registry.ContainerEngine().Unshare(registry.Context(), args, unshareOptions) + err := registry.ContainerEngine().Unshare(registry.Context(), args, unshareOptions) + if err != nil { + if exitError, ok := err.(*exec.ExitError); ok { + // the user command inside the unshare env has failed + // we set the exit code, do not return the error to the user + // otherwise "exit status X" will be printed + registry.SetExitCode(exitError.ExitCode()) + return nil + } + // cmd.Run() can return fs.ErrNotExist, fs.ErrPermission or exec.ErrNotFound + // follow podman run/exec standard with the exit codes + if errors.Is(err, os.ErrNotExist) || errors.Is(err, exec.ErrNotFound) { + registry.SetExitCode(127) + } else if errors.Is(err, os.ErrPermission) { + registry.SetExitCode(126) + } + return err + } + return nil } diff -Nru libpod-3.2.1+ds1/cmd/podman/system/version.go libpod-3.4.4+ds1/cmd/podman/system/version.go --- libpod-3.2.1+ds1/cmd/podman/system/version.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/system/version.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,8 +5,6 @@ "io" "os" "strings" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" @@ -22,7 +20,7 @@ versionCommand = &cobra.Command{ Use: "version [options]", Args: validate.NoArgs, - Short: "Display the Podman Version Information", + Short: "Display the Podman version information", RunE: version, ValidArgsFunction: completion.AutocompleteNone, } @@ -31,7 +29,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: versionCommand, }) flags := versionCommand.Flags() @@ -56,19 +53,22 @@ return nil } - w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + w, err := report.NewWriterDefault(os.Stdout) + if err != nil { + return err + } defer w.Flush() if cmd.Flag("format").Changed { row := report.NormalizeFormat(versionFormat) - tmpl, err := template.New("version 2.0.0").Parse(row) + tmpl, err := report.NewTemplate("version 2.0.0").Parse(row) if err != nil { return err } if err := tmpl.Execute(w, versions); err != nil { // On Failure, assume user is using older version of podman version --format and check client row = strings.Replace(row, ".Server.", ".", 1) - tmpl, err := template.New("version 1.0.0").Parse(row) + tmpl, err := report.NewTemplate("version 1.0.0").Parse(row) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/cmd/podman/utils/error.go libpod-3.4.4+ds1/cmd/podman/utils/error.go --- libpod-3.2.1+ds1/cmd/podman/utils/error.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/utils/error.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,9 @@ package utils -import "fmt" +import ( + "fmt" + "os" +) type OutputErrors []error @@ -10,7 +13,7 @@ } lastError = o[len(o)-1] for e := 0; e < len(o)-1; e++ { - fmt.Println(o[e]) + fmt.Fprintf(os.Stderr, "Error: %s\n", o[e]) } return } diff -Nru libpod-3.2.1+ds1/cmd/podman/validate/args.go libpod-3.4.4+ds1/cmd/podman/validate/args.go --- libpod-3.2.1+ds1/cmd/podman/validate/args.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/validate/args.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ import ( "fmt" "strconv" + "strings" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/pkg/errors" @@ -20,7 +21,11 @@ // SubCommandExists returns an error if no sub command is provided func SubCommandExists(cmd *cobra.Command, args []string) error { if len(args) > 0 { - return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0]) + suggestions := cmd.SuggestionsFor(args[0]) + if len(suggestions) == 0 { + return errors.Errorf("unrecognized command `%[1]s %[2]s`\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0]) + } + return errors.Errorf("unrecognized command `%[1]s %[2]s`\n\nDid you mean this?\n\t%[3]s\n\nTry '%[1]s --help' for more information.", cmd.CommandPath(), args[0], strings.Join(suggestions, "\n\t")) } return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath()) } diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/create.go libpod-3.4.4+ds1/cmd/podman/volumes/create.go --- libpod-3.2.1+ds1/cmd/podman/volumes/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,7 +37,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: createCommand, Parent: volumeCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/exists.go libpod-3.4.4+ds1/cmd/podman/volumes/exists.go --- libpod-3.2.1+ds1/cmd/podman/volumes/exists.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/exists.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/common" "github.com/containers/podman/v3/cmd/podman/registry" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -22,7 +21,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: volumeExistsCommand, Parent: volumeCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/export.go libpod-3.4.4+ds1/cmd/podman/volumes/export.go --- libpod-3.2.1+ds1/cmd/podman/volumes/export.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/export.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,96 @@ +package volumes + +import ( + "context" + "fmt" + + "github.com/containers/common/pkg/completion" + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/inspect" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/utils" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + volumeExportDescription = ` +podman volume export + +Allow content of volume to be exported into external tar.` + exportCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "export [options] VOLUME", + Short: "Export volumes", + Args: cobra.ExactArgs(1), + Long: volumeExportDescription, + RunE: export, + ValidArgsFunction: common.AutocompleteVolumes, + } +) + +var ( + // Temporary struct to hold cli values. + cliExportOpts = struct { + Output string + }{} +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: exportCommand, + Parent: volumeCmd, + }) + flags := exportCommand.Flags() + + outputFlagName := "output" + flags.StringVarP(&cliExportOpts.Output, outputFlagName, "o", "/dev/stdout", "Write to a specified file (default: stdout, which must be redirected)") + _ = exportCommand.RegisterFlagCompletionFunc(outputFlagName, completion.AutocompleteDefault) +} + +func export(cmd *cobra.Command, args []string) error { + var inspectOpts entities.InspectOptions + containerEngine := registry.ContainerEngine() + ctx := context.Background() + + if cliExportOpts.Output == "" { + return errors.New("expects output path, use --output=[path]") + } + inspectOpts.Type = inspect.VolumeType + volumeData, _, err := containerEngine.VolumeInspect(ctx, args, inspectOpts) + if err != nil { + return err + } + if len(volumeData) < 1 { + return errors.New("no volume data found") + } + mountPoint := volumeData[0].VolumeConfigResponse.Mountpoint + driver := volumeData[0].VolumeConfigResponse.Driver + volumeOptions := volumeData[0].VolumeConfigResponse.Options + volumeMountStatus, err := containerEngine.VolumeMounted(ctx, args[0]) + if err != nil { + return err + } + if mountPoint == "" { + return errors.New("volume is not mounted anywhere on host") + } + // Check if volume is using external plugin and export only if volume is mounted + if driver != "" && driver != "local" { + if !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + // Check if volume is using `local` driver and has mount options type other than tmpfs + if driver == "local" { + if mountOptionType, ok := volumeOptions["type"]; ok { + if mountOptionType != "tmpfs" && !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + } + logrus.Debugf("Exporting volume data from %s to %s", mountPoint, cliExportOpts.Output) + err = utils.CreateTarFromSrc(mountPoint, cliExportOpts.Output) + return err +} diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/import.go libpod-3.4.4+ds1/cmd/podman/volumes/import.go --- libpod-3.2.1+ds1/cmd/podman/volumes/import.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/import.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,97 @@ +package volumes + +import ( + "fmt" + "os" + + "github.com/containers/podman/v3/cmd/podman/common" + "github.com/containers/podman/v3/cmd/podman/inspect" + "github.com/containers/podman/v3/cmd/podman/parse" + "github.com/containers/podman/v3/cmd/podman/registry" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + importDescription = `Imports contents into a podman volume from specified tarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz).` + importCommand = &cobra.Command{ + Annotations: map[string]string{registry.EngineMode: registry.ABIMode}, + Use: "import VOLUME [SOURCE]", + Short: "Import a tarball contents into a podman volume", + Long: importDescription, + RunE: importVol, + Args: cobra.ExactArgs(2), + ValidArgsFunction: common.AutocompleteVolumes, + Example: `podman volume import my_vol /home/user/import.tar + cat ctr.tar | podman import volume my_vol -`, + } +) + +func init() { + registry.Commands = append(registry.Commands, registry.CliCommand{ + Command: importCommand, + Parent: volumeCmd, + }) +} + +func importVol(cmd *cobra.Command, args []string) error { + var inspectOpts entities.InspectOptions + var tarFile *os.File + containerEngine := registry.ContainerEngine() + ctx := registry.Context() + // create a slice of volumes since inspect expects slice as arg + volumes := []string{args[0]} + tarPath := args[1] + + if tarPath != "-" { + err := parse.ValidateFileName(tarPath) + if err != nil { + return err + } + + // open tar file + tarFile, err = os.Open(tarPath) + if err != nil { + return err + } + } else { + tarFile = os.Stdin + } + + inspectOpts.Type = inspect.VolumeType + volumeData, _, err := containerEngine.VolumeInspect(ctx, volumes, inspectOpts) + if err != nil { + return err + } + if len(volumeData) < 1 { + return errors.New("no volume data found") + } + mountPoint := volumeData[0].VolumeConfigResponse.Mountpoint + driver := volumeData[0].VolumeConfigResponse.Driver + volumeOptions := volumeData[0].VolumeConfigResponse.Options + volumeMountStatus, err := containerEngine.VolumeMounted(ctx, args[0]) + if err != nil { + return err + } + if mountPoint == "" { + return errors.New("volume is not mounted anywhere on host") + } + // Check if volume is using external plugin and export only if volume is mounted + if driver != "" && driver != "local" { + if !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + // Check if volume is using `local` driver and has mount options type other than tmpfs + if driver == "local" { + if mountOptionType, ok := volumeOptions["type"]; ok { + if mountOptionType != "tmpfs" && !volumeMountStatus.Value { + return fmt.Errorf("volume is using a driver %s and volume is not mounted on %s", driver, mountPoint) + } + } + } + // dont care if volume is mounted or not we are gonna import everything to mountPoint + return utils.UntarToFileSystem(mountPoint, tarFile, nil) +} diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/inspect.go libpod-3.4.4+ds1/cmd/podman/volumes/inspect.go --- libpod-3.2.1+ds1/cmd/podman/volumes/inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -32,7 +32,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: inspectCommand, Parent: volumeCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/list.go libpod-3.4.4+ds1/cmd/podman/volumes/list.go --- libpod-3.2.1+ds1/cmd/podman/volumes/list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/list.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,13 +5,10 @@ "fmt" "os" "strings" - "text/tabwriter" - "text/template" "github.com/containers/common/pkg/completion" "github.com/containers/common/pkg/report" "github.com/containers/podman/v3/cmd/podman/common" - "github.com/containers/podman/v3/cmd/podman/parse" "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" "github.com/containers/podman/v3/libpod/define" @@ -49,7 +46,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: lsCommand, Parent: volumeCmd, }) @@ -105,13 +101,17 @@ if cliOpts.Quiet { row = "{{.Name}}\n" } - format := parse.EnforceRange(row) + format := report.EnforceRange(row) - tmpl, err := template.New("list volume").Parse(format) + tmpl, err := report.NewTemplate("list").Parse(format) + if err != nil { + return err + } + + w, err := report.NewWriterDefault(os.Stdout) if err != nil { return err } - w := tabwriter.NewWriter(os.Stdout, 12, 2, 2, ' ', 0) defer w.Flush() if !(noHeading || cliOpts.Quiet || cmd.Flag("format").Changed) { diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/prune.go libpod-3.4.4+ds1/cmd/podman/volumes/prune.go --- libpod-3.2.1+ds1/cmd/podman/volumes/prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -35,7 +35,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: pruneCommand, Parent: volumeCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/rm.go libpod-3.4.4+ds1/cmd/podman/volumes/rm.go --- libpod-3.2.1+ds1/cmd/podman/volumes/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,7 +37,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: rmCommand, Parent: volumeCmd, }) diff -Nru libpod-3.2.1+ds1/cmd/podman/volumes/volume.go libpod-3.4.4+ds1/cmd/podman/volumes/volume.go --- libpod-3.2.1+ds1/cmd/podman/volumes/volume.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cmd/podman/volumes/volume.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,7 +3,6 @@ import ( "github.com/containers/podman/v3/cmd/podman/registry" "github.com/containers/podman/v3/cmd/podman/validate" - "github.com/containers/podman/v3/pkg/domain/entities" "github.com/spf13/cobra" ) @@ -22,7 +21,6 @@ func init() { registry.Commands = append(registry.Commands, registry.CliCommand{ - Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode}, Command: volumeCmd, }) } diff -Nru libpod-3.2.1+ds1/cni/README.md libpod-3.4.4+ds1/cni/README.md --- libpod-3.2.1+ds1/cni/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/cni/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -11,7 +11,7 @@ ```bash sudo mkdir -p /etc/cni/net.d -curl -qsSL https://raw.githubusercontent.com/containers/libpod/master/cni/87-podman-bridge.conflist | sudo tee /etc/cni/net.d/87-podman-bridge.conf +curl -qsSL https://raw.githubusercontent.com/containers/libpod/master/cni/87-podman-bridge.conflist | sudo tee /etc/cni/net.d/87-podman-bridge.conflist ``` Dependent upon your CNI configuration, you will need to install as a minimum the `port` and `bridge` [CNI plugins](https://github.com/containernetworking/plugins) into `/opt/cni/bin` (or the directory specified by `cni_plugin_dir` in containers.conf). Please refer to the [CNI](https://github.com/containernetworking) project page in GitHub for more information. diff -Nru libpod-3.2.1+ds1/CODE-OF-CONDUCT.md libpod-3.4.4+ds1/CODE-OF-CONDUCT.md --- libpod-3.2.1+ds1/CODE-OF-CONDUCT.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/CODE-OF-CONDUCT.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,3 @@ ## The Podman Project Community Code of Conduct -The Podman project which includes Libpod, follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/master/CODE-OF-CONDUCT.md). +The Podman project which includes Libpod, follows the [Containers Community Code of Conduct](https://github.com/containers/common/blob/main/CODE-OF-CONDUCT.md). diff -Nru libpod-3.2.1+ds1/completions/bash/podman libpod-3.4.4+ds1/completions/bash/podman --- libpod-3.2.1+ds1/completions/bash/podman 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/bash/podman 2021-12-26 00:45:51.000000000 +0000 @@ -1,38 +1,29 @@ -# bash completion for podman -*- shell-script -*- +# bash completion V2 for podman -*- shell-script -*- __podman_debug() { - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } -__podman_perform_completion() +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +__podman_init_completion() { - __podman_debug - __podman_debug "========= starting completion logic ==========" - __podman_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" - - # The user could have moved the cursor backwards on the command-line. - # We need to trigger completion from the $cword location, so we need - # to truncate the command-line ($words) up to the $cword location. - words=("${words[@]:0:$cword+1}") - __podman_debug "Truncated words[*]: ${words[*]}," - - local shellCompDirectiveError=1 - local shellCompDirectiveNoSpace=2 - local shellCompDirectiveNoFileComp=4 - local shellCompDirectiveFilterFileExt=8 - local shellCompDirectiveFilterDirs=16 - local shellCompDirectiveLegacyCustomComp=32 - local shellCompDirectiveLegacyCustomArgsComp=64 + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} - local out requestComp lastParam lastChar comp directive args flagPrefix +# This function calls the podman program to obtain the completion +# results and the directive. It fills the 'out' and 'directive' vars. +__podman_get_completion_results() { + local requestComp lastParam lastChar args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly podman allows to handle aliases args=("${words[@]:1}") - requestComp="${words[0]} __completeNoDesc ${args[*]}" + requestComp="${words[0]} __complete ${args[*]}" lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} @@ -42,14 +33,13 @@ # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __podman_debug "Adding extra empty parameter" - requestComp="${requestComp} \"\"" + requestComp="${requestComp} ''" fi # When completing a flag with an = (e.g., podman -n=) # bash focuses on the part after the =, so we need to remove # the flag part from $cur if [[ "${cur}" == -*=* ]]; then - flagPrefix="${cur%%=*}=" cur="${cur#*=}" fi @@ -67,6 +57,14 @@ fi __podman_debug "The completion directive is: ${directive}" __podman_debug "The completions are: ${out[*]}" +} + +__podman_process_completion_results() { + local shellCompDirectiveError=1 + local shellCompDirectiveNoSpace=2 + local shellCompDirectiveNoFileComp=4 + local shellCompDirectiveFilterFileExt=8 + local shellCompDirectiveFilterDirs=16 if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. @@ -77,12 +75,16 @@ if [[ $(type -t compopt) = "builtin" ]]; then __podman_debug "Activating no space" compopt -o nospace + else + __podman_debug "No space directive not supported in this version of bash" fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then __podman_debug "Activating no file completion" compopt +o default + else + __podman_debug "No file completion directive not supported in this version of bash" fi fi fi @@ -113,87 +115,56 @@ __podman_debug "Listing directories in ." _filedir -d fi - elif [ $((directive & shellCompDirectiveLegacyCustomComp)) -ne 0 ]; then - local cmd - __podman_debug "Legacy custom completion. Directive: $directive, cmds: ${out[*]}" - - # The following variables should get their value through the commands - # we have received as completions and are parsing below. - local last_command - local nouns - - # Execute every command received - while IFS='' read -r cmd; do - __podman_debug "About to execute: $cmd" - eval "$cmd" - done < <(printf "%s\n" "${out[@]}") - - __podman_debug "last_command: $last_command" - __podman_debug "nouns[0]: ${nouns[0]}, nouns[1]: ${nouns[1]}" - - if [ $((directive & shellCompDirectiveLegacyCustomArgsComp)) -ne 0 ]; then - # We should call the global legacy custom completion function, if it is defined - if declare -F __podman_custom_func >/dev/null; then - # Use command name qualified legacy custom func - __podman_debug "About to call: __podman_custom_func" - __podman_custom_func - elif declare -F __custom_func >/dev/null; then - # Otherwise fall back to unqualified legacy custom func for compatibility - __podman_debug "About to call: __custom_func" - __custom_func - fi - fi else - local tab - tab=$(printf '\t') - local longest=0 - # Look for the longest completion so that we can format things nicely - while IFS='' read -r comp; do - comp=${comp%%$tab*} - if ((${#comp}>longest)); then - longest=${#comp} - fi - done < <(printf "%s\n" "${out[@]}") - - local completions=() - while IFS='' read -r comp; do - if [ -z "$comp" ]; then - continue - fi - - __podman_debug "Original comp: $comp" - comp="$(__podman_format_comp_descriptions "$comp" "$longest")" - __podman_debug "Final comp: $comp" - completions+=("$comp") - done < <(printf "%s\n" "${out[@]}") - - while IFS='' read -r comp; do - # Although this script should only be used for bash - # there may be programs that still convert the bash - # script into a zsh one. To continue supporting those - # programs, we do this single adaptation for zsh - if [ -n "${ZSH_VERSION}" ]; then - # zsh completion needs --flag= prefix - COMPREPLY+=("$flagPrefix$comp") - else - COMPREPLY+=("$comp") - fi - done < <(compgen -W "${completions[*]}" -- "$cur") - - # If there is a single completion left, remove the description text - if [ ${#COMPREPLY[*]} -eq 1 ]; then - __podman_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%% *}" - __podman_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY=() - COMPREPLY+=("$comp") - fi + __podman_handle_standard_completion_case fi __podman_handle_special_char "$cur" : __podman_handle_special_char "$cur" = } +__podman_handle_standard_completion_case() { + local tab comp + tab=$(printf '\t') + + local longest=0 + # Look for the longest completion so that we can format things nicely + while IFS='' read -r comp; do + # Strip any description before checking the length + comp=${comp%%$tab*} + # Only consider the completions that match + comp=$(compgen -W "$comp" -- "$cur") + if ((${#comp}>longest)); then + longest=${#comp} + fi + done < <(printf "%s\n" "${out[@]}") + + local completions=() + while IFS='' read -r comp; do + if [ -z "$comp" ]; then + continue + fi + + __podman_debug "Original comp: $comp" + comp="$(__podman_format_comp_descriptions "$comp" "$longest")" + __podman_debug "Final comp: $comp" + completions+=("$comp") + done < <(printf "%s\n" "${out[@]}") + + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${completions[*]}" -- "$cur") + + # If there is a single completion left, remove the description text + if [ ${#COMPREPLY[*]} -eq 1 ]; then + __podman_debug "COMPREPLY[0]: ${COMPREPLY[0]}" + comp="${COMPREPLY[0]%% *}" + __podman_debug "Removed description from single completion, which is now: ${comp}" + COMPREPLY=() + COMPREPLY+=("$comp") + fi +} + __podman_handle_special_char() { local comp="$1" @@ -252,12 +223,31 @@ __start_podman() { - local cur prev words cword + local cur prev words cword split COMPREPLY=() - _get_comp_words_by_ref -n "=:" cur prev words cword - __podman_perform_completion + # Call _init_completion from the bash-completion package + # to prepare the arguments properly + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + __podman_init_completion -n "=:" || return + fi + + __podman_debug + __podman_debug "========= starting completion logic ==========" + __podman_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" + + # The user could have moved the cursor backwards on the command-line. + # We need to trigger completion from the $cword location, so we need + # to truncate the command-line ($words) up to the $cword location. + words=("${words[@]:0:$cword+1}") + __podman_debug "Truncated words[*]: ${words[*]}," + + local out directive + __podman_get_completion_results + __podman_process_completion_results } if [[ $(type -t compopt) = "builtin" ]]; then diff -Nru libpod-3.2.1+ds1/completions/bash/podman-remote libpod-3.4.4+ds1/completions/bash/podman-remote --- libpod-3.2.1+ds1/completions/bash/podman-remote 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/bash/podman-remote 2021-12-26 00:45:51.000000000 +0000 @@ -1,38 +1,29 @@ -# bash completion for podman-remote -*- shell-script -*- +# bash completion V2 for podman-remote -*- shell-script -*- __podman-remote_debug() { - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } -__podman-remote_perform_completion() +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +__podman-remote_init_completion() { - __podman-remote_debug - __podman-remote_debug "========= starting completion logic ==========" - __podman-remote_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" - - # The user could have moved the cursor backwards on the command-line. - # We need to trigger completion from the $cword location, so we need - # to truncate the command-line ($words) up to the $cword location. - words=("${words[@]:0:$cword+1}") - __podman-remote_debug "Truncated words[*]: ${words[*]}," - - local shellCompDirectiveError=1 - local shellCompDirectiveNoSpace=2 - local shellCompDirectiveNoFileComp=4 - local shellCompDirectiveFilterFileExt=8 - local shellCompDirectiveFilterDirs=16 - local shellCompDirectiveLegacyCustomComp=32 - local shellCompDirectiveLegacyCustomArgsComp=64 + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} - local out requestComp lastParam lastChar comp directive args flagPrefix +# This function calls the podman-remote program to obtain the completion +# results and the directive. It fills the 'out' and 'directive' vars. +__podman-remote_get_completion_results() { + local requestComp lastParam lastChar args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly podman-remote allows to handle aliases args=("${words[@]:1}") - requestComp="${words[0]} __completeNoDesc ${args[*]}" + requestComp="${words[0]} __complete ${args[*]}" lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} @@ -42,14 +33,13 @@ # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __podman-remote_debug "Adding extra empty parameter" - requestComp="${requestComp} \"\"" + requestComp="${requestComp} ''" fi # When completing a flag with an = (e.g., podman-remote -n=) # bash focuses on the part after the =, so we need to remove # the flag part from $cur if [[ "${cur}" == -*=* ]]; then - flagPrefix="${cur%%=*}=" cur="${cur#*=}" fi @@ -67,6 +57,14 @@ fi __podman-remote_debug "The completion directive is: ${directive}" __podman-remote_debug "The completions are: ${out[*]}" +} + +__podman-remote_process_completion_results() { + local shellCompDirectiveError=1 + local shellCompDirectiveNoSpace=2 + local shellCompDirectiveNoFileComp=4 + local shellCompDirectiveFilterFileExt=8 + local shellCompDirectiveFilterDirs=16 if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. @@ -77,12 +75,16 @@ if [[ $(type -t compopt) = "builtin" ]]; then __podman-remote_debug "Activating no space" compopt -o nospace + else + __podman-remote_debug "No space directive not supported in this version of bash" fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then __podman-remote_debug "Activating no file completion" compopt +o default + else + __podman-remote_debug "No file completion directive not supported in this version of bash" fi fi fi @@ -113,87 +115,56 @@ __podman-remote_debug "Listing directories in ." _filedir -d fi - elif [ $((directive & shellCompDirectiveLegacyCustomComp)) -ne 0 ]; then - local cmd - __podman-remote_debug "Legacy custom completion. Directive: $directive, cmds: ${out[*]}" - - # The following variables should get their value through the commands - # we have received as completions and are parsing below. - local last_command - local nouns - - # Execute every command received - while IFS='' read -r cmd; do - __podman-remote_debug "About to execute: $cmd" - eval "$cmd" - done < <(printf "%s\n" "${out[@]}") - - __podman-remote_debug "last_command: $last_command" - __podman-remote_debug "nouns[0]: ${nouns[0]}, nouns[1]: ${nouns[1]}" - - if [ $((directive & shellCompDirectiveLegacyCustomArgsComp)) -ne 0 ]; then - # We should call the global legacy custom completion function, if it is defined - if declare -F __podman-remote_custom_func >/dev/null; then - # Use command name qualified legacy custom func - __podman-remote_debug "About to call: __podman-remote_custom_func" - __podman-remote_custom_func - elif declare -F __custom_func >/dev/null; then - # Otherwise fall back to unqualified legacy custom func for compatibility - __podman-remote_debug "About to call: __custom_func" - __custom_func - fi - fi else - local tab - tab=$(printf '\t') - local longest=0 - # Look for the longest completion so that we can format things nicely - while IFS='' read -r comp; do - comp=${comp%%$tab*} - if ((${#comp}>longest)); then - longest=${#comp} - fi - done < <(printf "%s\n" "${out[@]}") - - local completions=() - while IFS='' read -r comp; do - if [ -z "$comp" ]; then - continue - fi - - __podman-remote_debug "Original comp: $comp" - comp="$(__podman-remote_format_comp_descriptions "$comp" "$longest")" - __podman-remote_debug "Final comp: $comp" - completions+=("$comp") - done < <(printf "%s\n" "${out[@]}") - - while IFS='' read -r comp; do - # Although this script should only be used for bash - # there may be programs that still convert the bash - # script into a zsh one. To continue supporting those - # programs, we do this single adaptation for zsh - if [ -n "${ZSH_VERSION}" ]; then - # zsh completion needs --flag= prefix - COMPREPLY+=("$flagPrefix$comp") - else - COMPREPLY+=("$comp") - fi - done < <(compgen -W "${completions[*]}" -- "$cur") - - # If there is a single completion left, remove the description text - if [ ${#COMPREPLY[*]} -eq 1 ]; then - __podman-remote_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%% *}" - __podman-remote_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY=() - COMPREPLY+=("$comp") - fi + __podman-remote_handle_standard_completion_case fi __podman-remote_handle_special_char "$cur" : __podman-remote_handle_special_char "$cur" = } +__podman-remote_handle_standard_completion_case() { + local tab comp + tab=$(printf '\t') + + local longest=0 + # Look for the longest completion so that we can format things nicely + while IFS='' read -r comp; do + # Strip any description before checking the length + comp=${comp%%$tab*} + # Only consider the completions that match + comp=$(compgen -W "$comp" -- "$cur") + if ((${#comp}>longest)); then + longest=${#comp} + fi + done < <(printf "%s\n" "${out[@]}") + + local completions=() + while IFS='' read -r comp; do + if [ -z "$comp" ]; then + continue + fi + + __podman-remote_debug "Original comp: $comp" + comp="$(__podman-remote_format_comp_descriptions "$comp" "$longest")" + __podman-remote_debug "Final comp: $comp" + completions+=("$comp") + done < <(printf "%s\n" "${out[@]}") + + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${completions[*]}" -- "$cur") + + # If there is a single completion left, remove the description text + if [ ${#COMPREPLY[*]} -eq 1 ]; then + __podman-remote_debug "COMPREPLY[0]: ${COMPREPLY[0]}" + comp="${COMPREPLY[0]%% *}" + __podman-remote_debug "Removed description from single completion, which is now: ${comp}" + COMPREPLY=() + COMPREPLY+=("$comp") + fi +} + __podman-remote_handle_special_char() { local comp="$1" @@ -252,12 +223,31 @@ __start_podman-remote() { - local cur prev words cword + local cur prev words cword split COMPREPLY=() - _get_comp_words_by_ref -n "=:" cur prev words cword - __podman-remote_perform_completion + # Call _init_completion from the bash-completion package + # to prepare the arguments properly + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + __podman-remote_init_completion -n "=:" || return + fi + + __podman-remote_debug + __podman-remote_debug "========= starting completion logic ==========" + __podman-remote_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" + + # The user could have moved the cursor backwards on the command-line. + # We need to trigger completion from the $cword location, so we need + # to truncate the command-line ($words) up to the $cword location. + words=("${words[@]:0:$cword+1}") + __podman-remote_debug "Truncated words[*]: ${words[*]}," + + local out directive + __podman-remote_get_completion_results + __podman-remote_process_completion_results } if [[ $(type -t compopt) = "builtin" ]]; then diff -Nru libpod-3.2.1+ds1/completions/fish/podman.fish libpod-3.4.4+ds1/completions/fish/podman.fish --- libpod-3.2.1+ds1/completions/fish/podman.fish 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/fish/podman.fish 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,7 @@ # fish completion for podman -*- shell-script -*- function __podman_debug - set file "$BASH_COMP_DEBUG_FILE" + set -l file "$BASH_COMP_DEBUG_FILE" if test -n "$file" echo "$argv" >> $file end @@ -10,32 +10,38 @@ function __podman_perform_completion __podman_debug "Starting __podman_perform_completion" - set args (string split -- " " (string trim -l (commandline -c))) - set lastArg "$args[-1]" + # Extract all args except the last one + set -l args (commandline -opc) + # Extract the last arg and escape it in case it is a space + set -l lastArg (string escape -- (commandline -ct)) __podman_debug "args: $args" __podman_debug "last arg: $lastArg" - set emptyArg "" - if test -z "$lastArg" - __podman_debug "Setting emptyArg" - set emptyArg \"\" - end - __podman_debug "emptyArg: $emptyArg" + set -l requestComp "$args[1] __complete $args[2..-1] $lastArg" - set requestComp "$args[1] __complete $args[2..-1] $emptyArg" __podman_debug "Calling $requestComp" + set -l results (eval $requestComp 2> /dev/null) + + # Some programs may output extra empty lines after the directive. + # Let's ignore them or else it will break completion. + # Ref: https://github.com/spf13/cobra/issues/1279 + for line in $results[-1..1] + if test (string trim -- $line) = "" + # Found an empty line, remove it + set results $results[1..-2] + else + # Found non-empty line, we have our proper output + break + end + end - # Call the command as a sub-shell so that we can redirect any errors - # For example, if $requestComp has an unmatched quote - # https://github.com/spf13/cobra/issues/1214 - set results (fish -c "$requestComp" 2> /dev/null) - set comps $results[1..-2] - set directiveLine $results[-1] + set -l comps $results[1..-2] + set -l directiveLine $results[-1] # For Fish, when completing a flag with an = (e.g., -n=) # completions must be prefixed with the flag - set flagPrefix (string match -r -- '-.*=' "$lastArg") + set -l flagPrefix (string match -r -- '-.*=' "$lastArg") __podman_debug "Comps: $comps" __podman_debug "DirectiveLine: $directiveLine" @@ -58,7 +64,7 @@ # Start fresh set --erase __podman_comp_results - set results (__podman_perform_completion) + set -l results (__podman_perform_completion) __podman_debug "Completion results: $results" if test -z "$results" @@ -67,39 +73,39 @@ return 1 end - set directive (string sub --start 2 $results[-1]) + set -l directive (string sub --start 2 $results[-1]) set --global __podman_comp_results $results[1..-2] __podman_debug "Completions are: $__podman_comp_results" __podman_debug "Directive is: $directive" - set shellCompDirectiveError 1 - set shellCompDirectiveNoSpace 2 - set shellCompDirectiveNoFileComp 4 - set shellCompDirectiveFilterFileExt 8 - set shellCompDirectiveFilterDirs 16 + set -l shellCompDirectiveError 1 + set -l shellCompDirectiveNoSpace 2 + set -l shellCompDirectiveNoFileComp 4 + set -l shellCompDirectiveFilterFileExt 8 + set -l shellCompDirectiveFilterDirs 16 if test -z "$directive" set directive 0 end - set compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) + set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) if test $compErr -eq 1 __podman_debug "Received error directive: aborting." # Might as well do file completion, in case it helps return 1 end - set filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) - set dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) + set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) + set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) if test $filefilter -eq 1; or test $dirfilter -eq 1 __podman_debug "File extension filtering or directory filtering not supported" # Do full file completion instead return 1 end - set nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) - set nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) + set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) + set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) __podman_debug "nospace: $nospace, nofiles: $nofiles" @@ -109,34 +115,34 @@ # may not already be filtered so as to allow fish to match on different # criteria than the prefix. if test $nospace -ne 0; or test $nofiles -eq 0 - set prefix (commandline -t) + set -l prefix (commandline -t | string escape --style=regex) __podman_debug "prefix: $prefix" - set completions - for comp in $__podman_comp_results - if test (string match -e -r -- "^$prefix" "$comp") - set -a completions $comp - end - end + set -l completions (string match -r -- "^$prefix.*" $__podman_comp_results) set --global __podman_comp_results $completions __podman_debug "Filtered completions are: $__podman_comp_results" # Important not to quote the variable for count to work - set numComps (count $__podman_comp_results) + set -l numComps (count $__podman_comp_results) __podman_debug "numComps: $numComps" if test $numComps -eq 1; and test $nospace -ne 0 - # To support the "nospace" directive we trick the shell - # by outputting an extra, longer completion. - # We must first split on \t to get rid of the descriptions because - # the extra character we add to the fake second completion must be - # before the description. We don't need descriptions anyway since - # there is only a single real completion which the shell will expand - # immediately. - __podman_debug "Adding second completion to perform nospace directive" - set split (string split --max 1 \t $__podman_comp_results[1]) - set --global __podman_comp_results $split[1] $split[1]. - __podman_debug "Completions are now: $__podman_comp_results" + # We must first split on \t to get rid of the descriptions to be + # able to check what the actual completion will be. + # We don't need descriptions anyway since there is only a single + # real completion which the shell will expand immediately. + set -l split (string split --max 1 \t $__podman_comp_results[1]) + + # Fish won't add a space if the completion ends with any + # of the following characters: @=/:., + set -l lastChar (string sub -s -1 -- $split) + if not string match -r -q "[@=/:.,]" -- "$lastChar" + # In other cases, to support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __podman_debug "Adding second completion to perform nospace directive" + set --global __podman_comp_results $split[1] $split[1]. + __podman_debug "Completions are now: $__podman_comp_results" + end end if test $numComps -eq 0; and test $nofiles -eq 0 @@ -152,10 +158,14 @@ # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves # so we can properly delete any completions provided by another script. -# The space after the program name is essential to trigger completion for the program -# and not completion of the program name itself. -complete --do-complete "podman " > /dev/null 2>&1 -# Using '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. +# Only do this if the program can be found, or else fish may print some errors; besides, +# the existing completions will only be loaded if the program can be found. +if type -q "podman" + # The space after the program name is essential to trigger completion for the program + # and not completion of the program name itself. + # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. + complete --do-complete "podman " > /dev/null 2>&1 +end # Remove any pre-existing completions for the program since we will be handling all of them. complete -c podman -e diff -Nru libpod-3.2.1+ds1/completions/fish/podman-remote.fish libpod-3.4.4+ds1/completions/fish/podman-remote.fish --- libpod-3.2.1+ds1/completions/fish/podman-remote.fish 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/fish/podman-remote.fish 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,7 @@ # fish completion for podman-remote -*- shell-script -*- function __podman_remote_debug - set file "$BASH_COMP_DEBUG_FILE" + set -l file "$BASH_COMP_DEBUG_FILE" if test -n "$file" echo "$argv" >> $file end @@ -10,32 +10,38 @@ function __podman_remote_perform_completion __podman_remote_debug "Starting __podman_remote_perform_completion" - set args (string split -- " " (string trim -l (commandline -c))) - set lastArg "$args[-1]" + # Extract all args except the last one + set -l args (commandline -opc) + # Extract the last arg and escape it in case it is a space + set -l lastArg (string escape -- (commandline -ct)) __podman_remote_debug "args: $args" __podman_remote_debug "last arg: $lastArg" - set emptyArg "" - if test -z "$lastArg" - __podman_remote_debug "Setting emptyArg" - set emptyArg \"\" - end - __podman_remote_debug "emptyArg: $emptyArg" + set -l requestComp "$args[1] __complete $args[2..-1] $lastArg" - set requestComp "$args[1] __complete $args[2..-1] $emptyArg" __podman_remote_debug "Calling $requestComp" + set -l results (eval $requestComp 2> /dev/null) + + # Some programs may output extra empty lines after the directive. + # Let's ignore them or else it will break completion. + # Ref: https://github.com/spf13/cobra/issues/1279 + for line in $results[-1..1] + if test (string trim -- $line) = "" + # Found an empty line, remove it + set results $results[1..-2] + else + # Found non-empty line, we have our proper output + break + end + end - # Call the command as a sub-shell so that we can redirect any errors - # For example, if $requestComp has an unmatched quote - # https://github.com/spf13/cobra/issues/1214 - set results (fish -c "$requestComp" 2> /dev/null) - set comps $results[1..-2] - set directiveLine $results[-1] + set -l comps $results[1..-2] + set -l directiveLine $results[-1] # For Fish, when completing a flag with an = (e.g., -n=) # completions must be prefixed with the flag - set flagPrefix (string match -r -- '-.*=' "$lastArg") + set -l flagPrefix (string match -r -- '-.*=' "$lastArg") __podman_remote_debug "Comps: $comps" __podman_remote_debug "DirectiveLine: $directiveLine" @@ -58,7 +64,7 @@ # Start fresh set --erase __podman_remote_comp_results - set results (__podman_remote_perform_completion) + set -l results (__podman_remote_perform_completion) __podman_remote_debug "Completion results: $results" if test -z "$results" @@ -67,39 +73,39 @@ return 1 end - set directive (string sub --start 2 $results[-1]) + set -l directive (string sub --start 2 $results[-1]) set --global __podman_remote_comp_results $results[1..-2] __podman_remote_debug "Completions are: $__podman_remote_comp_results" __podman_remote_debug "Directive is: $directive" - set shellCompDirectiveError 1 - set shellCompDirectiveNoSpace 2 - set shellCompDirectiveNoFileComp 4 - set shellCompDirectiveFilterFileExt 8 - set shellCompDirectiveFilterDirs 16 + set -l shellCompDirectiveError 1 + set -l shellCompDirectiveNoSpace 2 + set -l shellCompDirectiveNoFileComp 4 + set -l shellCompDirectiveFilterFileExt 8 + set -l shellCompDirectiveFilterDirs 16 if test -z "$directive" set directive 0 end - set compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) + set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) if test $compErr -eq 1 __podman_remote_debug "Received error directive: aborting." # Might as well do file completion, in case it helps return 1 end - set filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) - set dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) + set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) + set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) if test $filefilter -eq 1; or test $dirfilter -eq 1 __podman_remote_debug "File extension filtering or directory filtering not supported" # Do full file completion instead return 1 end - set nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) - set nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) + set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) + set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) __podman_remote_debug "nospace: $nospace, nofiles: $nofiles" @@ -109,34 +115,34 @@ # may not already be filtered so as to allow fish to match on different # criteria than the prefix. if test $nospace -ne 0; or test $nofiles -eq 0 - set prefix (commandline -t) + set -l prefix (commandline -t | string escape --style=regex) __podman_remote_debug "prefix: $prefix" - set completions - for comp in $__podman_remote_comp_results - if test (string match -e -r -- "^$prefix" "$comp") - set -a completions $comp - end - end + set -l completions (string match -r -- "^$prefix.*" $__podman_remote_comp_results) set --global __podman_remote_comp_results $completions __podman_remote_debug "Filtered completions are: $__podman_remote_comp_results" # Important not to quote the variable for count to work - set numComps (count $__podman_remote_comp_results) + set -l numComps (count $__podman_remote_comp_results) __podman_remote_debug "numComps: $numComps" if test $numComps -eq 1; and test $nospace -ne 0 - # To support the "nospace" directive we trick the shell - # by outputting an extra, longer completion. - # We must first split on \t to get rid of the descriptions because - # the extra character we add to the fake second completion must be - # before the description. We don't need descriptions anyway since - # there is only a single real completion which the shell will expand - # immediately. - __podman_remote_debug "Adding second completion to perform nospace directive" - set split (string split --max 1 \t $__podman_remote_comp_results[1]) - set --global __podman_remote_comp_results $split[1] $split[1]. - __podman_remote_debug "Completions are now: $__podman_remote_comp_results" + # We must first split on \t to get rid of the descriptions to be + # able to check what the actual completion will be. + # We don't need descriptions anyway since there is only a single + # real completion which the shell will expand immediately. + set -l split (string split --max 1 \t $__podman_remote_comp_results[1]) + + # Fish won't add a space if the completion ends with any + # of the following characters: @=/:., + set -l lastChar (string sub -s -1 -- $split) + if not string match -r -q "[@=/:.,]" -- "$lastChar" + # In other cases, to support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __podman_remote_debug "Adding second completion to perform nospace directive" + set --global __podman_remote_comp_results $split[1] $split[1]. + __podman_remote_debug "Completions are now: $__podman_remote_comp_results" + end end if test $numComps -eq 0; and test $nofiles -eq 0 @@ -152,10 +158,14 @@ # Since Fish completions are only loaded once the user triggers them, we trigger them ourselves # so we can properly delete any completions provided by another script. -# The space after the program name is essential to trigger completion for the program -# and not completion of the program name itself. -complete --do-complete "podman-remote " > /dev/null 2>&1 -# Using '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. +# Only do this if the program can be found, or else fish may print some errors; besides, +# the existing completions will only be loaded if the program can be found. +if type -q "podman-remote" + # The space after the program name is essential to trigger completion for the program + # and not completion of the program name itself. + # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. + complete --do-complete "podman-remote " > /dev/null 2>&1 +end # Remove any pre-existing completions for the program since we will be handling all of them. complete -c podman-remote -e diff -Nru libpod-3.2.1+ds1/completions/powershell/podman.ps1 libpod-3.4.4+ds1/completions/powershell/podman.ps1 --- libpod-3.2.1+ds1/completions/powershell/podman.ps1 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/powershell/podman.ps1 2021-12-26 00:45:51.000000000 +0000 @@ -123,19 +123,6 @@ $Space = "" } - if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { - __podman_debug "ShellCompDirectiveNoFileComp is called" - - if ($Values.Length -eq 0) { - # Just print an empty string here so the - # shell does not start to complete paths. - # We cannot use CompletionResult here because - # it does not accept an empty string as argument. - "" - return - } - } - if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { __podman_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" @@ -148,13 +135,26 @@ # filter the result $_.Name -like "$WordToComplete*" - # Join the flag back if we have a equal sign flag + # Join the flag back if we have an equal sign flag if ( $IsEqualFlag ) { __podman_debug "Join the equal sign flag back to the completion value" $_.Name = $Flag + "=" + $_.Name } } + if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { + __podman_debug "ShellCompDirectiveNoFileComp is called" + + if ($Values.Length -eq 0) { + # Just print an empty string here so the + # shell does not start to complete paths. + # We cannot use CompletionResult here because + # it does not accept an empty string as argument. + "" + return + } + } + # Get the current mode $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function __podman_debug "Mode: $Mode" @@ -216,7 +216,7 @@ Default { # Like MenuComplete but we don't want to add a space here because # the user need to press space anyway to get the completion. - # Description will not be shown because that's not possible with TabCompleteNext + # Description will not be shown because thats not possible with TabCompleteNext [System.Management.Automation.CompletionResult]::new($($comp.Name | __podman_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } } diff -Nru libpod-3.2.1+ds1/completions/powershell/podman-remote.ps1 libpod-3.4.4+ds1/completions/powershell/podman-remote.ps1 --- libpod-3.2.1+ds1/completions/powershell/podman-remote.ps1 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/powershell/podman-remote.ps1 2021-12-26 00:45:51.000000000 +0000 @@ -123,19 +123,6 @@ $Space = "" } - if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { - __podman-remote_debug "ShellCompDirectiveNoFileComp is called" - - if ($Values.Length -eq 0) { - # Just print an empty string here so the - # shell does not start to complete paths. - # We cannot use CompletionResult here because - # it does not accept an empty string as argument. - "" - return - } - } - if ((($Directive -band $ShellCompDirectiveFilterFileExt) -ne 0 ) -or (($Directive -band $ShellCompDirectiveFilterDirs) -ne 0 )) { __podman-remote_debug "ShellCompDirectiveFilterFileExt ShellCompDirectiveFilterDirs are not supported" @@ -148,13 +135,26 @@ # filter the result $_.Name -like "$WordToComplete*" - # Join the flag back if we have a equal sign flag + # Join the flag back if we have an equal sign flag if ( $IsEqualFlag ) { __podman-remote_debug "Join the equal sign flag back to the completion value" $_.Name = $Flag + "=" + $_.Name } } + if (($Directive -band $ShellCompDirectiveNoFileComp) -ne 0 ) { + __podman-remote_debug "ShellCompDirectiveNoFileComp is called" + + if ($Values.Length -eq 0) { + # Just print an empty string here so the + # shell does not start to complete paths. + # We cannot use CompletionResult here because + # it does not accept an empty string as argument. + "" + return + } + } + # Get the current mode $Mode = (Get-PSReadLineKeyHandler | Where-Object {$_.Key -eq "Tab" }).Function __podman-remote_debug "Mode: $Mode" @@ -216,7 +216,7 @@ Default { # Like MenuComplete but we don't want to add a space here because # the user need to press space anyway to get the completion. - # Description will not be shown because that's not possible with TabCompleteNext + # Description will not be shown because thats not possible with TabCompleteNext [System.Management.Automation.CompletionResult]::new($($comp.Name | __podman-remote_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") } } diff -Nru libpod-3.2.1+ds1/completions/zsh/_podman libpod-3.4.4+ds1/completions/zsh/_podman --- libpod-3.2.1+ds1/completions/zsh/_podman 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/zsh/_podman 2021-12-26 00:45:51.000000000 +0000 @@ -17,8 +17,6 @@ local shellCompDirectiveNoFileComp=4 local shellCompDirectiveFilterFileExt=8 local shellCompDirectiveFilterDirs=16 - local shellCompDirectiveLegacyCustomComp=32 - local shellCompDirectiveLegacyCustomArgsComp=64 local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace local -a completions diff -Nru libpod-3.2.1+ds1/completions/zsh/_podman-remote libpod-3.4.4+ds1/completions/zsh/_podman-remote --- libpod-3.2.1+ds1/completions/zsh/_podman-remote 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/completions/zsh/_podman-remote 2021-12-26 00:45:51.000000000 +0000 @@ -17,8 +17,6 @@ local shellCompDirectiveNoFileComp=4 local shellCompDirectiveFilterFileExt=8 local shellCompDirectiveFilterDirs=16 - local shellCompDirectiveLegacyCustomComp=32 - local shellCompDirectiveLegacyCustomArgsComp=64 local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace local -a completions diff -Nru libpod-3.2.1+ds1/contrib/cirrus/lib.sh libpod-3.4.4+ds1/contrib/cirrus/lib.sh --- libpod-3.2.1+ds1/contrib/cirrus/lib.sh 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/cirrus/lib.sh 2021-12-26 00:45:51.000000000 +0000 @@ -74,7 +74,7 @@ # Defaults when not running under CI export CI="${CI:-false}" CIRRUS_CI="${CIRRUS_CI:-false}" -DEST_BRANCH="${DEST_BRANCH:-master}" +DEST_BRANCH="${DEST_BRANCH:-main}" CONTINUOUS_INTEGRATION="${CONTINUOUS_INTEGRATION:-false}" CIRRUS_REPO_NAME=${CIRRUS_REPO_NAME:-podman} # Cirrus only sets $CIRRUS_BASE_SHA properly for PRs, but $EPOCH_TEST_COMMIT @@ -143,6 +143,8 @@ local rootless_uid local rootless_gid local env_var_val + local akfilepath + local sshcmd # Only do this once; established by setup_environment.sh # shellcheck disable=SC2154 @@ -169,24 +171,25 @@ ssh-keygen -P "" -f "$HOME/.ssh/id_rsa" msg "Allowing ssh key for $ROOTLESS_USER" + akfilepath="/home/$ROOTLESS_USER/.ssh/authorized_keys" (umask 077 && mkdir "/home/$ROOTLESS_USER/.ssh") chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh" install -o $ROOTLESS_USER -g $ROOTLESS_USER -m 0600 \ - "$HOME/.ssh/id_rsa.pub" "/home/$ROOTLESS_USER/.ssh/authorized_keys" + "$HOME/.ssh/id_rsa.pub" "$akfilepath" # Makes debugging easier - cat /root/.ssh/authorized_keys >> "/home/$ROOTLESS_USER/.ssh/authorized_keys" - - msg "Configuring subuid and subgid" - grep -q "${ROOTLESS_USER}" /etc/subuid || \ - echo "${ROOTLESS_USER}:$[rootless_uid * 100]:65536" | \ - tee -a /etc/subuid >> /etc/subgid + cat /root/.ssh/authorized_keys >> "$akfilepath" msg "Ensure the ssh daemon is up and running within 5 minutes" systemctl start sshd - lilto ssh $ROOTLESS_USER@localhost \ - -o UserKnownHostsFile=/dev/null \ - -o StrictHostKeyChecking=no \ - -o CheckHostIP=no true + sshcmd="ssh $ROOTLESS_USER@localhost + -o UserKnownHostsFile=/dev/null + -o StrictHostKeyChecking=no + -o CheckHostIP=no" + lilto $sshcmd true # retry until sshd is up + + msg "Configuring rootless user self-access to ssh to localhost" + $sshcmd ssh-keygen -P '""' -f "/home/$ROOTLESS_USER/.ssh/id_rsa" + cat "/home/$ROOTLESS_USER/.ssh/id_rsa" >> "$akfilepath" } install_test_configs() { diff -Nru libpod-3.2.1+ds1/contrib/cirrus/pr-should-include-tests libpod-3.4.4+ds1/contrib/cirrus/pr-should-include-tests --- libpod-3.2.1+ds1/contrib/cirrus/pr-should-include-tests 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/cirrus/pr-should-include-tests 2021-12-26 00:45:51.000000000 +0000 @@ -8,15 +8,15 @@ exit 0 fi -# So are PRs where 'NO TESTS NEEDED' appears in the Github message -if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.TESTS.NEEDED ]]; then +# So are PRs where 'NO NEW TESTS NEEDED' appears in the Github message +if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.NEW.TESTS.NEEDED ]]; then exit 0 fi # HEAD should be good enough, but the CIRRUS envariable allows us to test head=${CIRRUS_CHANGE_IN_REPO:-HEAD} # Base of this PR. Here we absolutely rely on cirrus. -base=$(git merge-base ${DEST_BRANCH:-master} $head) +base=$(git merge-base ${DEST_BRANCH:-main} $head) # This gives us a list of files touched in all commits, e.g. # A foo.c @@ -35,7 +35,6 @@ fgrep -vx .cirrus.yml | fgrep -vx .gitignore | fgrep -vx Makefile | - fgrep -vx changelog.txt | fgrep -vx go.mod | fgrep -vx go.sum | egrep -v '^[^/]+\.md$' | @@ -50,9 +49,9 @@ exit 0 fi -# One last chance: perhaps the developer included the magic '[NO TESTS NEEDED]' +# One last chance: perhaps the developer included the magic '[NO NEW TESTS NEEDED]' # string in an amended commit. -if git log --format=%B ${base}..${head} | fgrep '[NO TESTS NEEDED]'; then +if git log --format=%B ${base}..${head} | fgrep '[NO NEW TESTS NEEDED]'; then exit 0 fi @@ -68,7 +67,7 @@ Every second counts in CI. If your commit really, truly does not need tests, you can proceed -by adding '[NO TESTS NEEDED]' to the body of your commit message. +by adding '[NO NEW TESTS NEEDED]' to the body of your commit message. Please think carefully before doing so. EOF diff -Nru libpod-3.2.1+ds1/contrib/cirrus/pr-should-include-tests.t libpod-3.4.4+ds1/contrib/cirrus/pr-should-include-tests.t --- libpod-3.2.1+ds1/contrib/cirrus/pr-should-include-tests.t 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/cirrus/pr-should-include-tests.t 2021-12-26 00:45:51.000000000 +0000 @@ -36,9 +36,9 @@ 0 a47515008 ecedda63a PR 8816, unit tests only 0 caa84cd35 e55320efd PR 8565, hack/podman-socat only 0 c342583da 12f835d12 PR 8523, version.go + podman.spec.in -0 c342583da db1d2ff11 version bump to v2.2.0 0 8f75ed958 7b3ad6d89 PR 8835, only a README.md change 0 b6db60e58 f06dd45e0 PR 9420, a test rename +0 c6a896b0c 4ea5d6971 PR 11833, includes magic string " # The script we're testing diff -Nru libpod-3.2.1+ds1/contrib/cirrus/runner.sh libpod-3.4.4+ds1/contrib/cirrus/runner.sh --- libpod-3.2.1+ds1/contrib/cirrus/runner.sh 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/cirrus/runner.sh 2021-12-26 00:45:51.000000000 +0000 @@ -88,7 +88,8 @@ } function _run_docker-py() { - msg "This is docker-py stub, it is only a stub" + source venv/bin/activate + make run-docker-py-tests } function _run_endpoint() { @@ -145,9 +146,9 @@ elif [[ -n "$CIRRUS_TAG" ]]; then upload_bucket="libpod-master-releases" upload_filename="swagger-$CIRRUS_TAG.yaml" - elif [[ "$CIRRUS_BRANCH" == "master" ]]; then + elif [[ "$CIRRUS_BRANCH" == "main" ]]; then upload_bucket="libpod-master-releases" - # readthedocs versioning uses "latest" for "master" (default) branch + # readthedocs versioning uses "latest" for "main" (default) branch upload_filename="swagger-latest.yaml" elif [[ -n "$CIRRUS_BRANCH" ]]; then upload_bucket="libpod-master-releases" @@ -173,7 +174,7 @@ trap "rm -f $envvarsfile" EXIT # contains secrets # Warning: These values must _not_ be quoted, podman will not remove them. #shellcheck disable=SC2154 - cat <>$envvarsfile + cat <>$envvarsfile GCPJSON=$GCPJSON GCPNAME=$GCPNAME GCPPROJECT=$GCPPROJECT @@ -196,6 +197,8 @@ SUGGESTION="run 'make vendor' and commit all changes" ./hack/tree_status.sh make generate-bindings SUGGESTION="run 'make generate-bindings' and commit all changes" ./hack/tree_status.sh + make completions + SUGGESTION="run 'make completions' and commit all changes" ./hack/tree_status.sh } function _run_build() { @@ -334,6 +337,11 @@ # shellcheck disable=SC2154 if [[ "$PRIV_NAME" == "rootless" ]] && [[ "$UID" -eq 0 ]]; then + # Remove /var/lib/cni, it is not required for rootless cni. + # We have to test that it works without this directory. + # https://github.com/containers/podman/issues/10857 + rm -rf /var/lib/cni + req_env_vars ROOTLESS_USER msg "Re-executing runner through ssh as user '$ROOTLESS_USER'" msg "************************************************************" diff -Nru libpod-3.2.1+ds1/contrib/cirrus/setup_environment.sh libpod-3.4.4+ds1/contrib/cirrus/setup_environment.sh --- libpod-3.2.1+ds1/contrib/cirrus/setup_environment.sh 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/cirrus/setup_environment.sh 2021-12-26 00:45:51.000000000 +0000 @@ -77,6 +77,13 @@ else echo "OCI_RUNTIME=runc" >> /etc/ci_environment fi + + # As a general policy CGv1 + runc should coincide with the "older" + # VM Images in CI. Verify this is the case. + if [[ -n "$VM_IMAGE_NAME" ]] && [[ ! "$VM_IMAGE_NAME" =~ prior ]] + then + die "Most recent distro. version should never run with CGv1" + fi fi ;; cgroup2fs) @@ -85,6 +92,13 @@ # which uses runc as the default. warn "Forcing testing with crun instead of runc" echo "OCI_RUNTIME=crun" >> /etc/ci_environment + + # As a general policy CGv2 + crun should coincide with the "newer" + # VM Images in CI. Verify this is the case. + if [[ -n "$VM_IMAGE_NAME" ]] && [[ "$VM_IMAGE_NAME" =~ prior ]] + then + die "Least recent distro. version should never run with CGv2" + fi fi ;; *) die_unknown CG_FS_TYPE @@ -191,12 +205,24 @@ bigto dnf install -y glibc-minimal-langpack rpm-build fi ;& - docker-py) ;& + docker-py) + remove_packaged_podman_files + make install PREFIX=/usr ETCDIR=/etc + + # TODO: Don't install stuff at test runtime! Do this from + # cache_images/fedora_packaging.sh in containers/automation_images + # and STRONGLY prefer installing RPMs vs pip packages in venv + dnf install -y python3-virtualenv python3-pytest4 + virtualenv venv + source venv/bin/activate + pip install --upgrade pip + pip install --requirement $GOSRC/test/python/requirements.txt + ;; build) make clean ;; unit) ;; apiv2) ;& # use next item compose) - dnf install -y $PACKAGE_DOWNLOAD_DIR/podman-docker* + rpm -ivh $PACKAGE_DOWNLOAD_DIR/podman-docker* ;& # continue with next item int) ;& sys) ;& diff -Nru libpod-3.2.1+ds1/contrib/fedora-minimal/Dockerfile libpod-3.4.4+ds1/contrib/fedora-minimal/Dockerfile --- libpod-3.2.1+ds1/contrib/fedora-minimal/Dockerfile 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/fedora-minimal/Dockerfile 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -FROM registry.fedoraproject.org/fedora-minimal:latest diff -Nru libpod-3.2.1+ds1/contrib/fedora-minimal/README.md libpod-3.4.4+ds1/contrib/fedora-minimal/README.md --- libpod-3.2.1+ds1/contrib/fedora-minimal/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/fedora-minimal/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -This dockerfile exists so that the container image can be "mirrored" -onto quay.io automatically, so automated testing can be more resilient. - -https://quay.io/repository/libpod/fedora-minimal?tab=builds diff -Nru libpod-3.2.1+ds1/contrib/msi/podman.wxs libpod-3.4.4+ds1/contrib/msi/podman.wxs --- libpod-3.2.1+ds1/contrib/msi/podman.wxs 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/msi/podman.wxs 2021-12-26 00:45:51.000000000 +0000 @@ -32,7 +32,8 @@ - + + diff -Nru libpod-3.2.1+ds1/contrib/podmanimage/README.md libpod-3.4.4+ds1/contrib/podmanimage/README.md --- libpod-3.2.1+ds1/contrib/podmanimage/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/podmanimage/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -17,10 +17,10 @@ The container images are: * `quay.io/containers/podman:` and `quay.io/podman/stable:` - - These images are built when a new Podman version becomes available in - Fedora. These images are intended to be unchanging and stable, they will - never be updated by automation once they've been pushed. For build details, - please [see the configuration file](stable/Dockerfile). + These images are built daily. They are intended to contain an unchanging + and stable version of podman. Though for the most recent `` tag, + image contents will be updated to incorporate (especially) security upgrades. + For build details, please [see the configuration file](stable/Dockerfile). * `quay.io/containers/podman:latest` and `quay.io/podman/stable:latest` - Built daily using the same Dockerfile as above. The Podman version will remain the "latest" available in Fedora, however the other image diff -Nru libpod-3.2.1+ds1/contrib/podmanimage/stable/Dockerfile libpod-3.4.4+ds1/contrib/podmanimage/stable/Dockerfile --- libpod-3.2.1+ds1/contrib/podmanimage/stable/Dockerfile 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/podmanimage/stable/Dockerfile 2021-12-26 00:45:51.000000000 +0000 @@ -11,7 +11,7 @@ # Don't include container-selinux and remove # directories used by yum that are just taking # up space. -RUN dnf -y update; yum -y reinstall shadow-utils; \ +RUN dnf -y update; rpm --restore shadow-utils 2>/dev/null; \ yum -y install podman fuse-overlayfs --exclude container-selinux; \ rm -rf /var/cache /var/log/dnf* /var/log/yum.* @@ -19,13 +19,15 @@ echo podman:10000:5000 > /etc/subuid; \ echo podman:10000:5000 > /etc/subgid; -VOLUME /var/lib/containers -VOLUME /home/podman/.local/share/containers - ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf -RUN chown podman:podman -R /home/podman +RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman + +# Note VOLUME options must always happen after the chown call above +# RUN commands can not modify existing volumes +VOLUME /var/lib/containers +VOLUME /home/podman/.local/share/containers # chmod containers.conf and adjust storage.conf to enable Fuse storage. RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf diff -Nru libpod-3.2.1+ds1/contrib/podmanimage/testing/Dockerfile libpod-3.4.4+ds1/contrib/podmanimage/testing/Dockerfile --- libpod-3.2.1+ds1/contrib/podmanimage/testing/Dockerfile 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/podmanimage/testing/Dockerfile 2021-12-26 00:45:51.000000000 +0000 @@ -13,19 +13,21 @@ # Don't include container-selinux and remove # directories used by yum that are just taking # up space. -RUN yum -y update; yum -y reinstall shadow-utils; yum -y install podman fuse-overlayfs --exclude container-selinux --enablerepo updates-testing; rm -rf /var/cache /var/log/dnf* /var/log/yum.* +RUN yum -y update; rpm --restore shadow-utils 2>/dev/null; yum -y install podman fuse-overlayfs --exclude container-selinux --enablerepo updates-testing; rm -rf /var/cache /var/log/dnf* /var/log/yum.* RUN useradd podman; \ echo podman:10000:5000 > /etc/subuid; \ echo podman:10000:5000 > /etc/subgid; -VOLUME /var/lib/containers -VOLUME /home/podman/.local/share/containers - ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf -RUN chown podman:podman -R /home/podman +RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman + +# Note VOLUME options must always happen after the chown call above +# RUN commands can not modify existing volumes +VOLUME /var/lib/containers +VOLUME /home/podman/.local/share/containers # chmod containers.conf and adjust storage.conf to enable Fuse storage. RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf diff -Nru libpod-3.2.1+ds1/contrib/podmanimage/upstream/Dockerfile libpod-3.4.4+ds1/contrib/podmanimage/upstream/Dockerfile --- libpod-3.2.1+ds1/contrib/podmanimage/upstream/Dockerfile 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/podmanimage/upstream/Dockerfile 2021-12-26 00:45:51.000000000 +0000 @@ -17,7 +17,7 @@ # to the container. # Finally remove the podman directory and a few other packages # that are needed for building but not running Podman -RUN yum -y update; yum -y reinstall shadow-utils; yum -y install --exclude container-selinux \ +RUN yum -y update; rpm --restore shadow-utils 2>/dev/null; yum -y install --exclude container-selinux \ --enablerepo=updates-testing \ btrfs-progs-devel \ containernetworking-cni \ @@ -40,7 +40,8 @@ crun \ fuse-overlayfs \ fuse3 \ - containers-common; \ + containers-common \ + podman-plugins; \ mkdir /root/podman; \ git clone https://github.com/containers/podman /root/podman/src/github.com/containers/podman; \ cd /root/podman/src/github.com/containers/podman; \ @@ -67,13 +68,15 @@ echo podman:10000:5000 > /etc/subuid; \ echo podman:10000:5000 > /etc/subgid; -VOLUME /var/lib/containers -VOLUME /home/podman/.local/share/containers - ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/containers.conf /etc/containers/containers.conf ADD https://raw.githubusercontent.com/containers/libpod/master/contrib/podmanimage/stable/podman-containers.conf /home/podman/.config/containers/containers.conf -RUN chown podman:podman -R /home/podman +RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman + +# Note VOLUME options must always happen after the chown call above +# RUN commands can not modify existing volumes +VOLUME /var/lib/containers +VOLUME /home/podman/.local/share/containers # chmod containers.conf and adjust storage.conf to enable Fuse storage. RUN chmod 644 /etc/containers/containers.conf; sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' -e 's|^mountopt[[:space:]]*=.*$|mountopt = "nodev,fsync=0"|g' /etc/containers/storage.conf diff -Nru libpod-3.2.1+ds1/contrib/spec/podman.spec.in libpod-3.4.4+ds1/contrib/spec/podman.spec.in --- libpod-3.2.1+ds1/contrib/spec/podman.spec.in 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/spec/podman.spec.in 2021-12-26 00:45:51.000000000 +0000 @@ -36,7 +36,7 @@ %else Epoch: 0 %endif -Version: 3.2.1 +Version: 3.4.4 Release: #COMMITDATE#.git%{shortcommit0}%{?dist} Summary: Manage Pods, Containers and Container Images License: ASL 2.0 @@ -441,7 +441,6 @@ %if %{with doc} install.man-nobuild \ %endif - install.cni \ install.systemd \ install.completions @@ -526,15 +525,16 @@ %{_datadir}/bash-completion/completions/* %{_datadir}/zsh/site-functions/* %{_datadir}/fish/vendor_completions.d/* -%config(noreplace) %{_sysconfdir}/cni/net.d/87-%{name}-bridge.conflist %{_unitdir}/podman-auto-update.service %{_unitdir}/podman-auto-update.timer %{_unitdir}/podman.service %{_unitdir}/podman.socket +%{_unitdir}/podman-restart.service %{_usr}/lib/systemd/user/podman.service %{_usr}/lib/systemd/user/podman.socket %{_usr}/lib/systemd/user/podman-auto-update.service %{_usr}/lib/systemd/user/podman-auto-update.timer +%{_usr}/lib/systemd/user/podman-restart.service %{_usr}/lib/tmpfiles.d/podman.conf %if 0%{?with_devel} diff -Nru libpod-3.2.1+ds1/contrib/systemd/auto-update/podman-auto-update.service libpod-3.4.4+ds1/contrib/systemd/auto-update/podman-auto-update.service --- libpod-3.2.1+ds1/contrib/systemd/auto-update/podman-auto-update.service 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/systemd/auto-update/podman-auto-update.service 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,7 @@ [Unit] Description=Podman auto-update service Documentation=man:podman-auto-update(1) -Wants=network.target +Wants=network-online.target After=network-online.target [Service] @@ -10,4 +10,4 @@ ExecStartPost=/usr/bin/podman image prune -f [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/contrib/systemd/auto-update/podman-auto-update.timer libpod-3.4.4+ds1/contrib/systemd/auto-update/podman-auto-update.timer --- libpod-3.2.1+ds1/contrib/systemd/auto-update/podman-auto-update.timer 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/systemd/auto-update/podman-auto-update.timer 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ [Timer] OnCalendar=daily +RandomizedDelaySec=900 Persistent=true [Install] diff -Nru libpod-3.2.1+ds1/contrib/systemd/system/podman-restart.service libpod-3.4.4+ds1/contrib/systemd/system/podman-restart.service --- libpod-3.2.1+ds1/contrib/systemd/system/podman-restart.service 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/systemd/system/podman-restart.service 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,13 @@ +[Unit] +Description=Podman Start All Containers With Restart Policy Set To Always +Documentation=man:podman-start(1) +StartLimitIntervalSec=0 + +[Service] +Type=oneshot +RemainAfterExit=true +Environment=LOGGING="--log-level=info" +ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/contrib/systemd/system/podman.service libpod-3.4.4+ds1/contrib/systemd/system/podman.service --- libpod-3.2.1+ds1/contrib/systemd/system/podman.service 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/systemd/system/podman.service 2021-12-26 00:45:51.000000000 +0000 @@ -10,3 +10,6 @@ KillMode=process Environment=LOGGING="--log-level=info" ExecStart=/usr/bin/podman $LOGGING system service + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/contrib/systemd/user/podman-restart.service libpod-3.4.4+ds1/contrib/systemd/user/podman-restart.service --- libpod-3.2.1+ds1/contrib/systemd/user/podman-restart.service 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/systemd/user/podman-restart.service 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,13 @@ +[Unit] +Description=Podman Start All Containers With Restart Policy Set To Always +Documentation=man:podman-start(1) +StartLimitIntervalSec=0 + +[Service] +Type=oneshot +RemainAfterExit=true +Environment=LOGGING="--log-level=info" +ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/contrib/systemd/user/podman.service libpod-3.4.4+ds1/contrib/systemd/user/podman.service --- libpod-3.2.1+ds1/contrib/systemd/user/podman.service 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/systemd/user/podman.service 2021-12-26 00:45:51.000000000 +0000 @@ -10,3 +10,6 @@ KillMode=process Environment=LOGGING="--log-level=info" ExecStart=/usr/bin/podman $LOGGING system service + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/contrib/tmpfile/podman.conf libpod-3.4.4+ds1/contrib/tmpfile/podman.conf --- libpod-3.2.1+ds1/contrib/tmpfile/podman.conf 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/contrib/tmpfile/podman.conf 2021-12-26 00:45:51.000000000 +0000 @@ -2,5 +2,6 @@ # for many days. This following line prevents systemd from removing this content. x /tmp/podman-run-* x /tmp/containers-user-* +x /tmp/run-*/libpod D! /run/podman 0700 root root D! /var/lib/cni/networks diff -Nru libpod-3.2.1+ds1/CONTRIBUTING.md libpod-3.4.4+ds1/CONTRIBUTING.md --- libpod-3.2.1+ds1/CONTRIBUTING.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/CONTRIBUTING.md 2021-12-26 00:45:51.000000000 +0000 @@ -126,6 +126,10 @@ For further reading about branching [you can read this document](https://herve.beraud.io/containers/linux/podman/isolate/environment/2019/02/06/how-to-hack-on-podman.html). +## Documentation + +Make sure to update the documentation if needed. You can find the man pages under `docs/source/markdown`. The syntax for the formatting of all man pages can be found [here](docs/MANPAGE_SYNTAX.md). + ## Submitting Pull Requests No Pull Request (PR) is too small! Typos, additional comments in the code, @@ -239,7 +243,7 @@ The sign-off is a line at the end of the explanation for the patch. Your signature certifies that you wrote the patch or otherwise have the right to pass it on as an open-source patch. The rules are simple: if you can certify -the below (from [developercertificate.org](http://developercertificate.org/)): +the below (from [developercertificate.org](https://developercertificate.org/)): ``` Developer Certificate of Origin @@ -393,7 +397,7 @@ ## Communications For general questions and discussion, please use the -IRC `#podman` channel on `irc.freenode.net`. +IRC `#podman` channel on `irc.libera.chat`. For discussions around issues/bugs and features, you can use the GitHub [issues](https://github.com/containers/podman/issues) diff -Nru libpod-3.2.1+ds1/debian/changelog libpod-3.4.4+ds1/debian/changelog --- libpod-3.2.1+ds1/debian/changelog 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/changelog 2022-01-23 19:04:53.000000000 +0000 @@ -1,20 +1,93 @@ -libpod (3.2.1+ds1-2ubuntu3) impish; urgency=medium - - * Rebuild against newer containers-common to fix seccomp issue, LP: #1943049 - - -- Reinhard Tartler Wed, 22 Sep 2021 14:40:40 -0400 - -libpod (3.2.1+ds1-2ubuntu2) impish; urgency=medium +libpod (3.4.4+ds1-1ubuntu1) jammy; urgency=medium * Merge from Debian unstable. Remaining changes: - docker.io package in ubuntu installs some sources into different locations, work around that in debian/rules - - don't build binary package golang-github-containers-libpod-dev, - because some docker packages are vendored differently, that - package fails in autopkgtest and cannot posibly work. - * Fixup confused merge with debian - -- Reinhard Tartler Tue, 15 Jun 2021 14:12:41 -0400 + -- Reinhard Tartler Sun, 23 Jan 2022 14:04:53 -0500 + +libpod (3.4.4+ds1-1) unstable; urgency=medium + + * New upstream release + Confirming that CVE-2021-4024 is fixed in 3.4.3, Closes: #1000844 + + -- Reinhard Tartler Sat, 25 Dec 2021 19:48:14 -0500 + +libpod (3.4.3+ds1-1) unstable; urgency=medium + + * New upstream release, Closes: #1001321 + - podman machine spawns gvproxy with port binded to all IPs, + Closes: #1000844 + + -- Reinhard Tartler Fri, 24 Dec 2021 14:40:01 -0500 + +libpod (3.4.2+ds1-1) unstable; urgency=medium + + * New upstream release + * Compile against system github.com/dtylman/scp + * Revert upstream commit that requires newer godbus than we currently + have in unstable + + -- Reinhard Tartler Wed, 17 Nov 2021 14:30:31 -0500 + +libpod (3.4.1+ds1-2) unstable; urgency=medium + + * Upload to unstable + * podman: Install tmpfiles.d/podman.conf, Closes: #995586 + + -- Reinhard Tartler Tue, 26 Oct 2021 18:20:56 -0400 + +libpod (3.4.1+ds1-1) experimental; urgency=medium + + * New upstream release + + -- Reinhard Tartler Mon, 25 Oct 2021 13:58:58 -0400 + +libpod (3.4.0+ds1-1) experimental; urgency=medium + + * New upstream release + + -- Reinhard Tartler Fri, 08 Oct 2021 16:19:40 -0400 + +libpod (3.3.1+ds2-1) unstable; urgency=medium + + * New upstream release + - drop coreos/{go-iptables,go-systemd} + * Drop unneeded dependency on golang-github-openshift-api-dev + * Bump Standards-Version, no changes needed + + -- Reinhard Tartler Tue, 07 Sep 2021 11:53:40 +0200 + +libpod (3.3.0+ds2-2) unstable; urgency=medium + + * Apply missing patches that were forgotten in the last merge + from experimental: + - Prefer crun over runc + - Add depends in iptables + * Upstream improved documentation on requirements for rootless, + Closes: #983395 + + -- Reinhard Tartler Sat, 04 Sep 2021 18:57:31 +0200 + +libpod (3.3.0+ds2-1) unstable; urgency=medium + + * New upstream release, Closes: #992138, #992142 + + -- Reinhard Tartler Mon, 30 Aug 2021 12:37:34 +0200 + +libpod (3.2.3+ds1-1) experimental; urgency=medium + + * New upstream releases, Closes: #991197 + * Bump dependency on golang-github-containers-common,buildah + + -- Reinhard Tartler Tue, 20 Jul 2021 18:22:50 -0400 + +libpod (3.2.2+ds1-1) experimental; urgency=medium + + * New upstream releases, Closes: #990333 + * Bump dependency on golang-github-containers-common + + -- Reinhard Tartler Mon, 28 Jun 2021 08:15:51 -0400 libpod (3.2.1+ds1-2) experimental; urgency=medium @@ -31,18 +104,6 @@ -- Reinhard Tartler Mon, 14 Jun 2021 14:08:01 -0400 -libpod (3.2.0+ds5-2ubuntu1) impish; urgency=medium - - * Merge from Debian unstable. Remaining changes: - - docker.io package in ubuntu installs some sources into different - locations, work around that in debian/rules - - don't build binary package golang-github-containers-libpod-dev, - because some docker packages are vendored differently, that - package fails in autopkgtest and cannot posibly work. - * Tighten build-dendency on buildah - - -- Reinhard Tartler Sat, 12 Jun 2021 17:13:55 -0400 - libpod (3.2.0+ds5-2) experimental; urgency=medium * Add patch from upstream to fix FTBFS on !(arm64, amd64) @@ -57,17 +118,6 @@ -- Reinhard Tartler Tue, 08 Jun 2021 17:33:43 -0400 -libpod (3.1.2+ds1-2ubuntu1) impish; urgency=medium - - * Merge from Debian unstable. Remaining changes: - - docker.io package in ubuntu installs some sources into different - locations, work around that in debian/rules - - don't build binary package golang-github-containers-libpod-dev, - because some docker packages are vendored differently, that - package fails in autopkgtest and cannot posibly work. - - -- Reinhard Tartler Sun, 30 May 2021 18:16:23 -0400 - libpod (3.1.2+ds1-2) experimental; urgency=medium * add missing vendored files included in tarball @@ -97,13 +147,19 @@ -- Reinhard Tartler Sat, 03 Apr 2021 16:28:54 -0400 -libpod (3.0.1+dfsg1-1ubuntu1) hirsute; urgency=medium +libpod (3.0.1+dfsg1-3) unstable; urgency=medium - * Merge from Debian unstable. Remaining changes: - - docker.io package in ubuntu installs some sources into different - locations, work around that in debian/rules + * Add networking-lookup-child-IP-in-networks.patch, fixes rootless + connection issue "Connection reset by peer", Closes: #989803 + + -- Reinhard Tartler Sun, 13 Jun 2021 18:28:49 -0400 + +libpod (3.0.1+dfsg1-2) unstable; urgency=medium - -- Reinhard Tartler Fri, 26 Feb 2021 17:11:36 -0500 + * Prefer crun over runc, Closes: #985379 + * Add depends in iptables, Closes: #987207 + + -- Reinhard Tartler Wed, 21 Apr 2021 17:36:07 -0400 libpod (3.0.1+dfsg1-1) unstable; urgency=medium @@ -114,6 +170,28 @@ -- Reinhard Tartler Wed, 24 Feb 2021 06:46:17 -0500 +libpod (3.0.0+dfsg1-2ubuntu4) hirsute; urgency=medium + + * Tighten dependency on golang-golang-x-net (>> 1:0.0+git20210119) + + -- Reinhard Tartler Sun, 21 Feb 2021 08:27:28 -0500 + +libpod (3.0.0+dfsg1-2ubuntu2) hirsute; urgency=medium + + * Avoid building golang-github-containers-libpod-dev + - Because docker.io vendors golang modules differently, + this package is not usable on ubuntu anyways. + + -- Reinhard Tartler Sat, 20 Feb 2021 11:52:11 -0500 + +libpod (3.0.0+dfsg1-2ubuntu1) hirsute; urgency=medium + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + + -- Reinhard Tartler Wed, 17 Feb 2021 20:26:23 -0500 + libpod (3.0.0+dfsg1-2) unstable; urgency=medium * Adjust dependencies on containers/{storage,image,common,buildah} @@ -205,6 +283,14 @@ -- Reinhard Tartler Mon, 25 Jan 2021 07:49:44 -0500 +libpod (2.1.1+dfsg1-4ubuntu1) hirsute; urgency=medium + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + + -- Reinhard Tartler Thu, 21 Jan 2021 11:03:01 -0500 + libpod (2.1.1+dfsg1-4) unstable; urgency=medium * Ignore containers.conf sysctl when namespaces set to host @@ -248,6 +334,15 @@ -- Reinhard Tartler Fri, 27 Nov 2020 12:45:58 -0500 +libpod (2.0.6+dfsg1-2ubuntu1) hirsute; urgency=low + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + - allow building with older runc + + -- Steve Langasek Mon, 30 Nov 2020 12:57:54 -0800 + libpod (2.0.6+dfsg1-2) unstable; urgency=medium * Restored io.podman/varlink interface, which is still in use by @@ -255,6 +350,21 @@ -- Dmitry Smirnov Thu, 22 Oct 2020 21:33:07 +1100 +libpod (2.0.6+dfsg1-1ubuntu2) hirsute; urgency=medium + + * No-change rebuild using new golang + + -- Steve Langasek Wed, 11 Nov 2020 22:19:18 +0000 + +libpod (2.0.6+dfsg1-1ubuntu1) groovy; urgency=low + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + - allow building with older runc + + -- Reinhard Tartler Fri, 18 Sep 2020 13:52:59 -0400 + libpod (2.0.6+dfsg1-1) unstable; urgency=medium * New upstream release @@ -308,6 +418,14 @@ -- Reinhard Tartler Tue, 11 Aug 2020 07:41:44 -0400 +libpod (2.0.4+dfsg2-1ubuntu1) groovy; urgency=low + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + + -- Reinhard Tartler Tue, 04 Aug 2020 07:01:48 -0400 + libpod (2.0.4+dfsg2-1) unstable; urgency=medium * Vendor in protobuf 3 to workaround #961814 @@ -328,6 +446,14 @@ -- Reinhard Tartler Thu, 30 Jul 2020 07:12:41 -0400 +libpod (2.0.3+dfsg1-1ubuntu1) groovy; urgency=low + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + + -- Reinhard Tartler Wed, 29 Jul 2020 13:24:08 -0400 + libpod (2.0.3+dfsg1-1) unstable; urgency=medium * Team upload. @@ -336,6 +462,14 @@ -- Reinhard Tartler Sun, 26 Jul 2020 10:53:39 -0400 +libpod (2.0.2+dfsg1-3ubuntu1) groovy; urgency=low + + * Merge from Debian unstable. Remaining changes: + - docker.io package in ubuntu installs some sources into different + locations, work around that in debian/rules + + -- Reinhard Tartler Sat, 25 Jul 2020 10:07:52 -0400 + libpod (2.0.2+dfsg1-3) unstable; urgency=medium * Team upload. @@ -366,6 +500,13 @@ -- Shengjing Zhu Sun, 12 Jul 2020 18:51:51 +0800 +libpod (1.6.4+dfsg1-3ubuntu1) groovy; urgency=medium + + * Work around differences of github-golang-docker-docker-dev vendored + libraries in Debian and tighten build-dependency + + -- Reinhard Tartler Sat, 30 May 2020 08:59:54 -0400 + libpod (1.6.4+dfsg1-3) unstable; urgency=high * Team upload. diff -Nru libpod-3.2.1+ds1/debian/control libpod-3.4.4+ds1/debian/control --- libpod-3.2.1+ds1/debian/control 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/control 2022-01-23 19:04:53.000000000 +0000 @@ -1,8 +1,9 @@ Source: libpod Section: admin Priority: optional -Standards-Version: 4.5.0 -Maintainer: Debian Go Packaging Team +Standards-Version: 4.6.0 +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Debian Go Packaging Team Uploaders: Dmitry Smirnov , Reinhard Tartler Build-Depends: debhelper-compat (= 13) ,bash-completion @@ -14,15 +15,15 @@ ,golang-ginkgo-dev ,golang-github-appc-cni-dev (>= 0.8) ,golang-github-buger-goterm-dev - ,golang-github-checkpoint-restore-go-criu-dev + ,golang-github-checkpoint-restore-go-criu-dev (>> 5.1) ,golang-github-containerd-cgroups-dev ,golang-github-containernetworking-plugins-dev (>= 0.8.7) - ,golang-github-containers-buildah-dev (>= 1.21.0) - ,golang-github-containers-common-dev (>= 0.38.16+ds1-1ubuntu1) - ,golang-github-containers-image-dev (>= 5.10.5) + ,golang-github-containers-buildah-dev (>= 1.23.1) + ,golang-github-containers-common-dev (>= 0.44.4) + ,golang-github-containers-image-dev (>= 5.16) ,golang-github-containers-ocicrypt-dev ,golang-github-containers-psgo-dev - ,golang-github-containers-storage-dev (>= 1.28) + ,golang-github-containers-storage-dev (>= 1.36) ,golang-github-coreos-bbolt-dev (>= 1.3.3~) ,golang-github-coreos-go-iptables-dev (>= 0.4.2~) ,golang-github-coreos-go-systemd-dev (>= 20~) @@ -32,6 +33,7 @@ ,golang-github-docker-go-connections-dev (>= 0.4.0~) ,golang-github-docker-go-units-dev (>= 0.4.0~) ,golang-github-docker-spdystream-dev + ,golang-github-dtylman-scp-dev ,golang-github-fullsailor-pkcs7-dev ,golang-github-ghodss-yaml-dev ,golang-github-go-logr-logr-dev @@ -44,11 +46,9 @@ ,golang-github-mrunalp-fileutils-dev ,golang-github-opencontainers-go-digest-dev ,golang-github-opencontainers-image-spec-dev - ,golang-github-opencontainers-runc-dev (>= 1.0.0~rc92~) + ,golang-github-opencontainers-runc-dev ,golang-github-opencontainers-runtime-tools-dev (>= 0.9.0~) ,golang-github-opencontainers-selinux-dev (>= 1.2.2~) - ,golang-github-opencontainers-specs-dev (>= 1.0.2.41.g7413a7f-1ubuntu1) - ,golang-github-openshift-api-dev ,golang-github-openshift-imagebuilder-dev ,golang-github-pkg-errors-dev ,golang-github-pkg-profile-dev @@ -76,10 +76,12 @@ ,golang-gopkg-inf.v0-dev ,golang-gopkg-square-go-jose.v2-dev ,golang-gopkg-yaml.v3-dev + ,golang-k8s-klog-dev ,golang-k8s-sigs-structured-merge-diff-dev (>> 4) ,golang-k8s-sigs-yaml-dev ,golang-toml-dev ,libapparmor-dev + ,libseccomp-dev ,libbtrfs-dev ,libdevmapper-dev ,libglib2.0-dev @@ -99,7 +101,7 @@ ,conmon (>= 2.0.18~) ,containernetworking-plugins (>= 0.8.7) ,golang-github-containers-common - ,runc (>= 1.0.0~rc92~) | crun + ,crun | runc (>= 1.0.0~rc92~) Breaks: buildah (<< 1.10.1-6), slirp4netns (<< 0.4.1), fuse-overlayfs (<< 0.7.1) Recommends: ${misc:Recommends} ,buildah (>= 1.21.0) @@ -111,6 +113,7 @@ Suggests: ${misc:Suggests} ,containers-storage ,docker-compose + ,iptables Description: engine to run OCI-based containers in Pods Podman is an engine for running OCI-based containers in Pods. Podman provides a CLI interface for managing Pods, Containers, and @@ -130,8 +133,96 @@ . Podman is a daemon-less alternative to Docker. +# Package: golang-github-containers-libpod-dev +# Architecture: all +# Depends: ${misc:Depends}, +# ,golang-dbus-dev (>= 5.0.2~) +# ,golang-ginkgo-dev +# ,golang-github-appc-cni-dev (>= 0.8) +# ,golang-github-buger-goterm-dev +# ,golang-github-checkpoint-restore-go-criu-dev +# ,golang-github-containerd-cgroups-dev +# ,golang-github-containernetworking-plugins-dev (>= 0.8.7) +# ,golang-github-containers-buildah-dev (>= 1.23) +# ,golang-github-containers-common-dev (>= 0.44.4) +# ,golang-github-containers-image-dev (>= 5.16) +# ,golang-github-containers-ocicrypt-dev +# ,golang-github-containers-psgo-dev +# ,golang-github-containers-storage-dev (>= 1.36) +# ,golang-github-coreos-bbolt-dev (>= 1.3.3~) +# ,golang-github-coreos-go-iptables-dev (>= 0.4.2~) +# ,golang-github-coreos-go-systemd-dev (>= 20~) +# ,golang-github-cyphar-filepath-securejoin-dev (>= 0.2.2~) +# ,golang-github-docker-distribution-dev (>= 2.7.1~) +# ,golang-github-docker-docker-dev (>= 20.10.0~) +# ,golang-github-docker-go-connections-dev (>= 0.4.0~) +# ,golang-github-docker-go-units-dev (>= 0.4.0~) +# ,golang-github-docker-spdystream-dev +# ,golang-github-dtylman-scp-dev +# ,golang-github-fullsailor-pkcs7-dev +# ,golang-github-ghodss-yaml-dev +# ,golang-github-google-shlex-dev +# ,golang-github-google-uuid-dev +# ,golang-github-hashicorp-go-multierror-dev +# ,golang-github-influxdata-tail-dev (>= 1.0.0+git20180327.c434825-2~) | golang-github-hpcloud-tail-dev +# ,golang-github-json-iterator-go-dev +# ,golang-github-mrunalp-fileutils-dev +# ,golang-github-opencontainers-go-digest-dev +# ,golang-github-opencontainers-image-spec-dev +# ,golang-github-opencontainers-runc-dev (>= 1.0.0~rc92~) +# ,golang-github-opencontainers-runtime-tools-dev (>= 0.9.0~) +# ,golang-github-opencontainers-selinux-dev (>= 1.2.2~) +# ,golang-github-openshift-imagebuilder-dev +# ,golang-github-pkg-errors-dev +# ,golang-github-pkg-profile-dev +# ,golang-github-rootless-containers-rootlesskit-dev +# ,golang-github-seccomp-libseccomp-golang-dev +# ,golang-github-sirupsen-logrus-dev +# ,golang-github-stretchr-testify-dev +# ,golang-github-uber-go-atomic-dev +# ,golang-github-ulikunitz-xz-dev +# ,golang-github-vbatts-tar-split-dev +# ,golang-github-vishvananda-netlink-dev (>= 1.0.0+git20181030~) +# ,golang-github-vividcortex-ewma-dev +# ,golang-go-zfs-dev +# ,golang-go.opencensus-dev +# ,golang-golang-x-crypto-dev +# ,golang-golang-x-sys-dev +# ,golang-golang-x-text-dev +# ,golang-golang-x-xerrors-dev +# ,golang-gomega-dev +# ,golang-google-genproto-dev +# ,golang-google-grpc-dev +# ,golang-gopkg-inf.v0-dev +# ,golang-gopkg-square-go-jose.v2-dev +# ,golang-gopkg-yaml.v3-dev +# ,golang-k8s-sigs-structured-merge-diff-dev +# ,golang-toml-dev +# Description: engine to run OCI-based containers in Pods (library) +# Podman is an engine for running OCI-based containers in Pods. +# Podman provides a CLI interface for managing Pods, Containers, and +# Container Images. +# . +# At a high level, the scope of libpod and podman is the following: +# * Support multiple image formats including the OCI and Docker image +# formats. +# * Support for multiple means to download images including trust & image +# verification. +# * Container image management (managing image layers, overlay filesystems, +# etc). +# * Full management of container lifecycle. +# * Support for pods to manage groups of containers together. +# * Resource isolation of containers and pods. +# * Support for a Docker-compatible CLI interface through Podman. +# . +# Podman is a daemon-less alternative to Docker. +# . +# This package contains golang sources that other packages may require for +# building. + Package: podman-docker Architecture: any +Built-Using: ${misc:Built-Using} Depends: ${misc:Depends}, ${shlibs:Depends}, podman Conflicts: docker.io Recommends: docker-compose diff -Nru libpod-3.2.1+ds1/debian/copyright libpod-3.4.4+ds1/debian/copyright --- libpod-3.2.1+ds1/debian/copyright 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/copyright 2022-01-23 19:04:53.000000000 +0000 @@ -10,10 +10,10 @@ Files-Excluded: vendor Files-Included: - vendor/github.com/checkpoint-restore + vendor/github.com/checkpoint-restore/checkpointctl vendor/github.com/container-orchestrated-devices vendor/github.com/containers/conmon - vendor/github.com/coreos + vendor/github.com/coreos/stream-metadata-go vendor/github.com/cri-o/ocicni vendor/github.com/digitalocean vendor/github.com/docker/go-plugins-helpers @@ -22,7 +22,6 @@ vendor/github.com/uber/jaeger-client-go vendor/k8s.io/api vendor/k8s.io/apimachinery - vendor/k8s.io/klog Files: * Copyright: @@ -45,6 +44,16 @@ 2018-2019 Dmitry Smirnov License: Apache-2.0 +Files: docs/source/markdown/podman-image-inspect.1.md +Copyright: + 2016-2019 Red Hat, Inc. +License: Expat + +Files: libpod/network/cni/cni_exec.go +Copyright: 2021, Podman authors + 2016, CNI authors +License: Apache-2.0 + Files: pkg/cgroups/systemd.go Copyright: The containerd Authors. License: Apache-2.0 @@ -66,6 +75,32 @@ Copyright: 2015, Fatih Arslan License: Expat +Files: vendor/github.com/digitalocean/go-libvirt/const.gen.go + vendor/github.com/digitalocean/go-libvirt/doc.go + vendor/github.com/digitalocean/go-libvirt/libvirt.go + vendor/github.com/digitalocean/go-libvirt/qemu_protocol.gen.go + vendor/github.com/digitalocean/go-libvirt/remote_protocol.gen.go + vendor/github.com/digitalocean/go-libvirt/rpc.go + vendor/github.com/digitalocean/go-libvirt/units.go +Copyright: 2016, 2018, 2020, The go-libvirt Authors. +License: Apache-2.0 + +Files: vendor/github.com/digitalocean/go-libvirt/internal/* +Copyright: 2016, 2018, 2020, The go-libvirt Authors. +License: Apache-2.0 + +Files: vendor/github.com/digitalocean/go-libvirt/internal/go-xdr/* +Copyright: 2012-2014, Dave Collins +License: ISC + +Files: vendor/github.com/digitalocean/go-qemu/qmp/qmp.go + vendor/github.com/digitalocean/go-qemu/qmp/rpc.go + vendor/github.com/digitalocean/go-qemu/qmp/socket.go + vendor/github.com/digitalocean/go-qemu/qmp/socket_unix.go + vendor/github.com/digitalocean/go-qemu/qmp/socket_windows.go +Copyright: 2016, The go-qemu Authors. +License: Apache-2.0 + Files: vendor/github.com/docker/go-plugins-helpers/NOTICE Copyright: 2012-2015, Docker, Inc. License: Expat @@ -78,15 +113,6 @@ Copyright: 2012, Rodrigo Moraes License: BSD-3-clause -Files: vendor/github.com/nxadm/* -Copyright: 2015, Hewlett Packard Enterprise Development LP - 2013, 2014, ActiveState -License: Expat - -Files: vendor/github.com/nxadm/tail/ratelimiter/* -Copyright: 2013, 99designs -License: Expat - Files: vendor/github.com/uber/* Copyright: 2017,2018 Uber Technologies, Inc. License: Apache-2.0 @@ -167,16 +193,6 @@ Copyright: The Kubernetes Authors. License: Apache-2.0 -Files: vendor/k8s.io/klog/* -Copyright: - 2013 Google Inc -License: Apache-2.0 - -Files: vendor/k8s.io/klog/v2/klog.go - vendor/k8s.io/klog/v2/klog_file.go -Copyright: 2013, Google Inc. -License: Apache-2.0 - License: Apache-2.0 Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -273,3 +289,16 @@ COPYRIGHT HOLDERS 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. + +License: ISC + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff -Nru libpod-3.2.1+ds1/debian/etc/cni/net.d/87-podman-ptp.conflist libpod-3.4.4+ds1/debian/etc/cni/net.d/87-podman-ptp.conflist --- libpod-3.2.1+ds1/debian/etc/cni/net.d/87-podman-ptp.conflist 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/etc/cni/net.d/87-podman-ptp.conflist 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -{ - "cniVersion": "0.4.0", - "name": "podman", - "plugins": [ - { - "type": "ptp", - "Documentation": "/usr/share/doc/containernetworking-plugins/main_ptp.md", - "ipMasq": true, - "ipam": { - "type": "host-local", - "Documentation": "/usr/share/doc/containernetworking-plugins/ipam_host-local.md", - "subnet": "172.16.16.0/24", - "routes": [ - { "dst": "0.0.0.0/0" } - ] - } - }, - - { - "type": "portmap", - "Documentation": "/usr/share/doc/containernetworking-plugins/meta_portmap.md", - "capabilities": { "portMappings": true } - }, - - { - "type": "firewall", - "Documentation": "/usr/share/doc/containernetworking-plugins/meta_firewall.md", - "backend": "iptables" - } - ] -} diff -Nru libpod-3.2.1+ds1/debian/fill.copyright.blanks.yml libpod-3.4.4+ds1/debian/fill.copyright.blanks.yml --- libpod-3.2.1+ds1/debian/fill.copyright.blanks.yml 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/fill.copyright.blanks.yml 2022-01-23 19:04:53.000000000 +0000 @@ -1,7 +1,4 @@ --- -vendor/github.com/nxadm/: - skip: 1 - vendor/github.com/uber/jaeger-client-go/: license: Apache-2.0 'override-copyright': 2017,2018 Uber Technologies, Inc. diff -Nru libpod-3.2.1+ds1/debian/fix.scanned.copyright libpod-3.4.4+ds1/debian/fix.scanned.copyright --- libpod-3.2.1+ds1/debian/fix.scanned.copyright 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/fix.scanned.copyright 2022-01-23 19:04:53.000000000 +0000 @@ -9,12 +9,3 @@ ! Files:"vendor/github.com/gorilla/schema/*" Copyright="2012, Rodrigo Moraes" License short_name=BSD-3-clause - -! Files:"vendor/github.com/nxadm/*" - Copyright="2015, Hewlett Packard Enterprise Development LP -2013, 2014, ActiveState" - License short_name=Expat - -! Files:"vendor/github.com/nxadm/tail/ratelimiter/*" - Copyright="2013, 99designs" - License short_name=Expat diff -Nru libpod-3.2.1+ds1/debian/patches/mbp.patch libpod-3.4.4+ds1/debian/patches/mbp.patch --- libpod-3.2.1+ds1/debian/patches/mbp.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/debian/patches/mbp.patch 2022-01-23 19:04:53.000000000 +0000 @@ -0,0 +1,17 @@ +From: Reinhard Tartler +Description: Allow compilation with newer vbauerster/mbp +Forwarded: no + +--- a/pkg/machine/pull.go ++++ b/pkg/machine/pull.go +@@ -17,8 +17,8 @@ + "github.com/containers/image/v5/pkg/compression" + "github.com/containers/storage/pkg/archive" + "github.com/sirupsen/logrus" +- "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 -Nru libpod-3.2.1+ds1/debian/patches/old-godbus.patch libpod-3.4.4+ds1/debian/patches/old-godbus.patch --- libpod-3.2.1+ds1/debian/patches/old-godbus.patch 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/debian/patches/old-godbus.patch 2022-01-23 19:04:53.000000000 +0000 @@ -0,0 +1,26 @@ +From: Reinhard Tarter +Subject: Revert commit 0e1f67b725088c4d7425f56e260ddbb44078d11b +Original-Author: Giuseppe Scrivano +Date: Tue Oct 26 21:05:42 2021 +0200 + + cgroups: use SessionBusPrivateNoAutoStartup + + do not start up a dbus daemon if it is not already running. + + [NO NEW TESTS NEEDED] the fix is in a dependency. + + Closes: https://github.com/containers/podman/issues/9727 + + Signed-off-by: Giuseppe Scrivano + +--- a/pkg/cgroups/cgroups.go ++++ b/pkg/cgroups/cgroups.go +@@ -464,7 +464,7 @@ + // GetUserConnection returns a user connection to D-BUS + func GetUserConnection(uid int) (*systemdDbus.Conn, error) { + return systemdDbus.NewConnection(func() (*dbus.Conn, error) { +- return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup) ++ return dbusAuthConnection(uid, dbus.SessionBusPrivate) + }) + } + diff -Nru libpod-3.2.1+ds1/debian/patches/rm-containers-mounts-5.patch libpod-3.4.4+ds1/debian/patches/rm-containers-mounts-5.patch --- libpod-3.2.1+ds1/debian/patches/rm-containers-mounts-5.patch 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/patches/rm-containers-mounts-5.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -From: Reinhard Tartler -Description: Remove conflicting manpage -Bug-Debian: https://bugs.debian.org/977502 - ---- a/docs/source/markdown/containers-mounts.conf.5.md -+++ /dev/null -@@ -1,16 +0,0 @@ --% containers-mounts.conf(5) -- --## NAME --containers-mounts.conf - configuration file for default mounts in containers -- --## DESCRIPTION --The mounts.conf file specifies volume mount directories that are automatically mounted inside containers. Container processes can then use this content. Usually these directories are used for passing secrets or credentials required by the package software to access remote package repositories. Note that for security reasons, tools adhering to the mounts.conf are expected to copy the contents instead of bind mounting the paths from the host. -- --## FORMAT --The format of the mounts.conf is the volume format `/SRC:/DEST`, one mount per line. For example, a mounts.conf with the line `/usr/share/secrets:/run/secrets` would cause the contents of the `/usr/share/secrets` directory on the host to be mounted on the `/run/secrets` directory inside the container. Setting mountpoints allows containers to use the files of the host, for instance, to use the host's subscription to some enterprise Linux distribution. -- --## FILES --Some distributions may provide a `/usr/share/containers/mounts.conf` file to provide default mounts, but users can create a `/etc/containers/mounts.conf`, to specify their own special volumes to mount in the container. When Podman runs in rootless mode, the file `$HOME/.config/containers/mounts.conf` will override the default if it exists. -- --## HISTORY --Aug 2018, Originally compiled by Valentin Rothberg diff -Nru libpod-3.2.1+ds1/debian/patches/series libpod-3.4.4+ds1/debian/patches/series --- libpod-3.2.1+ds1/debian/patches/series 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/patches/series 2022-01-23 19:04:53.000000000 +0000 @@ -1,3 +1,3 @@ test--skip-TestPostDeleteHooks.patch -rm-containers-mounts-5.patch -systemd-tweaks.patch +mbp.patch +old-godbus.patch diff -Nru libpod-3.2.1+ds1/debian/patches/systemd-tweaks.patch libpod-3.4.4+ds1/debian/patches/systemd-tweaks.patch --- libpod-3.2.1+ds1/debian/patches/systemd-tweaks.patch 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/patches/systemd-tweaks.patch 2022-01-23 19:04:53.000000000 +0000 @@ -3,11 +3,3 @@ Date: Tue, 26 Jan 2021 11:46:06 +0100 Bugs-Debian: https://bugs.debian.org/981097 ---- a/contrib/systemd/auto-update/podman-auto-update.service -+++ b/contrib/systemd/auto-update/podman-auto-update.service -@@ -10,4 +10,4 @@ - ExecStartPost=/usr/bin/podman image prune -f - - [Install] --WantedBy=multi-user.target default.target -+WantedBy=multi-user.target diff -Nru libpod-3.2.1+ds1/debian/podman.bash-completion libpod-3.4.4+ds1/debian/podman.bash-completion --- libpod-3.2.1+ds1/debian/podman.bash-completion 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.bash-completion 2021-12-26 00:45:51.000000000 +0000 @@ -1,38 +1,29 @@ -# bash completion for podman -*- shell-script -*- +# bash completion V2 for podman -*- shell-script -*- __podman_debug() { - if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then + if [[ -n ${BASH_COMP_DEBUG_FILE:-} ]]; then echo "$*" >> "${BASH_COMP_DEBUG_FILE}" fi } -__podman_perform_completion() +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +__podman_init_completion() { - __podman_debug - __podman_debug "========= starting completion logic ==========" - __podman_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" - - # The user could have moved the cursor backwards on the command-line. - # We need to trigger completion from the $cword location, so we need - # to truncate the command-line ($words) up to the $cword location. - words=("${words[@]:0:$cword+1}") - __podman_debug "Truncated words[*]: ${words[*]}," - - local shellCompDirectiveError=1 - local shellCompDirectiveNoSpace=2 - local shellCompDirectiveNoFileComp=4 - local shellCompDirectiveFilterFileExt=8 - local shellCompDirectiveFilterDirs=16 - local shellCompDirectiveLegacyCustomComp=32 - local shellCompDirectiveLegacyCustomArgsComp=64 + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} - local out requestComp lastParam lastChar comp directive args flagPrefix +# This function calls the podman program to obtain the completion +# results and the directive. It fills the 'out' and 'directive' vars. +__podman_get_completion_results() { + local requestComp lastParam lastChar args # Prepare the command to request completions for the program. # Calling ${words[0]} instead of directly podman allows to handle aliases args=("${words[@]:1}") - requestComp="${words[0]} __completeNoDesc ${args[*]}" + requestComp="${words[0]} __complete ${args[*]}" lastParam=${words[$((${#words[@]}-1))]} lastChar=${lastParam:$((${#lastParam}-1)):1} @@ -42,14 +33,13 @@ # If the last parameter is complete (there is a space following it) # We add an extra empty parameter so we can indicate this to the go method. __podman_debug "Adding extra empty parameter" - requestComp="${requestComp} \"\"" + requestComp="${requestComp} ''" fi # When completing a flag with an = (e.g., podman -n=) # bash focuses on the part after the =, so we need to remove # the flag part from $cur if [[ "${cur}" == -*=* ]]; then - flagPrefix="${cur%%=*}=" cur="${cur#*=}" fi @@ -67,6 +57,14 @@ fi __podman_debug "The completion directive is: ${directive}" __podman_debug "The completions are: ${out[*]}" +} + +__podman_process_completion_results() { + local shellCompDirectiveError=1 + local shellCompDirectiveNoSpace=2 + local shellCompDirectiveNoFileComp=4 + local shellCompDirectiveFilterFileExt=8 + local shellCompDirectiveFilterDirs=16 if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then # Error code. No completion. @@ -77,12 +75,16 @@ if [[ $(type -t compopt) = "builtin" ]]; then __podman_debug "Activating no space" compopt -o nospace + else + __podman_debug "No space directive not supported in this version of bash" fi fi if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then if [[ $(type -t compopt) = "builtin" ]]; then __podman_debug "Activating no file completion" compopt +o default + else + __podman_debug "No file completion directive not supported in this version of bash" fi fi fi @@ -113,87 +115,56 @@ __podman_debug "Listing directories in ." _filedir -d fi - elif [ $((directive & shellCompDirectiveLegacyCustomComp)) -ne 0 ]; then - local cmd - __podman_debug "Legacy custom completion. Directive: $directive, cmds: ${out[*]}" - - # The following variables should get their value through the commands - # we have received as completions and are parsing below. - local last_command - local nouns - - # Execute every command received - while IFS='' read -r cmd; do - __podman_debug "About to execute: $cmd" - eval "$cmd" - done < <(printf "%s\n" "${out[@]}") - - __podman_debug "last_command: $last_command" - __podman_debug "nouns[0]: ${nouns[0]}, nouns[1]: ${nouns[1]}" - - if [ $((directive & shellCompDirectiveLegacyCustomArgsComp)) -ne 0 ]; then - # We should call the global legacy custom completion function, if it is defined - if declare -F __podman_custom_func >/dev/null; then - # Use command name qualified legacy custom func - __podman_debug "About to call: __podman_custom_func" - __podman_custom_func - elif declare -F __custom_func >/dev/null; then - # Otherwise fall back to unqualified legacy custom func for compatibility - __podman_debug "About to call: __custom_func" - __custom_func - fi - fi else - local tab - tab=$(printf '\t') - local longest=0 - # Look for the longest completion so that we can format things nicely - while IFS='' read -r comp; do - comp=${comp%%$tab*} - if ((${#comp}>longest)); then - longest=${#comp} - fi - done < <(printf "%s\n" "${out[@]}") - - local completions=() - while IFS='' read -r comp; do - if [ -z "$comp" ]; then - continue - fi - - __podman_debug "Original comp: $comp" - comp="$(__podman_format_comp_descriptions "$comp" "$longest")" - __podman_debug "Final comp: $comp" - completions+=("$comp") - done < <(printf "%s\n" "${out[@]}") - - while IFS='' read -r comp; do - # Although this script should only be used for bash - # there may be programs that still convert the bash - # script into a zsh one. To continue supporting those - # programs, we do this single adaptation for zsh - if [ -n "${ZSH_VERSION}" ]; then - # zsh completion needs --flag= prefix - COMPREPLY+=("$flagPrefix$comp") - else - COMPREPLY+=("$comp") - fi - done < <(compgen -W "${completions[*]}" -- "$cur") - - # If there is a single completion left, remove the description text - if [ ${#COMPREPLY[*]} -eq 1 ]; then - __podman_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%% *}" - __podman_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY=() - COMPREPLY+=("$comp") - fi + __podman_handle_standard_completion_case fi __podman_handle_special_char "$cur" : __podman_handle_special_char "$cur" = } +__podman_handle_standard_completion_case() { + local tab comp + tab=$(printf '\t') + + local longest=0 + # Look for the longest completion so that we can format things nicely + while IFS='' read -r comp; do + # Strip any description before checking the length + comp=${comp%%$tab*} + # Only consider the completions that match + comp=$(compgen -W "$comp" -- "$cur") + if ((${#comp}>longest)); then + longest=${#comp} + fi + done < <(printf "%s\n" "${out[@]}") + + local completions=() + while IFS='' read -r comp; do + if [ -z "$comp" ]; then + continue + fi + + __podman_debug "Original comp: $comp" + comp="$(__podman_format_comp_descriptions "$comp" "$longest")" + __podman_debug "Final comp: $comp" + completions+=("$comp") + done < <(printf "%s\n" "${out[@]}") + + while IFS='' read -r comp; do + COMPREPLY+=("$comp") + done < <(compgen -W "${completions[*]}" -- "$cur") + + # If there is a single completion left, remove the description text + if [ ${#COMPREPLY[*]} -eq 1 ]; then + __podman_debug "COMPREPLY[0]: ${COMPREPLY[0]}" + comp="${COMPREPLY[0]%% *}" + __podman_debug "Removed description from single completion, which is now: ${comp}" + COMPREPLY=() + COMPREPLY+=("$comp") + fi +} + __podman_handle_special_char() { local comp="$1" @@ -252,12 +223,31 @@ __start_podman() { - local cur prev words cword + local cur prev words cword split COMPREPLY=() - _get_comp_words_by_ref -n "=:" cur prev words cword - __podman_perform_completion + # Call _init_completion from the bash-completion package + # to prepare the arguments properly + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + __podman_init_completion -n "=:" || return + fi + + __podman_debug + __podman_debug "========= starting completion logic ==========" + __podman_debug "cur is ${cur}, words[*] is ${words[*]}, #words[@] is ${#words[@]}, cword is $cword" + + # The user could have moved the cursor backwards on the command-line. + # We need to trigger completion from the $cword location, so we need + # to truncate the command-line ($words) up to the $cword location. + words=("${words[@]:0:$cword+1}") + __podman_debug "Truncated words[*]: ${words[*]}," + + local out directive + __podman_get_completion_results + __podman_process_completion_results } if [[ $(type -t compopt) = "builtin" ]]; then diff -Nru libpod-3.2.1+ds1/debian/podman.install libpod-3.4.4+ds1/debian/podman.install --- libpod-3.2.1+ds1/debian/podman.install 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.install 2022-01-23 19:04:53.000000000 +0000 @@ -1,5 +1,7 @@ -debian/etc/cni/net.d/* /etc/cni/net.d/ completions/zsh/_podman /usr/share/zsh/vendor-completions completions/zsh/_podman-remote /usr/share/zsh/vendor-completions +cni/87-podman-bridge.conflist /etc/cni/net.d/ + +debian/etc/containers/libpod.conf /etc/containers/ usr/bin/podman diff -Nru libpod-3.2.1+ds1/debian/podman.maintscript libpod-3.4.4+ds1/debian/podman.maintscript --- libpod-3.2.1+ds1/debian/podman.maintscript 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.maintscript 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -## dpkg-maintscript-helper(1) - -rm_conffile /etc/cni/net.d/87-podman-bridge.conflist 1.6.2+dfsg-3~ diff -Nru libpod-3.2.1+ds1/debian/podman.podman-auto-update.service libpod-3.4.4+ds1/debian/podman.podman-auto-update.service --- libpod-3.2.1+ds1/debian/podman.podman-auto-update.service 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.podman-auto-update.service 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,7 @@ [Unit] Description=Podman auto-update service Documentation=man:podman-auto-update(1) -Wants=network.target +Wants=network-online.target After=network-online.target [Service] @@ -10,4 +10,4 @@ ExecStartPost=/usr/bin/podman image prune -f [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/debian/podman.podman-auto-update.timer libpod-3.4.4+ds1/debian/podman.podman-auto-update.timer --- libpod-3.2.1+ds1/debian/podman.podman-auto-update.timer 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.podman-auto-update.timer 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ [Timer] OnCalendar=daily +RandomizedDelaySec=900 Persistent=true [Install] diff -Nru libpod-3.2.1+ds1/debian/podman.podman-restart.service libpod-3.4.4+ds1/debian/podman.podman-restart.service --- libpod-3.2.1+ds1/debian/podman.podman-restart.service 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.podman-restart.service 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,13 @@ +[Unit] +Description=Podman Start All Containers With Restart Policy Set To Always +Documentation=man:podman-start(1) +StartLimitIntervalSec=0 + +[Service] +Type=oneshot +RemainAfterExit=true +Environment=LOGGING="--log-level=info" +ExecStart=/usr/bin/podman $LOGGING start --all --filter restart-policy=always + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/debian/podman.service libpod-3.4.4+ds1/debian/podman.service --- libpod-3.2.1+ds1/debian/podman.service 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.service 2021-12-26 00:45:51.000000000 +0000 @@ -10,3 +10,6 @@ KillMode=process Environment=LOGGING="--log-level=info" ExecStart=/usr/bin/podman $LOGGING system service + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/debian/podman.tmpfiles libpod-3.4.4+ds1/debian/podman.tmpfiles --- libpod-3.2.1+ds1/debian/podman.tmpfiles 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.tmpfiles 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,7 @@ +# /tmp/podman-run-* directory can contain content for Podman containers that have run +# for many days. This following line prevents systemd from removing this content. +x /tmp/podman-run-* +x /tmp/containers-user-* +x /tmp/run-*/libpod +D! /run/podman 0700 root root +D! /var/lib/cni/networks diff -Nru libpod-3.2.1+ds1/debian/podman.user.service libpod-3.4.4+ds1/debian/podman.user.service --- libpod-3.2.1+ds1/debian/podman.user.service 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/debian/podman.user.service 2021-12-26 00:45:51.000000000 +0000 @@ -10,3 +10,6 @@ KillMode=process Environment=LOGGING="--log-level=info" ExecStart=/usr/bin/podman $LOGGING system service + +[Install] +WantedBy=default.target diff -Nru libpod-3.2.1+ds1/debian/rules libpod-3.4.4+ds1/debian/rules --- libpod-3.2.1+ds1/debian/rules 2021-09-22 18:40:40.000000000 +0000 +++ libpod-3.4.4+ds1/debian/rules 2022-01-23 19:04:53.000000000 +0000 @@ -12,7 +12,7 @@ export DH_GOLANG_GO_GENERATE := 1 -export DH_GOLANG_INSTALL_EXTRA := $(wildcard libpod/*/testdata/*) +export DH_GOLANG_INSTALL_EXTRA := $(wildcard libpod/*/testdata/* libpod/*/cni/*/*/*) export DH_GOLANG_EXCLUDES := dependencies test/e2e contrib/perftest test/utils test/endpoint hack/podman-registry-go pkg/bindings/test export HOME=$(CURDIR)/debian/tmp @@ -26,6 +26,7 @@ override_dh_auto_configure: # force debhelper to symlink subdirs in docker, enables workarounds below + mkdir -v -p _output/src/github.com/docker _output/src/github.com/seccomp mkdir -v -p _output/src/github.com/docker/docker/vendor/github.com/docker/go-connections/ dh_auto_configure # workaround some vendoring issues - in ubuntu all docker vendored libraries @@ -33,6 +34,9 @@ test -d _output/src/github.com/docker/libnetwork || \ ln -sv docker/vendor/github.com/docker/libnetwork \ _output/src/github.com/docker + test -d _output/src/github.com/seccomp/libseccomp-golang || \ + ln -sv ../docker/docker/vendor/github.com/seccomp/libseccomp-golang \ + _output/src/github.com/seccomp # Golang insanity # workaround "sub-vendored" package github.com/docker/go-connections/nat @@ -60,7 +64,7 @@ XDG_DATA_HOME=$(CURDIR)/debian/tmp-home/.local/share \ XDG_RUNTIME_DIR=$(CURDIR)/debian/tmp-home/run \ PATH="$(CURDIR)/_output/bin:$$PATH" \ - DH_GOLANG_EXCLUDES="$${DH_GOLANG_EXCLUDES} libpod/lock/file libpod/hack/podman-registry-go libpod/pkg/bindings/test podman/cmd/podman/common" \ + DH_GOLANG_EXCLUDES="$${DH_GOLANG_EXCLUDES} libpod/network/cni libpod/lock/file libpod/hack/podman-registry-go libpod/pkg/bindings/test podman/cmd/podman/common" \ dh_auto_test -v --max-parallel=2 -- -tags "$(BUILDTAGS)" rm -rf $(CURDIR)/debian/tmp-home endif @@ -76,3 +80,4 @@ override_dh_installsystemd: dh_installsystemd dh_installsystemd --name=podman-auto-update + dh_installsystemd --name=podman-restart diff -Nru libpod-3.2.1+ds1/docs/MANPAGE_SYNTAX.md libpod-3.4.4+ds1/docs/MANPAGE_SYNTAX.md --- libpod-3.2.1+ds1/docs/MANPAGE_SYNTAX.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/MANPAGE_SYNTAX.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,142 @@ +% podman-command(1) + +## NAME +podman\-command - short description + +## SYNOPSIS +(Shows the command structure. If the command can be written in two different ways, both of them have to be shown.\ +Many manpages include the OPTIONS **--all**, **-a** and/or **--latest**, **-l**. In this case, there is no `container name` or `ID` needed after the initial command. Because most of the other OPTIONS still need the `container name` or ` ID`, it is defined that the *container* argument in the command should **not** be put in brackets. It should also be noted in the *IMPORTANT* section in the description of the OPTION with the following sentence: *IMPORTANT: This OPTION does not need a container name or ID as input argument.*.) + +**podman command** [*optional*] *mandatory value* + +**podman subcommand command** [*optional*] *mandatory value* + +(If there is the possibility to chose between two or more mandatory command values. There should also always be a space before and after a vertical bar to ensure better readability.) + +**podman command** [*optional*] *value1* | *value2* + +**podman subcommand command** [*optional*] *value1* | *value2* + +(If an optional value follows a mandatory one.) + +**podman command** [*optional*] *value1* | *value2* [*optional*] + +**podman subcommand command** [*optional*] *value1* | *value2* [*optional*] + +(If the command accepts an infinite number of values.) + +**podman command** [*optional*] *value* [*value* ...] + +**podman subcommand command** [*optional*] *value* [*value* ...] + +## DESCRIPTION +**podman command** is always the beginning of the DESCRIPTION section. Putting the command as the first part of the DESCRIPTION ensures uniformity. All commands mentioned in the text retain their appearance and form.\ +Example for the first sentence: **podman command** is an example command. + +Commands or files that are quoted from other podman manpages or podman repositories have to be linked to those. Non-podman commands are not to be linked.\ +Example sentence: Use **[podman-run](podman-run.1.md)** or **[containers.conf(5)](https://github.com/containers/common/blob/master/docs/containers.conf.5.md)** for the problem. + +It should also be specified if the command can only be run as root. In addition, it should be described when a command, OPTION, or other content cannot be executed with the remote client or in combination with other commands, OPTIONS, or content. In this case, the following sentence is put at the end of a command, OPTION, or content:\ +*IMPORTANT: This command/OPTION/content is not available with the command/OPTION/content/on the remote Podman client.*\ +For a command, this should be done in the DESCRIPTION section. For the OPTIONS, it should be done in the DESCRIPTION of the specified OPTION. + +Do not use pronouns in the man pages, especially the word `you`. + +There should be **no** new line after H2-headers (`##`). + +## OPTIONS +All flags are referred to as OPTIONS. The term flags should not be used. All OPTIONS are listed in this section. OPTIONS that appear in descriptions of other OPTIONS and sections retain their appearance, for example: **--exit**. + +OPTIONS that are quoted from other podman manpages or podman repositories have to be linked to those.\ +Example sentence: Use **[podman-generate-systemd --new](./source/markdown/podman-generate-systemd.1.md#--new)** for the problem. + + Each OPTION should be explained to the fullest extent below the OPTION itself. Each OPTION is behind an H4-header (`####`). If the OPTION has a default argument, it has to be explained in the description of the OPTION. If the OPTION is also not available with a command/OPTION/content/ on the remote Podman client, the sentence about the default argument should the second to last sentence. The sentence about the default argument should be in a new line as well as the *IMPORTANT* sentence. + + All OPTIONS are to be sorted in alphabetical order. + + Tables should be used when there is a different definition for multiple arguments, and these have to be explained. This is shown with the OPTION **--test**.\ + Lists should be used when arguments are used that do not need a definition for each argument and a single description explains them. An example is shown with **[podman-commit --change](./source/markdown/podman-commit.1.md#--change--cinstruction)** + + +#### **--version**, **-v** + +OPTIONS can be put after the command in two different ways. Either the long version with **--option** or as the short version **-o**. If there are two ways to write an OPTION they are separated by a comma. If there are two versions of one command the long version is always shown in front. If the arguments behind the OPTION are boolean, it is not shown behind the OPTION itself. The default boolean argument is shown in the same way normal default arguments are displayed.\ +Example: The default is **false**.\ +*IMPORTANT: This OPTION is not available with the remote Podman client.* + +#### **--exit** + +An example of an OPTION that has only one possible structure. Thus, it cannot be executed by the extension **-e**. + +#### **--answer**=, **-a**=**active** | *disable* + +The **--answer** OPTION above is an example of an OPTION that accepts two possible arguments as inputs. If a default argument is selected when the OPTION is not used in the command, it is shown in **bold**. If the OPTION is used, it must include an argument afterward. It must always be ensured that the standard argument is in the first position after the OPTION. In this example, there are two different ways to execute the command. Both possible OPTIONS have to be shown with the arguments following them.\ +The default value is shown as **active**. + +#### **--status**=**good** | *better* | *best* + +This is an example of three arguments following an OPTION. If the number of arguments is greater than three, the arguments are **not** listed after the equal sign. The arguments must be shown in a table like in **--test**=**_test_**. This form should also be used if the understanding of the content is in danger of becoming incomprehensible. An example for this is **[podman-container-prune --filters](./source/markdown/podman-container-prune.1.md#--filterfilters)**.\ +The default value is shown as **good**. + +#### **--test**=**test** + +OPTIONS that are followed by an equal sign include an argument after the equal sign in **bold** or *italic*. If there is a default argument that is used if the OPTION is not specified in the command, the argument after the equal sign is displayed in **bold**. All arguments must be listed and explained in the text below the OPTION. + +| Argument | Description | +| ------------------ | --------------------------------------------------------------------------- | +| **example one** | This argument is the default argument if the OPTION is not specified. | +| *example two* | If one refers to a command, one should use **bold** marks. | +| *example three* | Example: In combination with **podman command** highly effective. | +| *example four* | Example: Can be combined with **--exit**. | +| *example five* | The fifth description | + +The table shows an example for a listing of arguments. The contents in the table should be aligned left. If the content in the table conflicts with this, it can be aligned to support the understanding of the content. If there is a default argument, it **must** be listed as the first entry in the table.\ +The default value is shown as **example one**. + +If the number of arguments is smaller than four they have to be listed behind the OPTION as seen in the OPTION **--status**. + +#### **--problem**=*problem* + +OPTIONS that are followed by an equal sign that is then followed by an unspecified argument, have no default argument. If this OPTION is written with an equal sign and the argument is left empty, there will be no error, but the OPTION will be ignored. The meaning of the argument is described preferably in `one` word after the equal sign in *italic* format. + +## SUBCHAPTER +For chapters that are made specifically as an individual SUBCHAPTER in a man page, the previous conditions regarding formatting apply. + +There are no restrictions for the use of paragraphs and tables. Within these paragraphs and tables the previous conditions regarding formatting apply. + +Strings of characters or numbers can be highlighted with `backticks`. Paths of any kind **must** be highlighted. + +IMPORTANT: Only characters that are **not** part of categories mentioned before can be highlighted. This includes headers. For example it is not advised to highlight an OPTION or a **command**. + +SUBHEADINGS are displayed as follows: +### SUBHEADING +Text for SUBHEADINGS. + +## EXAMPLES +All EXAMPLES are listed in this section. This section should be at the end of each man page. Each EXAMPLE is always in one box. The box starts and ends with the last written line, **not** with a blank line. The `$` in front of the commands indicates that it can be run as a normal user, while the commands starting with `#` can only be run as root. If there is the need for a comment in a box the comment should have `###` in front of it. + +Description of the EXAMPLE +``` +### Example comment +$ podman command +$ podman command -o +$ cat $HOME/Dockerfile | podman command --option +``` + +Description of the EXAMPLE two +``` +# podman command --status=better +``` +## SEE ALSO +All commands, including commands with OPTIONS, and config-files mentioned in the manpage have to be listed here. Podman commands, including commands with OPTIONS, and config-files have to be linked. If a command is mentioned several times with different OPTIONS it just have to be linked once. All other commands, including commands with OPTIONS, and config-files just have to be mentioned. If a command is mentioned several times with different OPTIONS it just has to be linked once. + +Example: +**[podman(1)](podman.1.md)**, **[podman-run(1)](podman-run.1.md)**, **[podman-create(1)](podman-create.1.md)** + +## HISTORY +Normally, the dates of changes, the content of the changes and the person who provided them is listed here. Most manpages don't keep this record. + +Example:\ +December 2021, Originally compiled by Alexander Richter + +`Every manpage should end with an empty line.` diff -Nru libpod-3.2.1+ds1/docs/README.md libpod-3.4.4+ds1/docs/README.md --- libpod-3.2.1+ds1/docs/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -28,6 +28,10 @@ | docs/links-to-html.lua | pandoc filter to do aliases for html files | | docs/use-pagetitle.lua | pandoc filter to set html document title | +## Manpage Syntax + +The syntax for the formatting of all man pages can be found [here](MANPAGE_SYNTAX.md). + ## API Reference The [latest online documentation](http://docs.podman.io/en/latest/_static/api.html) is diff -Nru libpod-3.2.1+ds1/docs/requirements.txt libpod-3.4.4+ds1/docs/requirements.txt --- libpod-3.2.1+ds1/docs/requirements.txt 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/requirements.txt 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,4 @@ # requirements file for readthedocs pip installs # use md instead of rst -recommonmark -# needed for markdown table support -sphinx-markdown-tables +myst_parser diff -Nru libpod-3.2.1+ds1/docs/source/Commands.rst libpod-3.4.4+ds1/docs/source/Commands.rst --- libpod-3.2.1+ds1/docs/source/Commands.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/Commands.rst 2021-12-26 00:45:51.000000000 +0000 @@ -55,7 +55,7 @@ :doc:`logs ` Fetch the logs of a container -:doc:`machine ` Manage podman's virtual machine +:doc:`machine ` Manage podman's virtual machine :doc:`manifest ` Create and manipulate manifest lists and image indexes @@ -91,7 +91,7 @@ :doc:`search ` Search registry for image -:doc:`secret ` Manage podman secrets +:doc:`secret ` Manage podman secrets :doc:`start ` Start one or more containers diff -Nru libpod-3.2.1+ds1/docs/source/conf.py libpod-3.4.4+ds1/docs/source/conf.py --- libpod-3.2.1+ds1/docs/source/conf.py 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/conf.py 2021-12-26 00:45:51.000000000 +0000 @@ -15,7 +15,6 @@ # sys.path.insert(0, os.path.abspath('.')) import re -from recommonmark.transform import AutoStructify # -- Project information ----------------------------------------------------- @@ -29,7 +28,7 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx_markdown_tables", "recommonmark"] +extensions = ["myst_parser"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -63,27 +62,18 @@ # -- Extension configuration ------------------------------------------------- +# IMPORTANT: explicitly unset the extensions, by default dollarmath is enabled. +# We use the dollar sign as text and do not want it to be interpreted as math expression. +myst_enable_extensions = [] + def convert_markdown_title(app, docname, source): # Process markdown files only docpath = app.env.doc2path(docname) if docpath.endswith(".md"): - # Convert pandoc title line into eval_rst block for recommonmark - source[0] = re.sub(r"^% (.*)", r"```eval_rst\n.. title:: \g<1>\n```", source[0]) + # Convert pandoc title line into eval_rst block for myst_parser + source[0] = re.sub(r"^% (.*)", r"```{title} \g<1>\n```", source[0]) def setup(app): app.connect("source-read", convert_markdown_title) - - app.add_config_value( - "recommonmark_config", - { - "enable_eval_rst": True, - "enable_auto_doc_ref": False, - "enable_auto_toc_tree": False, - "enable_math": False, - "enable_inline_math": False, - }, - True, - ) - app.add_transform(AutoStructify) diff -Nru libpod-3.2.1+ds1/docs/source/image.rst libpod-3.4.4+ds1/docs/source/image.rst --- libpod-3.2.1+ds1/docs/source/image.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/image.rst 2021-12-26 00:45:51.000000000 +0000 @@ -30,6 +30,8 @@ :doc:`save ` Save image to an archive +:doc:`scp ` Securely copy an image from one host to another + :doc:`search ` Search a registry for an image :doc:`sign ` Sign an image diff -Nru libpod-3.2.1+ds1/docs/source/includes.rst libpod-3.4.4+ds1/docs/source/includes.rst --- libpod-3.2.1+ds1/docs/source/includes.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/includes.rst 2021-12-26 00:45:51.000000000 +0000 @@ -16,4 +16,4 @@ .. _podman run: http://docs.podman.io/en/latest/markdown/podman-run.1.html .. _podman build: http://docs.podman.io/en/latest/markdown/podman-build.1.html .. _podman push: http://docs.podman.io/en/latest/markdown/podman-push.1.html -.. image:: https://github.com/containers/podman/blob/master/logo/podman-logo.png?raw=true +.. image:: https://raw.githubusercontent.com/containers/podman/main/logo/podman-logo.png diff -Nru libpod-3.2.1+ds1/docs/source/machine.rst libpod-3.4.4+ds1/docs/source/machine.rst --- libpod-3.2.1+ds1/docs/source/machine.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/machine.rst 2021-12-26 00:45:51.000000000 +0000 @@ -3,8 +3,13 @@ :doc:`init ` Initialize a new virtual machine + :doc:`list ` List virtual machines + :doc:`rm ` Remove a virtual machine + :doc:`ssh ` SSH into a virtual machine + :doc:`start ` Start a virtual machine + :doc:`stop ` Stop a virtual machine diff -Nru libpod-3.2.1+ds1/docs/source/manifest.rst libpod-3.4.4+ds1/docs/source/manifest.rst --- libpod-3.2.1+ds1/docs/source/manifest.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/manifest.rst 2021-12-26 00:45:51.000000000 +0000 @@ -14,3 +14,5 @@ :doc:`push ` Push a manifest list or image index to a registry :doc:`remove ` Remove an image from a manifest list or image index + +:doc:`rm ` Remove manifest list or image index from local storage diff -Nru libpod-3.2.1+ds1/docs/source/markdown/containers-mounts.conf.5.md libpod-3.4.4+ds1/docs/source/markdown/containers-mounts.conf.5.md --- libpod-3.2.1+ds1/docs/source/markdown/containers-mounts.conf.5.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/containers-mounts.conf.5.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -% containers-mounts.conf(5) - -## NAME -containers-mounts.conf - configuration file for default mounts in containers - -## DESCRIPTION -The mounts.conf file specifies volume mount directories that are automatically mounted inside containers. Container processes can then use this content. Usually these directories are used for passing secrets or credentials required by the package software to access remote package repositories. Note that for security reasons, tools adhering to the mounts.conf are expected to copy the contents instead of bind mounting the paths from the host. - -## FORMAT -The format of the mounts.conf is the volume format `/SRC:/DEST`, one mount per line. For example, a mounts.conf with the line `/usr/share/secrets:/run/secrets` would cause the contents of the `/usr/share/secrets` directory on the host to be mounted on the `/run/secrets` directory inside the container. Setting mountpoints allows containers to use the files of the host, for instance, to use the host's subscription to some enterprise Linux distribution. - -## FILES -Some distributions may provide a `/usr/share/containers/mounts.conf` file to provide default mounts, but users can create a `/etc/containers/mounts.conf`, to specify their own special volumes to mount in the container. When Podman runs in rootless mode, the file `$HOME/.config/containers/mounts.conf` will override the default if it exists. - -## HISTORY -Aug 2018, Originally compiled by Valentin Rothberg diff -Nru libpod-3.2.1+ds1/docs/source/markdown/links/podman-container-diff.1 libpod-3.4.4+ds1/docs/source/markdown/links/podman-container-diff.1 --- libpod-3.2.1+ds1/docs/source/markdown/links/podman-container-diff.1 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/links/podman-container-diff.1 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -.so man1/podman-diff.1 diff -Nru libpod-3.2.1+ds1/docs/source/markdown/links/podman-container-inspect.1 libpod-3.4.4+ds1/docs/source/markdown/links/podman-container-inspect.1 --- libpod-3.2.1+ds1/docs/source/markdown/links/podman-container-inspect.1 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/links/podman-container-inspect.1 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -.so man1/podman-inspect.1 diff -Nru libpod-3.2.1+ds1/docs/source/markdown/links/podman-image-inspect.1 libpod-3.4.4+ds1/docs/source/markdown/links/podman-image-inspect.1 --- libpod-3.2.1+ds1/docs/source/markdown/links/podman-image-inspect.1 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/links/podman-image-inspect.1 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -.so man1/podman-inspect.1 diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman.1.md libpod-3.4.4+ds1/docs/source/markdown/podman.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -82,7 +82,7 @@ #### **--log-level**=*level* -Log messages above specified level: debug, info, warn, error (default), fatal or panic (default: "error") +Log messages at and above specified level: debug, info, warn, error, fatal or panic (default: "warn") #### **--namespace**=*namespace* @@ -120,6 +120,8 @@ Storage root dir in which data, including images, is stored (default: "/var/lib/containers/storage" for UID 0, "$HOME/.local/share/containers/storage" for other users). Default root dir configured in `/etc/containers/storage.conf`. +Overriding this option will cause the *storage-opt* settings in /etc/containers/storage.conf to be ignored. The user must specify additional options via the `--storage-opt` flag. + #### **--runroot**=*value* Storage state directory where all state information is stored (default: "/run/containers/storage" for UID 0, "/run/user/$UID/run" for other users). @@ -274,7 +276,7 @@ **containers.conf** (`/usr/share/containers/containers.conf`, `/etc/containers/containers.conf`, `$HOME/.config/containers/containers.conf`) - Podman has builtin defaults for command line options. These defaults can be overridden using the containers.conf configuration files. +Podman has builtin defaults for command line options. These defaults can be overridden using the containers.conf configuration files. Distributions ship the `/usr/share/containers/containers.conf` file with their default settings. Administrators can override fields in this file by creating the `/etc/containers/containers.conf` file. Users can further modify defaults by creating the `$HOME/.config/containers/containers.conf` file. Podman merges its builtin defaults with the specified fields from these files, if they exist. Fields specified in the users file override the administrator's file, which overrides the distribution's file, which override the built-in defaults. @@ -284,38 +286,38 @@ **mounts.conf** (`/usr/share/containers/mounts.conf`) - The mounts.conf file specifies volume mount directories that are automatically mounted inside containers when executing the `podman run` or `podman start` commands. Administrators can override the defaults file by creating `/etc/containers/mounts.conf`. +The mounts.conf file specifies volume mount directories that are automatically mounted inside containers when executing the `podman run` or `podman start` commands. Administrators can override the defaults file by creating `/etc/containers/mounts.conf`. When Podman runs in rootless mode, the file `$HOME/.config/containers/mounts.conf` will override the default if it exists. Please refer to containers-mounts.conf(5) for further details. **policy.json** (`/etc/containers/policy.json`) - Signature verification policy files are used to specify policy, e.g. trusted keys, applicable when deciding whether to accept an image, or individual signatures of that image, as valid. +Signature verification policy files are used to specify policy, e.g. trusted keys, applicable when deciding whether to accept an image, or individual signatures of that image, as valid. **registries.conf** (`/etc/containers/registries.conf`, `$HOME/.config/containers/registries.conf`) - registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. +registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. - Non root users of Podman can create the `$HOME/.config/containers/registries.conf` file to be used instead of the system defaults. +Non root users of Podman can create the `$HOME/.config/containers/registries.conf` file to be used instead of the system defaults. - If the **CONTAINERS_REGISTRIES_CONF** environment variable is set, then its value is used for the registries.conf file rather than the default. +If the **CONTAINERS_REGISTRIES_CONF** environment variable is set, then its value is used for the registries.conf file rather than the default. **storage.conf** (`/etc/containers/storage.conf`, `$HOME/.config/containers/storage.conf`) - storage.conf is the storage configuration file for all tools using containers/storage +storage.conf is the storage configuration file for all tools using containers/storage - The storage configuration file specifies all of the available container storage options for tools using shared container storage. +The storage configuration file specifies all of the available container storage options for tools using shared container storage. - When Podman runs in rootless mode, the file `$HOME/.config/containers/storage.conf` is used instead of the system defaults. +When Podman runs in rootless mode, the file `$HOME/.config/containers/storage.conf` is used instead of the system defaults. - If the **CONTAINERS_STORAGE_CONF** environment variable is set, the its value is used for the storage.conf file rather than the default. +If the **CONTAINERS_STORAGE_CONF** environment variable is set, the its value is used for the storage.conf file rather than the default. ## Rootless mode Podman can also be used as non-root user. When podman runs in rootless mode, a user namespace is automatically created for the user, defined in /etc/subuid and /etc/subgid. Containers created by a non-root user are not visible to other users and are not seen or managed by Podman running as root. -It is required to have multiple uids/gids set for an user. Be sure the user is present in the files `/etc/subuid` and `/etc/subgid`. +It is required to have multiple uids/gids set for a user. Be sure the user is present in the files `/etc/subuid` and `/etc/subgid`. If you have a recent version of usermod, you can execute the following commands to add the ranges to the files @@ -334,9 +336,11 @@ Currently the slirp4netns package is required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host. +In certain environments like HPC (High Performance Computing), users cannot take advantage of the additional UIDs and GIDs from the /etc/subuid and /etc/subgid systems. However, in this environment, rootless Podman can operate with a single UID. To make this work, set the `ignore_chown_errors` option in the /etc/containers/storage.conf or in ~/.config/containers/storage.conf files. This option tells Podman when pulling an image to ignore chown errors when attempting to change a file in a container image to match the non-root UID in the image. This means all files get saved as the user's UID. Note this could cause issues when running the container. + ### **NOTE:** Unsupported file systems in rootless mode -The Overlay file system (OverlayFS) is not supported in rootless mode. The fuse-overlayfs package is a tool that provides the functionality of OverlayFS in user namespace that allows mounting file systems in rootless environments. It is recommended to install the fuse-overlayfs package. In rootless mode Podman will automatically use the fuse-overlafs program as the mount_program if installed, as long as the $HOME/.config/containers/storage.conf file was not previously created. If storage.conf exists in the homedir, add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options.overlay]` to enable this feature. +The Overlay file system (OverlayFS) is not supported with kernels prior to 5.12.9 in rootless mode. The fuse-overlayfs package is a tool that provides the functionality of OverlayFS in user namespace that allows mounting file systems in rootless environments. It is recommended to install the fuse-overlayfs package. In rootless mode, Podman will automatically use the fuse-overlayfs program as the mount_program if installed, as long as the $HOME/.config/containers/storage.conf file was not previously created. If storage.conf exists in the homedir, add `mount_program = "/usr/bin/fuse-overlayfs"` under `[storage.options.overlay]` to enable this feature. The Network File System (NFS) and other distributed file systems (for example: Lustre, Spectrum Scale, the General Parallel File System (GPFS)) are not supported when running in rootless mode as these file systems do not understand user namespace. However, rootless Podman can make use of an NFS Homedir by modifying the `$HOME/.config/containers/storage.conf` to have the `graphroot` option point to a directory stored on local (Non NFS) storage. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-attach.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-attach.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-attach.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-attach.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -9,48 +9,50 @@ **podman container attach** [*options*] *container* ## DESCRIPTION -The attach command allows you to attach to a running container using the container's ID -or name, either to view its ongoing output or to control it interactively. - -You can detach from the container (and leave it running) using a configurable key sequence. The default -sequence is `ctrl-p,ctrl-q`. -Configure the keys sequence using the **--detach-keys** option, or specifying -it in the **containers.conf** file: see **containers.conf(5)** for more information. +**podman attach** attaches to a running *container* using the *container's name* or *ID*, to either view its ongoing output or to control it interactively.\ +The *container* can detached from (and leave it running) using a configurable key sequence. The default sequence is `ctrl-p,ctrl-q`. Configure the keys sequence using the **--detach-keys** OPTION, or specifying it in the `containers.conf` file: see **[containers.conf(5)](https://github.com/containers/common/blob/master/docs/containers.conf.5.md)** for more information. ## OPTIONS -#### **--detach-keys**=*sequence* +#### **--detach-keys**=**sequence** -Specify the key sequence for detaching a container. Format is a single character `[a-Z]` or one or more `ctrl-` characters where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will disable this feature. The default is *ctrl-p,ctrl-q*. +Specify the key **sequence** for detaching a *container*. Format is a single character `[a-Z]` or one or more `ctrl-` characters where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will disable this feature.\ +The default is `ctrl-p,ctrl-q`. #### **--latest**, **-l** -Instead of providing the container name or ID, use the last created container. If you use methods other than Podman -to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client) +Instead of providing the *container ID* or *name*, use the last created *container*. If other methods than Podman are used to run *containers* such as `CRI-O`, the last started *container* could be from either of those methods.\ +The default is **false**.\ +*IMPORTANT: This OPTION is not available with the remote Podman client. This OPTION does not need a container name or ID as input argument.* #### **--no-stdin** -Do not attach STDIN. The default is false. +Do not attach STDIN. The default is **false**. -#### **--sig-proxy**=*true*|*false* +#### **--sig-proxy** -Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied. The default is *true*. +Proxy received signals to the process (non-TTY mode only). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.\ +The default is **true**. ## EXAMPLES - +Attach to a container called "foobar". ``` $ podman attach foobar -[root@localhost /]# ``` + +Attach to the latest created container. ``` $ podman attach --latest -[root@localhost /]# ``` + +Attach to a container that start with the ID "1234". ``` $ podman attach 1234 -[root@localhost /]# ``` + +Attach to a container without attaching STDIN. ``` $ podman attach --no-stdin foobar ``` + ## SEE ALSO -podman(1), podman-exec(1), podman-run(1), containers.conf(5) +**[podman(1)](podman.1.md)**, **[podman-exec(1)](podman-exec.1.md)**, **[podman-run(1)](podman-run.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/master/docs/containers.conf.5.md)** diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-auto-update.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-auto-update.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-auto-update.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-auto-update.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,16 +1,15 @@ % podman-auto-update(1) ## NAME -podman-auto-update - Auto update containers according to their auto-update policy +podman\-auto-update - Auto update containers according to their auto-update policy ## SYNOPSIS **podman auto-update** [*options*] ## DESCRIPTION -`podman auto-update` looks up containers with a specified "io.containers.autoupdate" label (i.e., the auto-update policy). +**podman auto-update** looks up containers with a specified `io.containers.autoupdate` label (i.e., the auto-update policy). -If the label is present and set to "registry", Podman reaches out to the corresponding registry to check if the image has been updated. -The label "image" is an alternative to "registry" maintained for backwards compatibility. +If the label is present and set to `registry`, Podman reaches out to the corresponding registry to check if the image has been updated. The label `image` is an alternative to `registry` maintained for backwards compatibility. An image is considered updated if the digest in the local storage is different than the one of the remote image. If an image must be updated, Podman pulls it down and restarts the systemd unit executing the container. @@ -18,98 +17,128 @@ This enforcement is necessary to know which image to actually check and pull. If an image ID was used, Podman would not know which image to check/pull anymore. -Alternatively, if the autoupdate label is set to "local", Podman will compare the image a container is using to the image with it's raw name in local storage. +Alternatively, if the autoupdate label is set to `local`, Podman will compare the image a container is using to the image with its raw name in local storage. If an image is updated locally, Podman simply restarts the systemd unit executing the container. -If "io.containers.autoupdate.authfile" label is present, Podman reaches out to corresponding authfile when pulling images. +If `io.containers.autoupdate.authfile` label is present, Podman reaches out to the corresponding authfile when pulling images. -At container-creation time, Podman looks up the "PODMAN_SYSTEMD_UNIT" environment variables and stores it verbatim in the container's label. -This variable is now set by all systemd units generated by `podman-generate-systemd` and is set to `%n` (i.e., the name of systemd unit starting the container). +At container-creation time, Podman looks up the `PODMAN_SYSTEMD_UNIT` environment variable and stores it verbatim in the container's label. +This variable is now set by all systemd units generated by **[podman-generate-systemd](podman-generate-systemd.1.md)** and is set to `%n` (i.e., the name of systemd unit starting the container). This data is then being used in the auto-update sequence to instruct systemd (via DBUS) to restart the unit and hence to restart the container. -Note that`podman auto-update` relies on systemd. The systemd units are expected to be generated with `podman-generate-systemd --new`, or similar units that create new containers in order to run the updated images. +Note that **podman auto-update** relies on systemd. The systemd units are expected to be generated with **[podman-generate-systemd --new](podman-generate-systemd.1.md#--new)**, or similar units that create new containers in order to run the updated images. Systemd units that start and stop a container cannot run a new image. - ### Systemd Unit and Timer -Podman ships with a `podman-auto-update.service` systemd unit. This unit is triggered daily at midnight by the `podman-auto-update.timer` systemd timer. The timer can be altered for custom time-based updates if desired. The unit can further be invoked by other systemd units (e.g., via the dependency tree) or manually via `systemctl start podman-auto-update.service`. - +Podman ships with a `podman-auto-update.service` systemd unit. This unit is triggered daily at midnight by the `podman-auto-update.timer` systemd timer. The timer can be altered for custom time-based updates if desired. The unit can further be invoked by other systemd units (e.g., via the dependency tree) or manually via **systemctl start podman-auto-update.service**. ## OPTIONS - #### **--authfile**=*path* -Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. +Path of the authentication file. Default is `${XDG_RUNTIME_DIR}/containers/auth.json`, which is set using **[podman login](podman-login.1.md)**. +If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using **docker login**. -Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE -environment variable. `export REGISTRY_AUTH_FILE=path` +Note: There is also the option to override the default path of the authentication file by setting the `REGISTRY_AUTH_FILE` environment variable. This can be done with **export REGISTRY_AUTH_FILE=_path_**. -## EXAMPLES +#### **--dry-run**=*true|false* + +Check for the availability of new images but do not perform any pull operation or restart any service or container. +The `UPDATED` field indicates the availability of a new image with "pending". + +#### **--format**=*format* + +Change the default output format. This can be of a supported type like 'json' or a Go template. +Valid placeholders for the Go template are listed below: + +#### **--rollback**=*true|false* + +If restarting a systemd unit after updating the image has failed, rollback to using the previous image and restart the unit another time. Default is true. +Please note that detecting if a systemd unit has failed is best done by the container sending the READY message via SDNOTIFY. This way, restarting the unit will wait until having received the message or a timeout kicked in. Without that, restarting the systemd unit may succeed even if the container has failed shortly after. + +For a container to send the READY message via SDNOTIFY it must be created with the `--sdnotify=container` option (see podman-run(1)). The application running inside the container can then execute `systemd-notify --ready` when ready or use the sdnotify bindings of the specific programming language (e.g., sd_notify(3)). + + +| **Placeholder** | **Description** | +| --------------- | -------------------------------------- | +| .Unit | Name of the systemd unit | +| .ContainerName | Name of the container | +| .ContainerID | ID of the container | +| .Container | ID and name of the container | +| .Image | Name of the image | +| .Policy | Auto-update policy of the container | +| .Updated | Update status: true,false,failed | + + +## EXAMPLES Autoupdate with registry policy ``` -# Start a container +### Start a container $ podman run --label "io.containers.autoupdate=registry" \ --label "io.containers.autoupdate.authfile=/some/authfile.json" \ - -d busybox:latest top + -d --name=test registry.fedoraproject.org/fedora:latest sleep infinity bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d -# Generate a systemd unit for this container +### Generate a systemd unit for this container $ podman generate systemd --new --files bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d -/home/user/containers/libpod/container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service +/home/user/container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service -# Load the new systemd unit and start it -$ mv ./container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service ~/.config/systemd/user +### Load the new systemd unit and start it +$ mv ./container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service ~/.config/systemd/user/container-test.service $ systemctl --user daemon-reload -# If the previously created containers or pods are using shared resources, such as ports, make sure to remove them before starting the generated systemd units. +### If the previously created containers or pods are using shared resources, such as ports, make sure to remove them before starting the generated systemd units. $ podman stop bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d $ podman rm bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d -$ systemctl --user start container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service +$ systemctl --user start container-test.service + +### Check if a newer image is available +$ podman auto-update --dry-run --format "{{.Image}} {{.Updated}}" +registry.fedoraproject.org/fedora:latest pending -# Auto-update the container +### Autoupdate the services $ podman auto-update -container-bc219740a210455fa27deacc96d50a9e20516492f1417507c13ce1533dbdcd9d.service +UNIT CONTAINER IMAGE POLICY UPDATED +container-test.service 08fd34e533fd (test) registry.fedoraproject.org/fedora:latest registry false ``` Autoupdate with local policy ``` -# Start a container +### Start a container $ podman run --label "io.containers.autoupdate=local" \ -d busybox:latest top be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338 -# Generate a systemd unit for this container +### Generate a systemd unit for this container $ podman generate systemd --new --files be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338 -/home/user/containers/libpod/container-be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338.service +/home/user/container-be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338.service -# Load the new systemd unit and start it +### Load the new systemd unit and start it $ mv ./container-be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338.service ~/.config/systemd/user $ systemctl --user daemon-reload -# If the previously created containers or pods are using shared resources, such as ports, make sure to remove them before starting the generated systemd units. +### If the previously created containers or pods are using shared resources, such as ports, make sure to remove them before starting the generated systemd units. $ podman stop be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338 $ podman rm be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338 $ systemctl --user start container-be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338.service -# Get the name of the container +### Get the name of the container $ podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 01f5c8113e84 docker.io/library/busybox:latest top 2 seconds ago Up 3 seconds ago inspiring_galileo -# Modify the image +### Modify the image $ podman commit --change CMD=/bin/bash inspiring_galileo busybox:latest -# Auto-update the container +### Auto-update the container $ podman auto-update -container-be0889fd06f252a2e5141b37072c6bada68563026cb2b2649f53394d87ccc338.service +[...] ``` ## SEE ALSO -podman(1), podman-generate-systemd(1), podman-run(1), systemd.unit(5) +**[podman(1)](podman.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-run(1)](podman-run.1.md)**, sd_notify(3), systemd.unit(5) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-build.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-build.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-build.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-build.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -43,6 +43,8 @@ command to see these containers. External containers can be removed with the `podman rm --storage` command. +`podman buildx build` command is an alias of `podman build`. Not all `buildx build` features are available in Podman. The `buildx build` option is provided for scripting compatibility. + ## OPTIONS #### **--add-host**=*host* @@ -62,8 +64,10 @@ #### **--arch**=*arch* -Set the ARCH of the image to the provided value instead of the architecture of -the host. +Set the architecture of the image to be built, and that of the base image to be +pulled, if the build uses one, to the provided value instead of using the +architecture of the build host. (Examples: arm, arm64, 386, amd64, ppc64le, +s390x) #### **--authfile**=*path* @@ -110,7 +114,7 @@ #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--cgroup-parent**=*path* @@ -319,7 +323,8 @@ #### **--iidfile**=*ImageIDfile* -Write the image ID to the file. +Write the built image's ID to the file. When `--platform` is specified more +than once, attempting to use this option will trigger an error. #### **--ignorefile** @@ -387,6 +392,7 @@ if it does not exist. This option is useful for building multi architecture images. #### **--memory**, **-m**=*LIMIT* + Memory limit (format: `[]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes)) @@ -428,8 +434,9 @@ #### **--os**=*string* -Set the OS to the provided value instead of the current operating system of the -host. +Set the OS of the image to be built, and that of the base image to be pulled, +if the build uses one, instead of using the current operating system of the +build host. #### **--pid**=*pid* @@ -440,11 +447,28 @@ or it can be the path to a PID namespace which is already in use by another process. -#### **--platform**="Linux" +#### **--platform**="OS/ARCH[/VARIANT][,...]" -This option has no effect on the build. Other container engines use this option -to control the execution platform for the build (e.g., Windows, Linux) which is -not required for Buildah as it supports only Linux. +Set the OS/ARCH of the built image (and its base image, if your build uses one) +to the provided value instead of using the current operating system and +architecture of the host (for example `linux/arm`). If `--platform` is set, +then the values of the `--arch`, `--os`, and `--variant` options will be +overridden. + +The `--platform` flag can be specified more than once, or given a +comma-separated list of values as its argument. When more than one platform is +specified, the `--manifest` option should be used instead of the `--tag` +option. + +OS/ARCH pairs are those used by the Go Programming Language. In several cases +the ARCH value for a platform differs from one produced by other tools such as +the `arch` command. Valid OS and architecture name combinations are listed as +values for $GOOS and $GOARCH at https://golang.org/doc/install/source#environment, +and can also be found by running `go tool dist list`. + +While `podman build` is happy to use base images and build images for any +platform that exists, `RUN` instructions will not be able to succeed without +the help of emulation provided by packages like `qemu-user-static`. #### **--pull** @@ -484,7 +508,6 @@ Note: You can also override the default runtime by setting the BUILDAH\_RUNTIME environment variable. `export BUILDAH_RUNTIME=/usr/local/bin/runc` - #### **--secret**=**id=id,src=path** Pass secret information to be used in the Containerfile for building images @@ -495,7 +518,6 @@ `RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret` - #### **--security-opt**=*option* Security Options @@ -538,6 +560,15 @@ Squash all of the new image's layers (including those inherited from a base image) into a single new layer. +#### **--ssh**=*default|id[=socket>|[,]* + +SSH agent socket or keys to expose to the build. +The socket path can be left empty to use the value of `default=$SSH_AUTH_SOCK` + +To later use the ssh agent, use the --mount flag in a `RUN` instruction within a `Containerfile`: + +`RUN --mount=type=ssh,id=id mycmd` + #### **--stdin** Pass stdin into the RUN containers. Sometime commands being RUN within a Containerfile @@ -600,7 +631,7 @@ The configured value can be "" (the empty string) or "container" to indicate that a new user namespace should be created, it can be "host" to indicate that the user namespace in which `podman` itself is being run should be reused, or -it can be the path to an user namespace which is already in use by another +it can be the path to a user namespace which is already in use by another process. #### **--userns-uid-map**=*mapping* @@ -686,7 +717,9 @@ #### **--variant**="" -Set the architecture variant of the image to be pulled. +Set the architecture variant of the image to be built, and that of the base +image to be pulled, if the build uses one, to the provided value instead of +using the architecture variant of the build host. #### **--volume**, **-v**[=*[HOST-DIR:CONTAINER-DIR[:OPTIONS]]*] @@ -741,6 +774,14 @@ The `Z` option tells Podman to label the content with a private unshared label. Only the current container can use a private volume. +Note: Do not relabel system files and directories. Relabeling system content +might cause other confined services on your machine to fail. For these types +of containers, disabling SELinux separation is recommended. The option +`--security-opt label=disable` disables SELinux separation for the container. +For example, if a user wanted to volume mount their entire home directory into the build containers, they need to disable SELinux separation. + + $ podman build --security-opt label=disable -v $HOME:/home/user . + `Overlay Volume Mounts` The `:O` flag tells Podman to mount the directory from the host as a @@ -847,7 +888,7 @@ $ podman build --no-cache --rm=false -t imageName . ``` -### Building an multi-architecture image using a --manifest option (Requires emulation software) +### Building a multi-architecture image using the --manifest option (requires emulation software) ``` $ podman build --arch arm --manifest myimage /tmp/mysrc @@ -855,6 +896,10 @@ $ podman build --arch amd64 --manifest myimage /tmp/mysrc $ podman build --arch s390x --manifest myimage /tmp/mysrc + +$ podman build --platform linux/s390x,linux/ppc64le,linux/amd64 --manifest myimage /tmp/mysrc + +$ podman build --platform linux/arm64 --platform linux/amd64 --manifest myimage /tmp/mysrc ``` ### Building an image using a URL, Git repo, or archive diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-commit.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-commit.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-commit.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-commit.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -9,34 +9,37 @@ **podman container commit** [*options*] *container* [*image*] ## DESCRIPTION -**podman commit** creates an image based on a changed container. The author of the -image can be set using the `--author` flag. Various image instructions can be -configured with the `--change` flag and a commit message can be set using the -`--message` flag. The container and its processes are paused while the image is -committed. This minimizes the likelihood of data corruption when creating the new -image. If this is not desired, the `--pause` flag can be set to false. When the commit -is complete, Podman will print out the ID of the new image. +**podman commit** creates an image based on a changed *container*. The author of the image can be set using the **--author** OPTION. Various image instructions can be configured with the **--change** OPTION and a commit message can be set using the **--message** OPTION. The *container* and its processes are paused while the image is committed. This minimizes the likelihood of data corruption when creating the new image. If this is not desired, the **--pause** OPTION can be set to *false*. When the commit is complete, Podman will print out the ID of the new image. -If *image* does not begin with a registry name component, `localhost` will be added to the name. -If *image* is not provided, the values for the `REPOSITORY` and `TAG` values of the created image will each be set to ``. +If `image` does not begin with a registry name component, `localhost` will be added to the name. +If `image` is not provided, the values for the `REPOSITORY` and `TAG` values of the created image will each be set to ``. ## OPTIONS - #### **--author**, **-a**=*author* -Set the author for the committed image +Set the author for the committed image. #### **--change**, **-c**=*instruction* Apply the following possible instructions to the created image: -**CMD** | **ENTRYPOINT** | **ENV** | **EXPOSE** | **LABEL** | **ONBUILD** | **STOPSIGNAL** | **USER** | **VOLUME** | **WORKDIR** -Can be set multiple times +- *CMD* +- *ENTRYPOINT* +- *ENV* +- *EXPOSE* +- *LABEL* +- *ONBUILD* +- *STOPSIGNAL* +- *USER* +- *VOLUME* +- *WORKDIR* + +Can be set multiple times. -#### **--format**, **-f**=*format* +#### **--format**, **-f** =**oci** | *docker* -Set the format of the image manifest and metadata. The currently supported formats are _oci_ and _docker_. If -not specifically set, the default format used is _oci_. +Set the format of the image manifest and metadata. The currently supported formats are **oci** and *docker*.\ +The default is **oci**. #### **--iidfile**=*ImageIDfile* @@ -44,23 +47,26 @@ #### **--include-volumes** -Include in the committed image any volumes added to the container by the `--volume` or `--mount` options to the `podman create` and `podman run` commands. +Include in the committed image any volumes added to the container by the **--volume** or **--mount** OPTIONS to the **[podman create](podman-create.1.md)** and **[podman run](podman-run.1.md)** commands.\ +The default is **false**. #### **--message**, **-m**=*message* -Set commit message for committed image. The message field is not supported in _oci_ format. +Set commit message for committed image.\ +*IMPORTANT: The message field is not supported in `oci` format.* #### **--pause**, **-p** -Pause the container when creating an image +Pause the container when creating an image.\ +The default is **false**. #### **--quiet**, **-q** -Suppress output +Suppresses output.\ +The default is **false**. ## EXAMPLES - -### Create image from container with entrypoint and label +Create image from container with entrypoint and label ``` $ podman commit --change CMD=/bin/bash --change ENTRYPOINT=/bin/sh --change "LABEL blue=image" reverent_golick image-committed Getting image source signatures @@ -73,39 +79,39 @@ e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` -### Create image from container with commit message +Create image from container with commit message ``` $ podman commit -q --message "committing container to image" reverent_golick image-committed -e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` +e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` -### Create image from container with author +Create image from container with author ``` $ podman commit -q --author "firstName lastName" reverent_golick image-committed e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` -### Pause a running container while creating the image +Pause a running container while creating the image ``` $ podman commit -q --pause=true containerID image-committed e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` -### Create an image from a container with a default image tag +Create an image from a container with a default image tag ``` $ podman commit containerID e3ce4d93051ceea088d1c242624d659be32cf1667ef62f1d16d6b60193e2c7a8 ``` -### Create an image from container with default required capabilities are SETUID and SETGID +Create an image from container with default required capabilities are SETUID and SETGID ``` $ podman commit -q --change LABEL=io.containers.capabilities=setuid,setgid epic_nobel privimage 400d31a3f36dca751435e80a0e16da4859beb51ff84670ce6bdc5edb30b94066 ``` ## SEE ALSO -podman(1), podman-run(1), podman-create(1) +**[podman(1)](podman.1.md)**, **[podman-run(1)](podman-run.1.md)**, **[podman-create(1)](podman-create.1.md)** ## HISTORY December 2017, Originally compiled by Urvashi Mohnani diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-completion.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-completion.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-completion.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-completion.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,60 +4,61 @@ podman\-completion - Generate shell completion scripts ## SYNOPSIS -**podman completion** [*options*] *bash*|*zsh*|*fish*|*powershell* +**podman completion** [*options*] *bash* | *zsh* | *fish* | *powershell* ## DESCRIPTION -The completion command generates shell completion scripts for a variety of shells. Supported shells are **bash**, **zsh**, **fish** and **powershell**. +**podman completion** generates shell completion scripts for a variety of shells. Supported shells are *bash*, *zsh*, *fish* and *powershell*. -These script are used by the shell to provide suggestions and complete commands when you are typing the command and press [TAB]. +These script are used by the shell to provide suggestions and complete commands when the command is typed and `[TAB]` is pressed. Usually these scripts are automatically installed via the package manager. ## OPTIONS -#### **--file**, **-f** +#### **--file**, **-f**=*file* -Write the generated output to file. +Write the generated output to a file. #### **--no-desc** -Do not provide description in the completions. +Do not provide description in the completions.\ +The default is **false**. ## Installation ### BASH -Make sure you have `bash-completion` installed on the system. +`bash-completion` has to be installed on the system. -To load the completion script into the current session run: -`source <(podman completion bash)` +To load the completion script into the current session run:\ +**source <(podman completion bash)**. -To make it available for all bash sessions run: -`podman completion bash -f /etc/bash_completion.d/podman` +To make it available for all bash sessions run:\ +**podman completion -f /etc/bash_completion.d/podman bash**. ### ZSH -If shell completion is not already enabled in the environment you will need to enable it. You can execute the following once: -`echo "autoload -U compinit; compinit" >> ~/.zshrc` +Shell completion needs to be already enabled in the environment. The following can be executed:\ +**echo "autoload -U compinit; compinit" >> ~/.zshrc** -To make it available for all zsh sessions run: -`podman completion zsh -f "${fpath[1]}/_podman"` +To make it available for all zsh sessions run:\ +**podman completion -f "${fpath[1]}/_podman" zsh** -Once you reload the shell the auto-completion should be working. +Once the shell is reloaded the auto-completion should be working. ### FISH To load the completion script into the current session run: -`podman completion fish | source` +**podman completion fish | source** To make it available for all fish sessions run: -`podman completion fish -f ~/.config/fish/completions/podman.fish` +**podman completion -f ~/.config/fish/completions/podman.fish fish** ### POWERSHELL To load the completion script into the current session run: -`podman.exe completion powershell | Out-String | Invoke-Expression` +**podman.exe completion powershell | Out-String | Invoke-Expression** To make it available in all powershell sessions that a user has, write the completion output to a file and source that to the user's powershell profile. -More information about profiles is available with `Get-Help about_Profiles`. +More information about profiles is available with **Get-Help about_Profiles**. ## SEE ALSO -[podman(1)](podman.1.md) +**[podman(1)](podman.1.md)**, zsh(1), fish(1), powershell(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -19,12 +19,12 @@ | commit | [podman-commit(1)](podman-commit.1.md) | Create new image based on the changed container. | | cp | [podman-cp(1)](podman-cp.1.md) | Copy files/folders between a container and the local filesystem. | | create | [podman-create(1)](podman-create.1.md) | Create a new container. | -| diff | [podman-diff(1)](podman-diff.1.md) | Inspect changes on a container or image's filesystem. | +| diff | [podman-container-diff(1)](podman-container-diff.1.md) | Inspect changes on a container's filesystem | | exec | [podman-exec(1)](podman-exec.1.md) | Execute a command in a running container. | | exists | [podman-container-exists(1)](podman-container-exists.1.md) | Check if a container exists in local storage | | export | [podman-export(1)](podman-export.1.md) | Export a container's filesystem contents as a tar archive. | | init | [podman-init(1)](podman-init.1.md) | Initialize a container | -| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a container or image's configuration. | +| inspect | [podman-container-inspect(1)](podman-container-inspect.1.md)| Display a container's configuration. | | kill | [podman-kill(1)](podman-kill.1.md) | Kill the main process in one or more containers. | | list | [podman-ps(1)](podman-ps.1.md) | List the containers on the system.(alias ls) | | logs | [podman-logs(1)](podman-logs.1.md) | Display the logs of a container. | @@ -38,7 +38,7 @@ | restore | [podman-container-restore(1)](podman-container-restore.1.md) | Restores one or more containers from a checkpoint. | | rm | [podman-rm(1)](podman-rm.1.md) | Remove one or more containers. | | run | [podman-run(1)](podman-run.1.md) | Run a command in a container. | -| runlabel | [podman-container-runlabel(1)](podman-container-runlabel.1.md) | Executes a command as described by a container image label. | +| runlabel | [podman-container-runlabel(1)](podman-container-runlabel.1.md) | Executes a command as described by a container-image label. | | start | [podman-start(1)](podman-start.1.md) | Starts one or more containers. | | stats | [podman-stats(1)](podman-stats.1.md) | Display a live stream of one or more container's resource usage statistics. | | stop | [podman-stop(1)](podman-stop.1.md) | Stop one or more running containers. | diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-checkpoint.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-checkpoint.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-checkpoint.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-checkpoint.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,80 +4,109 @@ podman\-container\-checkpoint - Checkpoints one or more running containers ## SYNOPSIS -**podman container checkpoint** [*options*] *container* ... +**podman container checkpoint** [*options*] *container* [*container* ...] ## DESCRIPTION -Checkpoints all the processes in one or more containers. You may use container IDs or names as input. +**podman container checkpoint** checkpoints all the processes in one or more *containers*. A *container* can be restored from a checkpoint with **[podman-container-restore](podman-container-restore.1.md)**. The *container IDs* or *names* are used as input. ## OPTIONS -#### **--keep**, **-k** - -Keep all temporary log and statistics files created by CRIU during checkpointing. These files -are not deleted if checkpointing fails for further debugging. If checkpointing succeeds these -files are theoretically not needed, but if these files are needed Podman can keep the files -for further analysis. - #### **--all**, **-a** -Checkpoint all running containers. +Checkpoint all running *containers*.\ +The default is **false**.\ +*IMPORTANT: This OPTION does not need a container name or ID as input argument.* + +#### **--compress**, **-c**=**zstd** | *none* | *gzip* + +Specify the compression algorithm used for the checkpoint archive created +with the **--export, -e** OPTION. Possible algorithms are **zstd**, *none* +and *gzip*.\ +One possible reason to use *none* is to enable faster creation of checkpoint +archives. Not compressing the checkpoint archive can result in faster checkpoint +archive creation.\ +The default is **zstd**. -#### **--latest**, **-l** +#### **--export**, **-e**=*archive* -Instead of providing the container name or ID, checkpoint the last created container. (This option is not available with the remote Podman client) +Export the checkpoint to a tar.gz file. The exported checkpoint can be used +to import the *container* on another system and thus enabling container live +migration. This checkpoint archive also includes all changes to the *container's* +root file-system, if not explicitly disabled using **--ignore-rootfs**. -#### **--leave-running**, **-R** +#### **--ignore-rootfs** -Leave the container running after checkpointing instead of stopping it. +If a checkpoint is exported to a tar.gz file it is possible with the help of **--ignore-rootfs** to explicitly disable including changes to the root file-system into the checkpoint archive file.\ +The default is **false**.\ +*IMPORTANT: This OPTION only works in combination with **--export, -e**.* -#### **--tcp-established** +#### **--ignore-volumes** -Checkpoint a container with established TCP connections. If the checkpoint -image contains established TCP connections, this options is required during -restore. Defaults to not checkpointing containers with established TCP -connections. +This OPTION must be used in combination with the **--export, -e** OPTION. +When this OPTION is specified, the content of volumes associated with +the *container* will not be included into the checkpoint tar.gz file.\ +The default is **false**. -#### **--export**, **-e** +#### **--keep**, **-k** -Export the checkpoint to a tar.gz file. The exported checkpoint can be used -to import the container on another system and thus enabling container live -migration. This checkpoint archive also includes all changes to the container's -root file-system, if not explicitly disabled using **--ignore-rootfs** +Keep all temporary log and statistics files created by CRIU during checkpointing. These files are not deleted if checkpointing fails for further debugging. If checkpointing succeeds these files are theoretically not needed, but if these files are needed Podman can keep the files for further analysis.\ +The default is **false**. -#### **--ignore-rootfs** +#### **--latest**, **-l** -This only works in combination with **--export, -e**. If a checkpoint is -exported to a tar.gz file it is possible with the help of **--ignore-rootfs** -to explicitly disable including changes to the root file-system into -the checkpoint archive file. +Instead of providing the *container ID* or *name*, use the last created *container*. If other methods than Podman are used to run *containers* such as `CRI-O`, the last started *container* could be from either of those methods.\ +The default is **false**.\ +*IMPORTANT: This OPTION is not available with the remote Podman client. This OPTION does not need a container name or ID as input argument.* -#### **--ignore-volumes** +#### **--leave-running**, **-R** -This option must be used in combination with the **--export, -e** option. -When this option is specified, the content of volumes associated with -the container will not be included into the checkpoint tar.gz file. +Leave the *container* running after checkpointing instead of stopping it.\ +The default is **false**. #### **--pre-checkpoint**, **-P** -Dump the container's memory information only, leaving the container running. Later -operations will supersede prior dumps. It only works on runc 1.0-rc3 or higher. - -#### **--with-previous** - -Check out the container with previous criu image files in pre-dump. It only works -without **--pre-checkpoint** or **-P**. It only works on runc 1.0-rc3 or higher. - -## EXAMPLE +Dump the *container's* memory information only, leaving the *container* running. Later +operations will supersede prior dumps. It only works on `runc 1.0-rc3` or `higher`.\ +The default is **false**. -podman container checkpoint mywebserver +#### **--tcp-established** -podman container checkpoint 860a4b23 +Checkpoint a *container* with established TCP connections. If the checkpoint +image contains established TCP connections, this OPTION is required during +restore. Defaults to not checkpointing *containers* with established TCP +connections.\ +The default is **false**. -podman container checkpoint -P -e pre-checkpoint.tar.gz -l +#### **--with-previous** -podman container checkpoint --with-previous -e checkpoint.tar.gz -l +Check out the *container* with previous criu image files in pre-dump. It only works on `runc 1.0-rc3` or `higher`.\ +The default is **false**.\ +*IMPORTANT: This OPTION is not available with **--pre-checkpoint***. + + +## EXAMPLES +Make a checkpoint for the container "mywebserver". +``` +# podman container checkpoint mywebserver +``` + +Dumps the container's memory information of the latest container into an archive. +``` +# podman container checkpoint -P -e pre-checkpoint.tar.gz -l +``` + +Keep the container's memory information from an older dump and add the new container's memory information. +``` +# podman container checkpoint --with-previous -e checkpoint.tar.gz -l +``` + +Dump the container's memory information of the latest container into an archive with the specified compress method. +``` +# podman container checkpoint -l --compress=none --export=dump.tar +# podman container checkpoint -l --compress=gzip --export=dump.tar.gz +``` ## SEE ALSO -podman(1), podman-container-restore(1) +**[podman(1)](podman.1.md)**, **[podman-container-restore(1)](podman-container-restore.1.md)** ## HISTORY September 2018, Originally compiled by Adrian Reber diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-cleanup.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-cleanup.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-cleanup.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-cleanup.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,51 +4,54 @@ podman\-container\-cleanup - Cleanup the container's network and mountpoints ## SYNOPSIS -**podman container cleanup** [*options*] *container* +**podman container cleanup** [*options*] *container* [*container* ...] ## DESCRIPTION -**podman container cleanup** cleans up exited containers by removing all mountpoints and network configuration from the host. The container name or ID can be used. The cleanup command does not remove the containers. Running containers will not be cleaned up. -Sometimes container's mount points and network stacks can remain if the podman command was killed or the container ran in daemon mode. This command is automatically executed when you run containers in daemon mode by the conmon process when the container exits. +**podman container cleanup** cleans up exited *containers* by removing all mountpoints and network configuration from the host. The *container name* or *ID* can be used. The cleanup command does not remove the *containers*. Running *containers* will not be cleaned up.\ +Sometimes container mount points and network stacks can remain if the podman command was killed or the *container* ran in daemon mode. This command is automatically executed when *containers* are run in daemon mode by the `conmon process` when the *container* exits. ## OPTIONS - #### **--all**, **-a** -Cleanup all containers. - -#### **--exec**=_session_ - -Clean up an exec session for a single container. -Can only be specified if a single container is being cleaned up (conflicts with **--all** as such). -If **--rm** is not specified, temporary files for the exec session will be cleaned up; if it is, the exec session will be removed from the container. -Conflicts with **--rmi** as the container is not being cleaned up so the image cannot be removed. +Cleanup all *containers*.\ +The default is **false**.\ +*IMPORTANT: This OPTION does not need a container name or ID as input argument.* + +#### **--exec**=*session* + +Clean up an exec session for a single *container*. +Can only be specified if a single *container* is being cleaned up (conflicts with **--all** as such). If **--rm** is not specified, temporary files for the exec session will be cleaned up; if it is, the exec session will be removed from the *container*.\ +*IMPORTANT: Conflicts with **--rmi** as the container is not being cleaned up so the image cannot be removed.* #### **--latest**, **-l** -Instead of providing the container name or ID, use the last created container. If you use methods other than Podman -to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client) + +Instead of providing the *container ID* or *name*, use the last created *container*. If other methods than Podman are used to run *containers* such as `CRI-O`, the last started *container* could be from either of those methods.\ +The default is **false**.\ +*IMPORTANT: This OPTION is not available with the remote Podman client. This OPTION does not need a container name or ID as input argument.* #### **--rm** -After cleanup, remove the container entirely. +After cleanup, remove the *container* entirely.\ +The default is **false**. #### **--rmi** -After cleanup, remove the image entirely. - -## EXAMPLE - -`podman container cleanup mywebserver` - -`podman container cleanup mywebserver myflaskserver 860a4b23` - -`podman container cleanup 860a4b23` - -`podman container cleanup -a` +After cleanup, remove the image entirely.\ +The default is **false**. -`podman container cleanup --latest` +## EXAMPLES +Cleanup the container "mywebserver". +``` +$ podman container cleanup mywebserver +``` + +Cleanup the containers with the names "mywebserver", "myflaskserver", "860a4b23". +``` +$ podman container cleanup mywebserver myflaskserver 860a4b23 +``` ## SEE ALSO -**podman**(1), **podman-container**(1), **conmon**(8). +**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, conmon(8) ## HISTORY Jun 2018, Originally compiled by Dan Walsh diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-diff.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-diff.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-diff.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-diff.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,54 @@ +% podman-container-diff(1) + +## NAME +podman\-container\-diff - Inspect changes on a container's filesystem + +## SYNOPSIS +**podman container diff** [*options*] *container* [*container*] + +## DESCRIPTION +Displays changes on a container's filesystem. The container will be compared to its parent layer or the second argument when given. + +The output is prefixed with the following symbols: + +| Symbol | Description | +|--------|-------------| +| A | A file or directory was added. | +| D | A file or directory was deleted. | +| C | A file or directory was changed. | + +## OPTIONS + +#### **--format** + +Alter the output into a different format. The only valid format for **podman container diff** is `json`. + +#### **--latest**, **-l** + +Instead of providing the container name or ID, use the last created container. If you use methods other than Podman +to run containers such as CRI-O, the last started container could be from either of those methods. (This option is not available with the remote Podman client) + +## EXAMPLE + +``` +# podman container diff container1 +C /usr +C /usr/local +C /usr/local/bin +A /usr/local/bin/docker-entrypoint.sh +``` + +``` +$ podman container diff --format json container1 container2 +{ + "added": [ + "/test" + ] +} +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)** + +## HISTORY +July 2021, Originally compiled by Paul Holzinger diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-exists.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-exists.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-exists.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-exists.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,42 +1,43 @@ % podman-container-exists(1) ## NAME -podman-container-exists - Check if a container exists in local storage +podman\-container\-exists - Check if a container exists in local storage ## SYNOPSIS **podman container exists** [*options*] *container* ## DESCRIPTION -**podman container exists** checks if a container exists in local storage. The **ID** or **Name** -of the container may be used as input. Podman will return an exit code -of `0` when the container is found. A `1` will be returned otherwise. An exit code of `125` indicates there -was an issue accessing the local storage. +**podman container exists** checks if a container exists in local storage. The *container ID* or *name* is used as input. Podman will return an exit code +of `0` when the container is found. A `1` will be returned otherwise. An exit code of `125` indicates there was an issue accessing the local storage. ## OPTIONS +#### **--external** -#### **--external**=*true|false* -Check for external containers as well as Podman containers. These external containers are generally created via other container technology such as Buildah or CRI-O. +Check for external *containers* as well as Podman *containers*. These external *containers* are generally created via other container technology such as `Buildah` or `CRI-O`.\ +The default is **false**. **-h**, **--help** -Print usage statement + +Prints usage statement.\ +The default is **false**. ## EXAMPLES -Check if an container called `webclient` exists in local storage (the container does actually exist). +Check if an container called "webclient" exists in local storage. Here, the container does exist. ``` $ podman container exists webclient $ echo $? 0 ``` -Check if an container called `webbackend` exists in local storage (the container does not actually exist). +Check if an container called "webbackend" exists in local storage. Here, the container does not exist. ``` $ podman container exists webbackend $ echo $? 1 ``` -Check if an container called `ubi8-working-container` created via Buildah exists in local storage (the container does not actually exist). +Check if an container called "ubi8-working-container" created via Buildah exists in local storage. Here, the container does not exist. ``` $ podman container exists --external ubi8-working-container $ echo $? @@ -44,7 +45,7 @@ ``` ## SEE ALSO -podman(1) +**[podman(1)](podman.1.md)** ## HISTORY -November 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) +November 2018, Originally compiled by Brent Baude diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-inspect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-inspect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-inspect.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-inspect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,318 @@ +% podman-container-inspect(1) + +## NAME +podman\-container\-inspect - Display a container's configuration + +## SYNOPSIS +**podman container inspect** [*options*] *container* [*container* ...] + +## DESCRIPTION + +This displays the low-level information on containers identified by name or ID. By default, this will render +all results in a JSON array. If a format is specified, the given template will be executed for each result. + +## OPTIONS + +#### **--format**, **-f**=*format* + +Format the output using the given Go template. +The keys of the returned JSON can be used as the values for the --format flag (see examples below). + +#### **--latest**, **-l** + +Instead of providing the container name or ID, use the last created container. If you use methods other than Podman +to run containers such as CRI-O, the last started container could be from either of those methods. + +(This option is not available with the remote Podman client.) + +#### **--size**, **-s** + +In addition to normal output, display the total file size if the type is a container. + + +## EXAMPLE + +``` +$ podman container inspect foobar +[ + { + "Id": "99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6", + "Created": "2021-09-16T06:09:08.936623325-04:00", + "Path": "echo", + "Args": [ + "hi" + ], + "State": { + "OciVersion": "1.0.2-dev", + "Status": "exited", + "Running": false, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 0, + "ExitCode": 0, + "Error": "", + "StartedAt": "2021-09-16T06:09:09.033564436-04:00", + "FinishedAt": "2021-09-16T06:09:09.036184314-04:00", + "Healthcheck": { + "Status": "", + "FailingStreak": 0, + "Log": null + } + }, + "Image": "14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab", + "ImageName": "docker.io/library/alpine:latest", + "Rootfs": "", + "Pod": "", + "ResolvConfPath": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/resolv.conf", + "HostnamePath": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/hostname", + "HostsPath": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/hosts", + "StaticDir": "/home/dwalsh/.local/share/containers/storage/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata", + "OCIConfigPath": "/home/dwalsh/.local/share/containers/storage/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/config.json", + "OCIRuntime": "crun", + "ConmonPidFile": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/conmon.pid", + "PidFile": "/run/user/3267/containers/overlay-containers/99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6/userdata/pidfile", + "Name": "foobar", + "RestartCount": 0, + "Driver": "overlay", + "MountLabel": "system_u:object_r:container_file_t:s0:c25,c695", + "ProcessLabel": "system_u:system_r:container_t:s0:c25,c695", + "AppArmorProfile": "", + "EffectiveCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ], + "BoundingCaps": [ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_NET_BIND_SERVICE", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ], + "ExecIDs": [], + "GraphDriver": { + "Name": "overlay", + "Data": { + "LowerDir": "/home/dwalsh/.local/share/containers/storage/overlay/e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68/diff", + "UpperDir": "/home/dwalsh/.local/share/containers/storage/overlay/8f3d70434a3db17410ec4710caf4f251f3e4ed0a96a08124e4b3d4af0a0ea300/diff", + "WorkDir": "/home/dwalsh/.local/share/containers/storage/overlay/8f3d70434a3db17410ec4710caf4f251f3e4ed0a96a08124e4b3d4af0a0ea300/work" + } + }, + "Mounts": [], + "Dependencies": [], + "NetworkSettings": { + "EndpointID": "", + "Gateway": "", + "IPAddress": "", + "IPPrefixLen": 0, + "IPv6Gateway": "", + "GlobalIPv6Address": "", + "GlobalIPv6PrefixLen": 0, + "MacAddress": "", + "Bridge": "", + "SandboxID": "", + "HairpinMode": false, + "LinkLocalIPv6Address": "", + "LinkLocalIPv6PrefixLen": 0, + "Ports": {}, + "SandboxKey": "" + }, + "ExitCommand": [ + "/usr/bin/podman", + "--root", + "/home/dwalsh/.local/share/containers/storage", + "--runroot", + "/run/user/3267/containers", + "--log-level", + "warning", + "--cgroup-manager", + "systemd", + "--tmpdir", + "/run/user/3267/libpod/tmp", + "--runtime", + "crun", + "--storage-driver", + "overlay", + "--events-backend", + "journald", + "container", + "cleanup", + "99f66530fe9c7249f7cf29f78e8661669d5831cbe4ee80ea757d5e922dd6a8a6" + ], + "Namespace": "", + "IsInfra": false, + "Config": { + "Hostname": "99f66530fe9c", + "Domainname": "", + "User": "", + "AttachStdin": false, + "AttachStdout": false, + "AttachStderr": false, + "Tty": false, + "OpenStdin": false, + "StdinOnce": false, + "Env": [ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + "container=podman", + "HOME=/root", + "HOSTNAME=99f66530fe9c" + ], + "Cmd": [ + "echo", + "hi" + ], + "Image": "docker.io/library/alpine:latest", + "Volumes": null, + "WorkingDir": "/", + "Entrypoint": "", + "OnBuild": null, + "Labels": null, + "Annotations": { + "io.container.manager": "libpod", + "io.kubernetes.cri-o.Created": "2021-09-16T06:09:08.936623325-04:00", + "io.kubernetes.cri-o.TTY": "false", + "io.podman.annotations.autoremove": "FALSE", + "io.podman.annotations.init": "FALSE", + "io.podman.annotations.privileged": "FALSE", + "io.podman.annotations.publish-all": "FALSE", + "org.opencontainers.image.stopSignal": "15" + }, + "StopSignal": 15, + "CreateCommand": [ + "podman", + "run", + "--name", + "foobar", + "alpine", + "echo", + "hi" + ], + "Timezone": "local", + "Umask": "0022", + "Timeout": 0, + "StopTimeout": 10 + }, + "HostConfig": { + "Binds": [], + "CgroupManager": "systemd", + "CgroupMode": "private", + "ContainerIDFile": "", + "LogConfig": { + "Type": "journald", + "Config": null, + "Path": "", + "Tag": "", + "Size": "0B" + }, + "NetworkMode": "slirp4netns", + "PortBindings": {}, + "RestartPolicy": { + "Name": "", + "MaximumRetryCount": 0 + }, + "AutoRemove": false, + "VolumeDriver": "", + "VolumesFrom": null, + "CapAdd": [], + "CapDrop": [ + "CAP_AUDIT_WRITE", + "CAP_MKNOD", + "CAP_NET_RAW" + ], + "Dns": [], + "DnsOptions": [], + "DnsSearch": [], + "ExtraHosts": [], + "GroupAdd": [], + "IpcMode": "private", + "Cgroup": "", + "Cgroups": "default", + "Links": null, + "OomScoreAdj": 0, + "PidMode": "private", + "Privileged": false, + "PublishAllPorts": false, + "ReadonlyRootfs": false, + "SecurityOpt": [], + "Tmpfs": {}, + "UTSMode": "private", + "UsernsMode": "", + "ShmSize": 65536000, + "Runtime": "oci", + "ConsoleSize": [ + 0, + 0 + ], + "Isolation": "", + "CpuShares": 0, + "Memory": 0, + "NanoCpus": 0, + "CgroupParent": "user.slice", + "BlkioWeight": 0, + "BlkioWeightDevice": null, + "BlkioDeviceReadBps": null, + "BlkioDeviceWriteBps": null, + "BlkioDeviceReadIOps": null, + "BlkioDeviceWriteIOps": null, + "CpuPeriod": 0, + "CpuQuota": 0, + "CpuRealtimePeriod": 0, + "CpuRealtimeRuntime": 0, + "CpusetCpus": "", + "CpusetMems": "", + "Devices": [], + "DiskQuota": 0, + "KernelMemory": 0, + "MemoryReservation": 0, + "MemorySwap": 0, + "MemorySwappiness": 0, + "OomKillDisable": false, + "PidsLimit": 2048, + "Ulimits": [], + "CpuCount": 0, + "CpuPercent": 0, + "IOMaximumIOps": 0, + "IOMaximumBandwidth": 0, + "CgroupConf": null + } + } +] +``` + +``` +$ podman container inspect nervous_fermi --format "{{.ImageName}}" +registry.access.redhat.com/ubi8:latest +``` + +``` +$ podman container inspect foobar --format "{{.GraphDriver.Name}}" +overlay +``` + +``` +$ podman container inspect --latest --format {{.EffectiveCaps}} +[CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_FSETID CAP_KILL CAP_NET_BIND_SERVICE CAP_SETFCAP CAP_SETGID CAP_SETPCAP CAP_SETUID CAP_SYS_CHROOT] +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**,**[podman-container(1)](podman-container.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** + +## HISTORY +Sep 2021, Originally compiled by Dan Walsh diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-prune.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-prune.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-prune.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-prune.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,7 @@ % podman-container-prune(1) ## NAME -podman-container-prune - Remove all stopped containers from local storage +podman\-container\-prune - Remove all stopped containers from local storage ## SYNOPSIS **podman container prune** [*options*] @@ -10,35 +10,37 @@ **podman container prune** removes all stopped containers from local storage. ## OPTIONS - #### **--filter**=*filters* Provide filter values. -The --filter flag format is of “key=value”. If there is more than one filter, then pass multiple flags (e.g., --filter "foo=bar" --filter "bif=baz") +The *filters* argument format is of `key=value`. If there is more than one *filter*, then pass multiple OPTIONS: **--filter** *foo=bar* **--filter** *bif=baz*. Supported filters: -- `until` (_timestamp_) - only remove containers and images created before given timestamp -- `label` (label=_key_, label=_key=value_, label!=_key_, or label!=_key=value_) - only remove containers and images, with (or without, in case label!=... is used) the specified labels. +| Filter | Description | +| :----------------: | --------------------------------------------------------------------------- | +| *until* | Only remove containers and images created before given timestamp. | +| *label* | Only remove containers and images, with (or without, in the case of label!=[...] is used) the specified labels. | -The until filter can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the machine’s time. +The `until` *filter* can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the machine’s time. -The label filter accepts two formats. One is the label=... (label=_key_ or label=_key=value_), which removes containers with the specified labels. The other format is the label!=... (label!=_key_ or label!=_key=value_), which removes containers without the specified labels. +The `label` *filter* accepts two formats. One is the `label`=*key*, `label`=*key*=*value*, which removes containers with the specified labels. The other format is the `label!`=*key*, or `label!`=*key*=*value*, which removes containers without the specified labels. #### **--force**, **-f** -Do not provide an interactive prompt for container removal. +Do not provide an interactive prompt for container removal.\ +The default is **false**. **-h**, **--help** -Print usage statement +Print usage statement.\ +The default is **false**. ## EXAMPLES - Remove all stopped containers from local storage ``` -$ sudo podman container prune +$ podman container prune WARNING! This will remove all stopped containers. Are you sure you want to continue? [y/N] y 878392adf2e6c5c9bb1fc19b69d37d2e98c8abf9d539c0bce4b15b46bbcce471 @@ -51,27 +53,26 @@ Remove all stopped containers from local storage without confirmation. ``` -$ sudo podman container prune -f +$ podman container prune -f 878392adf2e6c5c9bb1fc19b69d37d2e98c8abf9d539c0bce4b15b46bbcce471 37664467fbe3618bf9479c34393ac29c02696675addf1750f9e346581636cde7 ed0c6468b8e1cb641b4621d1fe30cb477e1fefc5c0bceb66feaf2f7cb50e5962 6ac6c8f0067b7a4682e6b8e18902665b57d1a0e07e885d9abcd382232a543ccd fff1c5b6c3631746055ec40598ce8ecaa4b82aef122f9e3a85b03b55c0d06c23 602d343cd47e7cb3dfc808282a9900a3e4555747787ec6723bb68cedab8384d5 - ``` Remove all stopped containers from local storage created within last 10 minutes ``` -$ sudo podman container prune --filter until="10m" +$ podman container prune --filter until="10m" WARNING! This will remove all stopped containers. Are you sure you want to continue? [y/N] y 3d366295e33d8cc612c4d873199bacadd55088d90d17dcafaa9a2d317ad50b4e ``` ## SEE ALSO -podman(1), podman-ps +**[podman(1)](podman.1.md)**, **[podman-ps](podman-ps.1.md)** ## HISTORY -December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) -December 2020, converted filter information from docs.docker.com documentation by Dan Walsh (dwalsh at redhat dot com) +December 2018, Originally compiled by Brent Baude \ +December 2020, converted filter information from docs.docker.com documentation by Dan Walsh diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-restore.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-restore.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-restore.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-restore.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,107 +4,140 @@ podman\-container\-restore - Restores one or more containers from a checkpoint ## SYNOPSIS -**podman container restore** [*options*] *container* ... +**podman container restore** [*options*] *container* [*container* ...] ## DESCRIPTION -Restores a container from a checkpoint. You may use container IDs or names as input. +**podman container restore** restores a container from a checkpoint. The *container IDs* or *names* are used as input. ## OPTIONS +#### **--all**, **-a** + +Restore all checkpointed *containers*.\ +The default is **false**.\ +*IMPORTANT: This OPTION does not need a container name or ID as input argument.* + #### **--keep**, **-k** -Keep all temporary log and statistics files created by CRIU during +Keep all temporary log and statistics files created by `CRIU` during checkpointing as well as restoring. These files are not deleted if restoring fails for further debugging. If restoring succeeds these files are theoretically not needed, but if these files are needed Podman can keep the files for further analysis. This includes the checkpoint directory with all files created during checkpointing. The size required by the checkpoint directory is roughly the same as the amount of memory required by the -processes in the checkpointed container. +processes in the checkpointed *container*.\ +Without the **--keep**, **-k** option the checkpoint will be consumed and cannot be used again.\ +The default is **false**. -Without the **-k**, **--keep** option the checkpoint will be consumed and cannot be used -again. +#### **--latest**, **-l** -#### **--all**, **-a** +Instead of providing the *container ID* or *name*, use the last created *container*. If other tools than Podman are used to run *containers* such as `CRI-O`, the last started *container* could be from either tool.\ +The default is **false**.\ +*IMPORTANT: This OPTION is not available with the remote Podman client. This OPTION does not need a container name or ID as input argument.* -Restore all checkpointed containers. +#### **--ignore-rootfs** -#### **--latest**, **-l** +If a *container* is restored from a checkpoint tar.gz file it is possible that it also contains all root file-system changes. With **--ignore-rootfs** it is possible to explicitly disable applying these root file-system changes to the restored *container*.\ +The default is **false**.\ +*IMPORTANT: This OPTION is only available in combination with **--import, -i**.* -Instead of providing the container name or ID, restore the last created container. (This option is not available with the remote Podman client) +#### **--ignore-static-ip** -#### **--tcp-established** +If the *container* was started with **--ip** the restored *container* also tries to use that +IP address and restore fails if that IP address is already in use. This can happen, if +a *container* is restored multiple times from an exported checkpoint with **--name, -n**. -Restore a container with established TCP connections. If the checkpoint image -contains established TCP connections, this option is required during restore. -If the checkpoint image does not contain established TCP connections this -option is ignored. Defaults to not restoring containers with established TCP -connections. +Using **--ignore-static-ip** tells Podman to ignore the IP address if it was configured +with **--ip** during *container* creation. -#### **--import**, **-i** +The default is **false**. -Import a checkpoint tar.gz file, which was exported by Podman. This can be used -to import a checkpointed container from another host. Do not specify a *container* -argument when using this option. +#### **--ignore-static-mac** -#### **--import-previous** +If the *container* was started with **--mac-address** the restored *container* also +tries to use that MAC address and restore fails if that MAC address is already +in use. This can happen, if a *container* is restored multiple times from an +exported checkpoint with **--name, -n**. -Import a pre-checkpoint tar.gz file which was exported by Podman. This option -must be used with **-i** or **--import**. It only works on runc 1.0-rc3 or higher. +Using **--ignore-static-mac** tells Podman to ignore the MAC address if it was +configured with **--mac-address** during *container* creation. -#### **--name**, **-n** +The default is **false**. -This is only available in combination with **--import, -i**. If a container is restored -from a checkpoint tar.gz file it is possible to rename it with **--name, -n**. This -way it is possible to restore a container from a checkpoint multiple times with different -names. +#### **--ignore-volumes** -If the **--name, -n** option is used, Podman will not attempt to assign the same IP -address to the container it was using before checkpointing as each IP address can only -be used once and the restored container will have another IP address. This also means -that **--name, -n** cannot be used in combination with **--tcp-established**. +This option must be used in combination with the **--import, -i** option. +When restoring *containers* from a checkpoint tar.gz file with this option, +the content of associated volumes will not be restored.\ +The default is **false**. -#### **--ignore-rootfs** +#### **--import**, **-i**=*file* -This is only available in combination with **--import, -i**. If a container is restored -from a checkpoint tar.gz file it is possible that it also contains all root file-system -changes. With **--ignore-rootfs** it is possible to explicitly disable applying these -root file-system changes to the restored container. +Import a checkpoint tar.gz file, which was exported by Podman. This can be used +to import a checkpointed *container* from another host.\ +*IMPORTANT: This OPTION does not need a container name or ID as input argument.* -#### **--ignore-static-ip** +#### **--import-previous**=*file* -If the container was started with **--ip** the restored container also tries to use that -IP address and restore fails if that IP address is already in use. This can happen, if -a container is restored multiple times from an exported checkpoint with **--name, -n**. +Import a pre-checkpoint tar.gz file which was exported by Podman. This option +must be used with **-i** or **--import**. It only works on `runc 1.0-rc3` or `higher`. -Using **--ignore-static-ip** tells Podman to ignore the IP address if it was configured -with **--ip** during container creation. +#### **--name**, **-n**=*name* -#### **--ignore-static-mac** +If a *container* is restored from a checkpoint tar.gz file it is possible to rename it with **--name, -n**. This way it is possible to restore a *container* from a checkpoint multiple times with different +names. -If the container was started with **--mac-address** the restored container also -tries to use that MAC address and restore fails if that MAC address is already -in use. This can happen, if a container is restored multiple times from an -exported checkpoint with **--name, -n**. +If the **--name, -n** option is used, Podman will not attempt to assign the same IP +address to the *container* it was using before checkpointing as each IP address can only +be used once and the restored *container* will have another IP address. This also means +that **--name, -n** cannot be used in combination with **--tcp-established**.\ +*IMPORTANT: This OPTION is only available in combination with **--import, -i**.* -Using **--ignore-static-mac** tells Podman to ignore the MAC address if it was -configured with **--mac-address** during container creation. +#### **--pod**=*name* -#### **--ignore-volumes** +Restore a container into the pod *name*. The destination pod for this restore +has to have the same namespaces shared as the pod this container was checkpointed +from (see **[podman pod create --share](podman-pod-create.1.md#--share)**). +*IMPORTANT: This OPTION is only available in combination with **--import, -i**.* -This option must be used in combination with the **--import, -i** option. -When restoring containers from a checkpoint tar.gz file with this option, -the content of associated volumes will not be restored. +This option requires at least CRIU 3.16. -## EXAMPLE +#### **--publish**, **-p**=*port* -podman container restore mywebserver +Replaces the ports that the *container* publishes, as configured during the +initial *container* start, with a new set of port forwarding rules. -podman container restore 860a4b23 +For more details please see **[podman run --publish](podman-run.1.md#--publish)**. -podman container restore --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz +#### **--tcp-established** + +Restore a *container* with established TCP connections. If the checkpoint image +contains established TCP connections, this option is required during restore. +If the checkpoint image does not contain established TCP connections this +option is ignored. Defaults to not restoring *containers* with established TCP +connections.\ +The default is **false**. + +## EXAMPLE +Restores the container "mywebserver". +``` +# podman container restore mywebserver +``` + +Import a checkpoint file and a pre-checkpoint file. +``` +# podman container restore --import-previous pre-checkpoint.tar.gz --import checkpoint.tar.gz +``` + +Remove the container "mywebserver". Make a checkpoint of the container and export it. Restore the container with other port ranges from the exported file. +``` +$ podman run --rm -p 2345:80 -d webserver +# podman container checkpoint -l --export=dump.tar +# podman container restore -p 5432:8080 --import=dump.tar +``` ## SEE ALSO -podman(1), podman-container-checkpoint(1) +**[podman(1)](podman.1.md)**, **[podman-container-checkpoint(1)](podman-container-checkpoint.1.md)**, **[podman-run(1)](podman-run.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)** ## HISTORY September 2018, Originally compiled by Adrian Reber diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-container-runlabel.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-container-runlabel.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-container-runlabel.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-container-runlabel.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,76 +1,59 @@ % podman-container-runlabel(1) ## NAME -podman-container-runlabel - Executes a command as described by a container image label +podman-container-runlabel - Executes a command as described by a container-image label ## SYNOPSIS **podman container runlabel** [*options*] *label* *image* [*arg...*] ## DESCRIPTION -**podman container runlabel** reads the provided `LABEL` field in the container -IMAGE and executes the provided value for the label as a command. If this field does not -exist, `podman container runlabel` will just exit. +**podman container runlabel** reads the specified `label` of the `image` and executes it as command on the host. If the label does not exist, Podman will exit with an error. Additional arguments will be appended to the command. -If the container image has a LABEL INSTALL instruction like the following: +Historically, container images describe the contents (e.g., layers) and how a container runtime (e.g., crun(1) or runc(1)) should execute the container. For instance, an image may set the environment and the command in its configuration. However, a container image cannot directly specify how a container engine such as Podman should execute it. For instance, an image configuration does not include information about log drivers, namespaces or which capabilities it needs to run correctly. -`LABEL INSTALL /usr/bin/podman run -t -i --rm \${OPT1} --privileged -v /:/host --net=host --ipc=host --pid=host -e HOST=/host -e NAME=\${NAME} -e IMAGE=\${IMAGE} -e CONFDIR=/etc/\${NAME} -e LOGDIR=/var/log/\${NAME} -e DATADIR=/var/lib/\${NAME} \${IMAGE} \${OPT2} /bin/install.sh \${OPT3}` +`podman container runlabel` addresses the limitation of container images in a simple yet efficient way. Podman will read the contents of the label and interpret it as a command that will be executed on the host. This way an image can describe exactly how it should be executed by Podman. For instance, a label with the content `/usr/bin/podman run -d --pid=host --privileged \${IMAGE}` instructs the image to be executed in a detached, privileged container that is using the PID namespace of the host. This lifts the self-description of a container image from "what" to "how". -`podman container runlabel` will set the following environment variables for use in the command: +Please note that the `runlabel` command is intended to be run in trusted environments exclusively. Using the command on untrusted images is not recommended. -If the container image does not have the desired label, an error message will be displayed along with a non-zero -return code. If the image is not found in local storage, Podman will attempt to pull it first. +## VARIABLES -**LABEL** -The label name specified via the command. +The contents of a label may refer to the following variables which will be substituted while processing the label. **IMAGE** -Image name specified via the command. +The name of the image. When executing `podman container runlabel label fedora` the `IMAGE` variable will be replaced with `fedora`. Valid formats are `IMAGE`, `$IMAGE`, `${IMAGE}` and `=IMAGE`. -**SUDO_UID** -The `SUDO_UID` environment variable. This is useful with the podman -`-u` option for user space tools. If the environment variable is -not available, the value of `/proc/self/loginuid` is used. - -**SUDO_GID** -The `SUDO_GID` environment variable. This is useful with the podman -`-u` option for user space tools. If the environment variable is -not available, the default GID of the value for `SUDO_UID` is used. -If this value is not available, the value of `/proc/self/loginuid` -is used. +**NAME** +As specified by the `--name` option. The format is identical to the one of the IMAGE attribute. -Any additional arguments will be appended to the command. +**PWD** +Will be replaced with the current working directory. ## OPTIONS #### **--authfile**=*path* -Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. +Path of the containers-auth.json(5) file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. -Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE -environment variable. `export REGISTRY_AUTH_FILE=path` +Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` #### **--display** -Display the label's value of the image having populated its environment variables. -The runlabel command will not execute if --display is specified. +Display the label's value of the image having populated its environment variables. The runlabel command will not execute if --display is specified. #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--creds**=*[username[:password]]* -The [username[:password]] to use to authenticate with the registry if required. -If one or both values are not supplied, a command line prompt will appear and the -value can be entered. The password is entered without echo. +The [username[:password]] to use to authenticate with the registry if required. If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. #### **--help**, **-h** Print usage statement #### **--name**, **-n**=*name* -Use this name for creating content for the container. NAME will default to the IMAGENAME if it is not specified. +Use this name for creating content for the container. If not specified, name defaults to the name of the image. #### **--quiet**, **-q** @@ -78,34 +61,33 @@ #### **--replace** -If a container exists of the default or given name, as needed it will be stopped, deleted and a new container will be -created from this image. +If a container exists of the default or given name, as needed it will be stopped, deleted and a new container will be created from this image. #### **--tls-verify** -Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, -then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, -TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. +Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, TLS verification will be used unless the target registry is listed as an insecure registry in containers-registries.conf(5). ## EXAMPLES -Execute the run label of an image called foobar. +Execute the `run` label of an image called foobar. ``` -$ sudo podman container runlabel run foobar +$ podman container runlabel run foobar ``` -Execute the install label of an image called foobar with additional arguments. +Execute the `install` label of an image called foobar with additional arguments. ``` -$ sudo podman container runlabel install foobar apples oranges +$ podman container runlabel install foobar apples oranges ``` -Display the command that would be executed by runlabel. +Display the contents of the `run` label of image foobar. ``` -$ sudo podman container runlabel --display run foobar +$ podman container runlabel --display run foobar ``` ## SEE ALSO -podman(1), containers-certs.d(5) +podman(1), crun(1), runc(1), containers-auth.json(5), containers-certs.d(5), containers-registries.conf(5) ## HISTORY +August 2021, Refinements by Valentin Rothberg (rothberg at redhat dot com) + September 2018, Originally compiled by Brent Baude (bbaude at redhat dot com) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-cp.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-cp.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-cp.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-cp.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,109 +4,128 @@ podman\-cp - Copy files/folders between a container and the local filesystem ## SYNOPSIS -**podman cp** [*container*:]*src_path* [*container*:]*dest_path* +**podman cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path* -**podman container cp** [*container*:]*src_path* [*container*:]*dest_path* +**podman container cp** [*options*] [*container*:]*src_path* [*container*:]*dest_path* ## DESCRIPTION -Copy the contents of **src_path** to the **dest_path**. You can copy from the container's filesystem to the local machine or the reverse, from the local filesystem to the container. -If `-` is specified for either the SRC_PATH or DEST_PATH, you can also stream a tar archive from STDIN or to STDOUT. +**podman cp** allows copying the contents of **src_path** to the **dest_path**. Files can be copied from a container to the local machine and vice versa or between two containers. +If `-` is specified for either the `SRC_PATH` or `DEST_PATH`, one can also stream a tar archive from `STDIN` or to `STDOUT`. -The CONTAINER can be a running or stopped container. The **src_path** or **dest_path** can be a file or directory. +The containers can be either running or stopped and the *src_path* or *dest_path* can be a file or directory. -The **podman cp** command assumes container paths are relative to the container's root directory (i.e., `/`). - -This means supplying the initial forward slash is optional; - -The command sees **compassionate_darwin:/tmp/foo/myfile.txt** and **compassionate_darwin:tmp/foo/myfile.txt** as identical. +*IMPORTANT: The **podman cp** command assumes container paths are relative to the container's root directory (`/`), which means supplying the initial forward slash is optional and therefore sees `compassionate_darwin:/tmp/foo/myfile.txt` and `compassionate_darwin:tmp/foo/myfile.txt` as identical.* Local machine paths can be an absolute or relative value. The command interprets a local machine's relative paths as relative to the current working directory where **podman cp** is run. -Assuming a path separator of /, a first argument of **src_path** and second argument of **dest_path**, the behavior is as follows: +Assuming a path separator of `/`, a first argument of **src_path** and second argument of **dest_path**, the behavior is as follows: -**src_path** specifies a file +**src_path** specifies a file: - **dest_path** does not exist - - the file is saved to a file created at **dest_path** (note that parent directory must exist) + - the file is saved to a file created at **dest_path** (note that parent directory must exist). - **dest_path** exists and is a file - - the destination is overwritten with the source file's contents + - the destination is overwritten with the source file's contents. - **dest_path** exists and is a directory - - the file is copied into this directory using the base name from **src_path** + - the file is copied into this directory using the base name from **src_path**. -**src_path** specifies a directory +**src_path** specifies a directory: - **dest_path** does not exist - - **dest_path** is created as a directory and the contents of the source directory are copied into this directory + - **dest_path** is created as a directory and the contents of the source directory are copied into this directory. - **dest_path** exists and is a file - - Error condition: cannot copy a directory to a file + - Error condition: cannot copy a directory to a file. - **dest_path** exists and is a directory - **src_path** ends with `/` - - the source directory is copied into this directory + - the source directory is copied into this directory. - **src_path** ends with `/.` (i.e., slash followed by dot) - - the content of the source directory is copied into this directory + - the content of the source directory is copied into this directory. The command requires **src_path** and **dest_path** to exist according to the above rules. If **src_path** is local and is a symbolic link, the symbolic target, is copied by default. -A colon (:) is used as a delimiter between CONTAINER and its path. - -You can also use : when specifying paths to a **src_path** or **dest_path** on a local machine, for example, `file:name.txt`. +A *colon* ( : ) is used as a delimiter between a container and its path, it can also be used when specifying paths to a **src_path** or **dest_path** on a local machine, for example, `file:name.txt`. -If you use a : in a local machine path, you must be explicit with a relative or absolute path, for example: - `/path/to/file:name.txt` or `./file:name.txt` +*IMPORTANT: while using a *colon* ( : ) in a local machine path, one must be explicit with a relative or absolute path, for example: `/path/to/file:name.txt` or `./file:name.txt`* -Using `-` as the *src_path* streams the contents of STDIN as a tar archive. The command extracts the content of the tar to the *DEST_PATH* in the container. In this case, *dest_path* must specify a directory. Using `-` as the *dest_path* streams the contents of the resource (can be a directory) as a tar archive to STDOUT. +Using `-` as the **src_path** streams the contents of `STDIN` as a tar archive. The command extracts the content of the tar to the `DEST_PATH` in the container. In this case, **dest_path** must specify a directory. Using `-` as the **dest_path** streams the contents of the resource (can be a directory) as a tar archive to `STDOUT`. Note that `podman cp` ignores permission errors when copying from a running rootless container. The TTY devices inside a rootless container are owned by the host's root user and hence cannot be read inside the container's user namespace. +Further note that `podman cp` does not support globbing (e.g., `cp dir/*.txt`). If you want to copy multiple files from the host to the container you may use xargs(1) or find(1) (or similar tools for chaining commands) in conjunction with `podman cp`. If you want to copy multiple files from the container to the host, you may use `podman mount CONTAINER` and operate on the returned mount point instead (see ALTERNATIVES below). + ## OPTIONS -## ALTERNATIVES +#### **--archive**, **-a**=**true** | *false* -Podman has much stronger capabilities than just `podman cp` to achieve copy files between host and container. +Archive mode (copy all uid/gid information). +When set to true, files copied to a container will have changed ownership to the primary UID/GID of the container. +When set to false, maintain uid/gid from archive sources instead of changing them to the primary uid/gid of the destination container. +The default is **true**. -Using standard podman-mount and podman-umount takes advantage of the entire linux tool chain, rather -then just cp. +## ALTERNATIVES -If a user wants to copy contents out of a container or into a container, they can execute a few simple commands. +Podman has much stronger capabilities than just `podman cp` to achieve copying files between the host and containers. -You can copy from the container's file system to the local machine or the reverse, from the local filesystem to the container. +Using standard **[podman-mount(1)](podman-mount.1.md)** and **[podman-unmount(1)](podman-unmount.1.md)** takes advantage of the entire linux tool chain, rather than just cp. -If you want to copy the /etc/foobar directory out of a container and onto /tmp on the host, you could execute the following commands: +copying contents out of a container or into a container, can be achieved with a few simple commands. For example: + +To copy the `/etc/foobar` directory out of a container and onto `/tmp` on the host, the following commands can be executed: mnt=$(podman mount CONTAINERID) cp -R ${mnt}/etc/foobar /tmp podman umount CONTAINERID -If you want to untar a tar ball into a container, you can execute these commands: +To untar a tar ball into a container, following commands can be executed: mnt=$(podman mount CONTAINERID) tar xf content.tgz -C ${mnt} podman umount CONTAINERID -One last example, if you want to install a package into a container that -does not have dnf installed, you could execute something like: +To install a package into a container that +does not have dnf installed, following commands can be executed: mnt=$(podman mount CONTAINERID) dnf install --installroot=${mnt} httpd chroot ${mnt} rm -rf /var/log/dnf /var/cache/dnf podman umount CONTAINERID -This shows that using `podman mount` and `podman umount` you can use all of the +By using `podman mount` and `podman unmount`, one can use all of the standard linux tools for moving files into and out of containers, not just the cp command. -## EXAMPLE - -podman cp /myapp/app.conf containerID:/myapp/app.conf - -podman cp /home/myuser/myfiles.tar containerID:/tmp - -podman cp containerID:/myapp/ /myapp/ - -podman cp containerID:/home/myuser/. /home/myuser/ +## EXAMPLES -podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz +- Copy a file from host to a container. + ``` + podman cp /myapp/app.conf containerID:/myapp/app.conf + ``` + +- Copy a file from a container to a directory on another container. + ``` + podman cp containerID1:/myfile.txt containerID2:/tmp + ``` + +- Copy a directory on a container to a directory on the host. + ``` + podman cp containerID:/myapp/ /myapp/ + ``` + +- Copy the contents of a directory on a container to a directory on the host. + ``` + podman cp containerID:/home/myuser/. /home/myuser/ + ``` + +- Copy a directory on a container into a directory on another. + ``` + podman cp containerA:/myapp containerB:/yourapp + ``` + +- Stream a tar archive from `STDIN` to a container. + ``` + podman cp - containerID:/myfiles.tar.gz < myfiles.tar.gz + ``` ## SEE ALSO -podman(1), podman-mount(1), podman-umount(1) +**[podman(1)](podman.1.md)**, **[podman-mount(1)](podman-mount.1.md)**, **[podman-unmount(1)](podman-unmount.1.md)** diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-create.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-create.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-create.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-create.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -181,6 +181,8 @@ Limit the container's Real Time CPU usage. This flag tell the kernel to restrict the container's Real Time CPU usage to the period you specify. +This flag is not supported on cgroups V2 systems. + #### **--cpu-rt-runtime**=*microseconds* Limit the CPU real-time runtime in microseconds @@ -190,6 +192,8 @@ The sum of all runtimes across containers cannot exceed the amount allotted to the parent cgroup. +This flag is not supported on cgroups V2 systems. + #### **--cpu-shares**=*shares* CPU shares (relative weight) @@ -443,6 +447,20 @@ Run an init inside the container that forwards signals and reaps processes. +#### **--init-ctr**=*type* (pods only) + +When using pods, create an init style container, which is run after the infra container is started +but before regular pod containers are started. Init containers are useful for running +setup operations for the pod's applications. + +Valid values for `init-ctr` type are *always* or *once*. The *always* value +means the container will run with each and every `pod start`, whereas the *once* +value means the container will only run once when the pod is started and then the container is removed. + +Init containers are only run on pod `start`. Restarting a pod will not execute any init +containers should they be present. Furthermore, init containers can only be created in a +pod when that pod is not running. + #### **--init-path**=*path* Path to the container-init binary. @@ -479,6 +497,8 @@ of the operating system's page size and the value can be very large, millions of trillions. +This flag is not supported on cgroups V2 systems. + #### **--label**, **-l**=*label* Add metadata to a container (e.g., --label com.example.key=value) @@ -561,6 +581,8 @@ Tune a container's memory swappiness behavior. Accepts an integer between 0 and 100. +This flag is not supported on cgroups V2 systems. + #### **--mount**=*type=TYPE,TYPE-SPECIFIC-OPTION[,...]* Attach a filesystem mount to the container @@ -638,15 +660,15 @@ Valid _mode_ values are: -- **bridge**: create a network stack on the default bridge; -- **none**: no networking; -- **container:**_id_: reuse another container's network stack; -- **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- _network-id_: connect to a user-defined network, multiple networks should be comma-separated; -- **ns:**_path_: path to a network namespace to join; -- **private**: create a new namespace for the container (default) +- **bridge**: Create a network stack on the default bridge. This is the default for rootfull containers. +- **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity. +- **container:**_id_: Reuse another container's network stack. +- **host**: Do not create a network namespace, the container will use the host's network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. +- **network**: Connect to a user-defined network, multiple networks should be comma-separated. +- **ns:**_path_: Path to a network namespace to join. +- **private**: Create a new namespace for the container. This will use the **bridge** mode for rootfull containers and **slirp4netns** for rootless ones. - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. + - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`, which is added to `/etc/hosts` as `host.containers.internal` for your convenience). Default is false. - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`). @@ -655,11 +677,12 @@ - **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only). - **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to. - **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default. - - **port_handler=slirp4netns**: Use the slirp4netns port forwarding. + Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks. + - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks. #### **--network-alias**=*alias* -Add network-scoped alias for the container +Add network-scoped alias for the container. NOTE: A container will only have access to aliases on the first network that it joins. This is a limitation that will be removed in a later release. #### **--no-healthcheck**=*true|false* @@ -683,6 +706,10 @@ #### **--os**=*OS* Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`. +#### **--personality**=*persona* + +Personality sets the execution domain via Linux personality(2). + #### **--pid**=*pid* Set the PID mode for the container @@ -694,7 +721,7 @@ #### **--pids-limit**=*limit* -Tune the container's pids limit. Set `0` to have unlimited pids for the container. (default "4096" on systems that support PIDS cgroups). +Tune the container's pids limit. Set `-1` to have unlimited pids for the container. (default "4096" on systems that support PIDS cgroups). #### **--platform**=*OS/ARCH* @@ -858,7 +885,10 @@ Secret Options - `type=mount|env` : How the secret will be exposed to the container. Default mount. -- `target=target` : Target of secret. Defauts to secret name. +- `target=target` : Target of secret. Defaults to secret name. +- `uid=0` : UID of secret. Defaults to 0. Mount secret type only. +- `gid=0` : GID of secret. Defaults to 0. Mount secret type only. +- `mode=0` : Mode of secret. Defaults to 0444. Mount secret type only. #### **--security-opt**=*option* @@ -961,6 +991,10 @@ signal. By default containers will run until they exit or are stopped by `podman stop`. +#### **--tls-verify**=**true**|**false** + +Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. + #### **--tmpfs**=*fs* Create a tmpfs mount @@ -1083,15 +1117,29 @@ Valid _mode_ values are: -- **auto[:**_OPTIONS,..._**]**: automatically create a namespace. It is possible to specify these options to `auto`: - - **gidmapping=**_HOST_GID:CONTAINER_GID:SIZE_: to force a GID mapping to be present in the user namespace. - - **size=**_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. - - **uidmapping=**_HOST_UID:CONTAINER_UID:SIZE_: to force a UID mapping to be present in the user namespace. -- **container:**_id_: join the user namespace of the specified container. -- **host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). -- **keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user. -- **ns:**_namespace_: run the container in the given existing user namespace. -- **private**: create a new namespace for the container. +**auto**[:_OPTIONS,..._]: automatically create a unique user namespace. + +The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files. + +Example: `containers:2147483647:2147483648`. + +Podman allocates unique ranges of UIDs and GIDs from the `containers` subpordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. The `auto` options currently does not work in rootless mode + + Valid `auto` options: + + - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace. + - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. + - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace. + +**container:**_id_: join the user namespace of the specified container. + +**host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). + +**keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user. + +**ns:**_namespace_: run the container in the given existing user namespace. + +**private**: create a new namespace for the container. This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. @@ -1183,6 +1231,15 @@ The `Z` option tells Podman to label the content with a private unshared label. Only the current container can use a private volume. +Note: Do not relabel system files and directories. 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` disables SELinux separation for containers used in the build. +For example if a user wanted to volume mount their entire home directory into a +container, they need to disable SELinux separation. + + $ podman create --security-opt label=disable -v $HOME:/home/user fedora touch /home/user/file + `Overlay Volume Mounts` The `:O` flag tells Podman to mount the directory from the host as a @@ -1350,6 +1407,12 @@ $ podman create --uidmap 0:30000:7000 --gidmap 0:30000:7000 fedora echo hello ``` +### Setting automatic user namespace separated containers + +``` +# podman create --userns=auto:size=65536 ubi8-init +``` + ### Configure timezone in a container ``` @@ -1383,6 +1446,12 @@ $ podman create -v /var/lib/design:/var/lib/design --group-add keep-groups ubi8 ``` +### Configure execution domain for containers using personality flag + +``` +$ podman create --name container1 --personaity=LINUX32 fedora bash +``` + ### Rootless Containers Podman runs as a non root user on most systems. This feature requires that a new enough version of shadow-utils @@ -1393,7 +1462,7 @@ In order for users to run rootless, there must be an entry for their username in /etc/subuid and /etc/subgid which lists the UIDs for their user namespace. Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed. -The fuse-overlay package provides a userspace overlay storage driver, otherwise users need to use +The fuse-overlayfs package provides a userspace overlay storage driver, otherwise users need to use the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is required for VPN, without it containers need to be run with the --network=host flag. @@ -1445,7 +1514,7 @@ ## SEE ALSO **podman**(1), **podman-secret**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-start*(1), **podman-kill**(1), **podman-stop**(1), -**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5), **conmon**(8). +**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5), **conmon**(8), **personality**(2). ## HISTORY October 2017, converted from Docker documentation to Podman by Dan Walsh for Podman `` diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-diff.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-diff.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-diff.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-diff.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,18 +4,24 @@ podman\-diff - Inspect changes on a container or image's filesystem ## SYNOPSIS -**podman diff** [*options*] *name* - -**podman container diff** [*options*] *name* +**podman diff** [*options*] *container|image* [*container|image*] ## DESCRIPTION -Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer +Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer or the second argument when given. + +The output is prefixed with the following symbols: + +| Symbol | Description | +|--------|-------------| +| A | A file or directory was added. | +| D | A file or directory was deleted. | +| C | A file or directory was changed. | ## OPTIONS #### **--format** -Alter the output into a different format. The only valid format for diff is `json`. +Alter the output into a different format. The only valid format for **podman diff** is `json`. #### **--latest**, **-l** @@ -25,15 +31,12 @@ ## EXAMPLE ``` -# podman diff redis:alpine -C /usr -C /usr/local -C /usr/local/bin -A /usr/local/bin/docker-entrypoint.sh +$ podman diff container1 +A /myscript.sh ``` ``` -# podman diff --format json redis:alpine +$ podman diff --format json myimage { "changed": [ "/usr", @@ -46,8 +49,13 @@ } ``` +``` +$ podman diff container1 image1 +A /test +``` + ## SEE ALSO -podman(1) +**[podman(1)](podman.1.md)**, **[podman-container-diff(1)](podman-container-diff.1.md)**, **[podman-image-diff(1)](podman-image-diff.1.md)** ## HISTORY August 2017, Originally compiled by Ryan Cole diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-generate-kube.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-generate-kube.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-generate-kube.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-generate-kube.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -16,6 +16,15 @@ Potential name conflicts between volumes are avoided by using a standard naming scheme for each volume type. The *hostPath* volume types are named according to the path on the host machine, replacing forward slashes with hyphens less any leading and trailing forward slashes. The special case of the filesystem root, `/`, translates to the name `root`. Additionally, the name is suffixed with `-host` to avoid naming conflicts with *persistentVolumeClaim* volumes. Each *persistentVolumeClaim* volume type uses the name of its associated named volume suffixed with `-pvc`. +Note that if an init container is created with type `once` and the pod has been started, the init container will not show up in the generated kube YAML as `once` type init containers are deleted after they are run. If the pod has only been created and not started, it will be in the generated kube YAML. +Init containers created with type `always` will always be generated in the kube YAML as they are never deleted, even after running to completion. + +*Note*: When using volumes and generating a Kubernetes YAML for an unprivileged and rootless podman container on an **SELinux enabled system**, one of the following options must be completed: + * Add the "privileged: true" option to the pod spec + * Add `type: spc_t` under the `securityContext` `seLinuxOptions` in the pod spec + * Relabel the volume via the CLI command `chcon -t container_file_t context -R ` +Once completed, the correct permissions will be in place to access the volume when the pod/container is created in a Kubernetes cluster. + Note that the generated Kubernetes YAML file can be used to re-run the deployment via podman-play-kube(1). ## OPTIONS @@ -34,8 +43,6 @@ Create Kubernetes Pod YAML for a container called `some-mariadb`. ``` $ sudo podman generate kube some-mariadb -# Generation of Kubernetes YAML is still under development! -# # Save the output of this file and use kubectl create -f to import # it into Kubernetes. # @@ -53,13 +60,7 @@ - docker-entrypoint.sh - mysqld env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - name: HOSTNAME - - name: container - value: podman - name: GOSU_VERSION value: "1.10" - name: GPG_KEYS @@ -76,22 +77,20 @@ ports: - containerPort: 3306 hostPort: 36533 - protocol: TCP resources: {} securityContext: - allowPrivilegeEscalation: true - privileged: false - readOnlyRootFilesystem: false + capabilities: + drop: + - CAP_MKNOD + - CAP_NET_RAW + - CAP_AUDIT_WRITE tty: true - workingDir: / status: {} ``` Create Kubernetes Pod YAML for a container with the directory `/home/user/my-data` on the host bind-mounted in the container to `/volume`. ``` $ podman generate kube my-container-with-bind-mounted-data -# Generation of Kubernetes YAML is still under development! -# # Save the output of this file and use kubectl create -f to import # it into Kubernetes. # @@ -107,31 +106,18 @@ containers: - command: - /bin/sh - env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman image: docker.io/library/alpine:latest name: test-bind-mount resources: {} securityContext: - allowPrivilegeEscalation: true capabilities: drop: - CAP_MKNOD - CAP_NET_RAW - CAP_AUDIT_WRITE - privileged: false - readOnlyRootFilesystem: false - seLinuxOptions: {} volumeMounts: - mountPath: /volume name: home-user-my-data-host - workingDir: / - dnsConfig: {} restartPolicy: Never volumes: - hostPath: @@ -144,8 +130,6 @@ Create Kubernetes Pod YAML for a container with the named volume `priceless-data` mounted in the container at `/volume`. ``` $ podman generate kube my-container-using-priceless-data -# Generation of Kubernetes YAML is still under development! -# # Save the output of this file and use kubectl create -f to import # it into Kubernetes. # @@ -161,31 +145,18 @@ containers: - command: - /bin/sh - env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman image: docker.io/library/alpine:latest name: test-bind-mount resources: {} securityContext: - allowPrivilegeEscalation: true capabilities: drop: - CAP_MKNOD - CAP_NET_RAW - CAP_AUDIT_WRITE - privileged: false - readOnlyRootFilesystem: false - seLinuxOptions: {} volumeMounts: - mountPath: /volume name: priceless-data-pvc - workingDir: / - dnsConfig: {} restartPolicy: Never volumes: - name: priceless-data-pvc @@ -197,8 +168,6 @@ Create Kubernetes Pod YAML for a pod called `demoweb` and include a service. ``` $ sudo podman generate kube -s demoweb -# Generation of Kubernetes YAML is still under development! -# # Save the output of this file and use kubectl create -f to import # it into Kubernetes. # @@ -215,22 +184,9 @@ - command: - python3 - /root/code/graph.py - env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: HOSTNAME - - name: container - value: podman image: quay.io/baude/demoweb:latest name: practicalarchimedes resources: {} - securityContext: - allowPrivilegeEscalation: true - capabilities: {} - privileged: false - readOnlyRootFilesystem: false tty: true workingDir: /root/code status: {} @@ -247,7 +203,6 @@ - name: "8050" nodePort: 31269 port: 8050 - protocol: TCP targetPort: 0 selector: app: demoweb diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-generate-systemd.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-generate-systemd.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-generate-systemd.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-generate-systemd.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -32,6 +32,8 @@ Using this flag will yield unit files that do not expect containers and pods to exist. Instead, new containers and pods are created based on their configuration files. The unit files are created best effort and may need to be further edited; please review the generated files carefully before using them in production. +Note that `--new` only works on containers and pods created directly via Podman (i.e., `podman [container] {create,run}` or `podman pod create`). It does not work on containers or pods created via the REST API or via `podman play kube`. + #### **--no-header** Do not generate the header including meta data such as the Podman version and the timestamp. @@ -73,7 +75,7 @@ [Unit] Description=Podman container-de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/container/storage @@ -86,7 +88,7 @@ PIDFile=/run/user/1000/overlay-containers/de1e3223b1b888bc02d0962dd6cb5855eb00734061013ffdd3479d225abacdc6/userdata/conmon.pid [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ``` ### Generate systemd unit file for a container with `--new` flag @@ -102,7 +104,7 @@ [Unit] Description=Podman container-busy_moser.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/container/storage @@ -118,7 +120,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ``` ### Generate systemd unit files for a pod with two simple alpine containers @@ -144,7 +146,7 @@ Documentation=man:podman-generate-systemd(1) Requires=container-amazing_chandrasekhar.service container-jolly_shtern.service Before=container-amazing_chandrasekhar.service container-jolly_shtern.service -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/container/storage @@ -157,7 +159,7 @@ PIDFile=/run/user/1000/overlay-containers/ccfd5c71a088768774ca7bd05888d55cc287698dde06f475c8b02f696a25adcd/userdata/conmon.pid [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ``` ### Installation of generated systemd unit files. @@ -179,7 +181,6 @@ $ sudo cp pod-systemd-pod.service container-great_payne.service /etc/systemd/system $ systemctl enable pod-systemd-pod.service -Created symlink /etc/systemd/system/multi-user.target.wants/pod-systemd-pod.service → /etc/systemd/system/pod-systemd-pod.service. Created symlink /etc/systemd/system/default.target.wants/pod-systemd-pod.service → /etc/systemd/system/pod-systemd-pod.service. $ systemctl is-enabled pod-systemd-pod.service enabled diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-image.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-image.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-image.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-image.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -11,29 +11,30 @@ ## COMMANDS -| Command | Man Page | Description | -| -------- | ----------------------------------------------- | --------------------------------------------------------------------------- | -| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. | -| diff | [podman-image-diff(1)](podman-image-diff.1.md) | Inspect changes on an image's filesystem. | -| exists | [podman-image-exists(1)](podman-image-exists.1.md) | Check if an image exists in local storage. | -| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. | -| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | -| inspect | [podman-inspect(1)](podman-inspect.1.md) | Display a image or image's configuration. | -| list | [podman-images(1)](podman-images.1.md) | List the container images on the system.(alias ls) | -| load | [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. | -| mount | [podman-image-mount(1)](podman-image-mount.1.md) | Mount an image's root filesystem. | -| prune | [podman-image-prune(1)](podman-image-prune.1.md) | Remove all unused images from the local store. | -| pull | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | -| push | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | -| rm | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | -| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | -| search | [podman-search(1)](podman-search.1.md) | Search a registry for an image. | -| sign | [podman-image-sign(1)](podman-image-sign.1.md) | Create a signature for an image. | -| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | -| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. | -| trust | [podman-image-trust(1)](podman-image-trust.1.md) | Manage container registry image trust policy. | -| unmount | [podman-image-unmount(1)](podman-image-unmount.1.md) | Unmount an image's root filesystem. | -| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | +| Command | Man Page | Description | +| -------- | --------------------------------------------------- | ----------------------------------------------------------------------- | +| build | [podman-build(1)](podman-build.1.md) | Build a container using a Dockerfile. | +| diff | [podman-image-diff(1)](podman-image-diff.1.md) | Inspect changes on an image's filesystem. | +| exists | [podman-image-exists(1)](podman-image-exists.1.md) | Check if an image exists in local storage. | +| history | [podman-history(1)](podman-history.1.md) | Show the history of an image. | +| import | [podman-import(1)](podman-import.1.md) | Import a tarball and save it as a filesystem image. | +| inspect | [podman-image-inspect(1)](podman-image-inspect.1.md)| Display an image's configuration. | +| list | [podman-images(1)](podman-images.1.md) | List the container images on the system.(alias ls) | +| load | [podman-load(1)](podman-load.1.md) | Load an image from the docker archive. | +| mount | [podman-image-mount(1)](podman-image-mount.1.md) | Mount an image's root filesystem. | +| prune | [podman-image-prune(1)](podman-image-prune.1.md) | Remove all unused images from the local store. | +| pull | [podman-pull(1)](podman-pull.1.md) | Pull an image from a registry. | +| push | [podman-push(1)](podman-push.1.md) | Push an image from local storage to elsewhere. | +| rm | [podman-rmi(1)](podman-rmi.1.md) | Removes one or more locally stored images. | +| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. | +| scp | [podman-image-scp(1)](podman-image-scp.1.md) | Securely copy an image from one host to another. | +| search | [podman-search(1)](podman-search.1.md) | Search a registry for an image. | +| sign | [podman-image-sign(1)](podman-image-sign.1.md) | Create a signature for an image. | +| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. | +| tree | [podman-image-tree(1)](podman-image-tree.1.md) | Prints layer hierarchy of an image in a tree format. | +| trust | [podman-image-trust(1)](podman-image-trust.1.md) | Manage container registry image trust policy. | +| unmount | [podman-image-unmount(1)](podman-image-unmount.1.md) | Unmount an image's root filesystem. | +| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. | ## SEE ALSO podman diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-image-diff.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-image-diff.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-image-diff.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-image-diff.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,21 +4,29 @@ podman-image-diff - Inspect changes on an image's filesystem ## SYNOPSIS -**podman image diff** [*options*] *name* +**podman image diff** [*options*] *image* [*image*] ## DESCRIPTION -Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer +Displays changes on an image's filesystem. The image will be compared to its parent layer or the second argument when given. + +The output is prefixed with the following symbols: + +| Symbol | Description | +|--------|-------------| +| A | A file or directory was added. | +| D | A file or directory was deleted. | +| C | A file or directory was changed. | ## OPTIONS #### **--format** -Alter the output into a different format. The only valid format for diff is `json`. +Alter the output into a different format. The only valid format for **podman image diff** is `json`. ## EXAMPLE ``` -# podman diff redis:old redis:alpine +$ podman diff redis:old C /usr C /usr/local C /usr/local/bin @@ -26,7 +34,7 @@ ``` ``` -# podman diff --format json redis:old redis:alpine +$ podman diff --format json redis:old redis:alpine { "changed": [ "/usr", diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-image-inspect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-image-inspect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-image-inspect.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-image-inspect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,105 @@ +% podman-image-inspect(1) + +## NAME +podman\-image\-inspect - Display an image's configuration + +## SYNOPSIS +**podman image inspect** [*options*] *image* [*image* ...] + +## DESCRIPTION + +This displays the low-level information on images identified by name or ID. By default, this will render +all results in a JSON array. If a format is specified, the given template will be executed for each result. + +## OPTIONS + +#### **--format**, **-f**=*format* + +Format the output using the given Go template. +The keys of the returned JSON can be used as the values for the --format flag (see examples below). + +## EXAMPLE + +``` +$ podman image inspect fedora +[ + { + "Id": "37e5619f4a8ca9dbc4d6c0ae7890625674a10dbcfb76201399e2aaddb40da17d", + "Digest": "sha256:1b0d4ddd99b1a8c8a80e885aafe6034c95f266da44ead992aab388e6aa91611a", + "RepoTags": [ + "registry.fedoraproject.org/fedora:latest" + ], + "RepoDigests": [ + "registry.fedoraproject.org/fedora@sha256:1b0d4ddd99b1a8c8a80e885aafe6034c95f266da44ead992aab388e6aa91611a", + "registry.fedoraproject.org/fedora@sha256:b5290db40008aae9272ad3a6bd8070ef7ecd547c3bef014b894c327960acc582" + ], + "Parent": "", + "Comment": "Created by Image Factory", + "Created": "2021-08-09T05:48:47Z", + "Config": { + "Env": [ + "DISTTAG=f34container", + "FGC=f34", + "container=oci" + ], + "Cmd": [ + "/bin/bash" + ], + "Labels": { + "license": "MIT", + "name": "fedora", + "vendor": "Fedora Project", + "version": "34" + } + }, + "Version": "1.10.1", + "Author": "", + "Architecture": "amd64", + "Os": "linux", + "Size": 183852302, + "VirtualSize": 183852302, + "GraphDriver": { + "Name": "overlay", + "Data": { + "UpperDir": "/home/dwalsh/.local/share/containers/storage/overlay/0203e243f1ca4b6bb49371ecd21363212467ec6d7d3fa9f324cd4e78cc6b5fa2/diff", + "WorkDir": "/home/dwalsh/.local/share/containers/storage/overlay/0203e243f1ca4b6bb49371ecd21363212467ec6d7d3fa9f324cd4e78cc6b5fa2/work" + } + }, + "RootFS": { + "Type": "layers", + "Layers": [ + "sha256:0203e243f1ca4b6bb49371ecd21363212467ec6d7d3fa9f324cd4e78cc6b5fa2" + ] + }, + "Labels": { + "license": "MIT", + "name": "fedora", + "vendor": "Fedora Project", + "version": "34" + }, + "Annotations": {}, + "ManifestType": "application/vnd.docker.distribution.manifest.v2+json", + "User": "", + "History": [ + { + "created": "2021-08-09T05:48:47Z", + "comment": "Created by Image Factory" + } + ], + "NamesHistory": [ + "registry.fedoraproject.org/fedora:latest" + ] + } +] +``` + +``` +$ podman image inspect --format '{{ .Id }}' fedora +37e5619f4a8ca9dbc4d6c0ae7890625674a10dbcfb76201399e2aaddb40da17d +``` + +## SEE ALSO +**[podman(1)](podman.1.md)**,**[podman-image(1)](podman-image.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** + +## HISTORY +Sep 2021, Originally compiled by Dan Walsh diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-images.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-images.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-images.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-images.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -36,7 +36,7 @@ Filter on images created before the given IMAGE (name or tag). **dangling=true|false** - Show dangling images. Dangling images are a file system layer that was used in a previous build of an image and is no longer referenced by any active images. They are denoted with the `` tag, consume disk space and serve no active purpose. + Show dangling images. Dangling images are a file system layer that was used in a previous build of an image and is no longer referenced by any image. They are denoted with the `` tag, consume disk space and serve no active purpose. **label** Filter by images labels key and/or value. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-image-scp.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-image-scp.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-image-scp.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-image-scp.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,69 @@ +% podman-image-scp(1) + +## NAME +podman-image-scp - Securely copy an image from one host to another + +## SYNOPSIS +**podman image scp** [*options*] *name*[:*tag*] + +## DESCRIPTION +**podman image scp** copies container images between hosts on a network. You can load to the remote host or from the remote host as well as in between two remote hosts. +Note: `::` is used to specify the image name depending on if you are saving or loading. + +**podman image scp [GLOBAL OPTIONS]** + +**podman image** *scp [OPTIONS] NAME[:TAG] [HOSTNAME::]* + +**podman image** *scp [OPTIONS] [HOSTNAME::]IMAGENAME* + +**podman image** *scp [OPTIONS] [HOSTNAME::]IMAGENAME [HOSTNAME::]* + +## OPTIONS + +#### **--quiet**, **-q** + +Suppress the output + +#### **--help**, **-h** + +Print usage statement + +## EXAMPLES + + +``` +$ podman image scp alpine +Loaded image(s): docker.io/library/alpine:latest +``` + +``` +$ podman image scp alpine Fedora::/home/charliedoern/Documents/alpine +Getting image source signatures +Copying blob 72e830a4dff5 done +Copying config 85f9dc67c7 done +Writing manifest to image destination +Storing signatures +Loaded image(s): docker.io/library/alpine:latest +``` + +``` +$ podman image scp Fedora::alpine RHEL:: +Loaded image(s): docker.io/library/alpine:latest +``` + +``` +$ podman image scp charliedoern@192.168.68.126:22/run/user/1000/podman/podman.sock::alpine +WARN[0000] Unknown connection name given. Please use system connection add to specify the default remote socket location +Getting image source signatures +Copying blob 9450ef9feb15 [--------------------------------------] 0.0b / 0.0b +Copying config 1f97f0559c done +Writing manifest to image destination +Storing signatures +Loaded image(s): docker.io/library/alpine:latest +``` + +## SEE ALSO +podman(1), podman-load(1), podman-save(1), podman-remote(1), podman-system-connection-add(1), containers.conf(5), containers-transports(5) + +## HISTORY +July 2021, Originally written by Charlie Doern diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-image-sign.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-image-sign.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-image-sign.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-image-sign.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -25,7 +25,7 @@ #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--directory**, **-d**=*dir* diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-image-unmount.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-image-unmount.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-image-unmount.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-image-unmount.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -12,7 +12,7 @@ Unmounts the specified images' root file system, if no other processes are using it. -Image storage increments a mount counter each time a image is mounted. +Image storage increments a mount counter each time an image is mounted. When a image is unmounted, the mount counter is decremented, and the image's root filesystem is physically unmounted only when the mount counter reaches zero indicating no other processes are using the mount. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-info.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-info.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-info.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-info.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -31,23 +31,19 @@ $ podman info host: arch: amd64 - buildahVersion: 1.19.0-dev - cgroupControllers: - - cpuset - - cpu - - io - - memory - - pids + buildahVersion: 1.22.3 + cgroupControllers: [] cgroupManager: systemd cgroupVersion: v2 conmon: - package: conmon-2.0.22-2.fc33.x86_64 + package: conmon-2.0.29-2.fc34.x86_64 path: /usr/bin/conmon - version: 'conmon version 2.0.22, commit: 1be6c73605006a85f7ed60b7f76a51e28eb67e01' + version: 'conmon version 2.0.29, commit: ' cpus: 8 distribution: distribution: fedora - version: "33" + variant: workstation + version: "34" eventLogger: journald hostname: localhost.localdomain idMappings: @@ -65,108 +61,112 @@ - container_id: 1 host_id: 100000 size: 65536 - kernel: 5.9.11-200.fc33.x86_64 + kernel: 5.13.13-200.fc34.x86_64 linkmode: dynamic - memFree: 837505024 - memTotal: 16416481280 + logDriver: journald + memFree: 1351262208 + memTotal: 16401895424 ociRuntime: name: crun - package: crun-0.16-1.fc33.x86_64 + package: crun-1.0-1.fc34.x86_64 path: /usr/bin/crun version: |- - crun version 0.16 - commit: eb0145e5ad4d8207e84a327248af76663d4e50dd + crun version 1.0 + commit: 139dc6971e2f1d931af520188763e984d6cdfbf8 spec: 1.0.0 - +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL + +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +YAJL os: linux remoteSocket: - exists: true path: /run/user/3267/podman/podman.sock security: apparmorEnabled: false capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT rootless: true seccompEnabled: true + seccompProfilePath: /usr/share/containers/seccomp.json selinuxEnabled: true + serviceIsRemote: false slirp4netns: executable: /bin/slirp4netns - package: slirp4netns-1.1.4-4.dev.giteecccdb.fc33.x86_64 + package: slirp4netns-1.1.12-2.fc34.x86_64 version: |- - slirp4netns version 1.1.4+dev - commit: eecccdb96f587b11d7764556ffacfeaffe4b6e11 - libslirp: 4.3.1 + slirp4netns version 1.1.12 + commit: 7a104a101aa3278a2152351a082a6df71f57c9a3 + libslirp: 4.4.0 SLIRP_CONFIG_VERSION_MAX: 3 libseccomp: 2.5.0 - swapFree: 6509203456 - swapTotal: 12591292416 - uptime: 264h 14m 32.73s (Approximately 11.00 days) + swapFree: 16818888704 + swapTotal: 16886259712 + uptime: 33h 57m 32.85s (Approximately 1.38 days) +plugins: + log: + - k8s-file + - none + - journald + network: + - bridge + - macvlan + volume: + - local registries: + localhost:5000: + Blocked: false + Insecure: true + Location: localhost:5000 + MirrorByDigestOnly: false + Mirrors: null + Prefix: localhost:5000 search: - registry.fedoraproject.org - registry.access.redhat.com - - registry.centos.org - docker.io store: configFile: /home/dwalsh/.config/containers/storage.conf containerStore: - number: 3 + number: 2 paused: 0 - running: 0 - stopped: 3 + running: 1 + stopped: 1 graphDriverName: overlay - graphOptions: - overlay.mount_program: - Executable: /home/dwalsh/bin/fuse-overlayfs - Package: Unknown - Version: |- - fusermount3 version: 3.9.3 - fuse-overlayfs: version 0.7.2 - FUSE library version 3.9.3 - using FUSE kernel interface version 7.31 + graphOptions: {} graphRoot: /home/dwalsh/.local/share/containers/storage graphStatus: Backing Filesystem: extfs - Native Overlay Diff: "false" + Native Overlay Diff: "true" Supports d_type: "true" Using metacopy: "false" imageStore: - number: 77 + number: 37 runRoot: /run/user/3267/containers volumePath: /home/dwalsh/.local/share/containers/storage/volumes version: - APIVersion: 3.0.0 - Built: 1608562922 - BuiltTime: Mon Dec 21 10:02:02 2020 - GitCommit: d6925182cdaf94225908a386d02eae8fd3e01123-dirty - GoVersion: go1.15.5 + APIVersion: 3.3.1 + Built: 1631137208 + BuiltTime: Wed Sep 8 17:40:08 2021 + GitCommit: ab272d1e9bf4daac224fb230e0c9b5c56c4cab4d-dirty + GoVersion: go1.16.6 OsArch: linux/amd64 - Version: 3.0.0-dev - + Version: 3.3.1 ``` Run podman info with JSON formatted response: ``` +$ ./bin/podman info --format json { "host": { "arch": "amd64", - "buildahVersion": "1.19.0-dev", + "buildahVersion": "1.22.3", "cgroupManager": "systemd", "cgroupVersion": "v2", - "cgroupControllers": [ - "cpuset", - "cpu", - "io", - "memory", - "pids" - ], + "cgroupControllers": [], "conmon": { - "package": "conmon-2.0.22-2.fc33.x86_64", + "package": "conmon-2.0.29-2.fc34.x86_64", "path": "/usr/bin/conmon", - "version": "conmon version 2.0.22, commit: 1be6c73605006a85f7ed60b7f76a51e28eb67e01" + "version": "conmon version 2.0.29, commit: " }, "cpus": 8, "distribution": { "distribution": "fedora", - "version": "33" + "version": "34" }, "eventLogger": "journald", "hostname": "localhost.localdomain", @@ -196,81 +196,99 @@ } ] }, - "kernel": "5.9.11-200.fc33.x86_64", - "memFree": 894574592, - "memTotal": 16416481280, + "kernel": "5.13.13-200.fc34.x86_64", + "logDriver": "journald", + "memFree": 1274040320, + "memTotal": 16401895424, "ociRuntime": { "name": "crun", - "package": "crun-0.16-1.fc33.x86_64", + "package": "crun-1.0-1.fc34.x86_64", "path": "/usr/bin/crun", - "version": "crun version 0.16\ncommit: eb0145e5ad4d8207e84a327248af76663d4e50dd\nspec: 1.0.0\n+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +YAJL" + "version": "crun version 1.0\ncommit: 139dc6971e2f1d931af520188763e984d6cdfbf8\nspec: 1.0.0\n+SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +CRIU +YAJL" }, "os": "linux", "remoteSocket": { - "path": "/run/user/3267/podman/podman.sock", - "exists": true + "path": "/run/user/3267/podman/podman.sock" }, + "serviceIsRemote": false, "security": { "apparmorEnabled": false, "capabilities": "CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT", "rootless": true, "seccompEnabled": true, + "seccompProfilePath": "/usr/share/containers/seccomp.json", "selinuxEnabled": true }, "slirp4netns": { "executable": "/bin/slirp4netns", - "package": "slirp4netns-1.1.4-4.dev.giteecccdb.fc33.x86_64", - "version": "slirp4netns version 1.1.4+dev\ncommit: eecccdb96f587b11d7764556ffacfeaffe4b6e11\nlibslirp: 4.3.1\nSLIRP_CONFIG_VERSION_MAX: 3\nlibseccomp: 2.5.0" + "package": "slirp4netns-1.1.12-2.fc34.x86_64", + "version": "slirp4netns version 1.1.12\ncommit: 7a104a101aa3278a2152351a082a6df71f57c9a3\nlibslirp: 4.4.0\nSLIRP_CONFIG_VERSION_MAX: 3\nlibseccomp: 2.5.0" }, - "swapFree": 6509203456, - "swapTotal": 12591292416, - "uptime": "264h 13m 12.39s (Approximately 11.00 days)", + "swapFree": 16818888704, + "swapTotal": 16886259712, + "uptime": "33h 59m 25.69s (Approximately 1.38 days)", "linkmode": "dynamic" }, "store": { "configFile": "/home/dwalsh/.config/containers/storage.conf", "containerStore": { - "number": 3, + "number": 2, "paused": 0, - "running": 0, - "stopped": 3 + "running": 1, + "stopped": 1 }, "graphDriverName": "overlay", "graphOptions": { - "overlay.mount_program": { - "Executable": "/home/dwalsh/bin/fuse-overlayfs", - "Package": "Unknown", - "Version": "fusermount3 version: 3.9.3\nfuse-overlayfs: version 0.7.2\nFUSE library version 3.9.3\nusing FUSE kernel interface version 7.31" -} }, "graphRoot": "/home/dwalsh/.local/share/containers/storage", "graphStatus": { "Backing Filesystem": "extfs", - "Native Overlay Diff": "false", + "Native Overlay Diff": "true", "Supports d_type": "true", "Using metacopy": "false" }, "imageStore": { - "number": 77 + "number": 37 }, "runRoot": "/run/user/3267/containers", "volumePath": "/home/dwalsh/.local/share/containers/storage/volumes" }, "registries": { + "localhost:5000": { + "Prefix": "localhost:5000", + "Location": "localhost:5000", + "Insecure": true, + "Mirrors": null, + "Blocked": false, + "MirrorByDigestOnly": false +}, "search": [ "registry.fedoraproject.org", "registry.access.redhat.com", - "registry.centos.org", "docker.io" ] }, + "plugins": { + "volume": [ + "local" + ], + "network": [ + "bridge", + "macvlan" + ], + "log": [ + "k8s-file", + "none", + "journald" + ] + }, "version": { - "APIVersion": "3.0.0", - "Version": "3.0.0-dev", - "GoVersion": "go1.15.5", - "GitCommit": "d6925182cdaf94225908a386d02eae8fd3e01123-dirty", - "BuiltTime": "Mon Dec 21 10:02:02 2020", - "Built": 1608562922, + "APIVersion": "3.3.1", + "Version": "3.3.1", + "GoVersion": "go1.16.6", + "GitCommit": "", + "BuiltTime": "Mon Aug 30 16:46:36 2021", + "Built": 1630356396, "OsArch": "linux/amd64" } } diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-inspect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-inspect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-inspect.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-inspect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -13,14 +13,12 @@ So, if a container has the same name as an image, then the container JSON will be returned, and so on. If a format is specified, the given template will be executed for each result. -For more inspection options, see: - - podman container inspect - podman image inspect - podman network inspect - podman pod inspect - podman volume inspect - +For more inspection options, see also +[podman-container-inspect(1)](podman-container-inspect.1.md), +[podman-image-inspect(1)](podman-image-inspect.1.md), +[podman-network-inspect(1)](podman-network-inspect.1.md), +[podman-pod-inspect(1)](podman-pod-inspect.1.md), and +[podman-volume-inspect(1)](podman-volume-inspect.1.md). ## OPTIONS @@ -164,7 +162,7 @@ ``` ## SEE ALSO -podman(1) +**[podman(1)](podman.1.md)**,**[podman-container-inspect(1)](podman-container-inspect.1.md)**,**[podman-image-inspect(1)](podman-image-inspect.1.md)**,**[podman-network-inspect(1)](podman-network-inspect.1.md)**,**[podman-pod-inspect(1)](podman-pod-inspect.1.md)**,**[podman-volume-inspect(1)](podman-volume-inspect.1.md)**. ## HISTORY July 2017, Originally compiled by Dan Walsh diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-login.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-login.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-login.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-login.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -28,18 +28,6 @@ ## OPTIONS -#### **--password**, **-p**=*password* - -Password for registry - -#### **--password-stdin** - -Take the password from stdin - -#### **--username**, **-u**=*username* - -Username for registry - #### **--authfile**=*path* Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json. @@ -47,14 +35,26 @@ Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` +#### **--cert-dir**=*path* + +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) +Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) + #### **--get-login** Return the logged-in user for the registry. Return error if no login is found. -#### **--cert-dir**=*path* +#### **--help**, **-h** -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. -Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) +Print usage statement + +#### **--password**, **-p**=*password* + +Password for registry + +#### **--password-stdin** + +Take the password from stdin #### **--tls-verify**=*true|false* @@ -62,9 +62,13 @@ then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. -#### **--help**, **-h** +#### **--username**, **-u**=*username* -Print usage statement +Username for registry + +#### **--verbose**, **-v** + +print detailed information about credential store ## EXAMPLES @@ -107,6 +111,14 @@ Login Succeeded! ``` +``` +$ podman login quay.io --verbose +Username: myusername +Password: +Used: /run/user/1000/containers/auth.json +Login Succeeded! +``` + ## SEE ALSO podman(1), podman-logout(1), containers-auth.json(5), containers-certs.d(5), containers-registries.conf(5) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-logs.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-logs.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-logs.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-logs.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -39,6 +39,14 @@ time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00, and 2006-01-02. +#### **--until**=*TIMESTAMP* + +Show logs until TIMESTAMP. The --until option can be Unix timestamps, date formatted timestamps, or Go duration +strings (e.g. 10m, 1h30m) computed relative to the client machine's time. Supported formats for date formatted +time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00, +and 2006-01-02. + + #### **--tail**=*LINES* Output the specified number of LINES at the end of the logs. LINES must be an integer. Defaults to -1, @@ -74,6 +82,17 @@ # Server initialized ``` +To view all containers logs: +``` +podman logs -t --since 0 myserver + +1:M 07 Aug 14:10:09.055 # Server can't set maximum open files to 10032 because of OS error: Operation not permitted. +1:M 07 Aug 14:10:09.055 # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'. +1:M 07 Aug 14:10:09.056 * Running mode=standalone, port=6379. +1:M 07 Aug 14:10:09.056 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. +1:M 07 Aug 14:10:09.056 # Server initialized +``` + To view a containers logs since a certain time: ``` podman logs -t --since 2017-08-07T10:10:09.055837383-04:00 myserver @@ -93,6 +112,16 @@ # Current maximum open files is 4096. maxclients has been reduced to 4064 to compensate for low ulimit. If you need higher maxclients increase 'ulimit -n'. ``` +To view a container's logs until 30 minutes ago: +``` +podman logs --until 30m myserver + +AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.0.2.100. Set the 'ServerName' directive globally to suppress this message +AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 10.0.2.100. Set the 'ServerName' directive globally to suppress this message +[Tue Jul 20 13:18:14.223727 2021] [mpm_event:notice] [pid 1:tid 140021067187328] AH00489: Apache/2.4.48 (Unix) configured -- resuming normal operations +[Tue Jul 20 13:18:14.223819 2021] [core:notice] [pid 1:tid 140021067187328] AH00094: Command line: 'httpd -D FOREGROUND' +``` + ## SEE ALSO podman(1), podman-run(1), podman-container-rm(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-machine-init.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-machine-init.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-machine-init.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-machine-init.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -18,6 +18,10 @@ SSH keys are automatically generated to access the VM, and system connections to the root account and a user account inside the VM are added. +By default, the VM distribution is [Fedora CoreOS](https://getfedora.org/en/coreos?stream=testing). +Fedora CoreOS upgrades come out every 14 days and are detected and installed automatically. The VM will be rebooted during the upgrade. +For more information on updates and advanced configuration, please see the FCOS update docs [here](https://docs.fedoraproject.org/en-US/fedora-coreos/auto-updates/) and [here](https://coreos.github.io/zincati/usage/updates-strategy/). + ## OPTIONS #### **--cpus**=*number* @@ -39,7 +43,9 @@ #### **--image-path** -Fully qualified path of the uncompressed image file +Fully qualified path or URL to the VM image. +Can also be set to `testing`, `next`, or `stable` to pull down default image. +Defaults to `testing`. #### **--memory**, **-m**=*number* @@ -52,13 +58,14 @@ ## EXAMPLES ``` +$ podman machine init $ podman machine init myvm -$ podman machine init --device=/dev/xvdc:rw myvm +$ podman machine init --disk-size 50 $ podman machine init --memory=1024 myvm ``` ## SEE ALSO -podman-machine (1) +podman-machine(1) ## HISTORY March 2021, Originally compiled by Ashley Cui diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-machine-ssh.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-machine-ssh.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-machine-ssh.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-machine-ssh.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -9,7 +9,7 @@ ## DESCRIPTION SSH into a Podman-managed virtual machine and optionally execute a command -on the virtual machine. Unless using the default virtual machine, the +on the virtual machine. Unless using the default virtual machine, the first argument must be the virtual machine name. The optional command to execute can then follow. If no command is provided, an interactive session with the virtual machine is established. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-manifest.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-manifest.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-manifest.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-manifest.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -22,6 +22,51 @@ | inspect | [podman-manifest-inspect(1)](podman-manifest-inspect.1.md) | Display a manifest list or image index. | | push | [podman-manifest-push(1)](podman-manifest-push.1.md) | Push a manifest list or image index to a registry. | | remove | [podman-manifest-remove(1)](podman-manifest-remove.1.md) | Remove an image from a manifest list or image index. | +| rm | [podman-manifest-rme(1)](podman-manifest-rm.1.md) | Remove manifest list or image index from local storage. | + +## EXAMPLES + +### Building a multi-arch manifest list from a Containerfile + +Assuming the `Containerfile` uses `RUN` instructions, the host needs +a way to execute non-native binaries. Configuring this is beyond +the scope of this example. Building a multi-arch manifest list +`shazam` in parallel across 4-threads can be done like this: + + $ platarch=linux/amd64,linux/ppc64le,linux/arm64,linux/s390x + $ podman build --jobs=4 --platform=$platarch --manifest shazam . + +**Note:** The `--jobs` argument is optional, and the `-t` or `--tag` +option should *not* be used. + +### Assembling a multi-arch manifest from separately built images + +Assuming `example.com/example/shazam:$arch` images are built separately +on other hosts and pushed to the `example.com` registry. They may +be combined into a manifest list, and pushed using a simple loop: + + $ REPO=example.com/example/shazam + $ podman manifest create $REPO:latest + $ for IMGTAG in amd64 s390x ppc64le arm64; do \ + podman manifest add $REPO:latest docker://$REPO:IMGTAG; \ + done + $ podman manifest push --all $REPO:latest + +**Note:** The `add` instruction argument order is `` then ``. +Also, the `--all` push option is required to ensure all contents are +pushed, not just the native platform/arch. + +### Removing and tagging a manifest list before pushing + +Special care is needed when removing and pushing manifest lists, as opposed +to the contents. You almost always want to use the `manifest rm` and +`manifest push --all` subcommands. For example, a rename and push could +be performed like this: + + $ podman tag localhost/shazam example.com/example/shazam + $ podman manifest rm localhost/shazam + $ podman manifest push --all example.com/example/shazam + ## SEE ALSO podman(1), podman-manifest-add(1), podman-manifest-annotate(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-manifest-remove(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-manifest-add.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-manifest-add.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-manifest-add.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-manifest-add.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -43,7 +43,7 @@ #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--creds**=*creds* @@ -93,6 +93,28 @@ $ podman manifest add mylist:v1.11 containers-storage:quay.io/username/myimage + **dir:**_path_ + An existing local directory _path_ storing the manifest, layer tarballs, and signatures as individual files. This + is a non-standardized format, primarily useful for debugging or noninvasive container inspection. + + $ podman manifest add dir:/tmp/myimage + + **docker-archive:**_path_[**:**_docker-reference_] + An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a + file, and it must not contain a digest. + + $ podman manifest add docker-archive:/tmp/myimage + + **docker-daemon:**_docker-reference_ + An image in _docker-reference_ format stored in the docker daemon internal storage. The _docker-reference_ can also be an image ID (docker-daemon:algo:digest). + + $ sudo podman manifest add docker-daemon:docker.io/library/myimage:33 + + **oci-archive:**_path_**:**_tag_ + An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_. + + $ podman manifest add oci-archive:/tmp/myimage + ## EXAMPLE ``` diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-manifest-push.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-manifest-push.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-manifest-push.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-manifest-push.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -29,7 +29,7 @@ #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--creds**=*creds* diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-manifest-rm.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-manifest-rm.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-manifest-rm.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-manifest-rm.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,25 @@ +# podman-manifest-rm "1" "April 2021" "podman" + +## NAME +podman\-manifest\-rm - Remove manifest list or image index from local storage + +## SYNOPSIS +**podman manifest rm** *list-or-index* [...] + +## DESCRIPTION +Removes one or more locally stored manifest lists. + +## EXAMPLE + +podman manifest rm `` + +podman manifest rm listid1 listid2 + +**storage.conf** (`/etc/containers/storage.conf`) + +storage.conf is the storage configuration file for all tools using containers/storage + +The storage configuration file specifies all of the available container storage options for tools using shared container storage. + +## SEE ALSO +podman(1), containers-storage.conf(5), podman-manifest(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-network-connect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-network-connect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-network-connect.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-network-connect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -10,12 +10,12 @@ Connects a container to a network. A container can be connected to a network by name or by ID. Once connected, the container can communicate with other containers in the same network. -This command is not available for rootless users. - ## OPTIONS #### **--alias** Add network-scoped alias for the container. If the network is using the `dnsname` CNI plugin, these aliases can be used for name resolution on the given network. Multiple *--alias* options may be specified as input. +NOTE: A container will only have access to aliases on the first network that it joins. This is a limitation +that will be removed in a later release. ## EXAMPLE diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-network-create.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-network-create.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-network-create.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-network-create.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -25,7 +25,8 @@ #### **--driver**, **-d** -Driver to manage the network (default "bridge"). Currently only `bridge` is supported. +Driver to manage the network. Currently `bridge` and `macvlan` is supported. Defaults to `bridge`. +As rootless the `macvlan` driver has no access to the host network interfaces because rootless networking requires a separate network namespace. #### **--opt**=*option*, **-o** @@ -54,13 +55,6 @@ Set metadata for a network (e.g., --label mykey=value). -#### **--macvlan** - -*This option is being deprecated* - -Create a *Macvlan* based connection rather than a classic bridge. You must pass an interface name from the host for the -Macvlan connection. - #### **--subnet** The subnet in CIDR notation. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-network-disconnect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-network-disconnect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-network-disconnect.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-network-disconnect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -7,9 +7,10 @@ **podman network disconnect** [*options*] network container ## DESCRIPTION -Disconnects a container from a network. +Disconnects a container from a network. A container can be disconnected from a network by name or by ID. +If all networks are disconnected from the container, it will behave like a container created with `--network=none` +and it will longer have network connectivity until a network is connected again. -This command is not available for rootless users. ## OPTIONS #### **--force**, **-f** diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-network-reload.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-network-reload.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-network-reload.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-network-reload.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -13,8 +13,6 @@ this happens for example with `firewall-cmd --reload`, the container loses network connectivity. This command restores the network connectivity. -This command is not available for rootless users since rootless containers are not affected by such connectivity problems. - ## OPTIONS #### **--all**, **-a** diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pause.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pause.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pause.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pause.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -31,7 +31,7 @@ Pause all **running** containers. ``` -podman stop -a +podman pause -a ``` ## SEE ALSO diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-play-kube.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-play-kube.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-play-kube.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-play-kube.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -8,18 +8,21 @@ ## DESCRIPTION **podman play kube** will read in a structured file of Kubernetes YAML. It will then recreate the containers, pods or volumes described in the YAML. Containers within a pod are then started and the ID of the new Pod or the name of the new Volume is output. If the yaml file is specified as "-" then `podman play kube` will read the YAML file from stdin. - +Using the `--down` command line option, it is also capable of tearing down the pods created by a previous run of `podman play kube`. Ideally the input file would be one created by Podman (see podman-generate-kube(1)). This would guarantee a smooth import and expected results. Currently, the supported Kubernetes kinds are: - Pod - Deployment - PersistentVolumeClaim +- ConfigMap `Kubernetes Pods or Deployments` Only two volume types are supported by play kube, the *hostPath* and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, and *Socket* subtypes are supported. The *CharDevice* and *BlockDevice* subtypes are not supported. Podman interprets the value of *hostPath* *path* as a file path when it contains at least one forward slash, otherwise Podman treats the value as the name of a named volume. When using a *persistentVolumeClaim*, the value for *claimName* is the name for the Podman named volume. +Note: When playing a kube YAML with init containers, the init container will be created with init type value `always`. + Note: *hostPath* volume types created by play kube will be given an SELinux private label (Z) Note: If the `:latest` tag is used, Podman will attempt to pull the image from a registry. If the image was built locally with Podman or Buildah, it will have `localhost` as the domain, in that case, Podman will use the image from the local store even if it has the `:latest` tag. @@ -35,6 +38,70 @@ - volume.podman.io/gid - volume.podman.io/mount-options +Play kube is capable of building images on the fly given the correct directory layout and Containerfiles. This +option is not available for remote clients yet. Consider the following excerpt from a YAML file: +``` +apiVersion: v1 +kind: Pod +metadata: +... +spec: + containers: + - command: + - top + - name: container + value: podman + image: foobar +... +``` + +If there is a directory named `foobar` in the current working directory with a file named `Containerfile` or `Dockerfile`, +Podman play kube will build that image and name it `foobar`. An example directory structure for this example would look +like: +``` +|- mykubefiles + |- myplayfile.yaml + |- foobar + |- Containerfile +``` + +The build will consider `foobar` to be the context directory for the build. If there is an image in local storage +called `foobar`, the image will not be built unless the `--build` flag is used. + +`Kubernetes ConfigMap` + +Kubernetes ConfigMap can be referred as a source of environment variables in Pods or Deployments. + +For example ConfigMap defined in following YAML: +``` +apiVersion: v1 +kind: ConfigMap +metadata: + name: foo +data: + FOO: bar +``` + +can be referred in a Pod in following way: +``` +apiVersion: v1 +kind: Pod +metadata: +... +spec: + containers: + - command: + - top + name: container-1 + image: foobar + envFrom: + - configMapRef: + name: foo + optional: false +``` + +and as a result environment variable `FOO` will be set to `bar` for container `container-1`. + ## OPTIONS #### **--authfile**=*path* @@ -45,9 +112,13 @@ Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE environment variable. `export REGISTRY_AUTH_FILE=path` +#### **--build** + +Build images even if they are found in the local storage. + #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--configmap**=*path* @@ -62,6 +133,11 @@ If one or both values are not supplied, a command line prompt will appear and the value can be entered. The password is entered without echo. +#### **--down** + +Tears down the pods that were created by a previous run of `play kube`. The pods are stopped and then +removed. Any volumes created are left intact. + #### **--ip**=*IP address* Assign a static ip address to the pod. This option can be specified several times when play kube creates more than one pod. @@ -74,9 +150,28 @@ Assign a static mac address to the pod. This option can be specified several times when play kube creates more than one pod. -#### **--network**=*networks*, **--net** +#### **--network**=*mode*, **--net** + +Change the network mode of the pod. The host and bridge network mode should be configured in the yaml file. +Valid _mode_ values are: -A comma-separated list of the names of CNI networks the pod should join. +- **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity. +- **container:**_id_: Reuse another container's network stack. +- **network**: Connect to a user-defined network, multiple networks should be comma-separated. +- **ns:**_path_: Path to a network namespace to join. +- **private**: Create a new namespace for the container. This will use the **bridge** mode for rootfull containers and **slirp4netns** for rootless ones. +- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: + - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`, which is added to `/etc/hosts` as `host.containers.internal` for your convenience). Default is false. + - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). + - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). + - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`). + - **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only). + - **outbound_addr=IPv4**: Specify the outbound ipv4 address slirp should bind to. + - **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only). + - **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to. + - **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default. + Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks. + - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks. #### **--quiet**, **-q** @@ -112,6 +207,15 @@ ``` $ cat demo.yml | podman play kube - 52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6 + +``` +Teardown the pod and containers as described in a file `demo.yml` +``` +$ podman play kube --down demo.yml +Pods stopped: +52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6 +Pods removed: +52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6 ``` Provide `configmap-foo.yml` and `configmap-bar.yml` as sources for environment variables within the containers. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pod.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pod.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pod.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pod.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -17,11 +17,12 @@ | exists | [podman-pod-exists(1)](podman-pod-exists.1.md) | Check if a pod exists in local storage. | | inspect | [podman-pod-inspect(1)](podman-pod-inspect.1.md) | Displays information describing a pod. | | kill | [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in one or more pods. | +| logs | [podman-pod-logs(1)](podman-pod-logs.1.md) | Displays logs for pod with one or more containers. | | pause | [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. | -| prune | [podman-pod-prune(1)](podman-pod-prune.1.md) | Remove all stopped pods and their containers. | +| prune | [podman-pod-prune(1)](podman-pod-prune.1.md) | Remove all stopped pods and their containers. | | ps | [podman-pod-ps(1)](podman-pod-ps.1.md) | Prints out information about pods. | | restart | [podman-pod-restart(1)](podman-pod-restart.1.md) | Restart one or more pods. | -| rm | [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more stopped pods and containers. | +| rm | [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more stopped pods and containers. | | start | [podman-pod-start(1)](podman-pod-start.1.md) | Start one or more pods. | | stats | [podman-pod-stats(1)](podman-pod-stats.1.md) | Display a live stream of resource usage stats for containers in one or more pods. | | stop | [podman-pod-stop(1)](podman-pod-stop.1.md) | Stop one or more pods. | diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pod-create.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pod-create.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pod-create.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pod-create.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -35,7 +35,26 @@ Set custom DNS search domains in the /etc/resolv.conf file that will be shared between all containers in the pod. -#### **--help** +#### **--gidmap**=*container_gid:host_gid:amount* + +GID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subgidname` flags. + +#### **--uidmap**=*container_uid*:*from_uid*:*amount* + +Run the container in a new user namespace using the supplied mapping. This +option conflicts with the **--userns** and **--subuidname** options. This +option provides a way to map host UIDs to container UIDs. It can be passed +several times to map different ranges. + +#### **--subgidname**=*name* + +Name for GID map from the `/etc/subgid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--gidmap`. + +#### **--subuidname**=*name* + +Name for UID map from the `/etc/subuid` file. Using this flag will run the container with user namespace enabled. This flag conflicts with `--userns` and `--uidmap`. + +#### **--help**, **-h** Print usage statement. @@ -59,6 +78,10 @@ The image that will be created for the infra container. Default: "k8s.gcr.io/pause:3.1". +#### **--infra-name**=*name* + +The name that will be used for the pod's infra container. + #### **--ip**=*ipaddr* Set a static IP for the pod's shared network. @@ -81,12 +104,15 @@ #### **--network**=*mode*, **--net** -Set network mode for the pod. Supported values are +Set network mode for the pod. Supported values are: - **bridge**: Create a network stack on the default bridge. This is the default for rootfull containers. +- **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity. - **host**: Do not create a network namespace, all containers in the pod will use the host's network. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. -- Comma-separated list of the names of CNI networks the pod should join. -- **slirp4netns[:OPTIONS,...]**: use slirp4netns to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. +- **network**: Connect to a user-defined network, multiple networks should be comma-separated. +- **private**: Create a new namespace for the container. This will use the **bridge** mode for rootfull containers and **slirp4netns** for rootless ones. +- **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: + - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`, which is added to `/etc/hosts` as `host.containers.internal` for your convenience). Default is false. + - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`). - **outbound_addr=INTERFACE**: Specify the outbound interface slirp should bind to (ipv4 traffic only). @@ -94,16 +120,25 @@ - **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only). - **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to. - **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default. - - **port_handler=slirp4netns**: Use the slirp4netns port forwarding. + Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks. + - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks. #### **--network-alias**=strings -Add a DNS alias for the container. When the container is joined to a CNI network with support for the dnsname plugin, the container will be accessible through this name from other containers in the network. +Add a DNS alias for the pod. When the pod is joined to a CNI network with support for the dnsname plugin, the containers inside the pod will be accessible through this name from other containers in the network. #### **--no-hosts**=**true**|**false** Disable creation of /etc/hosts for the pod. +#### **--pid**=*pid* + +Set the PID mode for the pod. The default is to create a private PID namespace for the pod. Requires the PID namespace to be shared via --share. + + host: use the host’s PID namespace for the pod + ns: join the specified PID namespace + private: create a new namespace for the pod (default) + #### **--pod-id-file**=*path* Write the pod ID to the file. @@ -136,6 +171,19 @@ to the container with **--name** then a random string name will be generated for it. The name is useful any place you need to identify a pod. +#### **--userns**=*mode* + +Set the user namespace mode for all the containers in a pod. It defaults to the **PODMAN_USERNS** environment variable. An empty value ("") means user namespaces are disabled. + +Valid _mode_ values are: + +- *auto[:*_OPTIONS,..._*]*: automatically create a namespace. It is possible to specify these options to `auto`: + - *gidmapping=*_CONTAINER_GID:HOST_GID:SIZE_ to force a GID mapping to be present in the user namespace. + - *size=*_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. + - *uidmapping=*_CONTAINER_UID:HOST_UID:SIZE_ to force a UID mapping to be present in the user namespace. +- *host*: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). +- *keep-id*: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user. + ## EXAMPLES ``` diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pod-inspect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pod-inspect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pod-inspect.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pod-inspect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -70,7 +70,7 @@ ``` ## SEE ALSO -podman-pod(1), podman-pod-ps(1) +**[podman(1)](podman.1.md)**,**[podman-pod(1)](podman-pod.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** ## HISTORY August 2018, Originally compiled by Brent Baude diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pod-logs.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pod-logs.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pod-logs.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pod-logs.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,88 @@ +% podman-pod-logs(1) + +## NAME +podman\-pod\-logs - Displays logs for pod with one or more containers + +## SYNOPSIS +**podman pod logs** [*options*] *pod* + +## DESCRIPTION +The podman pod logs command batch-retrieves whatever logs are present with all the containers of a pod. Pod logs can be filtered by container name or id using flag **-c** or **--container** if needed. + +Note: Long running command of `podman pod log` with a `-f` or `--follow` needs to be reinvoked if new container is added to the pod dynamically otherwise logs of newly added containers would not be visible in log stream. + +## OPTIONS + +#### **--container**, **-c** + +By default `podman pod logs` retrives logs for all the containers available within the pod differentiate by field `container`. However there are use-cases where user would want to limit the log stream only to a particular container of a pod for such cases `-c` can be used like `podman pod logs -c ctrNameorID podname`. + +#### **--follow**, **-f** + +Follow log output. Default is false. + +Note: If you are following a pod which is removed `podman pod rm`, then there is a +chance the the log file will be removed before `podman pod logs` reads the final content. + +#### **--latest**, **-l** + +Instead of providing the pod name or id, get logs of the last created pod. (This option is not available with the remote Podman client) + +#### **--since**=*TIMESTAMP* + +Show logs since TIMESTAMP. The --since option can be Unix timestamps, date formatted timestamps, or Go duration +strings (e.g. 10m, 1h30m) computed relative to the client machine's time. Supported formats for date formatted +time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00, +and 2006-01-02. + +#### **--until**=*TIMESTAMP* + +Show logs until TIMESTAMP. The --until option can be Unix timestamps, date formatted timestamps, or Go duration +strings (e.g. 10m, 1h30m) computed relative to the client machine's time. Supported formats for date formatted +time stamps include RFC3339Nano, RFC3339, 2006-01-02T15:04:05, 2006-01-02T15:04:05.999999999, 2006-01-02Z07:00, +and 2006-01-02. + + +#### **--tail**=*LINES* + +Output the specified number of LINES at the end of the logs. LINES must be an integer. Defaults to -1, +which prints all lines + +#### **--timestamps**, **-t** + +Show timestamps in the log outputs. The default is false + +## EXAMPLE + +To view a pod's logs: +``` +podman pod logs -t podIdorName +``` + +To view logs of a specific container on the pod +``` +podman pod logs -c ctrIdOrName podIdOrName +``` + +To view all pod logs: +``` +podman pod logs -t --since 0 myserver-pod-1 +``` + +To view a pod's logs since a certain time: +``` +podman pod logs -t --since 2017-08-07T10:10:09.055837383-04:00 myserver-pod-1 +``` + +To view a pod's logs generated in the last 10 minutes: +``` +podman pod logs --since 10m myserver-pod-1 +``` + +To view a pod's logs until 30 minutes ago: +``` +podman pod logs --until 30m myserver-pod-1 +``` + +## SEE ALSO +podman(1), podman-pod-start(1), podman-pod-rm(1), podman-logs(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pod-ps.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pod-ps.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pod-ps.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pod-ps.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -12,7 +12,9 @@ * pod id * pod name + * the time the pod was created * number of containers attached to pod + * container id of the pod infra container * status of pod as defined by the following table | **Status** | **Description** | @@ -28,15 +30,15 @@ #### **--ctr-names** -Includes the container names in the container info field +Display the container names #### **--ctr-ids** -Includes the container IDs in the container info field +Display the container IDs #### **--ctr-status** -Includes the container statuses in the container info field +Display the container statuses #### **--latest**, **-l** @@ -96,6 +98,7 @@ | id | [ID] Pod's ID (accepts regex) | | name | [Name] Pod's name (accepts regex) | | label | [Key] or [Key=Value] Label assigned to a container | +| until | Only list pods created before given timestamp | | status | Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded` | | network | [Network] name or full ID of network | | ctr-names | Container name within the pod (accepts regex) | @@ -111,62 +114,55 @@ ``` $ podman pod ps -POD ID NAME STATUS NUMBER OF CONTAINERS -00dfd6fa02c0 jolly_goldstine Running 1 -f4df8692e116 nifty_torvalds Created 2 +POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS +00dfd6fa02c0 jolly_goldstine Running 31 hours ago ba465ab0a3a4 1 +f4df8692e116 nifty_torvalds Created 10 minutes ago 331693bff40a 2 ``` ``` $ podman pod ps --ctr-names -POD ID NAME STATUS CONTAINER INFO -00dfd6fa02c0 jolly_goldstine Running [ loving_archimedes ] -f4df8692e116 nifty_torvalds Created [ thirsty_hawking ] [ wizardly_golick ] +POD ID NAME STATUS CREATED INFRA ID NAMES +00dfd6fa02c0 jolly_goldstine Running 31 hours ago ba465ab0a3a4 loving_archimedes +f4df8692e116 nifty_torvalds Created 10 minutes ago 331693bff40a thirsty_hawking,wizardly_golick ``` ``` $ podman pod ps --ctr-status --ctr-names --ctr-ids -POD ID NAME STATUS CONTAINER INFO -00dfd6fa02c0 jolly_goldstine Running [ ba465ab0a3a4 loving_archimedes Running ] -f4df8692e116 nifty_torvalds Created [ 331693bff40a thirsty_hawking Created ] [ 8e428daeb89e wizardly_golick Created ] +POD ID NAME STATUS CREATED INFRA ID IDS NAMES STATUS +00dfd6fa02c0 jolly_goldstine Running 31 hours ago ba465ab0a3a4 ba465ab0a3a4 loving_archimedes running +f4df8692e116 nifty_torvalds Created 10 minutes ago 331693bff40a 331693bff40a,8e428daeb89e thirsty_hawking,wizardly_golick configured,configured ``` ``` -$ podman pod ps --format "{{.ID}} {{.ContainerInfo}} {{.Cgroup}}" --ctr-names -00dfd6fa02c0 [ loving_archimedes ] /libpod_parent -f4df8692e116 [ thirsty_hawking ] [ wizardly_golick ] /libpod_parent -``` - -``` -$ podman pod ps --cgroup -POD ID NAME STATUS NUMBER OF CONTAINERS CGROUP USE POD CGROUP -00dfd6fa02c0 jolly_goldstine Running 1 /libpod_parent true -f4df8692e116 nifty_torvalds Created 2 /libpod_parent true +$ podman pod ps --format "{{.ID}} {{.ContainerNames}} {{.Cgroup}}" +00dfd6fa02c0 loving_archimedes /libpod_parent +f4df8692e116 thirsty_hawking,wizardly_golick /libpod_parent ``` ``` $ podman pod ps --sort id --filter ctr-number=2 -POD ID NAME STATUS NUMBER OF CONTAINERS -f4df8692e116 nifty_torvalds Created 2 +POD ID NAME STATUS CREATED INFRA ID # OF CONTAINERS +f4df8692e116 nifty_torvalds Created 10 minutes ago 331693bff40a 2 ``` ``` $ podman pod ps --ctr-ids -POD ID NAME STATUS CONTAINER INFO -00dfd6fa02c0 jolly_goldstine Running [ ba465ab0a3a4 ] -f4df8692e116 nifty_torvalds Created [ 331693bff40a ] [ 8e428daeb89e ] +POD ID NAME STATUS CREATED INFRA ID IDS +00dfd6fa02c0 jolly_goldstine Running 31 hours ago ba465ab0a3a4 ba465ab0a3a4 +f4df8692e116 nifty_torvalds Created 10 minutes ago 331693bff40a 331693bff40a,8e428daeb89e ``` ``` $ podman pod ps --no-trunc --ctr-ids -POD ID NAME STATUS CONTAINER INFO -00dfd6fa02c0a2daaedfdf8fcecd06f22ad114d46d167d71777224735f701866 jolly_goldstine Running [ ba465ab0a3a4e15e3539a1e79c32d1213a02b0989371e274f98e0f1ae9de7050 ] -f4df8692e116a3e6d1d62572644ed36ca475d933808cc3c93435c45aa139314b nifty_torvalds Created [ 331693bff40a0ef2f05a3aba73ce49e3243108911927fff04d1f7fc44dda8022 ] [ 8e428daeb89e69b71e7916a13accfb87d122889442b5c05c2d99cf94a3230e9d ] +POD ID NAME STATUS CREATED INFRA ID IDS +00dfd6fa02c0a2daaedfdf8fcecd06f22ad114d46d167d71777224735f701866 jolly_goldstine Running 31 hours ago ba465ab0a3a4e15e3539a1e79c32d1213a02b0989371e274f98e0f1ae9de7050 ba465ab0a3a4e15e3539a1e79c32d1213a02b0989371e274f98e0f1ae9de7050 +f4df8692e116a3e6d1d62572644ed36ca475d933808cc3c93435c45aa139314b nifty_torvalds Created 10 minutes ago 331693bff40a926b6d52b184e116afd15497610c378d5d4c42945dd6e33b75b0 331693bff40a926b6d52b184e116afd15497610c378d5d4c42945dd6e33b75b0,8e428daeb89e69b71e7916a13accfb87d122889442b5c05c2d99cf94a3230e9d ``` ``` $ podman pod ps --ctr-names -POD ID NAME STATUS CONTAINER INFO -314f4da82d74 hi Created [ jovial_jackson ] [ hopeful_archimedes ] [ vibrant_ptolemy ] [ heuristic_jennings ] [ keen_raman ] [ hopeful_newton ] [ mystifying_bose ] [ silly_lalande ] [ serene_lichterman ] ... +POD ID NAME STATUS CREATED INFRA ID NAMES +314f4da82d74 hi Created 17 hours ago a9f2d2165675 jovial_jackson,hopeful_archimedes,vibrant_ptolemy,heuristic_jennings,keen_raman,hopeful_newton,mystifying_bose,silly_lalande,serene_lichterman ... ``` ## pod ps diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pod-rm.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pod-rm.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pod-rm.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pod-rm.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -7,7 +7,7 @@ **podman pod rm** [*options*] *pod* ## DESCRIPTION -**podman pod rm** will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod. +**podman pod rm** will remove one or more stopped pods and their containers from the host. The pod name or ID can be used. The \-f option stops all containers and then removes them before removing the pod. If all containers added by the user are in an exited state, the pod will be removed. ## OPTIONS diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-pull.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-pull.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-pull.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-pull.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,83 +4,66 @@ podman\-pull - Pull an image from a registry ## SYNOPSIS -**podman pull** [*options*] *source* +**podman pull** [*options*] *source* [*source*...] -**podman image pull** [*options*] *source* +**podman image pull** [*options*] *source* [*source*...] **podman pull** [*options*] [*transport*]*name*[:*tag*|@*digest*] **podman image pull** [*options*] [*transport*]*name*[:*tag*|@*digest*] ## DESCRIPTION -Copies an image from a registry onto the local machine. The **podman pull** command pulls an -image. If the image reference in the command line argument does not contain a registry, it is referred to as a`short-name` reference. If the image is a 'short-name' reference, Podman will prompt the user for the specific container registry to pull the image from, if an alias for the short-name has not been specified in the short-name-aliases.conf. If an image tag is not specified, **podman pull** defaults to the image with the **latest** tag (if it exists) and pulls it. After the image is pulled, podman will print the full image ID. **podman pull** can also pull an image using its digest **podman pull** *image*@*digest*. **podman pull** can be used to pull images from archives and local storage using different transports. - -## Image storage -Images are stored in local image storage. +podman pull copies an image from a registry onto the local machine. The command can pull one or more images. If the image reference in the command line argument does not contain a registry, it is referred to as a`short-name` reference. If the image is a 'short-name' reference, Podman will prompt the user for the specific container registry to pull the image from, if an alias for the short-name has not been specified in the `short-name-aliases.conf`. If an image tag is not specified, **podman pull** defaults to the image with the **latest** tag (if it exists) and pulls it. After the image is pulled, podman will print the full image ID. **podman pull** can also pull images using a digest **podman pull** *image*@*digest* and can also be used to pull images from archives and local storage using different transports. +*IMPORTANT: Images are stored in local image storage.* ## SOURCE +SOURCE is the location from the container image is pulled from. It supports all transports from **[containers-transports(5)](https://github.com/containers/image/blob/main/docs/containers-transports.5.md)**. If no transport is specified, the input is subject to short-name resolution and the `docker` (i.e., container registry) transport is used. For remote clients, `docker` is the only supported transport. - The SOURCE is the location from which the container images are pulled. - The Image "SOURCE" uses a "transport":"details" format. Only the `docker` (container registry) - transport is allowed for remote access. - - Multiple transports are supported: - - **dir:**_path_ - An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This - is a non-standardized format, primarily useful for debugging or noninvasive container inspection. - - $ podman pull dir:/tmp/myimage - - **docker://**_docker-reference_ (Default) - An image reference stored in a remote container image registry. The reference can include a path to a - specific registry; if it does not, the registries listed in registries.conf will be queried to find a matching - image. By default, credentials from podman login (stored at $XDG_RUNTIME_DIR/containers/auth.json by default) - will be used to authenticate; if these cannot be found, we will fall back to using credentials in - $HOME/.docker/config.json. - - $ podman pull quay.io/username/myimage +``` +# Pull from a container registry +$ podman pull quay.io/username/myimage - **docker-archive:**_path_[**:**_docker-reference_] - An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a - file, and it must not contain a digest. +# Pull from a container registry with short-name resolution +$ podman pull fedora - $ podman pull docker-archive:/tmp/myimage +# Pull from a container registry via the docker transport +$ podman pull docker://quay.io/username/myimage - **docker-daemon:**_docker-reference_ - An image in _docker-reference_ format stored in the docker daemon internal storage. The _docker-reference_ can also be an image ID (docker-daemon:algo:digest). +# Pull from a local directory +$ podman pull dir:/tmp/myimage - $ sudo podman pull docker-daemon:docker.io/library/myimage:33 +# Pull from a tarball in the docker-archive format +$ podman pull docker-archive:/tmp/myimage - **oci-archive:**_path_**:**_tag_ - An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_. +# Pull from a local docker daemon +$ sudo podman pull docker-daemon:docker.io/library/myimage:33 - $ podman pull oci-archive:/tmp/myimage +# Pull from a tarball in the OCI-archive format +$ podman pull oci-archive:/tmp/myimage +``` ## OPTIONS - -#### **--all-tags**, **a** +#### **--all-tags** All tagged images in the repository will be pulled. -Note: When using the all-tags flag, Podman will not iterate over the search registries in the containers-registries.conf(5) but will always use docker.io for unqualified image names. +*IMPORTANT: When using the all-tags flag, Podman will not iterate over the search registries in the **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)** but will always use docker.io for unqualified image names.* #### **--arch**=*ARCH* Override the architecture, defaults to hosts, of the image to be pulled. For example, `arm`. #### **--authfile**=*path* -Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`. -If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`. +Path of the authentication file. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `docker login`. -Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE -environment variable. `export REGISTRY_AUTH_FILE=path` +Default is `${XDG\_RUNTIME\_DIR}/containers/auth.json`, which is set using `podman login`. + +*IMPORTANT: The default path of the authentication file can be overwritten by setting the `REGISTRY\_AUTH\_FILE` environment variable. `export REGISTRY_AUTH_FILE=path`* #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. -Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) +Please refer to **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)** for details. (This option is not available with the remote Podman client) #### **--creds**=*[username[:password]]* @@ -96,15 +79,17 @@ #### **--help**, **-h** -Print usage statement +Print the usage statement. #### **--os**=*OS* + Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`. #### **--platform**=*OS/ARCH* -Specify the platform for selecting the image. (Conflicts with --arch and --os) -The `--platform` option can be used to override the current architecture and operating system. +Specify the platform for selecting the image. The `--platform` option can be used to override the current architecture and operating system. + +*IMPORTANT: Conflicts with --arch and --os* #### **--quiet**, **-q** @@ -120,22 +105,72 @@ Use _VARIANT_ instead of the default architecture variant of the container image. Some images can use multiple variants of the arm architectures, such as arm/v5 and arm/v7. -## EXAMPLES +## FILES + +**short-name-aliases.conf** (`/var/cache/containers/short-name-aliases.conf`, `$HOME/.cache/containers/short-name-aliases.conf`) + +When users specify images that do not include the container registry where the +image is stored, this is called a short name. The use of unqualified-search registries entails an ambiguity as it is unclear from which registry a given image, referenced by a short name, may be pulled from. + +Using short names is subject to the risk of hitting squatted registry namespaces. If the unqualified-search registries are set to ["public-registry.com", "my-private-registry.com"] an attacker may take over a namespace of `public-registry.com` such that an image may be pulled from `public-registry.com` instead of the intended source `my-private-registry.com`. + +While it is highly recommended to always use fully-qualified image references, existing deployments using short names may not be easily changed. To circumvent the aforementioned ambiguity, so called short-name aliases can be configured that point to a fully-qualified image reference. Distributions often ship a default shortnames.conf expansion file in /etc/containers/registries.conf.d/ directory. Administrators can use this directory to add their own local short-name expansion files. + +When pulling an image, if the user does not specify the complete registry, container engines attempt to expand the short-name into a full name. If the command is executed with a tty, the user will be prompted to select a registry from the +default list unqualified registries defined in registries.conf. The user's selection is then stored in a cache file to be used in all future short-name expansions. Rootfull short-names are stored in /var/cache/containers/short-name-aliases.conf. Rootless short-names are stored in the $HOME/.cache/containers/short-name-aliases.conf file. + +For more information on short-names, see `containers-registries.conf(5)` + +**registries.conf** (`/etc/containers/registries.conf`) + +registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. +NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`. + + +## EXAMPLES +Pull a single image with short name resolution. ``` $ podman pull alpine:latest -Trying to pull registry.access.redhat.com/alpine:latest... Failed -Trying to pull registry.fedoraproject.org/alpine:latest... Failed -Trying to pull docker.io/library/alpine:latest...Getting image source signatures -Copying blob sha256:88286f41530e93dffd4b964e1db22ce4939fffa4a4c665dab8591fbab03d4926 - 1.90 MB / 1.90 MB [========================================================] 0s -Copying config sha256:76da55c8019d7a47c347c0dceb7a6591144d232a7dd616242a367b8bed18ecbc - 1.48 KB / 1.48 KB [========================================================] 0s +Resolved "alpine" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf) +Trying to pull docker.io/library/alpine:latest... +Getting image source signatures +Copying blob 5843afab3874 done +Copying config d4ff818577 done +Writing manifest to image destination +Storing signatures +d4ff818577bc193b309b355b02ebc9220427090057b54a59e73b79bdfe139b83 +``` + +Pull multiple images with/without short name resolution. +``` +podman pull busybox:musl alpine quay.io/libpod/cirros +Trying to pull docker.io/library/busybox:musl... +Getting image source signatures +Copying blob 0c52b060233b [--------------------------------------] 0.0b / 0.0b +Copying config 9ad2c435a8 done +Writing manifest to image destination +Storing signatures +9ad2c435a887e3f723654e09b48563de44aa3c7950246b2e9305ec85dd3422db +Trying to pull docker.io/library/alpine:latest... +Getting image source signatures +Copying blob 5843afab3874 [--------------------------------------] 0.0b / 0.0b +Copying config d4ff818577 done +Writing manifest to image destination +Storing signatures +d4ff818577bc193b309b355b02ebc9220427090057b54a59e73b79bdfe139b83 +Trying to pull quay.io/libpod/cirros:latest... +Getting image source signatures +Copying blob 8da581cc9286 done +Copying blob 856628d95d17 done +Copying blob f513001ba4ab done +Copying config 3c82e4d066 done Writing manifest to image destination Storing signatures -04660052281190168dbb2362eb15bf7067a8dc642d2498055e0e72efa961a4b6 +3c82e4d066cf6f9e50efaead6e3ff7fddddf5527826afd68e5a969579fc4db4a ``` +Pull an image using its digest. ``` $ podman pull alpine@sha256:d7342993700f8cd7aba8496c2d0e57be0666e80b4c441925fc6f9361fa81d10e Trying to pull docker.io/library/alpine@sha256:d7342993700f8cd7aba8496c2d0e57be0666e80b4c441925fc6f9361fa81d10e... @@ -147,6 +182,7 @@ d6e46aa2470df1d32034c6707c8041158b652f38d2a9ae3d7ad7e7532d22ebe0 ``` +Pull an image by specifying an authentication file. ``` $ podman pull --authfile temp-auths/myauths.json docker://docker.io/umohnani/finaltest Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures @@ -159,6 +195,7 @@ 03290064078cb797f3e0a530e78c20c13dd22a3dd3adf84a5da2127b48df0438 ``` +Pull an image by authenticating to a registry. ``` $ podman pull --creds testuser:testpassword docker.io/umohnani/finaltest Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures @@ -171,6 +208,7 @@ 03290064078cb797f3e0a530e78c20c13dd22a3dd3adf84a5da2127b48df0438 ``` +Pull an image using tls verification. ``` $ podman pull --tls-verify=false --cert-dir image/certs docker.io/umohnani/finaltest Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures @@ -183,6 +221,7 @@ 03290064078cb797f3e0a530e78c20c13dd22a3dd3adf84a5da2127b48df0438 ``` +Pull an image by overriding the host architecture. ``` $ podman pull --arch=arm arm32v7/debian:stretch Trying to pull docker.io/arm32v7/debian:stretch... @@ -194,30 +233,8 @@ 3cba58dad5d9b35e755b48b634acb3fdd185ab1c996ac11510cc72c17780e13c ``` -## FILES - -**short-name-aliases.conf** (`/var/cache/containers/short-name-aliases.conf`, `$HOME/.cache/containers/short-name-aliases.conf`) - -When users specify images that do not include the container registry where the -image is stored, this is called a short name. The use of unqualified-search registries entails an ambiguity as it is unclear from which registry a given image, referenced by a short name, may be pulled from. - -Using short names is subject to the risk of hitting squatted registry namespaces. If the unqualified-search registries are set to ["public-registry.com", "my-private-registry.com"] an attacker may take over a namespace of `public-registry.com` such that an image may be pulled from `public-registry.com` instead of the intended source `my-private-registry.com`. - -While it is highly recommended to always use fully-qualified image references, existing deployments using short names may not be easily changed. To circumvent the aforementioned ambiguity, so called short-name aliases can be configured that point to a fully-qualified image reference. Distributions often ship a default shortnames.conf expansion file in /etc/containers/registries.conf.d/ directory. Administrators can use this directory to add their own local short-name expansion files. - -When pulling an image, if the user does not specify the complete registry, container engines attempt to expand the short-name into a full name. If the command is executed with a tty, the user will be prompted to select a registry from the -default list unqualified registries defined in registries.conf. The user's selection is then stored in a cache file to be used in all future short-name expansions. Rootfull short-names are stored in /var/cache/containers/short-name-aliases.conf. Rootless short-names are stored in the $HOME/.cache/containers/short-name-aliases.conf file. - -For more information on short-names, see `containers-registries.conf(5)` - -**registries.conf** (`/etc/containers/registries.conf`) - -registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. - -NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`. - ## SEE ALSO -podman(1), podman-push(1), podman-login(1), containers-certs.d(5), containers-registries.conf(5) +**[podman(1)](podman.1.md)**, **[podman-push(1)](podman-push.1.md)**, **[podman-login(1)](podman-login.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.d.5.md)**, **[containers-transports(5)](https://github.com/containers/image/blob/main/docs/containers-transports.5.md)** ## HISTORY July 2017, Originally compiled by Urvashi Mohnani diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-push.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-push.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-push.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-push.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -20,37 +20,30 @@ ## DESTINATION - The DESTINATION is a location to store container images - The Image "DESTINATION" uses a "transport":"details" format. - If a transport is not given, podman push will attempt to push - to a registry. + DESTINATION is the location the container image is pushed to. It supports all transports from `containers-transports(5)`. If no transport is specified, the `docker` (i.e., container registry) transport is used. For remote clients, `docker` is the only supported transport. - Multiple transports are supported: - - **dir:**_path_ - An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection. - - $ podman push myimage dir:/tmp/myimage - - **docker://**_docker-reference_ - An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set using `(podman login)`. If the authorization state is not found there, `$HOME/.docker/config.json` is checked, which is set using `(docker login)`. - - $ podman push myimage quay.io/username/myimage +``` +# Push to a container registry +$ podman push quay.io/podman/stable - **docker-archive:**_path_[**:**_docker-reference_] - An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest. +# Push to a container registry via the docker transport +$ podman push docker://quay.io/podman/stable - $ podman push myimage docker-archive:/tmp/myimage +# Push to a container registry with another tag +$ podman push myimage quay.io/username/myimage - **docker-daemon:**_docker-reference_ - An image in _docker-reference_ format stored in the docker daemon internal storage. _docker-reference_ must contain a tag. +# Push to a local directory +$ podman push myimage dir:/tmp/myimage - $ sudo podman push myimage docker-daemon:docker.io/library/myimage:33 +# Push to a tarball in the docker-archive format +$ podman push myimage docker-archive:/tmp/myimage - **oci-archive:**_path_**:**_tag_ - An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_. +# Push to a local docker daemon +$ sudo podman push myimage docker-daemon:docker.io/library/myimage:33 - $ podman push myimage oci-archive:/tmp/myimage +# Push to a tarball in the OCI format +$ podman push myimage oci-archive:/tmp/myimage +``` ## OPTIONS @@ -70,7 +63,7 @@ #### **--cert-dir**=*path* -Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. +Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry. (Default: /etc/containers/certs.d) Please refer to containers-certs.d(5) for details. (This option is not available with the remote Podman client) #### **--compress** @@ -161,4 +154,4 @@ ``` ## SEE ALSO -podman(1), podman-pull(1), podman-login(1), containers-certs.d(5) +podman(1), podman-pull(1), podman-login(1), containers-certs.d(5), containers-transports(5) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-rmi.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-rmi.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-rmi.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-rmi.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,9 @@ ## DESCRIPTION Removes one or more locally stored images. +Passing an argument _image_ deletes it, along with any of its dangling parent images. A dangling image is an image without a tag and without being referenced by another image. + +Note: To delete an image from a remote registry, use the [**skopeo delete**](https://github.com/containers/skopeo/blob/main/docs/skopeo-delete.1.md) command. Some registries do not allow users to delete an image via a CLI remotely. ## OPTIONS @@ -50,7 +53,7 @@ **125** The command fails for any other reason ## SEE ALSO -podman(1) +podman(1), skopeo-delete(1) ## HISTORY March 2017, Originally compiled by Dan Walsh diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-run.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-run.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-run.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-run.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -199,6 +199,8 @@ Limit the container's Real Time CPU usage. This flag tell the kernel to restrict the container's Real Time CPU usage to the period you specify. +This flag is not supported on cgroups V2 systems. + #### **--cpu-rt-runtime**=*microseconds* Limit the CPU real-time runtime in microseconds. @@ -208,6 +210,8 @@ The sum of all runtimes across containers cannot exceed the amount allotted to the parent cgroup. +This flag is not supported on cgroups V2 systems. + #### **--cpu-shares**=*shares* CPU shares (relative weight). @@ -281,12 +285,10 @@ #### **--detach-keys**=*sequence* -Specify the key sequence for detaching a container. Format is a single character `[a-Z]` or one or more `ctrl-` characters where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will disable this feature. The default is *ctrl-p,ctrl-q*. +Specify the key sequence for detaching a container. Format is a single character `[a-Z]` or one or more `ctrl-` characters where `` is one of: `a-z`, `@`, `^`, `[`, `,` or `_`. Specifying "" will set the sequence to the default value of *ctrl-p,ctrl-q*. This option can also be set in **containers.conf**(5) file. -Specifying "" will disable this feature. The default is **ctrl-p,ctrl-q**. - #### **--device**=_host-device_[**:**_container-device_][**:**_permissions_] Add a host device to the container. Optional *permissions* parameter @@ -520,6 +522,8 @@ of the operating system's page size and the value can be very large, millions of trillions. +This flag is not supported on cgroups V2 systems. + #### **--label**, **-l**=*key*=*value* Add metadata to a container. @@ -597,6 +601,8 @@ Tune a container's memory swappiness behavior. Accepts an integer between *0* and *100*. +This flag is not supported on cgroups V2 systems. + #### **--mount**=*type=TYPE,TYPE-SPECIFIC-OPTION[,...]* Attach a filesystem mount to the container @@ -674,15 +680,15 @@ Valid _mode_ values are: -- **bridge**: create a network stack on the default bridge; -- **none**: no networking; -- **container:**_id_: reuse another container's network stack; -- **host**: use the Podman host network stack. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure; -- _network-id_: connect to a user-defined network, multiple networks should be comma-separated; -- **ns:**_path_: path to a network namespace to join; -- **private**: create a new namespace for the container (default) +- **bridge**: Create a network stack on the default bridge. This is the default for rootfull containers. +- **none**: Create a network namespace for the container but do not configure network interfaces for it, thus the container has no network connectivity. +- **container:**_id_: Reuse another container's network stack. +- **host**: Do not create a network namespace, the container will use the host's network. Note: The host mode gives the container full access to local system services such as D-bus and is therefore considered insecure. +- **network**: Connect to a user-defined network, multiple networks should be comma-separated. +- **ns:**_path_: Path to a network namespace to join. +- **private**: Create a new namespace for the container. This will use the **bridge** mode for rootfull containers and **slirp4netns** for rootless ones. - **slirp4netns[:OPTIONS,...]**: use **slirp4netns**(1) to create a user network stack. This is the default for rootless containers. It is possible to specify these additional options: - - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`). Default is false. + - **allow_host_loopback=true|false**: Allow the slirp4netns to reach the host loopback IP (`10.0.2.2`, which is added to `/etc/hosts` as `host.containers.internal` for your convenience). Default is false. - **mtu=MTU**: Specify the MTU to use for this network. (Default is `65520`). - **cidr=CIDR**: Specify ip range to use for this network. (Default is `10.0.2.0/24`). - **enable_ipv6=true|false**: Enable IPv6. Default is false. (Required for `outbound_addr6`). @@ -691,11 +697,12 @@ - **outbound_addr6=INTERFACE**: Specify the outbound interface slirp should bind to (ipv6 traffic only). - **outbound_addr6=IPv6**: Specify the outbound ipv6 address slirp should bind to. - **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default. - - **port_handler=slirp4netns**: Use the slirp4netns port forwarding. + Note: Rootlesskit changes the source IP address of incoming packets to a IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks. + - **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks. #### **--network-alias**=*alias* -Add network-scoped alias for the container +Add network-scoped alias for the container. NOTE: A container will only have access to aliases on the first network that it joins. This is a limitation that will be removed in a later release. #### **--no-healthcheck**=*true|false* @@ -720,6 +727,10 @@ #### **--os**=*OS* Override the OS, defaults to hosts, of the image to be pulled. For example, `windows`. +#### **--personality**=*persona* + +Personality sets the execution domain via Linux personality(2). + #### **--pid**=*mode* Set the PID namespace mode for the container. @@ -732,7 +743,7 @@ #### **--pids-limit**=*limit* -Tune the container's pids limit. Set to **0** to have unlimited pids for the container. The default is **4096** on systems that support "pids" cgroup controller. +Tune the container's pids limit. Set to **-1** to have unlimited pids for the container. The default is **4096** on systems that support "pids" cgroup controller. #### **--platform**=*OS/ARCH* @@ -910,7 +921,10 @@ Secret Options - `type=mount|env` : How the secret will be exposed to the container. Default mount. -- `target=target` : Target of secret. Defauts to secret name. +- `target=target` : Target of secret. Defaults to secret name. +- `uid=0` : UID of secret. Defaults to 0. Mount secret type only. +- `gid=0` : GID of secret. Defaults to 0. Mount secret type only. +- `mode=0` : Mode of secret. Defaults to 0444. Mount secret type only. #### **--security-opt**=*option* @@ -1034,6 +1048,10 @@ signal. By default containers will run until they exit or are stopped by `podman stop`. +#### **--tls-verify**=**true**|**false** + +Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true, then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified, TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf. + #### **--tmpfs**=*fs* Create a tmpfs mount. @@ -1158,15 +1176,29 @@ Valid _mode_ values are: -- **auto[:**_OPTIONS,..._**]**: automatically create a namespace. It is possible to specify these options to `auto`: - - **gidmapping=**_HOST_GID:CONTAINER_GID:SIZE_: to force a GID mapping to be present in the user namespace. - - **size=**_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. - - **uidmapping=**_HOST_UID:CONTAINER_UID:SIZE_: to force a UID mapping to be present in the user namespace. -- **container:**_id_: join the user namespace of the specified container. -- **host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). -- **keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user. -- **ns:**_namespace_: run the container in the given existing user namespace. -- **private**: create a new namespace for the container. +**auto**[:_OPTIONS,..._]: automatically create a unique user namespace. + +The `--userns=auto` flag, requires that the user name `containers` and a range of subordinate user ids that the Podman container is allowed to use be specified in the /etc/subuid and /etc/subgid files. + +Example: `containers:2147483647:2147483648`. + +Podman allocates unique ranges of UIDs and GIDs from the `containers` subpordinate user ids. The size of the ranges is based on the number of UIDs required in the image. The number of UIDs and GIDs can be overridden with the `size` option. The `auto` options currently does not work in rootless mode + + Valid `auto` options: + + - *gidmapping*=_CONTAINER_GID:HOST_GID:SIZE_: to force a GID mapping to be present in the user namespace. + - *size*=_SIZE_: to specify an explicit size for the automatic user namespace. e.g. `--userns=auto:size=8192`. If `size` is not specified, `auto` will estimate a size for the user namespace. + - *uidmapping*=_CONTAINER_UID:HOST_UID:SIZE_: to force a UID mapping to be present in the user namespace. + +**container:**_id_: join the user namespace of the specified container. + +**host**: run in the user namespace of the caller. The processes running in the container will have the same privileges on the host as any other process launched by the calling user (default). + +**keep-id**: creates a user namespace where the current rootless user's UID:GID are mapped to the same values in the container. This option is ignored for containers created by the root user. + +**ns:**_namespace_: run the container in the given existing user namespace. + +**private**: create a new namespace for the container. This option is incompatible with **--gidmap**, **--uidmap**, **--subuidname** and **--subgidname**. @@ -1256,6 +1288,15 @@ content label. Shared volume labels allow all containers to read/write content. The **Z** option tells Podman to label the content with a private unshared label. +Note: Do not relabel system files and directories. 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` disables SELinux separation for the container. +For example if a user wanted to volume mount their entire home directory into a +container, they need to disable SELinux separation. + + $ podman run --security-opt label=disable -v $HOME:/home/user fedora touch /home/user/file + `Overlay Volume Mounts` The `:O` flag tells Podman to mount the directory from the host as a @@ -1675,6 +1716,15 @@ asdf ``` +### Setting automatic user namespace separated containers + +``` +# podman run --userns=auto:size=65536 ubi8-micro cat /proc/self/uid_map +0 2147483647 65536 +# podman run --userns=auto:size=65536 ubi8-micro cat /proc/self/uid_map +0 2147549183 65536 +``` + ### Setting Namespaced Kernel Parameters (Sysctls) The **--sysctl** sets namespaced kernel parameters (sysctls) in the @@ -1744,6 +1794,12 @@ $ podman run -v /var/lib/design:/var/lib/design --group-add keep-groups ubi8 ``` +### Configure execution domain for containers using personality flag + +``` +$ podman run --name container1 --personaity=LINUX32 fedora bash +``` + ### Rootless Containers Podman runs as a non root user on most systems. This feature requires that a new enough version of **shadow-utils** @@ -1754,7 +1810,7 @@ In order for users to run rootless, there must be an entry for their username in _/etc/subuid_ and _/etc/subgid_ which lists the UIDs for their user namespace. Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed. -The **fuse-overlay** package provides a userspace overlay storage driver, otherwise users need to use +The **fuse-overlayfs** package provides a userspace overlay storage driver, otherwise users need to use the **vfs** storage driver, which is diskspace expensive and does not perform well. slirp4netns is required for VPN, without it containers need to be run with the **--network=host** flag. @@ -1804,7 +1860,7 @@ ## SEE ALSO **podman**(1), **podman-save**(1), **podman-ps**(1), **podman-attach**(1), **podman-pod-create**(1), **podman-port**(1), **podman-start**(1), **podman-kill**(1), **podman-stop**(1), -**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5), **conmon**(8). +**podman-generate-systemd**(1) **podman-rm**(1), **subgid**(5), **subuid**(5), **containers.conf**(5), **systemd.unit**(5), **setsebool**(8), **slirp4netns**(1), **fuse-overlayfs**(1), **proc**(5), **conmon**(8), **personality**(2). ## HISTORY September 2018, updated by Kunal Kushwaha `` diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-save.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-save.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-save.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-save.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -9,8 +9,8 @@ **podman image save** [*options*] *name*[:*tag*] ## DESCRIPTION -**podman save** saves an image to either **docker-archive**, **oci-archive**, **oci-dir** (directory with oci manifest type), or **docker-dir** (directory with v2s2 manifest type) on the local machine, -default is **docker-archive**. **podman save** writes to STDOUT by default and can be redirected to a +**podman save** saves an image to a local file or directory. +**podman save** writes to STDOUT by default and can be redirected to a file using the **output** flag. The **quiet** flag suppresses the output when set. **podman save** will save parent layers of the image(s) and the image(s) can be loaded using **podman load**. To export the containers, use the **podman export**. @@ -35,16 +35,18 @@ #### **--format**=*format* -Save image to **oci-archive, oci-dir** (directory with oci manifest type), or **docker-dir** (directory with v2s2 manifest type) -``` ---format oci-archive ---format oci-dir ---format docker-dir -``` +An image format to produce, one of: + +| Format | Description | +| ------------------ | ---------------------------------------------------------------------------- | +| **docker-archive** | A tar archive interoperable with **docker load(1)** (the default) | +| **oci-archive** | A tar archive using the OCI Image Format | +| **oci-dir** | A directory using the OCI Image Format | +| **docker-dir** | **dir** transport (see **containers-transports(5)**) with v2s2 manifest type | #### **--multi-image-archive**, **-m** -Allow for creating archives with more than one image. Additional names will be interpreted as images instead of tags. Only supported for **docker-archive**. +Allow for creating archives with more than one image. Additional names will be interpreted as images instead of tags. Only supported for **--format=docker-archive**. The default for this option can be modified via the `multi_image_archive="true"|"false"` flag in containers.conf. #### **--quiet**, **-q** @@ -100,7 +102,7 @@ ``` ## SEE ALSO -podman(1), podman-load(1), containers.conf(5) +podman(1), podman-load(1), containers.conf(5), containers-transports(5) ## HISTORY July 2017, Originally compiled by Urvashi Mohnani diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-search.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-search.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-search.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-search.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -9,13 +9,18 @@ ## DESCRIPTION **podman search** searches a registry or a list of registries for a matching image. The user can specify which registry to search by prefixing the registry in the search term -(example **registry.fedoraproject.org/fedora**), default is the registries in the -**registries.search** table in the config file - **/etc/containers/registries.conf**. +(e.g., **registry.fedoraproject.org/fedora**). By default, all +unqualified-search registries in `containers-registries.conf(5)` are used. + The default number of results is 25. The number of results can be limited using the **--limit** flag. If more than one registry is being searched, the limit will be applied to each registry. The output can be filtered using the **--filter** flag. To get all available images in a registry without a specific search term, the user can just enter the registry name with a trailing "/" (example **registry.fedoraproject.org/**). -Note, searching without a search term will only work for registries that implement the v2 API. + +Note that **podman search** is not a reliable way to determine the presence or existence of an image. +The search behavior of the v1 and v2 Docker distribution API is specific to the implementation of each registry. +Some registries may not support searching at all. +Further note that searching without a search term will only work for registries that implement the v2 API. **podman [GLOBAL OPTIONS]** @@ -164,7 +169,7 @@ **registries.conf** (`/etc/containers/registries.conf`) - registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. +registries.conf is the configuration file which specifies which container registries should be consulted when completing image names which do not include a registry or domain portion. ## SEE ALSO podman(1), containers-registries.conf(5) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-secret-create.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-secret-create.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-secret-create.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-secret-create.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -28,6 +28,10 @@ Specify the secret driver (default **file**, which is unencrypted). +#### **--driver-opts**=*key1=val1,key2=val2* + +Specify driver specific options + #### **--help** Print usage statement. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-stats.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-stats.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-stats.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-stats.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -37,6 +37,10 @@ Disable streaming stats and only pull the first result, default setting is false +#### **--interval**=*seconds*, **-i**=*seconds* + +Time in seconds between stats reports, defaults to 5 seconds. + #### **--format**=*template* Pretty-print container statistics to JSON or using a Go template diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-system-connection-add.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-system-connection-add.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-system-connection-add.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-system-connection-add.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,8 @@ Record ssh destination for remote podman service(s). The ssh destination is given as one of: - [user@]hostname[:port] - ssh://[user@]hostname[:port] + - unix://path + - tcp://hostname:port The user will be prompted for the remote ssh login password or key file pass phrase as required. The `ssh-agent` is supported if it is running. @@ -38,6 +40,10 @@ $ podman system connection add QA podman.example.com $ podman system connection add --identity ~/.ssh/dev_rsa production ssh://root@server.example.com:2222 + +$ podman system connection add testing unix:///run/podman/podman.sock + +$ podman system connection add debug tcp://localhost:8080 ``` ## SEE ALSO podman-system(1) , podman-system-connection(1) , containers.conf(5) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-system-connection-list.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-system-connection-list.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-system-connection-list.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-system-connection-list.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -4,13 +4,28 @@ podman\-system\-connection\-list - List the destination for the Podman service(s) ## SYNOPSIS -**podman system connection list** +**podman system connection list** [*options*] -**podman system connection ls** +**podman system connection ls** [*options*] ## DESCRIPTION List ssh destination(s) for podman service(s). +## OPTIONS + +#### **--format**=*format* + +Change the default output format. This can be of a supported type like 'json' or a Go template. +Valid placeholders for the Go template listed below: + +| **Placeholder** | **Description** | +| --------------- | ----------------------------------------------------------------------------- | +| *.Name* | Connection Name/Identifier | +| *.Identity* | Path to file containing SSH identity | +| *.URI* | URI to podman service. Valid schemes are ssh://[user@]*host*[:port]*Unix domain socket*[?secure=True], unix://*Unix domain socket*, and tcp://localhost[:*port*] | + +An asterisk is appended to the default connection. + ## EXAMPLE ``` $ podman system connection list diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-system-service.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-system-service.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-system-service.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-system-service.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -30,6 +30,10 @@ The time until the session expires in _seconds_. The default is 5 seconds. A value of `0` means no timeout, therefore the session will not expire. +#### **--cors** + +CORS headers to inject to the HTTP response. The default value is empty string which disables CORS headers. + #### **--help**, **-h** Print usage statement. diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-unshare.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-unshare.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-unshare.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-unshare.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -37,6 +37,35 @@ not possible from the host network namespace. _Note: Using this option with more than one unshare session can have unexpected results._ +## Exit Codes + +The exit code from `podman unshare` gives information about why the container +failed to run or why it exited. When `podman unshare` commands exit with a non-zero code, +the exit codes follow the `chroot` standard, see below: + + **125** The error is with podman **_itself_** + + $ podman unshare --foo; echo $? + Error: unknown flag: --foo + 125 + + **126** Executing a _contained command_ and the _command_ cannot be invoked + + $ podman unshare /etc; echo $? + Error: fork/exec /etc: permission denied + 126 + + **127** Executing a _contained command_ and the _command_ cannot be found + + $ podman run busybox foo; echo $? + Error: fork/exec /usr/bin/bogus: no such file or directory + 127 + + **Exit code** _contained command_ exit code + + $ podman run busybox /bin/sh -c 'exit 3'; echo $? + 3 + ## EXAMPLE ``` diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -15,6 +15,8 @@ | ------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ | | create | [podman-volume-create(1)](podman-volume-create.1.md) | Create a new volume. | | exists | [podman-volume-exists(1)](podman-volume-exists.1.md) | Check if the given volume exists. | +| export | [podman-volume-export(1)](podman-volume-export.1.md) | Exports volume to external tar. | +| import | [podman-volume-import(1)](podman-volume-import.1.md) | Import tarball contents into a podman volume. | | inspect | [podman-volume-inspect(1)](podman-volume-inspect.1.md) | Get detailed information on one or more volumes. | | ls | [podman-volume-ls(1)](podman-volume-ls.1.md) | List all the available volumes. | | prune | [podman-volume-prune(1)](podman-volume-prune.1.md) | Remove all unused volumes. | diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume-create.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume-create.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume-create.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume-create.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -17,7 +17,7 @@ #### **--driver**=*driver* -Specify the volume driver name (default **local**). Setting this to a value other than **local** Podman will attempt to create the volume using a volume plugin with the given name. Such plugins must be defined in the **volume_plugins** section of the **containers.conf**(5) configuration file. +Specify the volume driver name (default **local**). Setting this to a value other than **local** Podman attempts to create the volume using a volume plugin with the given name. Such plugins must be defined in the **volume_plugins** section of the **containers.conf**(5) configuration file. #### **--help** @@ -34,10 +34,14 @@ For the `local` driver the following options are supported: `type`, `device`, and `o`. The `type` option sets the type of the filesystem to be mounted, and is equivalent to the `-t` flag to **mount(8)**. The `device` option sets the device to be mounted, and is equivalent to the `device` argument to **mount(8)**. -The `o` option sets options for the mount, and is equivalent to the `-o` flag to **mount(8)** with two exceptions. -The `o` option supports `uid` and `gid` options to set the UID and GID of the created volume that are not normally supported by **mount(8)**. -Using volume options with the **local** driver requires root privileges. -When not using the **local** driver, the given options will be passed directly to the volume plugin. In this case, supported options will be dictated by the plugin in question, not Podman. + +The `o` option sets options for the mount, and is equivalent to the `-o` flag to **mount(8)** with these exceptions: + + - The `o` option supports `uid` and `gid` options to set the UID and GID of the created volume that are not normally supported by **mount(8)**. + - The `o` option supports the `size` option to set the maximum size of the created volume and the `inodes` option to set the maximum number of inodes for the volume. Currently these flags are only supported on "xfs" file system mounted with the `prjquota` flag described in the **xfs_quota(8)** man page. + - Using volume options other then the UID/GID options with the **local** driver requires root privileges. + +When not using the **local** driver, the given options are passed directly to the volume plugin. In this case, supported options are dictated by the plugin in question, not Podman. ## EXAMPLES @@ -53,8 +57,36 @@ # podman volume create --opt device=tmpfs --opt type=tmpfs --opt o=uid=1000,gid=1000 testvol ``` +## QUOTAS + +podman volume create uses `XFS project quota controls` for controlling the size and the number of inodes of builtin volumes. The directory used to store the volumes must be an`XFS` file system and be mounted with the `pquota` option. + +Example /etc/fstab entry: +``` +/dev/podman/podman-var /var xfs defaults,x-systemd.device-timeout=0,pquota 1 2 +``` + +Podman generates project ids for each builtin volume, but these project ids need to be unique for the XFS file system. These project ids by default are generated randomly, with a potential for overlap with other quotas on the same file +system. + +The xfs_quota tool can be used to assign a project id to the storage driver directory, e.g.: + +``` +echo 100000:/var/lib/containers/storage/overlay >> /etc/projects +echo 200000:/var/lib/containers/storage/volumes >> /etc/projects +echo storage:100000 >> /etc/projid +echo volumes:200000 >> /etc/projid +xfs_quota -x -c 'project -s storage volumes' / +``` + +In the example above we are configuring the overlay storage driver for newly +created containers as well as volumes to use project ids with a **start offset**. +All containers will be assigned larger project ids (e.g. >= 100000). +All volume assigned project ids larger project ids starting with 200000. +This prevents xfs_quota management conflicts with containers/storage. + ## SEE ALSO -**podman-volume**(1), **mount**(8), **containers.conf**(5) +**podman-volume**(1), **mount**(8), **containers.conf**(5), **xfs_quota**(8), `xfs_quota(8)`, `projects(5)`, `projid(5)` ## HISTORY January 2020, updated with information on volume plugins by Matthew Heon diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume-export.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume-export.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume-export.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume-export.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,38 @@ +% podman-volume-export(1) + +## NAME +podman\-volume\-export - Exports volume to external tar + +## SYNOPSIS +**podman volume export** [*options*] *volume* + +## DESCRIPTION + +**podman volume export** exports the contents of a podman volume and saves it as a tarball +on the local machine. **podman volume export** writes to STDOUT by default and can be +redirected to a file using the `--output` flag. + +Note: Following command is not supported by podman-remote. + +**podman volume export [OPTIONS] VOLUME** + +## OPTIONS + +#### **--output**, **-o**=*file* + +Write to a file, default is STDOUT + +#### **--help** + +Print usage statement + + +## EXAMPLES + +``` +$ podman volume export myvol --output myvol.tar + +``` + +## SEE ALSO +podman-volume(1), podman-volume-import(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume-import.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume-import.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume-import.1.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume-import.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,35 @@ +% podman-volume-import(1) + +## NAME +podman\-volume\-import - Import tarball contents into a podman volume + +## SYNOPSIS +**podman volume import** *volume* [*source*] + +## DESCRIPTION + +**podman volume import** imports the contents of a tarball into the podman volume's mount point. +**podman volume import** can consume piped input when using `-` as source path. + +Note: Following command is not supported by podman-remote. + +**podman volume import VOLUME [SOURCE]** + +#### **--help** + +Print usage statement + +## EXAMPLES + +``` +$ gunzip -c hellow.tar.gz | podman volume import myvol - +``` +``` +$ podman volume import myvol test.tar +``` +``` +$ podman volume export myvol | podman volume import oldmyvol - +``` + +## SEE ALSO +podman-volume(1), podman-volume-export(1) diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume-inspect.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume-inspect.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume-inspect.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume-inspect.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -40,7 +40,7 @@ ``` ## SEE ALSO -podman-volume(1) +**[podman(1)](podman.1.md)**,**[podman-volume(1)](podman-volume.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)** ## HISTORY November 2018, Originally compiled by Urvashi Mohnani diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume-ls.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume-ls.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume-ls.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume-ls.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -18,12 +18,15 @@ Volumes can be filtered by the following attributes: -- dangling -- driver -- label -- name -- opt -- scope +| **Filter** | **Description** | +| ---------- | ------------------------------------------------------------------------------------- | +| dangling | [Dangling] Matches all volumes not referenced by any containers | +| driver | [Driver] Matches volumes based on their driver | +| label | [Key] or [Key=Value] Label assigned to a volume | +| name | [Name] Volume name (accepts regex) | +| opt | Matches a storage driver options | +| scope | Filters volume by scope | +| until | Only remove volumes created before given timestamp | #### **--format**=*format* diff -Nru libpod-3.2.1+ds1/docs/source/markdown/podman-volume-prune.1.md libpod-3.4.4+ds1/docs/source/markdown/podman-volume-prune.1.md --- libpod-3.2.1+ds1/docs/source/markdown/podman-volume-prune.1.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/markdown/podman-volume-prune.1.md 2021-12-26 00:45:51.000000000 +0000 @@ -23,12 +23,10 @@ Filter volumes to be pruned. Volumes can be filtered by the following attributes: -- dangling -- driver -- label -- name -- opt -- scope +| **Filter** | **Description** | +| ---------- | ------------------------------------------------------------------------------------- | +| label | [Key] or [Key=Value] Label assigned to a volume | +| until | Only remove volumes created before given timestamp | #### **--help** diff -Nru libpod-3.2.1+ds1/docs/source/pod.rst libpod-3.4.4+ds1/docs/source/pod.rst --- libpod-3.2.1+ds1/docs/source/pod.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/pod.rst 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,8 @@ :doc:`kill ` Send the specified signal or SIGKILL to containers in pod +:doc:`logs ` Displays logs for pod with one or more containers + :doc:`pause ` Pause one or more pods :doc:`prune ` Remove all stopped pods and their containers diff -Nru libpod-3.2.1+ds1/docs/source/Tutorials.rst libpod-3.4.4+ds1/docs/source/Tutorials.rst --- libpod-3.2.1+ds1/docs/source/Tutorials.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/Tutorials.rst 2021-12-26 00:45:51.000000000 +0000 @@ -10,5 +10,5 @@ * `How to sign and distribute container images using Podman `_: Learn how to setup and use image signing with Podman. * `Podman remote-client tutorial `_: A brief how-to on using the Podman remote-client. * `How to use libpod for custom/derivative projects `_: How the libpod API can be used within your own project. -* `How to use Podman's Go bindings `_: A brief how-to on using Podman's Go bindings in external applications. +* `How to use Podman's Go RESTful bindings `_: An introduction to using our RESTful Golang bindings in an external application. * `Common network setups `_: A basic guide to common network setups for Podman. diff -Nru libpod-3.2.1+ds1/docs/source/volume.rst libpod-3.4.4+ds1/docs/source/volume.rst --- libpod-3.2.1+ds1/docs/source/volume.rst 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/source/volume.rst 2021-12-26 00:45:51.000000000 +0000 @@ -4,6 +4,10 @@ :doc:`exists ` Check if the given volume exists +:doc:`export ` Exports volume to external tar + +:doc:`import ` Import tarball contents into a podman volume + :doc:`inspect ` Display detailed information on one or more volumes :doc:`ls ` List volumes diff -Nru libpod-3.2.1+ds1/docs/tutorials/basic_networking.md libpod-3.4.4+ds1/docs/tutorials/basic_networking.md --- libpod-3.2.1+ds1/docs/tutorials/basic_networking.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/basic_networking.md 2021-12-26 00:45:51.000000000 +0000 @@ -237,7 +237,7 @@ $ podman run -dt --name webserver -p 8080:80 quay.io/libpod/banner 17ea33ccd7f55ff45766b3ec596b990a5f2ba66eb9159cb89748a85dc3cebfe0 ``` -Because rootfull containers cannot communicate with each other directly with TCP/IP +Because rootless containers cannot communicate with each other directly with TCP/IP via IP addresses, the host and the port mapping are used. To do so, the IP address of the host (interface) must be known. ``` diff -Nru libpod-3.2.1+ds1/docs/tutorials/mac_experimental.md libpod-3.4.4+ds1/docs/tutorials/mac_experimental.md --- libpod-3.2.1+ds1/docs/tutorials/mac_experimental.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/mac_experimental.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,99 @@ +# Using podman-machine on MacOS (x86_64 and Apple silicon) + +## Setup + +You must obtain a compressed tarball that contains the following: +* a qcow image +* a podman binary +* a gvproxy binary + +You must also have installed brew prior to following this process. See https://brew.sh/ for +installation instructions. + +Note: If your user has admin rights, you can ignore the use of `sudo` in these instructions. + + +1. Install qemu from brew to obtain the required runtime dependencies. + + ``` + brew install qemu + ``` + +2. If you are running MacOS on the Intel architecture, you can skip to step 8. +3. Uninstall the brew package + + ``` + brew uninstall qemu + ``` + +4. Get upstream qemu source code. + + ``` + git clone https://github.com/qemu/qemu + ``` + +5. Apply patches that have not been merged into upstream qemu. + + ``` + cd qemu + git config user.name "YOUR_NAME" + git config user.email johndoe@example.com + git checkout v5.2.0 + curl https://patchwork.kernel.org/series/418581/mbox/ | git am --exclude=MAINTAINERS + curl -L https://gist.github.com/citruz/9896cd6fb63288ac95f81716756cb9aa/raw/2d613e9a003b28dfe688f33055706d3873025a40/xcode-12-4.patch | git apply - + ``` + +6. Install qemu build dependencies + + ``` + brew install libffi gettext pkg-config autoconf automake pixman ninja make + ``` + +7. Configure, compile, and install qemu + ``` + mkdir build + cd build + ../configure --target-list=aarch64-softmmu --disable-gnutls + gmake -j8 + sudo gmake install + ``` + + +8. Uncompress and place provided binaries into filesystem + + **Note**: In the following instructions, you need to know the name of the compressed file +that you were given. It will be used in two of the steps below. + + ``` + cd ~ + tar xvf `compressed_file_ending_in_xz` + sudo cp -v `unpacked_directory`/{gvproxy,podman} /usr/local/bin + ``` + +9. Sign all binaries + + If you have a Mac with Apple Silicon, issue the following command: + ``` + sudo codesign --entitlements ~/qemu/accel/hvf/entitlements.plist --force -s - /usr/local/bin/qemu-* /usr/local/bin/gvproxy /usr/local/bin/podman + ``` + + If you have a Mac with an Intel processor, issue the following command: + + ``` + echo ' + + com.apple.security.hypervisor + ' > ~/entitlements.plist + sudo codesign --entitlements ~/entitlements.plist --force -s - /usr/local/bin/qemu-* /usr/local/bin/gvproxy /usr/local/bin/podman + ``` + + +## Test podman + +1. podman machine init --image-path /path/to/image +2. podman machine start +3. podman images +4. git clone http://github.com/baude/alpine_nginx && cd alpine_nginx +5. podman build -t alpine_nginx . +4. podman run -dt -p 9999:80 alpine_nginx +5. curl http://localhost:9999 diff -Nru libpod-3.2.1+ds1/docs/tutorials/podman-go-bindings.md libpod-3.4.4+ds1/docs/tutorials/podman-go-bindings.md --- libpod-3.2.1+ds1/docs/tutorials/podman-go-bindings.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/podman-go-bindings.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,543 +0,0 @@ -![PODMAN logo](../../logo/podman-logo-source.svg) - -# Podman Go bindings - -## Introduction - -In the release of Podman 2.0, we removed the experimental tag -from its recently introduced RESTful service. While it might -be interesting to interact with a RESTFul server using curl, -using a set of Go based bindings is probably a more direct -route to a production ready application. Let’s take a look -at how easily that can be accomplished. - -If you haven't yet, [install Go](https://golang.org/doc/install). - -Be careful to double-check that the version of golang is new -enough (i.e. `go version`), version 1.13.x or higher is -supported. If needed, Go sources and binaries can be fetched -from the [official Go website](https://golang.org/dl/). - -The Podman Go bindings are a set of functions to allow -developers to execute Podman operations from within their Go -based application. The Go bindings connect to a Podman service -which can run locally or on a remote machine. You can perform -many operations including pulling and listing images, starting, -stopping or inspecting containers. Currently, the Podman -repository has bindings available for operations on images, -containers, pods, networks and manifests among others. The -bindings are available on the [v2.0 branch in the -upstream Podman repository](https://github.com/containers/podman/tree/v2.0). -You can fetch the bindings for your application using Go modules: - -```bash -$ cd $HOME -$ mkdir example && cd example -$ go mod init example.com -go: creating new go.mod: module example.com -$ go get github.com/containers/podman/v3 -[...] -``` - -This creates a new `go.mod` file in the current directory that looks as follows: - -```bash -module example.com - -go 1.16 - -require github.com/containers/libpod/v3 v3.0.1 // indirect -``` - -You can also try a demo application with the Go modules created already: - -```bash -$ git clone https://github.com/containers/Demos -$ cd Demos/podman_go_bindings -$ ls -README.md go.mod go.sum main.go -``` - - -## How do I use them - -In this tutorial, you will learn through basic examples how to: - -0. [Start the Podman system service](#start-service) -1. [Connect to the Podman system service](#connect-service) -2. [Pull images](#pull-images) -3. [List images](#list-images) -4. [Create and start a container from an image](#create-start-container) -5. [List containers](#list-containers) -6. [Inspect the container](#inspect-container) -7. [Stop the container](#stop-container) -8. [Debugging tips](#debugging-tips) - - -### Start the Podman system service -The recommended way to start Podman system service in production mode -is via systemd socket-activation: - -```bash -$ systemctl --user start podman.socket -``` - -There’s no timeout specified when starting the system service via socket-activation. - -For purposes of this demo, we will start the service using the Podman -command itself. If you prefer the system service to timeout after, say, -5000 seconds, you can run it like so: - -```bash -$ podman system service -t 5000 -``` - -Note that the 5000 seconds uptime is refreshed after every command is received. -If you want the service to stay up until the machine is shutdown or the process -is terminated, use `0` (zero) instead of 5000. For this demo, we will use no timeout: - -```bash -$ podman system service -t 0 -``` - - -Open another terminal window and check if the Podman socket exists: - -```bash -$ ls /run/user/${UID}/podman -podman.sock -``` - -If you’re running the system service as root, podman.sock will be found in /run/podman: -```bash -# ls /run/podman -podman.sock -``` - - -### Connect to the Podman system service -First, you need to create a connection that connects to the system service. -The critical piece of information for setting up a new connection is the endpoint. -The endpoint comes in the form of an URI (method:/path/to/socket). For example, -to connect to the local rootful socket the URI would be `unix:/run/podman/podman.sock` -and for a rootless user it would be `unix:$(XDG_RUNTIME_DIR)/podman/podman.sock`, -typically: `unix:/run/user/${UID}/podman/podman.sock`. - - -The following Go example snippet shows how to set up a connection for a rootless user. -```Go -package main - -import ( - "context" - "fmt" - "os" - - "github.com/containers/libpod/v3/libpod/define" - "github.com/containers/libpod/v3/pkg/bindings" - "github.com/containers/libpod/v3/pkg/bindings/containers" - "github.com/containers/libpod/v3/pkg/bindings/images" - "github.com/containers/libpod/v3/pkg/domain/entities" - "github.com/containers/libpod/v3/pkg/specgen" -) - -func main() { - fmt.Println("Welcome to the Podman Go bindings tutorial") - - // Get Podman socket location - sock_dir := os.Getenv("XDG_RUNTIME_DIR") - socket := "unix:" + sock_dir + "/podman/podman.sock" - - // Connect to Podman socket - connText, err := bindings.NewConnection(context.Background(), socket) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} -``` - -The `connText` variable received from the NewConnection function is of type -context.Context(). In subsequent uses of the bindings, you will use this context -to direct the bindings to your connection. This can be seen in the examples below. - -### Pull an image - -Next, we will pull a couple of images using the images.Pull() binding. -This binding takes three arguments: - - The context variable created by the bindings.NewConnection() call in the first example - - The image name - - Options for image pull - -**Append the following lines to your function:** - -```Go - // Pull Busybox image (Sample 1) - fmt.Println("Pulling Busybox image...") - _, err = images.Pull(connText, "docker.io/busybox", entities.ImagePullOptions{}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Pull Fedora image (Sample 2) - rawImage := "registry.fedoraproject.org/fedora:latest" - fmt.Println("Pulling Fedora image...") - _, err = images.Pull(connText, rawImage, entities.ImagePullOptions{}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -$ -``` - -The system service side should echo messages like so: - -```bash -Trying to pull docker.io/busybox... -Getting image source signatures -Copying blob 61c5ed1cbdf8 [--------------------------------------] 0.0b / 0.0b -Copying config 018c9d7b79 done -Writing manifest to image destination -Storing signatures -Trying to pull registry.fedoraproject.org/fedora:latest... -Getting image source signatures -Copying blob dd9f43919ba0 [--------------------------------------] 0.0b / 0.0b -Copying config 00ff39a8bf done -Writing manifest to image destination -Storing signatures -``` - - -### List images -Next, we will pull an image using the images.List() binding. -This binding takes three arguments: - - The context variable created earlier - - An optional bool 'all' - - An optional map of filters - -**Append the following lines to your function:** - -```Go - // List images - imageSummary, err := images.List(connText, nil, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - var names []string - for _, i := range imageSummary { - names = append(names, i.RepoTags...) - } - fmt.Println("Listing images...") - fmt.Println(names) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -$ -``` - - -### Create and Start a Container from an Image - -To create the container spec, we use specgen.NewSpecGenerator() followed by -calling containers.CreateWithSpec() to actually create a new container. -specgen.NewSpecGenerator() takes 2 arguments: - - name of the image - - whether it's a rootfs - -containers.CreateWithSpec() takes 2 arguments: - - the context created earlier - - the spec created by NewSpecGenerator - -Next, the container is actually started using the containers.Start() binding. -containers.Start() takes three arguments: - - the context - - the name or ID of the container created - - an optional parameter for detach keys - -After the container is started, it's a good idea to ensure the container is -in a running state before you proceed with further operations. -The containers.Wait() takes care of that. -containers.Wait() takes three arguments: - - the context - - the name or ID of the container created - - container state (running/paused/stopped) - -**Append the following lines to your function:** - -```Go - // Container create - s := specgen.NewSpecGenerator(rawImage, false) - s.Terminal = true - r, err := containers.CreateWithSpec(connText, s) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Container start - fmt.Println("Starting Fedora container...") - err = containers.Start(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - running := define.ContainerStateRunning - _, err = containers.Wait(connText, r.ID, &running) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling image... -Starting Fedora container... -$ -``` - -Check if the container is running: - -```bash -$ podman ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -665831d31e90 registry.fedoraproject.org/fedora:latest /bin/bash Less than a second ago Up Less than a second ago dazzling_mclean -$ -``` - - -### List Containers - -Containers can be listed using the containers.List() binding. -containers.List() takes seven arguments: - - the context - - output filters - - boolean to show all containers, by default only running containers are listed - - number of latest created containers, all states (running/paused/stopped) - - boolean to print pod information - - boolean to print rootfs size - - boolean to print oci runtime and container state - -**Append the following lines to your function:** - -```Go - // Container list - var latestContainers = 1 - containerLatestList, err := containers.List(connText, nil, nil, &latestContainers, nil, nil, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Latest container is %s\n", containerLatestList[0].Names[0]) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -Starting Fedora container... -Latest container is dazzling_mclean -$ -``` - - -### Inspect Container -Containers can be inspected using the containers.Inspect() binding. -containers.Inspect() takes 3 arguments: - - context - - image name or ID - - optional boolean to check for container size - - -**Append the following lines to your function:** - -```Go - // Container inspect - ctrData, err := containers.Inspect(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Container uses image %s\n", ctrData.ImageName) - fmt.Printf("Container running status is %s\n", ctrData.State.Status) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -Starting Fedora container... -Latest container is peaceful_noether -Fedora Container uses image registry.fedoraproject.org/fedora:latest -Fedora Container running status is running -$ -``` - - -### Stop Container - -A container can be stopped by the containers.Stop() binding. -containers.Stop() takes 3 arguments: - - context - - image name or ID - - optional timeout - -**Append the following lines to your function:** - -```Go - // Container stop - fmt.Println("Stopping the container...") - err = containers.Stop(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - ctrData, err = containers.Inspect(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Container running status is now %s\n", ctrData.State.Status) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -Starting Fedora container... -Latest container is peaceful_noether -Fedora Container uses image registry.fedoraproject.org/fedora:latest -Fedora Container running status is running -Stopping Fedora container... -Container running status is now exited -``` - - -### Debugging tips - -To debug in a development setup, you can start the Podman system service -in debug mode like so: - -```bash -$ podman --log-level=debug system service -t 0 -``` - -The `--log-level=debug` echoes all the logged requests and is useful to -trace the execution path at a finer granularity. A snippet of a sample run looks like: - -```bash -INFO[0000] podman filtering at log level debug -DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) -DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. -DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" -DEBU[0000] Merged system config "/usr/share/containers/containers.conf": {Editors note: the remainder of this line was removed due to Jekyll formatting errors.} -DEBU[0000] Using conmon: "/usr/bin/conmon" -DEBU[0000] Initializing boltdb state at /home/lsm5/.local/share/containers/storage/libpod/bolt_state.db -DEBU[0000] Overriding run root "/run/user/1000/containers" with "/run/user/1000" from database -DEBU[0000] Using graph driver overlay -DEBU[0000] Using graph root /home/lsm5/.local/share/containers/storage -DEBU[0000] Using run root /run/user/1000 -DEBU[0000] Using static dir /home/lsm5/.local/share/containers/storage/libpod -DEBU[0000] Using tmp dir /run/user/1000/libpod/tmp -DEBU[0000] Using volume path /home/lsm5/.local/share/containers/storage/volumes -DEBU[0000] Set libpod namespace to "" -DEBU[0000] Not configuring container store -DEBU[0000] Initializing event backend file -DEBU[0000] using runtime "/usr/bin/runc" -DEBU[0000] using runtime "/usr/bin/crun" -WARN[0000] Error initializing configured OCI runtime kata: no valid executable found for OCI runtime kata: invalid argument -DEBU[0000] using runtime "/usr/bin/crun" -INFO[0000] Setting parallel job count to 25 -INFO[0000] podman filtering at log level debug -DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) -DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. -DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" -``` - -If the Podman system service has been started via systemd socket activation, -you can view the logs using journalctl. The logs after a sample run look like so: - -```bash -$ journalctl --user --no-pager -u podman.socket --- Reboot -- -Jul 22 13:50:40 nagato.nanadai.me systemd[1048]: Listening on Podman API Socket. -$ -``` - -```bash -$ journalctl --user --no-pager -u podman.service -Jul 22 13:50:53 nagato.nanadai.me systemd[1048]: Starting Podman API Service... -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: error acquiring lock 0 for volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: error acquiring lock 0 for volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: error acquiring lock 0 for volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: error acquiring lock 0 for volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: error acquiring lock 0 for volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: error acquiring lock 0 for volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: Trying to pull registry.fedoraproject.org/fedora:latest... -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Getting image source signatures -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying blob sha256:dd9f43919ba05f05d4f783c31e83e5e776c4f5d29dd72b9ec5056b9576c10053 -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying config sha256:00ff39a8bf19f810a7e641f7eb3ddc47635913a19c4996debd91fafb6b379069 -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Writing manifest to image destination -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Storing signatures -Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: podman.service: unit configures an IP firewall, but not running as root. -Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: (This warning is only shown for the first unit using IP firewalling.) -Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Succeeded. -Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: Finished Podman API Service. -Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Consumed 1.339s CPU time. -$ -``` - - -## Wrap Up -Podman provides a set of Go bindings to allow developers to integrate Podman -functionality conveniently in their Go application. These Go bindings require -the Podman system service to be running in the background and this can easily -be achieved using systemd socket activation. Once set up, you are able to use a -set of Go based bindings to create, maintain and monitor your container images, -containers and pods in a way which fits very nicely in many production environments. - - -## References -- Podman is available for most major distributions along with macOS and Windows. -Installation details are available on the [Podman official website](https://podman.io/getting-started/). - -- Documentation can be found at the [Podman Docs page](https://docs.podman.io). -It also includes a section on the [RESTful API](https://docs.podman.io/en/latest/Reference.html). diff -Nru libpod-3.2.1+ds1/docs/tutorials/podman_tutorial.md libpod-3.4.4+ds1/docs/tutorials/podman_tutorial.md --- libpod-3.2.1+ds1/docs/tutorials/podman_tutorial.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/podman_tutorial.md 2021-12-26 00:45:51.000000000 +0000 @@ -50,11 +50,11 @@ of -l. ### Testing the httpd server -Now that we have the IP address of the container, we can test the network communication between the host +As we do not have the IP address of the container, we can test the network communication between the host operating system and the container using curl. The following command should display the index page of our containerized httpd server. ```console -curl http://:8080 +curl http://localhost:8080 ``` ### Viewing the container's logs diff -Nru libpod-3.2.1+ds1/docs/tutorials/README.md libpod-3.4.4+ds1/docs/tutorials/README.md --- libpod-3.2.1+ds1/docs/tutorials/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -28,10 +28,6 @@ Learn how to setup and use image signing with Podman. -**[Go Bindings](podman-go-bindings.md)** - -A brief how-to on using Podman's Go bindings in external applications. - -**[Go Bindings](basic_networking.md)** +**[Basic Networking](basic_networking.md)** A basic guide to common network setups with Podman diff -Nru libpod-3.2.1+ds1/docs/tutorials/remote_client.md libpod-3.4.4+ds1/docs/tutorials/remote_client.md --- libpod-3.2.1+ds1/docs/tutorials/remote_client.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/remote_client.md 2021-12-26 00:45:51.000000000 +0000 @@ -108,5 +108,9 @@ You can use the Podman remote clients to manage your containers running on a Linux server. The communication between client and server relies heavily on SSH connections and the use of SSH keys are encouraged. Once you have Podman installed on your remote client, you should set up a connection using `podman-remote system connection add` which will then be used by subsequent Podman commands. +# Troubleshooting + +See the [Troubleshooting](../../troubleshooting.md) document if you run into issues. + ## History Adapted from the [Mac and Windows tutorial](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md) diff -Nru libpod-3.2.1+ds1/docs/tutorials/rootless_tutorial.md libpod-3.4.4+ds1/docs/tutorials/rootless_tutorial.md --- libpod-3.2.1+ds1/docs/tutorials/rootless_tutorial.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/docs/tutorials/rootless_tutorial.md 2021-12-26 00:45:51.000000000 +0000 @@ -13,31 +13,31 @@ ``` podman --runtime crun ``` -or for all commands by changing the value for the "Default OCI runtime" in the containers.conf file either at the system level or at the [user level](#user-configuration-files) from `runtime = "runc"` to `runtime = "crun"`. +or for all commands by changing the value for the "Default OCI runtime" in the `containers.conf` file either at the system level or at the [user level](#user-configuration-files) from `runtime = "runc"` to `runtime = "crun"`. ## Administrator Actions ### Installing Podman -For installing Podman, please see the [installation instructions](https://github.com/containers/podman/blob/master/install.md). +For installing Podman, please see the [installation instructions](https://github.com/containers/podman/blob/main/install.md). ### Building Podman -For building Podman, please see the [installation instructions](https://github.com/containers/podman/blob/master/install.md#building-from-scratch). +For building Podman, please see the [installation instructions](https://github.com/containers/podman/blob/main/install.md#building-from-scratch). -### Install slirp4netns +### Install `slirp4netns` -The [slirp4netns](https://github.com/rootless-containers/slirp4netns) package provides user-mode networking for unprivileged network namespaces and must be installed on the machine in order for Podman to run in a rootless environment. The package is available on most Linux distributions via their package distribution software such as yum, dnf, apt, zypper, etc. If the package is not available, you can build and install slirp4netns from [GitHub](https://github.com/rootless-containers/slirp4netns). +The [slirp4netns](https://github.com/rootless-containers/slirp4netns) package provides user-mode networking for unprivileged network namespaces and must be installed on the machine in order for Podman to run in a rootless environment. The package is available on most Linux distributions via their package distribution software such as `yum`, `dnf`, `apt`, `zypper`, etc. If the package is not available, you can build and install `slirp4netns` from [GitHub](https://github.com/rootless-containers/slirp4netns). -### Ensure fuse-overlayfs is installed +### Ensure `fuse-overlayfs` is installed -When using Podman in a rootless environment, it is recommended to use fuse-overlayfs rather than the VFS file system. For that you need the `fuse-overlayfs` executable available in `$PATH`. +When using Podman in a rootless environment, it is recommended to use `fuse-overlayfs` rather than the VFS file system. For that you need the `fuse-overlayfs` executable available in `$PATH`. Your distribution might already provide it in the `fuse-overlayfs` package, but be aware that you need at least version **0.7.6**. This especially needs to be checked on Ubuntu distributions as `fuse-overlayfs` is not generally installed by default and the 0.7.6 version is not available natively on Ubuntu releases prior to **20.04**. -The fuse-overlayfs project is available from [GitHub](https://github.com/containers/fuse-overlayfs), and provides instructions for easily building a static `fuse-overlayfs` executable. +The `fuse-overlayfs` project is available from [GitHub](https://github.com/containers/fuse-overlayfs), and provides instructions for easily building a static `fuse-overlayfs` executable. -If Podman is used before fuse-overlayfs is installed, it may be necessary to adjust the `storage.conf` file (see "User Configuration Files" below) to change the `driver` option under `[storage]` to `"overlay"` and point the `mount_program` option in `[storage.options]` to the path of the `fuse-overlayfs` executable: +If Podman is used before `fuse-overlayfs` is installed, it may be necessary to adjust the `storage.conf` file (see "User Configuration Files" below) to change the `driver` option under `[storage]` to `"overlay"` and point the `mount_program` option in `[storage.options]` to the path of the `fuse-overlayfs` executable: ``` [storage] @@ -54,13 +54,13 @@ ### Enable user namespaces (on RHEL7 machines) -The number of user namespaces that are allowed on the system is specified in the file `/proc/sys/user/max_user_namespaces`. On most Linux platforms this is preset by default and no adjustment is necessary. However on RHEL7 machines a user with root privileges may need to set that to a reasonable value by using this command: `sysctl user.max_user_namespaces=15000`. +The number of user namespaces that are allowed on the system is specified in the file `/proc/sys/user/max_user_namespaces`. On most Linux platforms this is preset by default and no adjustment is necessary. However, on RHEL7 machines, a user with root privileges may need to set that to a reasonable value by using this command: `sysctl user.max_user_namespaces=15000`. -### /etc/subuid and /etc/subgid configuration +### `/etc/subuid` and `/etc/subgid` configuration -Rootless Podman requires the user running it to have a range of UIDs listed in /etc/subuid and /etc/subgid files. The `shadow-utils` or `newuid` package provides these files on different distributions and they must be installed on the system. These files will need someone with root privileges on the system to add or update the entries within them. The following is a summarization from the [How does rootless Podman work?](https://opensource.com/article/19/2/how-does-rootless-podman-work) article by Dan Walsh on [opensource.com](https://opensource.com) +Rootless Podman requires the user running it to have a range of UIDs listed in the files `/etc/subuid` and `/etc/subgid`. The `shadow-utils` or `newuid` package provides these files on different distributions and they must be installed on the system. Root privileges are required to add or update entries within these files. The following is a summary from the [How does rootless Podman work?](https://opensource.com/article/19/2/how-does-rootless-podman-work) article by Dan Walsh on [opensource.com](https://opensource.com) -Update the /etc/subuid and /etc/subgid with fields for each user that will be allowed to create containers that look like the following. Note that the values for each user must be unique and without any overlap. If there is an overlap, there is a potential for a user to use another’s namespace and they could corrupt it. +For each user that will be allowed to create containers, update `/etc/subuid` and `/etc/subgid` for the user with fields that look like the following. Note that the values for each user must be unique. If there is overlap, there is a potential for a user to use another user's namespace and they could corrupt it. ``` cat /etc/subuid @@ -68,17 +68,17 @@ test:165536:65536 ``` -The format of this file is USERNAME:UID:RANGE +The format of this file is `USERNAME:UID:RANGE` -* username as listed in /etc/passwd or getpwent. -* The initial uid allocated for the user. +* username as listed in `/etc/passwd` or in the output of [`getpwent`](https://man7.org/linux/man-pages/man3/getpwent.3.html). +* The initial UID allocated for the user. * The size of the range of UIDs allocated for the user. -This means the user johndoe is allocated UIDS 100000-165535 as well as their standard UID in the /etc/passwd file. NOTE: this is not currently supported with network installs. These files must be available locally to the host machine. It is not possible to configure this with LDAP or Active Directory. +This means the user `johndoe` is allocated UIDs 100000-165535 as well as their standard UID in the `/etc/passwd` file. NOTE: this is not currently supported with network installs; these files must be available locally to the host machine. It is not possible to configure this with LDAP or Active Directory. -If you update either the /etc/subuid or the /etc/subgid file, you need to stop all the running containers owned by the user and kill the pause process that is running on the system for that user. This can be done automatically by using the [`podman system migrate`](https://github.com/containers/podman/blob/master/docs/podman-system-migrate.1.md) command which will stop all the containers for the user and will kill the pause process. +If you update either `/etc/subuid` or `/etc/subgid`, you need to stop all the running containers owned by the user and kill the pause process that is running on the system for that user. This can be done automatically by using the [`podman system migrate`](https://github.com/containers/podman/blob/main/docs/source/markdown/podman-system-migrate.1.md) command which will stop all the containers for the user and will kill the pause process. -Rather than updating the files directly, the usermod program can be used to assign UIDs and GIDs to a user. +Rather than updating the files directly, the `usermod` program can be used to assign UIDs and GIDs to a user. ``` usermod --add-subuids 200000-201000 --add-subgids 200000-201000 johndoe @@ -102,13 +102,13 @@ The majority of the work necessary to run Podman in a rootless environment is on the shoulders of the machine’s administrator. -Once the Administrator has completed the setup on the machine and then the configurations for the user in /etc/subuid and /etc/subgid, the user can just start using any Podman command that they wish. +Once the Administrator has completed the setup on the machine and then the configurations for the user in `/etc/subuid` and `/etc/subgid`, the user can just start using any Podman command that they wish. ### User Configuration Files The Podman configuration files for root reside in `/usr/share/containers` with overrides in `/etc/containers`. In the rootless environment they reside in `${XDG_CONFIG_HOME}/containers` (usually `~/.config/containers`) and are owned by each individual user. -The three main configuration files are [containers.conf](https://github.com/containers/common/blob/master/docs/containers.conf.5.md), [storage.conf](https://github.com/containers/storage/blob/master/docs/containers-storage.conf.5.md) and [registries.conf](https://github.com/containers/image/blob/master/docs/containers-registries.conf.5.md). The user can modify these files as they wish. +The three main configuration files are [containers.conf](https://github.com/containers/common/blob/main/docs/containers.conf.5.md), [storage.conf](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md) and [registries.conf](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md). The user can modify these files as they wish. #### containers.conf Podman reads @@ -123,7 +123,7 @@ 1. `/etc/containers/storage.conf` 2. `$HOME/.config/containers/storage.conf` -In rootless podman certain fields in `/etc/containers/storage.conf` are ignored. These fields are: +In rootless Podman certain fields in `/etc/containers/storage.conf` are ignored. These fields are: ``` graphroot="" container storage graph dir (default: "/var/lib/containers/storage") @@ -133,7 +133,7 @@ container storage run dir (default: "/run/containers/storage") Default directory to store all temporary writable content created by container storage programs. ``` -In rootless podman these fields default to +In rootless Podman these fields default to ``` graphroot="$HOME/.local/share/containers/storage" runroot="$XDG_RUNTIME_DIR/containers" @@ -146,14 +146,14 @@ 2. `/etc/containers/registries.d/*` 3. `HOME/.config/containers/registries.conf` -The files in the home directory should be used to configure rootless podman for personal needs. These files are not created by default. Users can copy the files from `/usr/share/containers` or `/etc/containers` and modify them. +The files in the home directory should be used to configure rootless Podman for personal needs. These files are not created by default. Users can copy the files from `/usr/share/containers` or `/etc/containers` and modify them. #### Authorization files The default authorization file used by the `podman login` and `podman logout` commands reside in `${XDG_RUNTIME_DIR}/containers/auth.json`. ### Using volumes -Rootless Podman is not, and will never be, root; it's not a setuid binary, and gains no privileges when it runs. Instead, Podman makes use of a user namespace to shift the UIDs and GIDs of a block of users it is given access to on the host (via the newuidmap and newgidmap executables) and your own user within the containers that Podman creates. +Rootless Podman is not, and will never be, root; it's not a `setuid` binary, and gains no privileges when it runs. Instead, Podman makes use of a user namespace to shift the UIDs and GIDs of a block of users it is given access to on the host (via the `newuidmap` and `newgidmap` executables) and your own user within the containers that Podman creates. If your container runs with the root user, then `root` in the container is actually your user on the host. UID/GID 1 is the first UID/GID specified in your user's mapping in `/etc/subuid` and `/etc/subgid`, etc. If you mount a directory from the host into a container as a rootless user, and create a file in that directory as root in the container, you'll see it's actually owned by your user on the host. @@ -193,6 +193,6 @@ ## More information -If you are still experiencing problems running Podman in a rootless environment, please refer to the [Shortcomings of Rootless Podman](https://github.com/containers/podman/blob/master/rootless.md) page which lists known issues and solutions to known issues in this environment. +If you are still experiencing problems running Podman in a rootless environment, please refer to the [Shortcomings of Rootless Podman](https://github.com/containers/podman/blob/main/rootless.md) page which lists known issues and solutions to known issues in this environment. -For more information on Podman and its subcommands, checkout the asciiart demos on the [README.md](../../README.md#commands) page or the [podman.io](https://podman.io) web site. +For more information on Podman and its subcommands, follow the links on the main [README.md](../../README.md#podman-information-for-developers) page or the [podman.io](https://podman.io) web site. diff -Nru libpod-3.2.1+ds1/.github/workflows/check_cirrus_cron.yml libpod-3.4.4+ds1/.github/workflows/check_cirrus_cron.yml --- libpod-3.2.1+ds1/.github/workflows/check_cirrus_cron.yml 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/.github/workflows/check_cirrus_cron.yml 2021-12-26 00:45:51.000000000 +0000 @@ -6,10 +6,12 @@ name: check_cirrus_cron on: - # Note: This only applies to the master branch. + # Note: This only applies to the main branch. schedule: - # Assume cirrus cron jobs runs at least once per day - - cron: '59 23 * * *' + # N/B: This should correspond to a period slightly after + # the last job finishes running. See job defs. at: + # https://cirrus-ci.com/settings/repository/6707778565701632 + - cron: '59 23 * * 1-5' # Debug: Allow triggering job manually in github-actions WebUI workflow_dispatch: {} @@ -30,7 +32,6 @@ steps: - uses: actions/checkout@v2 with: - ref: master persist-credentials: false - name: Get failed cron names and Build IDs diff -Nru libpod-3.2.1+ds1/.github/workflows/multi-arch-build.yaml libpod-3.4.4+ds1/.github/workflows/multi-arch-build.yaml --- libpod-3.2.1+ds1/.github/workflows/multi-arch-build.yaml 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/.github/workflows/multi-arch-build.yaml 2021-12-26 00:45:51.000000000 +0000 @@ -1,12 +1,17 @@ --- -# Please see contrib/podmanimage/README.md for details on the intentions +# Please see contrib/image/README.md for details on the intentions # of this workflow. +# +# BIG FAT WARNING: This workflow is duplicated across containers/skopeo, +# containers/buildah, and containers/podman. ANY AND +# ALL CHANGES MADE HERE MUST BE MANUALLY DUPLICATED +# TO THE OTHER REPOS. name: build multi-arch images on: - # Upstream podman tends to be very active, with many merges per day. + # Upstream tends to be very active, with many merges per day. # Only run this daily via cron schedule, or manually, not by branch push. schedule: - cron: '0 8 * * *' @@ -15,19 +20,23 @@ jobs: multi: - name: multi-arch Podman build + name: multi-arch image build env: - PODMAN_QUAY_REGISTRY: quay.io/podman + REPONAME: podman # No easy way to parse this out of $GITHUB_REPOSITORY + # Server/namespace value used to format FQIN + REPONAME_QUAY_REGISTRY: quay.io/podman CONTAINERS_QUAY_REGISTRY: quay.io/containers # list of architectures for build PLATFORMS: linux/amd64,linux/s390x,linux/ppc64le,linux/arm64 + # Command to execute in container to obtain project version number + VERSION_CMD: "podman --version" # build several images (upstream, testing, stable) in parallel strategy: # By default, failure of one matrix item cancels all others fail-fast: false matrix: - # Builds are located under contrib/podmanimage/ directory + # Builds are located under contrib/image/ directory source: - upstream - testing @@ -52,14 +61,14 @@ driver-opts: network=host install: true - - name: Build and locally push Podman + - name: Build and locally push image uses: docker/build-push-action@v2 with: - context: contrib/podmanimage/${{ matrix.source }} - file: ./contrib/podmanimage/${{ matrix.source }}/Dockerfile + context: contrib/${{ env.REPONAME }}image/${{ matrix.source }} + file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile platforms: ${{ env.PLATFORMS }} push: true - tags: localhost:5000/podman/${{ matrix.source }} + tags: localhost:5000/${{ env.REPONAME }}/${{ matrix.source }} # Simple verification that stable images work, and # also grab version number use in forming the FQIN. @@ -67,39 +76,31 @@ if: matrix.source == 'stable' id: sniff_test run: | - VERSION_OUTPUT="$(docker run localhost:5000/podman/${{ matrix.source }} \ - podman --storage-driver=vfs version)" + podman pull --tls-verify=false \ + localhost:5000/$REPONAME/${{ matrix.source }} + VERSION_OUTPUT=$(podman run \ + localhost:5000/$REPONAME/${{ matrix.source }} \ + $VERSION_CMD) echo "$VERSION_OUTPUT" - VERSION=$(grep -Em1 '^Version: ' <<<"$VERSION_OUTPUT" | awk '{print $2}') + VERSION=$(awk -r -e "/^${REPONAME} version /"'{print $3}' <<<"$VERSION_OUTPUT") test -n "$VERSION" - echo "::set-output name=version::${VERSION}" + echo "::set-output name=version::$VERSION" - - name: Generate podman reg. image FQIN(s) - id: podman_reg + - name: Generate image FQIN(s) to push + id: reponame_reg run: | if [[ "${{ matrix.source }}" == 'stable' ]]; then - # The `podman version` in image just built + # The command version in image just built VERSION='v${{ steps.sniff_test.outputs.version }}' # workaround vim syntax-highlight bug: ' - # Image tags previously pushed to quay - ALLTAGS=$(skopeo list-tags \ - docker://$PODMAN_QUAY_REGISTRY/stable | \ - jq -r '.Tags[]') - - # New image? Push quay.io/podman/stable:vX.X.X and :latest - if ! fgrep -qx "$VERSION" <<<"$ALLTAGS"; then - # Assume version-tag is also the most up to date (i.e. "latest") - FQIN="$PODMAN_QUAY_REGISTRY/stable:$VERSION,$PODMAN_QUAY_REGISTRY/stable:latest" - else # Not a new version-tagged image - # Assume other contents changed, so this is the "new" latest. - FQIN="$PODMAN_QUAY_REGISTRY/stable:latest" - fi + # Push both new|updated version-tag and latest-tag FQINs + FQIN="$REPONAME_QUAY_REGISTRY/stable:$VERSION,$REPONAME_QUAY_REGISTRY/stable:latest" elif [[ "${{ matrix.source }}" == 'testing' ]]; then # Assume some contents changed, always push latest testing. - FQIN="$PODMAN_QUAY_REGISTRY/testing:latest" + FQIN="$REPONAME_QUAY_REGISTRY/testing:latest" elif [[ "${{ matrix.source }}" == 'upstream' ]]; then # Assume some contents changed, always push latest upstream. - FQIN="$PODMAN_QUAY_REGISTRY/upstream:latest" + FQIN="$REPONAME_QUAY_REGISTRY/upstream:latest" else echo "::error::Unknown matrix item '${{ matrix.source }}'" exit 1 @@ -108,32 +109,17 @@ echo "::set-output name=fqin::${FQIN}" echo '::set-output name=push::true' - # This is substantially the same as the above step, except the - # $CONTAINERS_QUAY_REGISTRY is used and the "testing" - # flavor is never pushed. + # This is substantially similar to the above logic, + # but only handles $CONTAINERS_QUAY_REGISTRY for + # the stable "latest" and named-version tagged images. - name: Generate containers reg. image FQIN(s) - if: matrix.source != 'testing' + if: matrix.source == 'stable' id: containers_reg run: | - if [[ "${{ matrix.source }}" == 'stable' ]]; then - VERSION='v${{ steps.sniff_test.outputs.version }}' - # workaround vim syntax-highlight bug: ' - ALLTAGS=$(skopeo list-tags \ - docker://$CONTAINERS_QUAY_REGISTRY/podman | \ - jq -r '.Tags[]') - - # New image? Push quay.io/containers/podman:vX.X.X and :latest - if ! fgrep -qx "$VERSION" <<<"$ALLTAGS"; then - FQIN="$CONTAINERS_QUAY_REGISTRY/podman:$VERSION,$CONTAINERS_QUAY_REGISTRY/podman:latest" - else # Not a new version-tagged image, but contents may be updated - FQIN="$CONTAINERS_QUAY_REGISTRY/podman:latest" - fi - elif [[ "${{ matrix.source }}" == 'upstream' ]]; then - FQIN="$CONTAINERS_QUAY_REGISTRY/podman:latest" - else - echo "::error::Unknown matrix item '${{ matrix.source }}'" - exit 1 - fi + VERSION='v${{ steps.sniff_test.outputs.version }}' + # workaround vim syntax-highlight bug: ' + # Push both new|updated version-tag and latest-tag FQINs + FQIN="$CONTAINERS_QUAY_REGISTRY/$REPONAME:$VERSION,$CONTAINERS_QUAY_REGISTRY/$REPONAME:latest" echo "::warning::Pushing $FQIN" echo "::set-output name=fqin::${FQIN}" echo '::set-output name=push::true' @@ -142,50 +128,65 @@ run: | # This is a really hacky/strange workflow idiom, required # for setting multi-line $LABELS value for consumption in - # a future step. + # a future step. There is literally no cleaner way to do this :< # https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#multiline-strings - cat << EOF | tee -a $GITHUB_ENV - LABELS<> "$GITHUB_ENV" + for line; do + echo "$line" | tee -a "$GITHUB_ENV" + done + echo "DELIMITER" >> "$GITHUB_ENV" + } + + declare -a lines + lines=(\ + "org.opencontainers.image.source=https://github.com/${GITHUB_REPOSITORY}.git" + "org.opencontainers.image.revision=${GITHUB_SHA}" + "org.opencontainers.image.created=$(date -u --iso-8601=seconds)" + ) + + # Only the 'stable' matrix source obtains $VERSION + if [[ "${{ matrix.source }}" == "stable" ]]; then + lines+=(\ + "org.opencontainers.image.version=${{ steps.sniff_test.outputs.version }}" + ) + fi + + set_labels "${lines[@]}" + + # Separate steps to login and push for $REPONAME_QUAY_REGISTRY and + # $CONTAINERS_QUAY_REGISTRY are required, because 2 sets of credentials + # are used and namespaced within the registry. At the same time, reuse + # of non-shell steps is not supported by Github Actions nor are YAML + # anchors/aliases, nor composite actions. - # Push to 'podman' Quay repo for stable, testing. and upstream - - name: Login to 'podman' Quay registry + # Push to $REPONAME_QUAY_REGISTRY for stable, testing. and upstream + - name: Login to ${{ env.REPONAME_QUAY_REGISTRY }} uses: docker/login-action@v1 - if: steps.podman_reg.outputs.push == 'true' + if: steps.reponame_reg.outputs.push == 'true' with: - registry: ${{ env.PODMAN_QUAY_REGISTRY }} + registry: ${{ env.REPONAME_QUAY_REGISTRY }} # N/B: Secrets are not passed to workflows that are triggered # by a pull request from a fork - username: ${{ secrets.PODMAN_QUAY_USERNAME }} - password: ${{ secrets.PODMAN_QUAY_PASSWORD }} + username: ${{ secrets.REPONAME_QUAY_USERNAME }} + password: ${{ secrets.REPONAME_QUAY_PASSWORD }} - - name: Push images to 'podman' Quay + - name: Push images to ${{ steps.reponame_reg.outputs.fqin }} uses: docker/build-push-action@v2 - if: steps.podman_reg.outputs.push == 'true' + if: steps.reponame_reg.outputs.push == 'true' with: - cache-from: type=registry,ref=localhost:5000/podman/${{ matrix.source }} + cache-from: type=registry,ref=localhost:5000/${{ env.REPONAME }}/${{ matrix.source }} cache-to: type=inline - context: contrib/podmanimage/${{ matrix.source }} - file: ./contrib/podmanimage/${{ matrix.source }}/Dockerfile + context: contrib/${{ env.REPONAME }}image/${{ matrix.source }} + file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile platforms: ${{ env.PLATFORMS }} push: true - tags: ${{ steps.podman_reg.outputs.fqin }} + tags: ${{ steps.reponame_reg.outputs.fqin }} labels: | ${{ env.LABELS }} - # Push to 'containers' Quay repo only stable podman - - name: Login to 'containers' Quay registry + # Push to $CONTAINERS_QUAY_REGISTRY only stable + - name: Login to ${{ env.CONTAINERS_QUAY_REGISTRY }} if: steps.containers_reg.outputs.push == 'true' uses: docker/login-action@v1 with: @@ -193,14 +194,14 @@ username: ${{ secrets.CONTAINERS_QUAY_USERNAME }} password: ${{ secrets.CONTAINERS_QUAY_PASSWORD }} - - name: Push images to 'containers' Quay + - name: Push images to ${{ steps.containers_reg.outputs.fqin }} if: steps.containers_reg.outputs.push == 'true' uses: docker/build-push-action@v2 with: - cache-from: type=registry,ref=localhost:5000/podman/${{ matrix.source }} + cache-from: type=registry,ref=localhost:5000/${{ env.REPONAME }}/${{ matrix.source }} cache-to: type=inline - context: contrib/podmanimage/${{ matrix.source }} - file: ./contrib/podmanimage/${{ matrix.source }}/Dockerfile + context: contrib/${{ env.REPONAME }}image/${{ matrix.source }} + file: ./contrib/${{ env.REPONAME }}image/${{ matrix.source }}/Dockerfile platforms: ${{ env.PLATFORMS }} push: true tags: ${{ steps.containers_reg.outputs.fqin }} diff -Nru libpod-3.2.1+ds1/.gitignore libpod-3.4.4+ds1/.gitignore --- libpod-3.2.1+ds1/.gitignore 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/.gitignore 2021-12-26 00:45:51.000000000 +0000 @@ -33,6 +33,7 @@ /test/goecho/goecho /test/testvol/testvol .vscode* +tags result # Necessary to prevent hack/tree-status.sh false-positive /*runner_stats.log diff -Nru libpod-3.2.1+ds1/go.mod libpod-3.4.4+ds1/go.mod --- libpod-3.2.1+ds1/go.mod 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/go.mod 2021-12-26 00:45:51.000000000 +0000 @@ -3,69 +3,70 @@ go 1.13 require ( - github.com/BurntSushi/toml v0.3.1 + github.com/BurntSushi/toml v0.4.1 github.com/blang/semver v3.5.1+incompatible github.com/buger/goterm v0.0.0-20181115115552-c206103e1f37 github.com/checkpoint-restore/checkpointctl v0.0.0-20210301084134-a2024f5584e7 - github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b + github.com/checkpoint-restore/go-criu/v5 v5.1.0 github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9 github.com/containernetworking/cni v0.8.1 github.com/containernetworking/plugins v0.9.1 - github.com/containers/buildah v1.21.0 - github.com/containers/common v0.38.9 + github.com/containers/buildah v1.23.1 + github.com/containers/common v0.44.4 github.com/containers/conmon v2.0.20+incompatible - github.com/containers/image/v5 v5.12.0 - github.com/containers/ocicrypt v1.1.1 - github.com/containers/psgo v1.5.2 - github.com/containers/storage v1.31.3 + github.com/containers/image/v5 v5.17.0 + github.com/containers/ocicrypt v1.1.2 + github.com/containers/psgo v1.7.1 + github.com/containers/storage v1.37.0 github.com/coreos/go-systemd/v22 v22.3.2 github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3 - github.com/cri-o/ocicni v0.2.1-0.20210301205850-541cf7c703cf - github.com/cyphar/filepath-securejoin v0.2.2 + github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283 + github.com/cyphar/filepath-securejoin v0.2.3 github.com/davecgh/go-spew v1.1.1 github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f github.com/docker/distribution v2.7.1+incompatible - github.com/docker/docker v20.10.6+incompatible + github.com/docker/docker v20.10.11+incompatible github.com/docker/go-connections v0.4.0 github.com/docker/go-plugins-helpers v0.0.0-20200102110956-c9a8a2d92ccc github.com/docker/go-units v0.4.0 - github.com/fsnotify/fsnotify v1.4.9 + github.com/dtylman/scp v0.0.0-20181017070807-f3000a34aef4 + github.com/fsnotify/fsnotify v1.5.1 github.com/ghodss/yaml v1.0.0 - github.com/godbus/dbus/v5 v5.0.4 + github.com/godbus/dbus/v5 v5.0.6 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf - github.com/google/uuid v1.2.0 + github.com/google/uuid v1.3.0 + github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 github.com/gorilla/mux v1.8.0 github.com/gorilla/schema v1.2.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hpcloud/tail v1.0.0 - github.com/json-iterator/go v1.1.11 - github.com/mattn/go-colorable v0.1.8 // indirect - github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 + github.com/json-iterator/go v1.1.12 + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 github.com/mrunalp/fileutils v0.5.0 - github.com/onsi/ginkgo v1.16.2 - github.com/onsi/gomega v1.12.0 + github.com/onsi/ginkgo v1.16.4 + github.com/onsi/gomega v1.16.0 github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 - github.com/opencontainers/runc v1.0.0-rc95 + github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 + github.com/opencontainers/runc v1.0.2 github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 github.com/opencontainers/runtime-tools v0.9.0 - github.com/opencontainers/selinux v1.8.1 + github.com/opencontainers/selinux v1.9.1 github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 - github.com/rootless-containers/rootlesskit v0.14.2 + github.com/rootless-containers/rootlesskit v0.14.5 github.com/sirupsen/logrus v1.8.1 - github.com/spf13/cobra v1.1.3 + github.com/spf13/cobra v1.2.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.7.0 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 - github.com/uber/jaeger-client-go v2.28.0+incompatible - github.com/vbauerster/mpb/v6 v6.0.3 + github.com/uber/jaeger-client-go v2.29.1+incompatible + github.com/vbauerster/mpb/v6 v6.0.4 github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 - go.etcd.io/bbolt v1.3.5 - golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 - golang.org/x/sync v0.0.0-20201207232520-09787c993a3a - golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 - gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 - k8s.io/api v0.21.0 - k8s.io/apimachinery v0.21.0 + go.etcd.io/bbolt v1.3.6 + golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 + golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b + k8s.io/api v0.22.1 + k8s.io/apimachinery v0.22.1 ) diff -Nru libpod-3.2.1+ds1/go.sum libpod-3.4.4+ds1/go.sum --- libpod-3.2.1+ds1/go.sum 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/go.sum 2021-12-26 00:45:51.000000000 +0000 @@ -10,24 +10,40 @@ cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774 h1:SCbEWT58NSt7d2mcFdvxC9uyrdcTfvBbPLThhkDmXzg= github.com/14rcole/gopopulate v0.0.0-20180821133914-b175b219e774/go.mod h1:6/0dYRLLXyJjbkIPeeGyoJ/eKOSI0eU6eTlCBYibgd0= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= @@ -38,8 +54,9 @@ github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= @@ -58,8 +75,11 @@ github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.17 h1:yFHH5bghP9ij5Y34PPaMOE8g//oXZ0uJQeMENVo2zcI= -github.com/Microsoft/hcsshim v0.8.17/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.18/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim v0.8.22 h1:CulZ3GW8sNJExknToo+RWD+U+6ZM5kkNfuxywSDPd08= +github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0= github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= @@ -67,8 +87,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM= -github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA= +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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= @@ -76,6 +96,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -89,7 +110,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= @@ -108,11 +131,11 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/checkpoint-restore/checkpointctl v0.0.0-20210301084134-a2024f5584e7 h1:ZmSAEFFtv3mepC4/Ze6E/hi6vGZlhRvywqp1l+w+qqw= github.com/checkpoint-restore/checkpointctl v0.0.0-20210301084134-a2024f5584e7/go.mod h1:Kp3ezoDVdhfYxZUtgs4OL8sVvgOLz3txk0sbQD0opvw= -github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b h1:T4nWG1TXIxeor8mAu5bFguPJgSIGhZqv/f0z55KCrJM= -github.com/checkpoint-restore/go-criu v0.0.0-20190109184317-bdb7599cd87b/go.mod h1:TrMrLQfeENAPYPRsJuq3jsqdlRh3lvi6trTZJG8+tho= github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= +github.com/checkpoint-restore/go-criu/v5 v5.1.0 h1:BkVH17kcwgmKMnFArsvLrxuBbMwfvPNYRB7mfJ0lzyI= +github.com/checkpoint-restore/go-criu/v5 v5.1.0/go.mod h1:iaS8bb7p6zKJanp1Qe8mpl7+bnkYBR500psJR6mwma0= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= @@ -124,9 +147,13 @@ github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9 h1:Kn0s9/APRtr5dk/83aXj97WX0+PYnJK9BO8g0Xclm0I= github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9/go.mod h1:eQt66kIaJpUhCrjCtBFQGQxGLbAUl0OuuwjTH16ON4s= @@ -159,12 +186,15 @@ github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1 h1:xWHPAoe6VkUiI9GAvndJM7s/0MTrmwX3AQiYTr3olf0= github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.5/go.mod h1:oSTh0QpT1w6jYcGmbiSbxv9OSQYaa88mPyWIuU79zyo= +github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -193,6 +223,9 @@ github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= 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.8.0/go.mod h1:mwIwuwb+D8FX2t45Trwi0hmWmZm5VW7zPP/rekwhWQU= +github.com/containerd/stargz-snapshotter/estargz v0.9.0 h1:PkB6BSTfOKX23erT2GkoUKkJEcXfNcyKskIViK770v8= +github.com/containerd/stargz-snapshotter/estargz v0.9.0/go.mod h1:aE5PCyhFMwR8sbrErO5eM2GcvkyXTTJremG883D4qF0= 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= @@ -215,28 +248,30 @@ github.com/containernetworking/plugins v0.8.7/go.mod h1:R7lXeZaBzpfqapcAbHRW8/CYwm0dHzbz0XEjofx0uB0= github.com/containernetworking/plugins v0.9.1 h1:FD1tADPls2EEi3flPc2OegIY1M9pUa9r2Quag7HMLV8= github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containers/buildah v1.21.0 h1:LuwuqRPjan3X3AIdGwfkEkqMgmrDMNpQznFqNdHgCz8= -github.com/containers/buildah v1.21.0/go.mod h1:yPdlpVd93T+i91yGxrJbW1YOWrqN64j5ZhHOZmHUejs= -github.com/containers/common v0.38.4/go.mod h1:egfpX/Y3+19Dz4Wa1eRZDdgzoEOeneieF9CQppKzLBg= -github.com/containers/common v0.38.9 h1:73TUUqIIMRU6hNqrgmZy4vlWPgMz4A/oFfi+v2VEdkg= -github.com/containers/common v0.38.9/go.mod h1:egfpX/Y3+19Dz4Wa1eRZDdgzoEOeneieF9CQppKzLBg= +github.com/containers/buildah v1.23.1 h1:Tpc9DsRuU+0Oofewpxb6OJVNQjCu7yloN/obUqzfDTY= +github.com/containers/buildah v1.23.1/go.mod h1:4WnrN0yrA7ab0ppgunixu2WM1rlD2rG8QLJAKbEkZlQ= +github.com/containers/common v0.44.2/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= +github.com/containers/common v0.44.4 h1:R9ggz2RmbNzu7gdxBHMr4p57fywTwuoZ67jgjt8/RFg= +github.com/containers/common v0.44.4/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo= 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.12.0 h1:1hNS2QkzFQ4lH3GYQLyAXB0acRMhS1Ubm6oV++8vw4w= -github.com/containers/image/v5 v5.12.0/go.mod h1:VasTuHmOw+uD0oHCfApQcMO2+36SfyncoSahU7513Xs= +github.com/containers/image/v5 v5.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4= +github.com/containers/image/v5 v5.17.0 h1:KS5pro80CCsSp5qDBTMmSAWQo+xcBX19zUPExmYX2OQ= +github.com/containers/image/v5 v5.17.0/go.mod h1:GnYVusVRFPMMTAAUkrcS8NNSpBp8oyrjOUe04AAmRr4= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE= github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1 h1:prL8l9w3ntVqXvNH1CiNn5ENjcCnr38JqpSyvKKB4GI= github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/psgo v1.5.2 h1:3aoozst/GIwsrr/5jnFy3FrJay98uujPCu9lTuSZ/Cw= -github.com/containers/psgo v1.5.2/go.mod h1:2ubh0SsreMZjSXW1Hif58JrEcFudQyIy9EzPUWfawVU= +github.com/containers/ocicrypt v1.1.2 h1:Ez+GAMP/4GLix5Ywo/fL7O0nY771gsBIigiqUm1aXz0= +github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/containers/psgo v1.7.1 h1:2N6KADeFvBm1aI2iXxu6+/Xh7CCkdh8p8F3F/cpIU5I= +github.com/containers/psgo v1.7.1/go.mod h1:mWGpFzW73qWFA+blhF6l7GuKzbrACkYgr/ajiNQR+RM= github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM= -github.com/containers/storage v1.30.1/go.mod h1:NDJkiwxnSHD1Is+4DGcyR3SIEYSDOa0xnAW+uGQFx9E= -github.com/containers/storage v1.31.1/go.mod h1:IFEf+yRTS0pvCGQt2tBv1Kzz2XUSPvED6uFBmWG7V/E= -github.com/containers/storage v1.31.3 h1:SpjU8xjUJR+y3gtx7685fkVpm43yBiS35g72ME+kFVA= -github.com/containers/storage v1.31.3/go.mod h1:J3q772EVbN9vgqoN/dkvInKnp4xK9ZXm7wHNfuiIDgE= +github.com/containers/storage v1.35.0/go.mod h1:qzYhasQP2/V9D9XdO+vRwkHBhsBO0oznMLzzRDQ8s20= +github.com/containers/storage v1.36.0/go.mod h1:vbd3SKVQNHdmU5qQI6hTEcKPxnZkGqydG4f6uwrI5a8= +github.com/containers/storage v1.37.0 h1:HVhDsur6sx889ZIZ1d1kEiOzv3gsr5q0diX2VZmOdSg= +github.com/containers/storage v1.37.0/go.mod h1:kqeJeS0b7DO2ZT1nVWs0XufrmPFbgV3c+Q/45RlH6r4= 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= @@ -252,7 +287,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.1/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= @@ -265,14 +299,16 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cri-o/ocicni v0.2.1-0.20210301205850-541cf7c703cf h1:k2wrxBiBseRfOD7h+9fABEuesABBQuUuW5fWwpARbeI= -github.com/cri-o/ocicni v0.2.1-0.20210301205850-541cf7c703cf/go.mod h1:vingr1ztOAzP2WyTgGbpMov9dFhbjNxdLtDv0+PhAvY= -github.com/cyphar/filepath-securejoin v0.2.2 h1:jCwT2GTP+PY5nBz3c/YL5PAIbusElVrPujOBSCj8xRg= +github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283 h1:7FyIYKksGvRF8XjMkG5T6uIxg8PcgZoPyO+f6kHT5+s= +github.com/cri-o/ocicni v0.2.1-0.20210621164014-d0acc7862283/go.mod h1:vingr1ztOAzP2WyTgGbpMov9dFhbjNxdLtDv0+PhAvY= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= +github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -291,12 +327,11 @@ 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 h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20191219165747-a9416c67da9f/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.6+incompatible h1:oXI3Vas8TI8Eu/EjH4srKHJBVqraSzJybhxY7Om9faQ= -github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.11+incompatible h1:OqzI/g/W54LczvhnccGqniFoQghHx3pklbLuhfXpqGo= +github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56WZ2Pwu/fKWtKuZB0o= +github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= @@ -315,6 +350,8 @@ github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dtylman/scp v0.0.0-20181017070807-f3000a34aef4 h1:Tc//0LMiRsUsOIu4S+HFKleax9X1+3SRKo+36ldZX0c= +github.com/dtylman/scp v0.0.0-20181017070807-f3000a34aef4/go.mod h1:jN1ZaUPSNA8jm10nmaRLky84qV/iCeiHmcEf3EbP+dc= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= @@ -323,18 +360,25 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsouza/go-dockerclient v1.7.2 h1:bBEAcqLTkpq205jooP5RVroUKiVEWgGecHyeZc4OFjo= -github.com/fsouza/go-dockerclient v1.7.2/go.mod h1:+ugtMCVRwnPfY7d8/baCzZ3uwB0BrG5DB8OzbtxaRz8= +github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI= +github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= +github.com/fsouza/go-dockerclient v1.7.4 h1:daYb0km2a91aNt2KTc4AEcTwgExYtQXHhkt5mjdRD1o= +github.com/fsouza/go-dockerclient v1.7.4/go.mod h1:het+LPt7NaTEVGgwXJAKxPn77RZrQKb2EXJb4e+BHv0= github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -367,9 +411,10 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -384,13 +429,17 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -406,9 +455,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -416,35 +465,50 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM= github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg= github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= +github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33 h1:893HsJqtxp9z1SF76gg6hY70hRY1wVlTSnC/h1yUDCo= github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= @@ -461,6 +525,7 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -491,20 +556,21 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/insomniacslk/dhcp v0.0.0-20210120172423-cc9239ac6294/go.mod h1:TKl4jN3Voofo4UJIicyNhWGp/nlQqQkFxmwIFTvBkKI= github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee h1:PAXLXk1heNZ5yokbMBpVLZQxo43wCZxRwl00mX+dd44= github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/jamescun/tuntap v0.0.0-20190712092105-cb1fb277045c/go.mod h1:zzwpsgcYhzzIP5WyF8g9ivCv38cY9uAV9Gu0m3lThhE= -github.com/jinzhu/copier v0.3.0 h1:P5zN9OYSxmtzZmwgcVmt5Iu8egfP53BGMPAFgEksKPI= -github.com/jinzhu/copier v0.3.0/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= +github.com/jinzhu/copier v0.3.2 h1:QdBOCbaouLDYaIPFfi1bKv5F5tPpeTwXe4sD0jqtz5w= +github.com/jinzhu/copier v0.3.2/go.mod h1:24xnZezI2Yqac9J61UC6/dG/k76ttpq0DdJI3QmUvro= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -515,12 +581,12 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= @@ -530,13 +596,16 @@ github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= 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.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8= -github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= +github.com/klauspost/compress v1.13.6/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= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -546,31 +615,29 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/manifoldco/promptui v0.8.0 h1:R95mMF+McvXZQ7j1g8ucVZE1gLP3Sv6j9vlF9kyRqQo= github.com/manifoldco/promptui v0.8.0/go.mod h1:n4zTdgP0vr0S3w7/O/g98U+e0gwLScEXGwov2nIKuGQ= +github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= +github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +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= github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/mattn/go-shellwords v1.0.11 h1:vCoR9VPpsk/TZFW2JwK5I9S0xdrtUq2bph6/YjEPnaw= -github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -595,6 +662,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= @@ -606,15 +675,17 @@ github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/moby/vpnkit v0.5.0/go.mod h1:KyjUrL9cb6ZSNNAUwZfqRjhwwgJ3BJN+kXh0t43WTUQ= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= @@ -643,9 +714,10 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg= -github.com/onsi/ginkgo v1.16.2 h1:HFB2fbVIlhIfCfOW81bZFbiC/RvnpXSdhbF2/DJr134= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= @@ -654,8 +726,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/onsi/gomega v1.12.0 h1:p4oGGk2M2UJc0wWN4lHFvIB71lxsh0T/UiKCCgFADY8= -github.com/onsi/gomega v1.12.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= @@ -664,18 +736,17 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6 h1:yN8BPXVwMBAm3Cuvh1L5XE8XpvYRMdsVLd82ILprhUU= -github.com/opencontainers/image-spec v1.0.2-0.20190823105129-775207bd45b6/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283 h1:TVzvdjOalkJBNkbpPVMAr4KV9QRf2IjfxdyxwAK78Gs= +github.com/opencontainers/image-spec v1.0.2-0.20210819154149-5ad6f50d6283/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.0.0-20190425234816-dae70e8efea4/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v1.0.0-rc91/go.mod h1:3Sm6Dt7OT8z88EbdQqqcRN2oCT54jbi72tT/HqgflT8= github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.0-rc94/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= -github.com/opencontainers/runc v1.0.0-rc95 h1:RMuWVfY3E1ILlVsC3RhIq38n4sJtlOFwU9gfFZSqrd0= -github.com/opencontainers/runc v1.0.0-rc95/go.mod h1:z+bZxa/+Tz/FmYVWkhUajJdzFeOqjc5vrqskhVyHGUM= +github.com/opencontainers/runc v1.0.1/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runc v1.0.2 h1:opHZMaswlyxz1OuGpBE53Dwe4/xF7EZTY0A2L/FpCOg= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= @@ -690,8 +761,11 @@ github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.1 h1:yvEZh7CsfnJNwKzG9ZeXwbvR05RAZsu5RS/3vA6qFTA= -github.com/opencontainers/selinux v1.8.1/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= +github.com/opencontainers/selinux v1.8.4/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= +github.com/opencontainers/selinux v1.8.5/go.mod h1:HTvjPFoGMbpQsG886e3lQwnsRWtE4TC1OF3OUvG9FAo= +github.com/opencontainers/selinux v1.9.1 h1:b4VPEF3O5JLZgdTDBmGepaaIbAo0GqoF6EBRq5f/g3Y= +github.com/opencontainers/selinux v1.9.1/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656 h1:WaxyNFpmIDu4i6so9r6LVFIbSaXqsj8oitMitt86ae4= github.com/openshift/imagebuilder v1.2.2-0.20210415181909-87f3e48c2656/go.mod h1:9aJRczxCH0mvT6XQ+5STAQaPWz7OsWcU5/mRkt8IWeo= github.com/ostreedev/ostree-go v0.0.0-20190702140239-759a8c1ac913 h1:TnbXhKzrTOyuvWrjI8W6pcoI9XPbLHFXCdN2dtUw7Rw= @@ -699,19 +773,19 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= -github.com/pquerna/ffjson v0.0.0-20190813045741-dac163c6c0a9/go.mod h1:YARuvh7BUWHNhzDq2OM5tzR2RiCcN2D7sapiKyCel/M= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -749,9 +823,10 @@ 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= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rootless-containers/rootlesskit v0.14.2 h1:jmsSyNyRG0QdWc3usppt5jEy5qOheeUsIINcymPrOFg= -github.com/rootless-containers/rootlesskit v0.14.2/go.mod h1:nV3TpRISvwhZQSwo0nmQQnxjCxXr3mvrMi0oASLvzcg= +github.com/rootless-containers/rootlesskit v0.14.5 h1:X4eNt2e1h/uSjlssKqpeTY5fatrjDz9F9FX05RJB7Tw= +github.com/rootless-containers/rootlesskit v0.14.5/go.mod h1:Ai3detLzryb/4EkzXmNfh8aByUcBXp/qqkQusJs1SO8= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -763,7 +838,6 @@ github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf h1:b0+ZBD3rohnkQ4q5duD1+RyTXTg9yk+qTOPMSQtapO0= github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -777,17 +851,21 @@ github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= +github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -796,8 +874,10 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -822,8 +902,8 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY= -github.com/uber/jaeger-client-go v2.28.0+incompatible h1:G4QSBfvPKvg5ZM2j9MrJFdfI5iSljY/WnJqOGFao6HI= -github.com/uber/jaeger-client-go v2.28.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-client-go v2.29.1+incompatible h1:R9ec3zO3sGpzs0abd43Y+fBZRJ9uiH6lXyR/+u6brW4= +github.com/uber/jaeger-client-go v2.29.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= @@ -831,11 +911,16 @@ github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= -github.com/vbauerster/mpb/v6 v6.0.3 h1:j+twHHhSUe8aXWaT/27E98G5cSBeqEuJSVCMjmLg0PI= -github.com/vbauerster/mpb/v6 v6.0.3/go.mod h1:5luBx4rDLWxpA4t6I5sdeeQuZhqDxc+wr5Nqf35+tnM= +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.1.3/go.mod h1:X5GlohZw2fIpypMXWaKart+HGSAjpz49skxkDk+ZL7c= +github.com/vbauerster/mpb/v7 v7.1.5 h1:vtUEUfQHmNeJETyF4AcRCOV6RC4wqFwNORy52UMXPbQ= +github.com/vbauerster/mpb/v7 v7.1.5/go.mod h1:4M8+qAoQqV60WDNktBM5k05i1iTrXE7rjKOHEVkVlec= github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA= @@ -845,7 +930,6 @@ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae h1:4hwBBUfQCFe3Cym0ZtKyq7L16eZUtYKs+BaHDN6mAns= github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190809123943-df4f5c81cb3b h1:6cLsL+2FW6dRAdl5iMtHgRogVCff0QpRi9653YmdcJA= @@ -857,28 +941,41 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -888,12 +985,14 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -916,6 +1015,8 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -924,6 +1025,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -944,6 +1048,7 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -956,35 +1061,54 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b h1:SXy8Ld8oKlcogOvUAh0J5Pm5RKzgYBMMxLxt6n5XW50= +golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a h1:DcqTD9SDLc+1P/r1EmRBwnVsrOwW+kk2vWf9n+1sGhs= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -999,7 +1123,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1028,7 +1151,6 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1039,14 +1161,23 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1055,26 +1186,43 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210216224549-f992740a1bac/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887 h1:dXfMednGJh/SUUFjTLsWJz3P+TQt9qnR11GgeI3vWKs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210817190340-bfb29a6856f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210910150752-751e447fb3d0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef h1:fPxZ3Umkct3LZ8gK9nbk+DWDJ9fstZa2grBn+lWVKPs= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE= +golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1116,12 +1264,29 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200711155855-7342f9734a7d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2 h1:kRBLX7v7Af8W7Gdbbc908OJcdgtK8bOz9Uaj8/F1ACA= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1137,12 +1302,26 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1163,10 +1342,34 @@ google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20211005153810-c76a74d43a8e h1:Im71rbA1N3CbIag/PumYhQcNR8bLNmuOtRIyOnnLsT8= +google.golang.org/genproto v0.0.0-20211005153810-c76a74d43a8e/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -1179,9 +1382,21 @@ google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1193,8 +1408,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1211,6 +1427,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -1230,8 +1447,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= @@ -1243,16 +1461,17 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.21.0 h1:gu5iGF4V6tfVCQ/R+8Hc0h7H1JuEhzyEi9S4R5LM8+Y= -k8s.io/api v0.21.0/go.mod h1:+YbrhBBGgsxbF6o6Kj4KJPJnBmAKuXDeS3E18bgHNVU= +k8s.io/api v0.22.1 h1:ISu3tD/jRhYfSW8jI/Q1e+lRxkR7w9UwQEZ7FgslrwY= +k8s.io/api v0.22.1/go.mod h1:bh13rkTp3F1XEaLGykbyRD2QaTTzPm0e/BMd8ptFONY= k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.21.0 h1:3Fx+41if+IRavNcKOz09FwEXDBG6ORh6iMsTSelhkMA= -k8s.io/apimachinery v0.21.0/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= +k8s.io/apimachinery v0.22.1 h1:DTARnyzmdHMz7bFWFDDm22AM4pLWTQECMpRTFu2d2OM= +k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= @@ -1271,10 +1490,10 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= +k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM= +k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= +k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -1284,8 +1503,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno= +sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff -Nru libpod-3.2.1+ds1/hack/get_release_info.sh libpod-3.4.4+ds1/hack/get_release_info.sh --- libpod-3.2.1+ds1/hack/get_release_info.sh 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/hack/get_release_info.sh 2021-12-26 00:45:51.000000000 +0000 @@ -16,7 +16,7 @@ # `git describe` will never produce a useful version number under all # branches. This is because the podman release process (see `RELEASE_PROCESS.md`) -# tags release versions only on release-branches (i.e. never on master). +# tags release versions only on release-branches (i.e. never on main). # Scraping the version number directly from the source, is the only way # to reliably obtain the number from all the various contexts supported by # the `Makefile`. diff -Nru libpod-3.2.1+ds1/hack/libsubid_tag.sh libpod-3.4.4+ds1/hack/libsubid_tag.sh --- libpod-3.2.1+ds1/hack/libsubid_tag.sh 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/hack/libsubid_tag.sh 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +if test $(${GO:-go} env GOOS) != "linux" ; then + exit 0 +fi +tmpdir="$PWD/tmp.$RANDOM" +mkdir -p "$tmpdir" +trap 'rm -fr "$tmpdir"' EXIT +cc -o "$tmpdir"/libsubid_tag -l subid -x c - > /dev/null 2> /dev/null << EOF +#include +int main() { + struct subid_range *ranges = NULL; + get_subuid_ranges("root", &ranges); + free(ranges); + return 0; +} +EOF +if test $? -eq 0 ; then + echo libsubid +fi diff -Nru libpod-3.2.1+ds1/hack/podman-registry libpod-3.4.4+ds1/hack/podman-registry --- libpod-3.2.1+ds1/hack/podman-registry 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/hack/podman-registry 2021-12-26 00:45:51.000000000 +0000 @@ -7,7 +7,7 @@ ############################################################################### # BEGIN defaults -PODMAN_REGISTRY_IMAGE=docker.io/library/registry:2.6 +PODMAN_REGISTRY_IMAGE=quay.io/libpod/registry:2.6 PODMAN_REGISTRY_USER= PODMAN_REGISTRY_PASS= @@ -117,7 +117,7 @@ # If we ever get here, it's a given that the registry is not running. # Clean up after ourselves. - rm -rf ${PODMAN_REGISTRY_WORKDIR} + ${PODMAN} unshare rm -rf ${PODMAN_REGISTRY_WORKDIR} exit 1 fi } @@ -214,7 +214,8 @@ podman stop registry podman rm -f registry - rm -rf ${PODMAN_REGISTRY_WORKDIR} + # Use straight podman, not our alias function, to avoid 'overlay: EBUSY' + ${PODMAN} unshare rm -rf ${PODMAN_REGISTRY_WORKDIR} } diff -Nru libpod-3.2.1+ds1/hack/release.sh libpod-3.4.4+ds1/hack/release.sh --- libpod-3.2.1+ds1/hack/release.sh 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/hack/release.sh 2021-12-26 00:45:51.000000000 +0000 @@ -36,20 +36,10 @@ sed -i "s/^\(Version: *\).*/\1${LOCAL_VERSION}/" contrib/spec/podman.spec.in } -write_changelog() -{ - echo "- Changelog for v${VERSION} (${DATE})" >.changelog.txt && - git log --no-merges --format=' * %s' "${LAST_TAG}..HEAD" >>.changelog.txt && - echo >>.changelog.txt && - cat changelog.txt >>.changelog.txt && - mv -f .changelog.txt changelog.txt -} - release_commit() { write_go_version "${VERSION}" && write_spec_version "${VERSION}" && - write_changelog && git commit -asm "Bump to v${VERSION}" } @@ -61,7 +51,7 @@ } git fetch origin && -git checkout -b "bump-${VERSION}" origin/master && +git checkout -b "bump-${VERSION}" origin/main && release_commit && git tag -s -m "version ${VERSION}" "v${VERSION}" && dev_version_commit diff -Nru libpod-3.2.1+ds1/hack/swagger-check libpod-3.4.4+ds1/hack/swagger-check --- libpod-3.2.1+ds1/hack/swagger-check 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/hack/swagger-check 2021-12-26 00:45:51.000000000 +0000 @@ -320,6 +320,9 @@ if ($action eq 'df') { $action = 'dataUsage'; } + elsif ($action eq "delete" && $endpoint eq "/libpod/play/kube") { + $action = "KubeDown" + } # Grrrrrr, this one is annoying: some operations get an extra 'All' elsif ($action =~ /^(delete|get|stats)$/ && $endpoint !~ /\{/) { $action .= "All"; diff -Nru libpod-3.2.1+ds1/hack/xref-helpmsgs-manpages libpod-3.4.4+ds1/hack/xref-helpmsgs-manpages --- libpod-3.2.1+ds1/hack/xref-helpmsgs-manpages 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/hack/xref-helpmsgs-manpages 2021-12-26 00:45:51.000000000 +0000 @@ -244,6 +244,13 @@ if ($line =~ /^\s{1,4}(\S+)\s/) { my $subcommand = $1; print "> podman @_ $subcommand\n" if $debug; + + # check that the same subcommand is not listed twice (#12356) + if (exists $help{$subcommand}) { + warn "$ME: 'podman @_ help' lists '$subcommand' twice\n"; + ++$Errs; + } + $help{$subcommand} = podman_help(@_, $subcommand) unless $subcommand eq 'help'; # 'help' not in man } diff -Nru libpod-3.2.1+ds1/libpod/boltdb_state.go libpod-3.4.4+ds1/libpod/boltdb_state.go --- libpod-3.2.1+ds1/libpod/boltdb_state.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/boltdb_state.go 2021-12-26 00:45:51.000000000 +0000 @@ -1756,6 +1756,23 @@ if err := allCtrsBkt.Put([]byte(ctr.ID()), []byte(newName)); err != nil { return errors.Wrapf(err, "error renaming container %s in all containers bucket in DB", ctr.ID()) } + if ctr.config.Pod != "" { + podsBkt, err := getPodBucket(tx) + if err != nil { + return err + } + podBkt := podsBkt.Bucket([]byte(ctr.config.Pod)) + if podBkt == nil { + return errors.Wrapf(define.ErrInternal, "bucket for pod %s does not exist", ctr.config.Pod) + } + podCtrBkt := podBkt.Bucket(containersBkt) + if podCtrBkt == nil { + return errors.Wrapf(define.ErrInternal, "pod %s does not have a containers bucket", ctr.config.Pod) + } + if err := podCtrBkt.Put([]byte(ctr.ID()), []byte(newName)); err != nil { + return errors.Wrapf(err, "error renaming container %s in pod %s members bucket", ctr.ID(), ctr.config.Pod) + } + } } } diff -Nru libpod-3.2.1+ds1/libpod/boltdb_state_unsupported.go libpod-3.4.4+ds1/libpod/boltdb_state_unsupported.go --- libpod-3.2.1+ds1/libpod/boltdb_state_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/boltdb_state_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -// +build !linux - -package libpod - -// replaceNetNS is exclusive to the Linux platform and is a no-op elsewhere -func replaceNetNS(netNSPath string, ctr *Container, newState *ContainerState) error { - return nil -} - -// getNetNSPath is exclusive to the Linux platform and is a no-op elsewhere -func getNetNSPath(ctr *Container) string { - return "" -} diff -Nru libpod-3.2.1+ds1/libpod/container_api.go libpod-3.4.4+ds1/libpod/container_api.go --- libpod-3.2.1+ds1/libpod/container_api.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_api.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/signal" + "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -183,7 +184,7 @@ return define.ErrCtrStopped } - if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { + if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStateStopping) { return errors.Wrapf(define.ErrCtrStateInvalid, "can only stop created or running containers. %s is in state %s", c.ID(), c.state.State.String()) } @@ -685,7 +686,7 @@ // If runtime knows about the container, update its status in runtime // And then save back to disk - if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped) { + if c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning, define.ContainerStatePaused, define.ContainerStateStopped, define.ContainerStateStopping) { oldState := c.state.State if err := c.ociRuntime.UpdateContainerStatus(c); err != nil { return err @@ -776,6 +777,19 @@ // ImportPrevious tells the API to restore container with two // images. One is TargetFile, the other is ImportPrevious. ImportPrevious string + // Compression tells the API which compression to use for + // the exported checkpoint archive. + Compression archive.Compression + // If Pod is set the container should be restored into the + // given Pod. If Pod is empty it is a restore without a Pod. + // Restoring a non Pod container into a Pod or a Pod container + // without a Pod is theoretically possible, but will + // probably not work if a PID namespace is shared. + // A shared PID namespace means that a Pod container has PID 1 + // in the infrastructure container, but without the infrastructure + // container no PID 1 will be in the namespace and that is not + // possible. + Pod string } // Checkpoint checkpoints a container @@ -807,7 +821,11 @@ // Restore restores a container func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) error { - logrus.Debugf("Trying to restore container %s", c.ID()) + if options.Pod == "" { + logrus.Debugf("Trying to restore container %s", c.ID()) + } else { + logrus.Debugf("Trying to restore container %s into pod %s", c.ID(), options.Pod) + } if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -836,7 +854,7 @@ // CopyFromArchive copies the contents from the specified tarStream to path // *inside* the container. -func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, tarStream io.Reader) (func() error, error) { +func (c *Container) CopyFromArchive(ctx context.Context, containerPath string, chown bool, rename map[string]string, tarStream io.Reader) (func() error, error) { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -846,7 +864,7 @@ } } - return c.copyFromArchive(ctx, containerPath, tarStream) + return c.copyFromArchive(ctx, containerPath, chown, rename, tarStream) } // CopyToArchive copies the contents from the specified path *inside* the diff -Nru libpod-3.2.1+ds1/libpod/container_commit.go libpod-3.4.4+ds1/libpod/container_commit.go --- libpod-3.2.1+ds1/libpod/container_commit.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_commit.go 2021-12-26 00:45:51.000000000 +0000 @@ -99,6 +99,11 @@ for _, p := range c.config.PortMappings { importBuilder.SetPort(fmt.Sprintf("%d/%s", p.ContainerPort, p.Protocol)) } + for port, protocols := range c.config.ExposedPorts { + for _, protocol := range protocols { + importBuilder.SetPort(fmt.Sprintf("%d/%s", port, protocol)) + } + } // Labels for k, v := range c.Labels() { importBuilder.SetLabel(k, v) diff -Nru libpod-3.2.1+ds1/libpod/container_config.go libpod-3.4.4+ds1/libpod/container_config.go --- libpod-3.2.1+ds1/libpod/container_config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_config.go 2021-12-26 00:45:51.000000000 +0000 @@ -148,7 +148,7 @@ // default, but others do not. CreateWorkingDir bool `json:"createWorkingDir,omitempty"` // Secrets lists secrets to mount into the container - Secrets []*secrets.Secret `json:"secrets,omitempty"` + Secrets []*ContainerSecret `json:"secrets,omitempty"` // SecretPath is the secrets location in storage SecretsPath string `json:"secretsPath"` // Volatile specifies whether the container storage can be optimized @@ -229,6 +229,12 @@ // namespace // These are not used unless CreateNetNS is true PortMappings []ocicni.PortMapping `json:"portMappings,omitempty"` + // ExposedPorts are the ports which are exposed but not forwarded + // into the container. + // The map key is the port and the string slice contains the protocols, + // e.g. tcp and udp + // These are only set when exposed ports are given but not published. + ExposedPorts map[uint16][]string `json:"exposedPorts,omitempty"` // UseImageResolvConf indicates that resolv.conf should not be // bind-mounted inside the container. // Conflicts with DNSServer, DNSSearch, DNSOption. @@ -375,4 +381,7 @@ CDIDevices []string `json:"cdiDevices,omitempty"` // EnvSecrets are secrets that are set as environment variables EnvSecrets map[string]*secrets.Secret `json:"secret_env,omitempty"` + // InitContainerType specifies if the container is an initcontainer + // and if so, what type: always or once are possible non-nil entries + InitContainerType string `json:"init_container_type,omitempty"` } diff -Nru libpod-3.2.1+ds1/libpod/container_copy_linux.go libpod-3.4.4+ds1/libpod/container_copy_linux.go --- libpod-3.2.1+ds1/libpod/container_copy_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_copy_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,15 +15,15 @@ "github.com/containers/buildah/util" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/idtools" - "github.com/docker/docker/pkg/archive" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) -func (c *Container) copyFromArchive(ctx context.Context, path string, reader io.Reader) (func() error, error) { +func (c *Container) copyFromArchive(ctx context.Context, path string, chown bool, rename map[string]string, reader io.Reader) (func() error, error) { var ( mountPoint string resolvedRoot string @@ -62,13 +62,16 @@ } } - // Make sure we chown the files to the container's main user and group ID. - user, err := getContainerUser(c, mountPoint) - if err != nil { - unmount() - return nil, err + var idPair *idtools.IDPair + if chown { + // Make sure we chown the files to the container's main user and group ID. + user, err := getContainerUser(c, mountPoint) + if err != nil { + unmount() + return nil, err + } + idPair = &idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} } - idPair := idtools.IDPair{UID: int(user.UID), GID: int(user.GID)} decompressed, err := archive.DecompressStream(reader) if err != nil { @@ -84,8 +87,9 @@ putOptions := buildahCopiah.PutOptions{ UIDMap: c.config.IDMappings.UIDMap, GIDMap: c.config.IDMappings.GIDMap, - ChownDirs: &idPair, - ChownFiles: &idPair, + ChownDirs: idPair, + ChownFiles: idPair, + Rename: rename, } return c.joinMountAndExec(ctx, @@ -170,7 +174,7 @@ // getContainerUser returns the specs.User and ID mappings of the container. func getContainerUser(container *Container, mountPoint string) (specs.User, error) { - userspec := container.Config().User + userspec := container.config.User uid, gid, _, err := chrootuser.GetUser(mountPoint, userspec) u := specs.User{ diff -Nru libpod-3.2.1+ds1/libpod/container_copy_unsupported.go libpod-3.4.4+ds1/libpod/container_copy_unsupported.go --- libpod-3.2.1+ds1/libpod/container_copy_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_copy_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -// +build !linux - -package libpod - -import ( - "context" - "io" -) - -func (c *Container) copyFromArchive(ctx context.Context, path string, reader io.Reader) (func() error, error) { - return nil, nil -} - -func (c *Container) copyToArchive(ctx context.Context, path string, writer io.Writer) (func() error, error) { - return nil, nil -} diff -Nru libpod-3.2.1+ds1/libpod/container_exec.go libpod-3.4.4+ds1/libpod/container_exec.go --- libpod-3.2.1+ds1/libpod/container_exec.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_exec.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package libpod import ( + "context" "io/ioutil" "net/http" "os" @@ -276,9 +277,10 @@ } // ExecStartAndAttach starts and attaches to an exec session in a container. +// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty // TODO: Should we include detach keys in the signature to allow override? // TODO: How do we handle AttachStdin/AttachStdout/AttachStderr? -func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams) error { +func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachStreams, newSize *define.TerminalSize) error { if !c.batched { c.lock.Lock() defer c.lock.Unlock() @@ -309,7 +311,7 @@ return err } - pid, attachChan, err := c.ociRuntime.ExecContainer(c, session.ID(), opts, streams) + pid, attachChan, err := c.ociRuntime.ExecContainer(c, session.ID(), opts, streams, newSize) if err != nil { return err } @@ -372,7 +374,9 @@ } // ExecHTTPStartAndAttach starts and performs an HTTP attach to an exec session. -func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool) error { +// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty +func (c *Container) ExecHTTPStartAndAttach(sessionID string, r *http.Request, w http.ResponseWriter, + streams *HTTPAttachStreams, detachKeys *string, cancel <-chan bool, hijackDone chan<- bool, newSize *define.TerminalSize) error { // TODO: How do we combine streams with the default streams set in the exec session? // Ensure that we don't leak a goroutine here @@ -430,7 +434,7 @@ close(holdConnOpen) }() - pid, attachChan, err := c.ociRuntime.ExecContainerHTTP(c, session.ID(), execOpts, r, w, streams, cancel, hijackDone, holdConnOpen) + pid, attachChan, err := c.ociRuntime.ExecContainerHTTP(c, session.ID(), execOpts, r, w, streams, cancel, hijackDone, holdConnOpen, newSize) if err != nil { session.State = define.ExecStateStopped session.ExitCode = define.TranslateExecErrorToExitCode(define.ExecErrorCodeGeneric, err) @@ -539,18 +543,7 @@ var cleanupErr error // Retrieve exit code and update status - exitCode, err := c.readExecExitCode(session.ID()) - if err != nil { - cleanupErr = err - } - session.ExitCode = exitCode - session.PID = 0 - session.State = define.ExecStateStopped - - if err := c.save(); err != nil { - if cleanupErr != nil { - logrus.Errorf("Error stopping container %s exec session %s: %v", c.ID(), session.ID(), cleanupErr) - } + if err := retrieveAndWriteExecExitCode(c, session.ID()); err != nil { cleanupErr = err } @@ -592,15 +585,7 @@ return errors.Wrapf(define.ErrExecSessionStateInvalid, "cannot clean up container %s exec session %s as it is running", c.ID(), session.ID()) } - exitCode, err := c.readExecExitCode(session.ID()) - if err != nil { - return err - } - session.ExitCode = exitCode - session.PID = 0 - session.State = define.ExecStateStopped - - if err := c.save(); err != nil { + if err := retrieveAndWriteExecExitCode(c, session.ID()); err != nil { return err } } @@ -637,9 +622,9 @@ return err } if !running { - session.State = define.ExecStateStopped - // TODO: should we retrieve exit code here? - // TODO: Might be worth saving state here. + if err := retrieveAndWriteExecExitCode(c, session.ID()); err != nil { + return err + } } } @@ -653,6 +638,10 @@ return err } + if err := retrieveAndWriteExecExitCode(c, session.ID()); err != nil { + return err + } + if err := c.cleanupExecBundle(session.ID()); err != nil { return err } @@ -733,7 +722,10 @@ // API there. // TODO: Refactor so this is closed here, before we remove the exec // session. + var size *define.TerminalSize if resize != nil { + s := <-resize + size = &s go func() { logrus.Debugf("Sending resize events to exec session %s", sessionID) for resizeRequest := range resize { @@ -751,16 +743,31 @@ }() } - if err := c.ExecStartAndAttach(sessionID, streams); err != nil { + if err := c.ExecStartAndAttach(sessionID, streams, size); err != nil { return -1, err } session, err := c.ExecSession(sessionID) if err != nil { + if errors.Cause(err) == define.ErrNoSuchExecSession { + // TODO: If a proper Context is ever plumbed in here, we + // should use it. + // As things stand, though, it's not worth it - this + // should always terminate quickly since it's not + // streaming. + diedEvent, err := c.runtime.GetExecDiedEvent(context.Background(), c.ID(), sessionID) + if err != nil { + return -1, errors.Wrapf(err, "error retrieving exec session %s exit code", sessionID) + } + return diedEvent.ContainerExitCode, nil + } return -1, err } exitCode := session.ExitCode if err := c.ExecRemove(sessionID, false); err != nil { + if errors.Cause(err) == define.ErrNoSuchExecSession { + return exitCode, nil + } return -1, err } @@ -927,6 +934,8 @@ session.PID = 0 session.State = define.ExecStateStopped + c.newExecDiedEvent(session.ID(), exitCode) + needSave = true } if err := c.cleanupExecBundle(id); err != nil { @@ -1036,6 +1045,22 @@ return errors.Wrapf(err, "error syncing container %s state to remove exec session %s", c.ID(), sessionID) } + return justWriteExecExitCode(c, sessionID, exitCode) +} + +func retrieveAndWriteExecExitCode(c *Container, sessionID string) error { + exitCode, err := c.readExecExitCode(sessionID) + if err != nil { + return err + } + + return justWriteExecExitCode(c, sessionID, exitCode) +} + +func justWriteExecExitCode(c *Container, sessionID string, exitCode int) error { + // Write an event first + c.newExecDiedEvent(sessionID, exitCode) + session, ok := c.state.ExecSessions[sessionID] if !ok { // Exec session already removed. diff -Nru libpod-3.2.1+ds1/libpod/container.go libpod-3.4.4+ds1/libpod/container.go --- libpod-3.2.1+ds1/libpod/container.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,15 +6,16 @@ "io/ioutil" "net" "os" + "strings" "time" "github.com/containernetworking/cni/pkg/types" cnitypes "github.com/containernetworking/cni/pkg/types/current" + "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" - "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/storage" "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -127,6 +128,10 @@ // This is true if a container is restored from a checkpoint. restoreFromCheckpoint bool + // Used to query the NOTIFY_SOCKET once along with setting up + // mounts etc. + notifySocket string + slirp4netnsSubnet *net.IPNet } @@ -156,6 +161,9 @@ // OOMKilled indicates that the container was killed as it ran out of // memory OOMKilled bool `json:"oomKilled,omitempty"` + // Checkpointed indicates that the container was stopped by a checkpoint + // operation. + Checkpointed bool `json:"checkpointed,omitempty"` // PID is the PID of a running container PID int `json:"pid,omitempty"` // ConmonPID is the PID of the container's conmon @@ -237,6 +245,20 @@ ReadWrite bool `json:"rw"` } +// ContainerSecret is a secret that is mounted in a container +type ContainerSecret struct { + // Secret is the secret + *secrets.Secret + // UID is the UID of the secret file + UID uint32 + // GID is the GID of the secret file + GID uint32 + // Mode is the mode of the secret file + Mode uint32 + // Secret target inside container + Target string +} + // ContainerNetworkDescriptions describes the relationship between the CNI // network and the ethN where N is an integer type ContainerNetworkDescriptions map[string]int @@ -955,6 +977,11 @@ procPath := fmt.Sprintf("/proc/%d/cgroup", c.state.PID) lines, err := ioutil.ReadFile(procPath) if err != nil { + // If the file doesn't exist, it means the container could have been terminated + // so report it. + if os.IsNotExist(err) { + return "", errors.Wrapf(define.ErrCtrStopped, "cannot get cgroup path unless container %s is running", c.ID()) + } return "", err } @@ -981,6 +1008,29 @@ return "", errors.Errorf("could not find any cgroup in %q", procPath) } + cgroupManager := c.CgroupManager() + switch { + case c.config.CgroupsMode == cgroupSplit: + name := fmt.Sprintf("/libpod-payload-%s/", c.ID()) + if index := strings.LastIndex(cgroupPath, name); index >= 0 { + return cgroupPath[:index+len(name)-1], nil + } + case cgroupManager == config.CgroupfsCgroupsManager: + name := fmt.Sprintf("/libpod-%s/", c.ID()) + if index := strings.LastIndex(cgroupPath, name); index >= 0 { + return cgroupPath[:index+len(name)-1], nil + } + case cgroupManager == config.SystemdCgroupsManager: + // When running under systemd, try to detect the scope that was requested + // to be created. It improves the heuristic since we report the first + // cgroup that was created instead of the cgroup where PID 1 might have + // moved to. + name := fmt.Sprintf("/libpod-%s.scope/", c.ID()) + if index := strings.LastIndex(cgroupPath, name); index >= 0 { + return cgroupPath[:index+len(name)-1], nil + } + } + return cgroupPath, nil } @@ -1009,8 +1059,8 @@ } // IDMappings returns the UID/GID mapping used for the container -func (c *Container) IDMappings() (storage.IDMappingOptions, error) { - return c.config.IDMappings, nil +func (c *Container) IDMappings() storage.IDMappingOptions { + return c.config.IDMappings } // RootUID returns the root user mapping from container @@ -1044,6 +1094,11 @@ return c.config.IsInfra } +// IsInitCtr returns whether the container is an init container +func (c *Container) IsInitCtr() bool { + return len(c.config.InitContainerType) > 0 +} + // IsReadOnly returns whether the container is running in read only mode func (c *Container) IsReadOnly() bool { return c.config.Spec.Root.Readonly @@ -1136,7 +1191,7 @@ } //Secrets return the secrets in the container -func (c *Container) Secrets() []*secrets.Secret { +func (c *Container) Secrets() []*ContainerSecret { return c.config.Secrets } @@ -1162,11 +1217,51 @@ return c.networks() } +// NetworkMode gets the configured network mode for the container. +// Get actual value from the database +func (c *Container) NetworkMode() string { + networkMode := "" + ctrSpec := c.config.Spec + + switch { + case c.config.CreateNetNS: + // We actually store the network + // mode for Slirp and Bridge, so + // we can just use that + networkMode = string(c.config.NetMode) + case c.config.NetNsCtr != "": + networkMode = fmt.Sprintf("container:%s", c.config.NetNsCtr) + default: + // Find the spec's network namespace. + // If there is none, it's host networking. + // If there is one and it has a path, it's "ns:". + foundNetNS := false + for _, ns := range ctrSpec.Linux.Namespaces { + if ns.Type == spec.NetworkNamespace { + foundNetNS = true + if ns.Path != "" { + networkMode = fmt.Sprintf("ns:%s", ns.Path) + } else { + // We're making a network ns, but not + // configuring with Slirp or CNI. That + // means it's --net=none + networkMode = "none" + } + break + } + } + if !foundNetNS { + networkMode = "host" + } + } + return networkMode +} + // Unlocked accessor for networks func (c *Container) networks() ([]string, bool, error) { networks, err := c.runtime.state.GetNetworks(c) if err != nil && errors.Cause(err) == define.ErrNoSuchNetwork { - if len(c.config.Networks) == 0 && !rootless.IsRootless() { + if len(c.config.Networks) == 0 && c.config.NetMode.IsBridge() { return []string{c.runtime.netPlugin.GetDefaultNetworkName()}, true, nil } return c.config.Networks, false, nil diff -Nru libpod-3.2.1+ds1/libpod/container_graph.go libpod-3.4.4+ds1/libpod/container_graph.go --- libpod-3.2.1+ds1/libpod/container_graph.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_graph.go 2021-12-26 00:45:51.000000000 +0000 @@ -259,7 +259,7 @@ } // Start the container (only if it is not running) - if !ctrErrored { + if !ctrErrored && len(node.container.config.InitContainerType) < 1 { if !restart && node.container.state.State != define.ContainerStateRunning { if err := node.container.initAndStart(ctx); err != nil { ctrErrored = true diff -Nru libpod-3.2.1+ds1/libpod/container_inspect.go libpod-3.4.4+ds1/libpod/container_inspect.go --- libpod-3.2.1+ds1/libpod/container_inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -97,24 +97,36 @@ return nil, err } + cgroupPath, err := c.cGroupPath() + if err != nil { + // Handle the case where the container is not running or has no cgroup. + if errors.Is(err, define.ErrNoCgroups) || errors.Is(err, define.ErrCtrStopped) { + cgroupPath = "" + } else { + return nil, err + } + } + data := &define.InspectContainerData{ ID: config.ID, Created: config.CreatedTime, Path: path, Args: args, State: &define.InspectContainerState{ - OciVersion: ctrSpec.Version, - Status: runtimeInfo.State.String(), - Running: runtimeInfo.State == define.ContainerStateRunning, - Paused: runtimeInfo.State == define.ContainerStatePaused, - OOMKilled: runtimeInfo.OOMKilled, - Dead: runtimeInfo.State.String() == "bad state", - Pid: runtimeInfo.PID, - ConmonPid: runtimeInfo.ConmonPID, - ExitCode: runtimeInfo.ExitCode, - Error: "", // can't get yet - StartedAt: runtimeInfo.StartedTime, - FinishedAt: runtimeInfo.FinishedTime, + OciVersion: ctrSpec.Version, + Status: runtimeInfo.State.String(), + Running: runtimeInfo.State == define.ContainerStateRunning, + Paused: runtimeInfo.State == define.ContainerStatePaused, + OOMKilled: runtimeInfo.OOMKilled, + Dead: runtimeInfo.State.String() == "bad state", + Pid: runtimeInfo.PID, + ConmonPid: runtimeInfo.ConmonPID, + ExitCode: runtimeInfo.ExitCode, + Error: "", // can't get yet + StartedAt: runtimeInfo.StartedTime, + FinishedAt: runtimeInfo.FinishedTime, + Checkpointed: runtimeInfo.Checkpointed, + CgroupPath: cgroupPath, }, Image: config.RootfsImageID, ImageName: config.RootfsImageName, @@ -150,7 +162,7 @@ if c.config.HealthCheckConfig != nil { // This container has a healthcheck defined in it; we need to add it's state - healthCheckState, err := c.GetHealthCheckLog() + healthCheckState, err := c.getHealthCheckLog() if err != nil { // An error here is not considered fatal; no health state will be displayed logrus.Error(err) @@ -343,11 +355,13 @@ ctrConfig.CreateCommand = c.config.CreateCommand ctrConfig.Timezone = c.config.Timezone - for _, secret := range c.config.Secrets { newSec := define.InspectSecret{} newSec.Name = secret.Name newSec.ID = secret.ID + newSec.UID = secret.UID + newSec.GID = secret.GID + newSec.Mode = secret.Mode ctrConfig.Secrets = append(ctrConfig.Secrets, &newSec) } @@ -616,44 +630,13 @@ hostConfig.Tmpfs = tmpfs // Network mode parsing. - networkMode := "" - switch { - case c.config.CreateNetNS: - // We actually store the network - // mode for Slirp and Bridge, so - // we can just use that - networkMode = string(c.config.NetMode) - case c.config.NetNsCtr != "": - networkMode = fmt.Sprintf("container:%s", c.config.NetNsCtr) - default: - // Find the spec's network namespace. - // If there is none, it's host networking. - // If there is one and it has a path, it's "ns:". - foundNetNS := false - for _, ns := range ctrSpec.Linux.Namespaces { - if ns.Type == spec.NetworkNamespace { - foundNetNS = true - if ns.Path != "" { - networkMode = fmt.Sprintf("ns:%s", ns.Path) - } else { - // We're making a network ns, but not - // configuring with Slirp or CNI. That - // means it's --net=none - networkMode = "none" - } - break - } - } - if !foundNetNS { - networkMode = "host" - } - } + networkMode := c.NetworkMode() hostConfig.NetworkMode = networkMode // Port bindings. // Only populate if we're using CNI to configure the network. if c.config.CreateNetNS { - hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings) + hostConfig.PortBindings = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts) } else { hostConfig.PortBindings = make(map[string][]define.InspectHostPort) } diff -Nru libpod-3.2.1+ds1/libpod/container_internal.go libpod-3.4.4+ds1/libpod/container_internal.go --- libpod-3.2.1+ds1/libpod/container_internal.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_internal.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,7 +15,7 @@ metadata "github.com/checkpoint-restore/checkpointctl/lib" "github.com/containers/buildah/copier" - "github.com/containers/common/pkg/secrets" + butil "github.com/containers/buildah/util" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/cgroups" @@ -24,6 +24,7 @@ "github.com/containers/podman/v3/pkg/hooks/exec" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/selinux" + "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/idtools" @@ -35,6 +36,7 @@ "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) const ( @@ -292,6 +294,15 @@ } } + // setup rootlesskit port forwarder again since it dies when conmon exits + // we use rootlesskit port forwarder only as rootless and when bridge network is used + if rootless.IsRootless() && c.config.NetMode.IsBridge() && len(c.config.PortMappings) > 0 { + err := c.runtime.setupRootlessPortMappingViaRLK(c, c.state.NetNS.Path()) + if err != nil { + return false, err + } + } + if c.state.State == define.ContainerStateStopped { // Reinitialize the container if we need to if err := c.reinit(ctx, true); err != nil { @@ -366,6 +377,12 @@ return } *dest = *from + // If we are creating a container inside a pod, we always want to inherit the + // userns settings from the infra container. So clear the auto userns settings + // so that we don't request storage for a new uid/gid map. + if c.PodID() != "" && !c.IsInfra() { + dest.AutoUserNs = false + } if dest.AutoUserNs { overrides := c.getUserOverrides() dest.AutoUserNsOpts.PasswdFile = overrides.ContainerEtcPasswdPath @@ -419,7 +436,6 @@ if c.config.Rootfs == "" && (c.config.RootfsImageID == "" || c.config.RootfsImageName == "") { return errors.Wrapf(define.ErrInvalidArg, "must provide image ID and image name to use an image") } - options := storage.ContainerOptions{ IDMappingOptions: storage.IDMappingOptions{ HostUIDMapping: true, @@ -427,7 +443,7 @@ }, LabelOpts: c.config.LabelOpts, } - if c.restoreFromCheckpoint { + if c.restoreFromCheckpoint && !c.config.Privileged { // If restoring from a checkpoint, the root file-system // needs to be mounted with the same SELinux labels as // it was mounted previously. @@ -464,28 +480,36 @@ c.setupStorageMapping(&options.IDMappingOptions, &c.config.IDMappings) - containerInfo, err := c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, options) - if err != nil { - return errors.Wrapf(err, "error creating container storage") + // Unless the user has specified a name, use a randomly generated one. + // Note that name conflicts may occur (see #11735), so we need to loop. + generateName := c.config.Name == "" + var containerInfo ContainerInfo + var containerInfoErr error + for { + if generateName { + name, err := c.runtime.generateName() + if err != nil { + return err + } + c.config.Name = name + } + containerInfo, containerInfoErr = c.runtime.storageService.CreateContainerStorage(ctx, c.runtime.imageContext, c.config.RootfsImageName, c.config.RootfsImageID, c.config.Name, c.config.ID, options) + + if !generateName || errors.Cause(containerInfoErr) != storage.ErrDuplicateName { + break + } + } + if containerInfoErr != nil { + return errors.Wrapf(containerInfoErr, "error creating container storage") } c.config.IDMappings.UIDMap = containerInfo.UIDMap c.config.IDMappings.GIDMap = containerInfo.GIDMap - processLabel := containerInfo.ProcessLabel - switch { - case c.ociRuntime.SupportsKVM(): - processLabel, err = selinux.KVMLabel(processLabel) - if err != nil { - return err - } - case c.config.Systemd: - processLabel, err = selinux.InitLabel(processLabel) - if err != nil { - return err - } + processLabel, err := c.processLabel(containerInfo.ProcessLabel) + if err != nil { + return err } - c.config.ProcessLabel = processLabel c.config.MountLabel = containerInfo.MountLabel c.config.StaticDir = containerInfo.Dir @@ -520,6 +544,26 @@ return nil } +func (c *Container) processLabel(processLabel string) (string, error) { + if !c.config.Systemd && !c.ociRuntime.SupportsKVM() { + return processLabel, nil + } + ctrSpec, err := c.specFromState() + if err != nil { + return "", err + } + label, ok := ctrSpec.Annotations[define.InspectAnnotationLabel] + if !ok || !strings.Contains(label, "type:") { + switch { + case c.ociRuntime.SupportsKVM(): + return selinux.KVMLabel(processLabel) + case c.config.Systemd: + return selinux.InitLabel(processLabel) + } + } + return processLabel, nil +} + // Tear down a container's storage prior to removal func (c *Container) teardownStorage() error { if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { @@ -568,6 +612,7 @@ state.StoppedByUser = false state.RestartPolicyMatch = false state.RestartCount = 0 + state.Checkpointed = false } // Refresh refreshes the container's state after a restart. @@ -960,7 +1005,7 @@ if err != nil { return nil, errors.Wrapf(err, "error retrieving state of dependency %s of container %s", dep, c.ID()) } - if state != define.ContainerStateRunning { + if state != define.ContainerStateRunning && !depCtr.config.IsInfra { notRunning = append(notRunning, dep) } depCtrs[dep] = depCtr @@ -1036,7 +1081,7 @@ var hosts string if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 { ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0] - hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.Config().Name) + hosts += fmt.Sprintf("%s\t%s %s\n", ipAddress, c.Hostname(), c.config.Name) } return hosts } @@ -1055,13 +1100,18 @@ return err } + // Make sure the workdir exists while initializing container + if err := c.resolveWorkDir(); err != nil { + return err + } + // Save the OCI newSpec to disk if err := c.saveSpec(newSpec); err != nil { return err } for _, v := range c.config.NamedVolumes { - if err := c.chownVolume(v.Name); err != nil { + if err := c.fixVolumePermissions(v); err != nil { return err } } @@ -1088,6 +1138,7 @@ c.state.ExecSessions = make(map[string]*ExecSession) } + c.state.Checkpointed = false c.state.ExitCode = 0 c.state.Exited = false c.state.State = define.ContainerStateCreated @@ -1531,6 +1582,51 @@ }() } + rootUID, rootGID := c.RootUID(), c.RootGID() + + dirfd, err := unix.Open(mountPoint, unix.O_RDONLY|unix.O_PATH, 0) + if err != nil { + return "", errors.Wrap(err, "open mount point") + } + defer unix.Close(dirfd) + + err = unix.Mkdirat(dirfd, "etc", 0755) + if err != nil && !os.IsExist(err) { + return "", errors.Wrap(err, "create /etc") + } + // If the etc directory was created, chown it to root in the container + if err == nil && (rootUID != 0 || rootGID != 0) { + err = unix.Fchownat(dirfd, "etc", rootUID, rootGID, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + return "", errors.Wrap(err, "chown /etc") + } + } + + etcInTheContainerPath, err := securejoin.SecureJoin(mountPoint, "etc") + if err != nil { + return "", errors.Wrap(err, "resolve /etc in the container") + } + + etcInTheContainerFd, err := unix.Open(etcInTheContainerPath, unix.O_RDONLY|unix.O_PATH, 0) + if err != nil { + return "", errors.Wrap(err, "open /etc in the container") + } + defer unix.Close(etcInTheContainerFd) + + // If /etc/mtab does not exist in container image, then we need to + // create it, so that mount command within the container will work. + err = unix.Symlinkat("/proc/mounts", etcInTheContainerFd, "mtab") + if err != nil && !os.IsExist(err) { + return "", errors.Wrap(err, "creating /etc/mtab symlink") + } + // If the symlink was created, then also chown it to root in the container + if err == nil && (rootUID != 0 || rootGID != 0) { + err = unix.Fchownat(etcInTheContainerFd, "mtab", rootUID, rootGID, unix.AT_SYMLINK_NOFOLLOW) + if err != nil { + return "", errors.Wrap(err, "chown /etc/mtab") + } + } + // Request a mount of all named volumes for _, v := range c.config.NamedVolumes { vol, err := c.mountNamedVolume(v, mountPoint) @@ -1670,64 +1766,6 @@ return vol, nil } -// Chown the specified volume if necessary. -func (c *Container) chownVolume(volumeName string) error { - vol, err := c.runtime.state.Volume(volumeName) - if err != nil { - return errors.Wrapf(err, "error retrieving named volume %s for container %s", volumeName, c.ID()) - } - - vol.lock.Lock() - defer vol.lock.Unlock() - - // The volume may need a copy-up. Check the state. - if err := vol.update(); err != nil { - return err - } - - // TODO: For now, I've disabled chowning volumes owned by non-Podman - // drivers. This may be safe, but it's really going to be a case-by-case - // thing, I think - safest to leave disabled now and re-enable later if - // there is a demand. - if vol.state.NeedsChown && !vol.UsesVolumeDriver() { - vol.state.NeedsChown = false - - uid := int(c.config.Spec.Process.User.UID) - gid := int(c.config.Spec.Process.User.GID) - - if c.config.IDMappings.UIDMap != nil { - p := idtools.IDPair{ - UID: uid, - GID: gid, - } - mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap) - newPair, err := mappings.ToHost(p) - if err != nil { - return errors.Wrapf(err, "error mapping user %d:%d", uid, gid) - } - uid = newPair.UID - gid = newPair.GID - } - - vol.state.UIDChowned = uid - vol.state.GIDChowned = gid - - if err := vol.save(); err != nil { - return err - } - - mountPoint, err := vol.MountPoint() - if err != nil { - return err - } - - if err := os.Lchown(mountPoint, uid, gid); err != nil { - return err - } - } - return nil -} - // cleanupStorage unmounts and cleans up the container's root filesystem func (c *Container) cleanupStorage() error { if !c.state.Mounted { @@ -2116,7 +2154,7 @@ return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is in invalid state", c.ID()) } - if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { + if c.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) && !c.IsInfra() { return errors.Wrapf(define.ErrCtrStateInvalid, "cannot remove container %s as it is %s - running or paused containers cannot be removed without force", c.ID(), c.state.State.String()) } @@ -2142,7 +2180,7 @@ // JSON files for later export func (c *Container) prepareCheckpointExport() error { // save live config - if _, err := metadata.WriteJSONFile(c.Config(), c.bundlePath(), metadata.ConfigDumpFile); err != nil { + if _, err := metadata.WriteJSONFile(c.config, c.bundlePath(), metadata.ConfigDumpFile); err != nil { return err } @@ -2236,21 +2274,31 @@ } // extractSecretToStorage copies a secret's data from the secrets manager to the container's static dir -func (c *Container) extractSecretToCtrStorage(name string) error { - manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir()) +func (c *Container) extractSecretToCtrStorage(secr *ContainerSecret) error { + manager, err := c.runtime.SecretsManager() if err != nil { return err } - secr, data, err := manager.LookupSecretData(name) + _, data, err := manager.LookupSecretData(secr.Name) if err != nil { return err } secretFile := filepath.Join(c.config.SecretsPath, secr.Name) + hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), secr.UID, secr.GID) + if err != nil { + return errors.Wrap(err, "unable to extract secret") + } err = ioutil.WriteFile(secretFile, data, 0644) if err != nil { return errors.Wrapf(err, "unable to create %s", secretFile) } + if err := os.Lchown(secretFile, int(hostUID), int(hostGID)); err != nil { + return err + } + if err := os.Chmod(secretFile, os.FileMode(secr.Mode)); err != nil { + return err + } if err := label.Relabel(secretFile, c.config.MountLabel, false); err != nil { return err } diff -Nru libpod-3.2.1+ds1/libpod/container_internal_linux.go libpod-3.4.4+ds1/libpod/container_internal_linux.go --- libpod-3.2.1+ds1/libpod/container_internal_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_internal_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +//go:build linux // +build linux package libpod @@ -29,7 +30,6 @@ "github.com/containers/common/pkg/apparmor" "github.com/containers/common/pkg/chown" "github.com/containers/common/pkg/config" - "github.com/containers/common/pkg/secrets" "github.com/containers/common/pkg/subscriptions" "github.com/containers/common/pkg/umask" "github.com/containers/podman/v3/libpod/define" @@ -177,11 +177,6 @@ return err } - // Make sure the workdir exists - if err := c.resolveWorkDir(); err != nil { - return err - } - return nil } @@ -353,12 +348,35 @@ return nil, err } + if err := c.mountNotifySocket(g); err != nil { + return nil, err + } + // Get host UID and GID based on the container process UID and GID. hostUID, hostGID, err := butil.GetHostIDs(util.IDtoolsToRuntimeSpec(c.config.IDMappings.UIDMap), util.IDtoolsToRuntimeSpec(c.config.IDMappings.GIDMap), uint32(execUser.Uid), uint32(execUser.Gid)) if err != nil { return nil, err } + // Add named volumes + for _, namedVol := range c.config.NamedVolumes { + volume, err := c.runtime.GetVolume(namedVol.Name) + if err != nil { + return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID()) + } + mountPoint, err := volume.MountPoint() + if err != nil { + return nil, err + } + volMount := spec.Mount{ + Type: "bind", + Source: mountPoint, + Destination: namedVol.Dest, + Options: namedVol.Options, + } + g.AddMount(volMount) + } + // Check if the spec file mounts contain the options z, Z or U. // If they have z or Z, relabel the source directory and then remove the option. // If they have U, chown the source directory and them remove the option. @@ -392,25 +410,6 @@ g.SetProcessSelinuxLabel(c.ProcessLabel()) g.SetLinuxMountLabel(c.MountLabel()) - // Add named volumes - for _, namedVol := range c.config.NamedVolumes { - volume, err := c.runtime.GetVolume(namedVol.Name) - if err != nil { - return nil, errors.Wrapf(err, "error retrieving volume %s to add to container %s", namedVol.Name, c.ID()) - } - mountPoint, err := volume.MountPoint() - if err != nil { - return nil, err - } - volMount := spec.Mount{ - Type: "bind", - Source: mountPoint, - Destination: namedVol.Dest, - Options: namedVol.Options, - } - g.AddMount(volMount) - } - // Add bind mounts to container for dstPath, srcPath := range c.state.BindMounts { newMount := spec.Mount{ @@ -660,7 +659,7 @@ } } - if c.config.IDMappings.AutoUserNs { + if c.config.UserNsCtr == "" && c.config.IDMappings.AutoUserNs { if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { return nil, err } @@ -759,7 +758,10 @@ return nil, errors.Wrapf(err, "error setting up OCI Hooks") } if len(c.config.EnvSecrets) > 0 { - manager, err := secrets.NewManager(c.runtime.GetSecretsStorageDir()) + manager, err := c.runtime.SecretsManager() + if err != nil { + return nil, err + } if err != nil { return nil, err } @@ -772,9 +774,56 @@ } } + // Pass down the LISTEN_* environment (see #10443). + for _, key := range []string{"LISTEN_PID", "LISTEN_FDS", "LISTEN_FDNAMES"} { + if val, ok := os.LookupEnv(key); ok { + // Force the PID to `1` since we cannot rely on (all + // versions of) all runtimes to do it for us. + if key == "LISTEN_PID" { + val = "1" + } + g.AddProcessEnv(key, val) + } + } + return g.Config, nil } +// mountNotifySocket mounts the NOTIFY_SOCKET into the container if it's set +// and if the sdnotify mode is set to container. It also sets c.notifySocket +// to avoid redundantly looking up the env variable. +func (c *Container) mountNotifySocket(g generate.Generator) error { + notify, ok := os.LookupEnv("NOTIFY_SOCKET") + if !ok { + return nil + } + c.notifySocket = notify + + if c.config.SdNotifyMode != define.SdNotifyModeContainer { + return nil + } + + notifyDir := filepath.Join(c.bundlePath(), "notify") + logrus.Debugf("checking notify %q dir", notifyDir) + if err := os.MkdirAll(notifyDir, 0755); err != nil { + if !os.IsExist(err) { + return errors.Wrapf(err, "unable to create notify %q dir", notifyDir) + } + } + if err := label.Relabel(notifyDir, c.MountLabel(), true); err != nil { + return errors.Wrapf(err, "relabel failed %q", notifyDir) + } + logrus.Debugf("add bindmount notify %q dir", notifyDir) + if _, ok := c.state.BindMounts["/run/notify"]; !ok { + c.state.BindMounts["/run/notify"] = notifyDir + } + + // Set the container's notify socket to the proxy socket created by conmon + g.AddProcessEnv("NOTIFY_SOCKET", "/run/notify/notify.sock") + + return nil +} + // systemd expects to have /run, /run/lock and /tmp on tmpfs // It also expects to be able to write to /sys/fs/cgroup/systemd and /var/log/journal func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) error { @@ -899,19 +948,41 @@ } func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error { - if len(c.Dependencies()) > 0 { - return errors.Errorf("Cannot export checkpoints of containers with dependencies") + if len(c.Dependencies()) == 1 { + // Check if the dependency is an infra container. If it is we can checkpoint + // the container out of the Pod. + if c.config.Pod == "" { + return errors.Errorf("cannot export checkpoints of containers with dependencies") + } + + pod, err := c.runtime.state.Pod(c.config.Pod) + if err != nil { + return errors.Wrapf(err, "container %s is in pod %s, but pod cannot be retrieved", c.ID(), c.config.Pod) + } + infraID, err := pod.InfraContainerID() + if err != nil { + return errors.Wrapf(err, "cannot retrieve infra container ID for pod %s", c.config.Pod) + } + if c.Dependencies()[0] != infraID { + return errors.Errorf("cannot export checkpoints of containers with dependencies") + } + } + if len(c.Dependencies()) > 1 { + return errors.Errorf("cannot export checkpoints of containers with dependencies") } logrus.Debugf("Exporting checkpoint image of container %q to %q", c.ID(), options.TargetFile) includeFiles := []string{ "artifacts", - "ctr.log", metadata.ConfigDumpFile, metadata.SpecDumpFile, metadata.NetworkStatusFile, } + if c.LogDriver() == define.KubernetesLogging || + c.LogDriver() == define.JSONLogging { + includeFiles = append(includeFiles, "ctr.log") + } if options.PreCheckPoint { includeFiles = append(includeFiles, preCheckpointDir) } else { @@ -921,7 +992,7 @@ var addToTarFiles []string if !options.IgnoreRootfs { // To correctly track deleted files, let's go through the output of 'podman diff' - rootFsChanges, err := c.runtime.GetDiff("", c.ID()) + rootFsChanges, err := c.runtime.GetDiff("", c.ID(), define.DiffContainer) if err != nil { return errors.Wrapf(err, "error exporting root file-system diff for %q", c.ID()) } @@ -984,7 +1055,7 @@ } input, err := archive.TarWithOptions(c.bundlePath(), &archive.TarOptions{ - Compression: archive.Gzip, + Compression: options.Compression, IncludeSourceDir: true, IncludeFiles: includeFiles, }) @@ -1019,9 +1090,9 @@ return nil } -func (c *Container) checkpointRestoreSupported() error { - if !criu.CheckForCriu() { - return errors.Errorf("checkpoint/restore requires at least CRIU %d", criu.MinCriuVersion) +func (c *Container) checkpointRestoreSupported(version int) error { + if !criu.CheckForCriu(version) { + return errors.Errorf("checkpoint/restore requires at least CRIU %d", version) } if !c.ociRuntime.SupportsCheckpoint() { return errors.Errorf("configured runtime does not support checkpoint/restore") @@ -1030,7 +1101,7 @@ } func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error { - if err := c.checkpointRestoreSupported(); err != nil { + if err := c.checkpointRestoreSupported(criu.MinCriuVersion); err != nil { return err } @@ -1078,6 +1149,7 @@ if !options.KeepRunning && !options.PreCheckPoint { c.state.State = define.ContainerStateStopped + c.state.Checkpointed = true // Cleanup Storage and Network if err := c.cleanup(ctx); err != nil { @@ -1134,10 +1206,20 @@ } func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) { - if err := c.checkpointRestoreSupported(); err != nil { + minCriuVersion := func() int { + if options.Pod == "" { + return criu.MinCriuVersion + } + return criu.PodCriuVersion + }() + if err := c.checkpointRestoreSupported(minCriuVersion); err != nil { return err } + if options.Pod != "" && !crutils.CRRuntimeSupportsPodCheckpointRestore(c.ociRuntime.Path()) { + return errors.Errorf("runtime %s does not support pod restore", c.ociRuntime.Path()) + } + if !c.ensureState(define.ContainerStateConfigured, define.ContainerStateExited) { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID()) } @@ -1245,6 +1327,83 @@ } } + if options.Pod != "" { + // Running in a Pod means that we have to change all namespace settings to + // the ones from the infrastructure container. + pod, err := c.runtime.LookupPod(options.Pod) + if err != nil { + return errors.Wrapf(err, "pod %q cannot be retrieved", options.Pod) + } + + infraContainer, err := pod.InfraContainer() + if err != nil { + return errors.Wrapf(err, "cannot retrieved infra container from pod %q", options.Pod) + } + + infraContainer.lock.Lock() + if err := infraContainer.syncContainer(); err != nil { + infraContainer.lock.Unlock() + return errors.Wrapf(err, "Error syncing infrastructure container %s status", infraContainer.ID()) + } + if infraContainer.state.State != define.ContainerStateRunning { + if err := infraContainer.initAndStart(ctx); err != nil { + infraContainer.lock.Unlock() + return errors.Wrapf(err, "Error starting infrastructure container %s status", infraContainer.ID()) + } + } + infraContainer.lock.Unlock() + + if c.config.IPCNsCtr != "" { + nsPath, err := infraContainer.namespacePath(IPCNS) + if err != nil { + return errors.Wrapf(err, "cannot retrieve IPC namespace path for Pod %q", options.Pod) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), nsPath); err != nil { + return err + } + } + + if c.config.NetNsCtr != "" { + nsPath, err := infraContainer.namespacePath(NetNS) + if err != nil { + return errors.Wrapf(err, "cannot retrieve network namespace path for Pod %q", options.Pod) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), nsPath); err != nil { + return err + } + } + + if c.config.PIDNsCtr != "" { + nsPath, err := infraContainer.namespacePath(PIDNS) + if err != nil { + return errors.Wrapf(err, "cannot retrieve PID namespace path for Pod %q", options.Pod) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), nsPath); err != nil { + return err + } + } + + if c.config.UTSNsCtr != "" { + nsPath, err := infraContainer.namespacePath(UTSNS) + if err != nil { + return errors.Wrapf(err, "cannot retrieve UTS namespace path for Pod %q", options.Pod) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), nsPath); err != nil { + return err + } + } + + if c.config.CgroupNsCtr != "" { + nsPath, err := infraContainer.namespacePath(CgroupNS) + if err != nil { + return errors.Wrapf(err, "cannot retrieve Cgroup namespace path for Pod %q", options.Pod) + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), nsPath); err != nil { + return err + } + } + } + if err := c.makeBindMounts(); err != nil { return err } @@ -1324,6 +1483,7 @@ logrus.Debugf("Restored container %s", c.ID()) c.state.State = define.ContainerStateRunning + c.state.Checkpointed = false if !options.Keep { // Delete all checkpoint related files. At this point, in theory, all files @@ -1617,11 +1777,21 @@ return errors.Wrapf(err, "error creating secrets mount") } for _, secret := range c.Secrets() { + secretFileName := secret.Name + base := "/run/secrets" + if secret.Target != "" { + secretFileName = secret.Target + //If absolute path for target given remove base. + if filepath.IsAbs(secretFileName) { + base = "" + } + } src := filepath.Join(c.config.SecretsPath, secret.Name) - dest := filepath.Join("/run/secrets", secret.Name) + dest := filepath.Join(base, secretFileName) c.state.BindMounts[dest] = src } } + return nil } @@ -1659,9 +1829,13 @@ // check if systemd-resolved is used, assume it is used when 127.0.0.53 is the only nameserver if len(ns) == 1 && ns[0] == "127.0.0.53" { // read the actual resolv.conf file for systemd-resolved - contents, err = ioutil.ReadFile("/run/systemd/resolve/resolv.conf") + resolvedContents, err := ioutil.ReadFile("/run/systemd/resolve/resolv.conf") if err != nil { - return "", errors.Wrapf(err, "detected that systemd-resolved is in use, but could not locate real resolv.conf") + if !os.IsNotExist(err) { + return "", errors.Wrapf(err, "detected that systemd-resolved is in use, but could not locate real resolv.conf") + } + } else { + contents = resolvedContents } } @@ -1670,7 +1844,7 @@ cniResponse := c.state.NetworkStatus for _, i := range cniResponse { for _, ip := range i.IPs { - // Note: only using To16() does not work since it also returns a vaild ip for ipv4 + // Note: only using To16() does not work since it also returns a valid ip for ipv4 if ip.Address.IP.To4() == nil && ip.Address.IP.To16() != nil { ipv6 = true } @@ -1772,7 +1946,7 @@ return "", err } - return filepath.Join(c.state.RunDir, "resolv.conf"), nil + return destPath, nil } // generateHosts creates a containers hosts file @@ -1783,9 +1957,24 @@ } hosts := string(orig) hosts += c.getHosts() + + hosts = c.appendLocalhost(hosts) + return c.writeStringToRundir("hosts", hosts) } +// based on networking mode we may want to append the localhost +// if there isn't any record for it and also this shoud happen +// in slirp4netns and similar network modes. +func (c *Container) appendLocalhost(hosts string) string { + if !strings.Contains(hosts, "localhost") && + !c.config.NetMode.IsHost() { + hosts += "127.0.0.1\tlocalhost\n::1\tlocalhost\n" + } + + return hosts +} + // appendHosts appends a container's config and state pertaining to hosts to a container's // local hosts file. netCtr is the container from which the netNS information is // taken. @@ -1800,6 +1989,7 @@ // and returns a string in a format that can be written to the host file func (c *Container) getHosts() string { var hosts string + if len(c.config.HostAdd) > 0 { for _, host := range c.config.HostAdd { // the host format has already been verified at this point @@ -1810,66 +2000,68 @@ hosts += c.cniHosts() - // If not making a network namespace, add our own hostname. + // Add hostname for slirp4netns if c.Hostname() != "" { if c.config.NetMode.IsSlirp4netns() { // When using slirp4netns, the interface gets a static IP - slirp4netnsIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) + slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet) if err != nil { - logrus.Warn("failed to determine slirp4netnsIP: ", err.Error()) + logrus.Warnf("failed to determine slirp4netnsIP: %v", err.Error()) } else { hosts += fmt.Sprintf("# used by slirp4netns\n%s\t%s %s\n", slirp4netnsIP.String(), c.Hostname(), c.config.Name) } - } else { - hasNetNS := false - netNone := false + } + + // Do we have a network namespace? + netNone := false + if c.config.NetNsCtr == "" && !c.config.CreateNetNS { for _, ns := range c.config.Spec.Linux.Namespaces { if ns.Type == spec.NetworkNamespace { - hasNetNS = true - if ns.Path == "" && !c.config.CreateNetNS { + if ns.Path == "" { netNone = true } break } } - if !hasNetNS { - // 127.0.1.1 and host's hostname to match Docker - osHostname, _ := os.Hostname() - hosts += fmt.Sprintf("127.0.1.1 %s %s %s\n", osHostname, c.Hostname(), c.config.Name) - } - if netNone { - hosts += fmt.Sprintf("127.0.1.1 %s %s\n", c.Hostname(), c.config.Name) - } + } + // If we are net=none (have a network namespace, but not connected to + // anything) add the container's name and hostname to localhost. + if netNone { + hosts += fmt.Sprintf("127.0.0.1 %s %s\n", c.Hostname(), c.config.Name) } } - // Add gateway entry - var depCtr *Container - if c.config.NetNsCtr != "" { - // ignoring the error because there isn't anything to do - depCtr, _ = c.getRootNetNsDepCtr() - } else if len(c.state.NetworkStatus) != 0 { - depCtr = c - } else { - depCtr = nil - } + // Add gateway entry if we are not in a machine. If we use podman machine + // the gvproxy dns server will take care of host.containers.internal. + // https://github.com/containers/gvisor-tap-vsock/commit/1108ea45162281046d239047a6db9bc187e64b08 + if !c.runtime.config.Engine.MachineEnabled { + var depCtr *Container + if c.config.NetNsCtr != "" { + // ignoring the error because there isn't anything to do + depCtr, _ = c.getRootNetNsDepCtr() + } else if len(c.state.NetworkStatus) != 0 { + depCtr = c + } else { + depCtr = nil + } - if depCtr != nil { - for _, pluginResultsRaw := range depCtr.state.NetworkStatus { - pluginResult, _ := cnitypes.GetResult(pluginResultsRaw) - for _, ip := range pluginResult.IPs { - hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway) + if depCtr != nil { + for _, pluginResultsRaw := range depCtr.state.NetworkStatus { + pluginResult, _ := cnitypes.GetResult(pluginResultsRaw) + for _, ip := range pluginResult.IPs { + hosts += fmt.Sprintf("%s host.containers.internal\n", ip.Gateway) + } + } + } else if c.config.NetMode.IsSlirp4netns() { + gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) + if err != nil { + logrus.Warn("failed to determine gatewayIP: ", err.Error()) + } else { + hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String()) } - } - } else if c.config.NetMode.IsSlirp4netns() { - gatewayIP, err := GetSlirp4netnsGateway(c.slirp4netnsSubnet) - if err != nil { - logrus.Warn("failed to determine gatewayIP: ", err.Error()) } else { - hosts += fmt.Sprintf("%s host.containers.internal\n", gatewayIP.String()) + logrus.Debug("network configuration does not support host.containers.internal address") } - } else { - logrus.Debug("network configuration does not support host.containers.internal address") } return hosts @@ -2315,20 +2507,12 @@ switch { case c.config.NoCgroups: return "", nil - case (rootless.IsRootless() && (cgroupManager == config.CgroupfsCgroupsManager || !unified)): - if !isRootlessCgroupSet(c.config.CgroupParent) { - return "", nil - } - return c.config.CgroupParent, nil case c.config.CgroupsMode == cgroupSplit: - if c.config.CgroupParent != "" { - return c.config.CgroupParent, nil - } selfCgroup, err := utils.GetOwnCgroup() if err != nil { return "", err } - return filepath.Join(selfCgroup, "container"), nil + return filepath.Join(selfCgroup, fmt.Sprintf("libpod-payload-%s", c.ID())), nil case cgroupManager == config.SystemdCgroupsManager: // When the OCI runtime is set to use Systemd as a cgroup manager, it // expects cgroups to be passed as follows: @@ -2336,6 +2520,11 @@ systemdCgroups := fmt.Sprintf("%s:libpod:%s", path.Base(c.config.CgroupParent), c.ID()) logrus.Debugf("Setting CGroups for container %s to %s", c.ID(), systemdCgroups) return systemdCgroups, nil + case (rootless.IsRootless() && (cgroupManager == config.CgroupfsCgroupsManager || !unified)): + if c.config.CgroupParent == "" || !isRootlessCgroupSet(c.config.CgroupParent) { + return "", nil + } + fallthrough case cgroupManager == config.CgroupfsCgroupsManager: cgroupPath := filepath.Join(c.config.CgroupParent, fmt.Sprintf("libpod-%s", c.ID())) logrus.Debugf("Setting CGroup path for container %s to %s", c.ID(), cgroupPath) @@ -2409,7 +2598,7 @@ oldUmask := umask.Set(0) defer umask.Set(oldUmask) - if err := os.MkdirAll(src, 0644); err != nil { + if err := os.MkdirAll(src, 0755); err != nil { return err } if err := label.Relabel(src, c.config.MountLabel, false); err != nil { @@ -2424,3 +2613,82 @@ return err } + +// Fix ownership and permissions of the specified volume if necessary. +func (c *Container) fixVolumePermissions(v *ContainerNamedVolume) error { + vol, err := c.runtime.state.Volume(v.Name) + if err != nil { + return errors.Wrapf(err, "error retrieving named volume %s for container %s", v.Name, c.ID()) + } + + vol.lock.Lock() + defer vol.lock.Unlock() + + // The volume may need a copy-up. Check the state. + if err := vol.update(); err != nil { + return err + } + + // TODO: For now, I've disabled chowning volumes owned by non-Podman + // drivers. This may be safe, but it's really going to be a case-by-case + // thing, I think - safest to leave disabled now and re-enable later if + // there is a demand. + if vol.state.NeedsChown && !vol.UsesVolumeDriver() { + vol.state.NeedsChown = false + + uid := int(c.config.Spec.Process.User.UID) + gid := int(c.config.Spec.Process.User.GID) + + if c.config.IDMappings.UIDMap != nil { + p := idtools.IDPair{ + UID: uid, + GID: gid, + } + mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap) + newPair, err := mappings.ToHost(p) + if err != nil { + return errors.Wrapf(err, "error mapping user %d:%d", uid, gid) + } + uid = newPair.UID + gid = newPair.GID + } + + vol.state.UIDChowned = uid + vol.state.GIDChowned = gid + + if err := vol.save(); err != nil { + return err + } + + mountPoint, err := vol.MountPoint() + if err != nil { + return err + } + + if err := os.Lchown(mountPoint, uid, gid); err != nil { + return err + } + + // Make sure the new volume matches the permissions of the target directory. + // https://github.com/containers/podman/issues/10188 + st, err := os.Lstat(filepath.Join(c.state.Mountpoint, v.Dest)) + if err == nil { + if stat, ok := st.Sys().(*syscall.Stat_t); ok { + if err := os.Lchown(mountPoint, int(stat.Uid), int(stat.Gid)); err != nil { + return err + } + } + if err := os.Chmod(mountPoint, st.Mode()); err != nil { + return err + } + stat := st.Sys().(*syscall.Stat_t) + atime := time.Unix(int64(stat.Atim.Sec), int64(stat.Atim.Nsec)) + if err := os.Chtimes(mountPoint, atime, st.ModTime()); err != nil { + return err + } + } else if !os.IsNotExist(err) { + return err + } + } + return nil +} diff -Nru libpod-3.2.1+ds1/libpod/container_internal_linux_test.go libpod-3.4.4+ds1/libpod/container_internal_linux_test.go --- libpod-3.2.1+ds1/libpod/container_internal_linux_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_internal_linux_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +//go:build linux // +build linux package libpod @@ -7,6 +8,7 @@ "os" "testing" + "github.com/containers/podman/v3/pkg/namespaces" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/stretchr/testify/assert" ) @@ -68,3 +70,30 @@ } assert.Equal(t, group, "567:x:567:567\n") } + +func TestAppendLocalhost(t *testing.T) { + { + c := Container{ + config: &ContainerConfig{ + ContainerNetworkConfig: ContainerNetworkConfig{ + NetMode: namespaces.NetworkMode("slirp4netns"), + }, + }, + } + + assert.Equal(t, "127.0.0.1\tlocalhost\n::1\tlocalhost\n", c.appendLocalhost("")) + assert.Equal(t, "127.0.0.1\tlocalhost", c.appendLocalhost("127.0.0.1\tlocalhost")) + } + { + c := Container{ + config: &ContainerConfig{ + ContainerNetworkConfig: ContainerNetworkConfig{ + NetMode: namespaces.NetworkMode("host"), + }, + }, + } + + assert.Equal(t, "", c.appendLocalhost("")) + assert.Equal(t, "127.0.0.1\tlocalhost", c.appendLocalhost("127.0.0.1\tlocalhost")) + } +} diff -Nru libpod-3.2.1+ds1/libpod/container_internal_unsupported.go libpod-3.4.4+ds1/libpod/container_internal_unsupported.go --- libpod-3.2.1+ds1/libpod/container_internal_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_internal_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -// +build !linux - -package libpod - -import ( - "context" - - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/lookup" - spec "github.com/opencontainers/runtime-spec/specs-go" -) - -func (c *Container) mountSHM(shmOptions string) error { - return define.ErrNotImplemented -} - -func (c *Container) unmountSHM(mount string) error { - return define.ErrNotImplemented -} - -func (c *Container) prepare() error { - return define.ErrNotImplemented -} - -func (c *Container) cleanupNetwork() error { - return define.ErrNotImplemented -} - -func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { - return nil, define.ErrNotImplemented -} - -func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error { - return define.ErrNotImplemented -} - -func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) error { - return define.ErrNotImplemented -} - -func (c *Container) copyOwnerAndPerms(source, dest string) error { - return nil -} - -func (c *Container) getOCICgroupPath() (string, error) { - return "", define.ErrNotImplemented -} - -func (c *Container) cleanupOverlayMounts() error { - return nil -} - -func (c *Container) reloadNetwork() error { - return define.ErrNotImplemented -} - -func (c *Container) getUserOverrides() *lookup.Overrides { - return nil -} diff -Nru libpod-3.2.1+ds1/libpod/container_log.go libpod-3.4.4+ds1/libpod/container_log.go --- libpod-3.2.1+ds1/libpod/container_log.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_log.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,14 +4,23 @@ "context" "fmt" "os" + "time" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/libpod/logs" + "github.com/hpcloud/tail/watch" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +// logDrivers stores the currently available log drivers, do not modify +var logDrivers []string + +func init() { + logDrivers = append(logDrivers, define.KubernetesLogging, define.NoLogging) +} + // Log is a runtime function that can read one or more container logs. func (r *Runtime) Log(ctx context.Context, containers []*Container, options *logs.LogOptions, logChannel chan *logs.LogLine) error { for _, ctr := range containers { @@ -54,7 +63,7 @@ for _, nll := range tailLog { nll.CID = c.ID() nll.CName = c.Name() - if nll.Since(options.Since) { + if nll.Since(options.Since) && nll.Until(options.Until) { logChannel <- nll } } @@ -74,7 +83,7 @@ } nll, err := logs.NewLogLine(line.Text) if err != nil { - logrus.Error(err) + logrus.Errorf("Error getting new log line: %v", err) continue } if nll.Partial() { @@ -86,25 +95,30 @@ } nll.CID = c.ID() nll.CName = c.Name() - if nll.Since(options.Since) { + if nll.Since(options.Since) && nll.Until(options.Until) { logChannel <- nll } } }() // Check if container is still running or paused if options.Follow { + // If the container isn't running or if we encountered an error + // getting its state, instruct the logger to read the file + // until EOF. state, err := c.State() if err != nil || state != define.ContainerStateRunning { - // If the container isn't running or if we encountered - // an error getting its state, instruct the logger to - // read the file until EOF. - tailError := t.StopAtEOF() - if tailError != nil && fmt.Sprintf("%v", tailError) != "tail: stop at eof" { - logrus.Error(tailError) - } - if errors.Cause(err) != define.ErrNoSuchCtr { - logrus.Error(err) + if err != nil && errors.Cause(err) != define.ErrNoSuchCtr { + logrus.Errorf("Error getting container state: %v", err) } + go func() { + // Make sure to wait at least for the poll duration + // before stopping the file logger (see #10675). + time.Sleep(watch.POLL_DURATION) + tailError := t.StopAtEOF() + if tailError != nil && tailError.Error() != "tail: stop at eof" { + logrus.Errorf("Error stopping logger: %v", tailError) + } + }() return nil } @@ -124,9 +138,12 @@ // Now wait for the died event and signal to finish // reading the log until EOF. <-eventChannel + // Make sure to wait at least for the poll duration + // before stopping the file logger (see #10675). + time.Sleep(watch.POLL_DURATION) tailError := t.StopAtEOF() if tailError != nil && fmt.Sprintf("%v", tailError) != "tail: stop at eof" { - logrus.Error(tailError) + logrus.Errorf("Error stopping logger: %v", tailError) } }() } diff -Nru libpod-3.2.1+ds1/libpod/container_log_linux.go libpod-3.4.4+ds1/libpod/container_log_linux.go --- libpod-3.2.1+ds1/libpod/container_log_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_log_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,14 +6,14 @@ import ( "context" "fmt" - "io" - "math" "strings" "time" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/libpod/logs" - journal "github.com/coreos/go-systemd/v22/sdjournal" + "github.com/coreos/go-systemd/v22/journal" + "github.com/coreos/go-systemd/v22/sdjournal" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -24,122 +24,236 @@ // journaldLogErr is the journald priority signifying stderr journaldLogErr = "3" - - // bufLen is the length of the buffer to read from a k8s-file - // formatted log line - // let's set it as 2k just to be safe if k8s-file format ever changes - bufLen = 16384 ) +func init() { + logDrivers = append(logDrivers, define.JournaldLogging) +} + +// initializeJournal will write an empty string to the journal +// when a journal is created. This solves a problem when people +// attempt to read logs from a container that has never had stdout/stderr +func (c *Container) initializeJournal(ctx context.Context) error { + m := make(map[string]string) + m["SYSLOG_IDENTIFIER"] = "podman" + m["PODMAN_ID"] = c.ID() + history := events.History + m["PODMAN_EVENT"] = history.String() + container := events.Container + m["PODMAN_TYPE"] = container.String() + m["PODMAN_TIME"] = time.Now().Format(time.RFC3339Nano) + return journal.Send("", journal.PriInfo, m) +} + func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOptions, logChannel chan *logs.LogLine) error { - var config journal.JournalReaderConfig - if options.Tail < 0 { - config.NumFromTail = 0 - } else if options.Tail == 0 { - config.NumFromTail = math.MaxUint64 - } else { - config.NumFromTail = uint64(options.Tail) - } - if options.Multi { - config.Formatter = journalFormatterWithID - } else { - config.Formatter = journalFormatter - } - defaultTime := time.Time{} - if options.Since != defaultTime { - // coreos/go-systemd/sdjournal doesn't correctly handle requests for data in the future - // return nothing instead of falsely printing - if time.Now().Before(options.Since) { - return nil - } - // coreos/go-systemd/sdjournal expects a negative time.Duration for times in the past - config.Since = -time.Since(options.Since) + // We need the container's events in the same journal to guarantee + // consistency, see #10323. + if options.Follow && c.runtime.config.Engine.EventsLogger != "journald" { + return errors.Errorf("using --follow with the journald --log-driver but without the journald --events-backend (%s) is not supported", c.runtime.config.Engine.EventsLogger) } - config.Matches = append(config.Matches, journal.Match{ - Field: "CONTAINER_ID_FULL", - Value: c.ID(), - }) - options.WaitGroup.Add(1) - r, err := journal.NewJournalReader(config) + journal, err := sdjournal.NewJournal() if err != nil { return err } - if r == nil { - return errors.Errorf("journal reader creation failed") + // While logs are written to the `logChannel`, we inspect each event + // and stop once the container has died. Having logs and events in one + // stream prevents a race condition that we faced in #10323. + + // Add the filters for events. + match := sdjournal.Match{Field: "SYSLOG_IDENTIFIER", Value: "podman"} + if err := journal.AddMatch(match.String()); err != nil { + return errors.Wrapf(err, "adding filter to journald logger: %v", match) } - if options.Tail == math.MaxInt64 { - r.Rewind() + match = sdjournal.Match{Field: "PODMAN_ID", Value: c.ID()} + if err := journal.AddMatch(match.String()); err != nil { + return errors.Wrapf(err, "adding filter to journald logger: %v", match) } - state, err := c.State() - if err != nil { + + // Add the filter for logs. Note the disjunction so that we match + // either the events or the logs. + if err := journal.AddDisjunction(); err != nil { + return errors.Wrap(err, "adding filter disjunction to journald logger") + } + match = sdjournal.Match{Field: "CONTAINER_ID_FULL", Value: c.ID()} + if err := journal.AddMatch(match.String()); err != nil { + return errors.Wrapf(err, "adding filter to journald logger: %v", match) + } + + if err := journal.SeekHead(); err != nil { return err } + // API requires Next() immediately after SeekHead(). + if _, err := journal.Next(); err != nil { + return errors.Wrap(err, "next journal") + } + + // API requires a next|prev before getting a cursor. + if _, err := journal.Previous(); err != nil { + return errors.Wrap(err, "previous journal") + } + + // Note that the initial cursor may not yet be ready, so we'll do an + // exponential backoff. + var cursor string + var cursorError error + var containerCouldBeLogging bool + for i := 1; i <= 3; i++ { + cursor, cursorError = journal.GetCursor() + hundreds := 1 + for j := 1; j < i; j++ { + hundreds *= 2 + } + if cursorError != nil { + time.Sleep(time.Duration(hundreds*100) * time.Millisecond) + continue + } + break + } + if cursorError != nil { + return errors.Wrap(cursorError, "initial journal cursor") + } - if options.Follow && state == define.ContainerStateRunning { - go func() { - done := make(chan bool) - until := make(chan time.Time) - go func() { - select { - case <-ctx.Done(): - until <- time.Time{} - case <-done: - // nothing to do anymore - } - }() - go func() { - // FIXME (#10323): we are facing a terrible - // race condition here. At the time the - // container dies and `c.Wait()` has returned, - // we may not have received all journald logs. - // So far there is no other way than waiting - // for a second. Ultimately, `r.Follow` is - // racy and we may have to implement our custom - // logic here. - c.Wait(ctx) - time.Sleep(time.Second) - until <- time.Time{} - }() - follower := journaldFollowBuffer{logChannel, options.Multi} - err := r.Follow(until, follower) - if err != nil { - logrus.Debugf(err.Error()) - } - r.Close() + options.WaitGroup.Add(1) + go func() { + defer func() { options.WaitGroup.Done() - done <- true - return + if err := journal.Close(); err != nil { + logrus.Errorf("Unable to close journal: %v", err) + } }() - return nil - } - go func() { - bytes := make([]byte, bufLen) - // /me complains about no do-while in go - ec, err := r.Read(bytes) - for ec != 0 && err == nil { - // because we are reusing bytes, we need to make - // sure the old data doesn't get into the new line - bytestr := string(bytes[:ec]) - logLine, err2 := logs.NewJournaldLogLine(bytestr, options.Multi) - if err2 != nil { - logrus.Error(err2) + tailQueue := []*logs.LogLine{} // needed for options.Tail + doTail := options.Tail >= 0 + doTailFunc := func() { + // Flush *once* we hit the end of the journal. + startIndex := int64(len(tailQueue)) + outputLines := int64(0) + for startIndex > 0 && outputLines < options.Tail { + startIndex-- + for startIndex > 0 && tailQueue[startIndex].Partial() { + startIndex-- + } + outputLines++ + } + for i := startIndex; i < int64(len(tailQueue)); i++ { + logChannel <- tailQueue[i] + } + tailQueue = nil + doTail = false + } + lastReadCursor := "" + partial := "" + for { + select { + case <-ctx.Done(): + // Remote client may have closed/lost the connection. + return + default: + // Fallthrough + } + + if lastReadCursor != "" { + // Advance to next entry if we read this one. + if _, err := journal.Next(); err != nil { + logrus.Errorf("Failed to move journal cursor to next entry: %v", err) + return + } + } + + // Fetch the location of this entry, presumably either + // the one that follows the last one we read, or that + // same last one, if there is no next entry (yet). + cursor, err = journal.GetCursor() + if err != nil { + logrus.Errorf("Failed to get journal cursor: %v", err) + return + } + + // Hit the end of the journal (so far?). + if cursor == lastReadCursor { + if doTail { + doTailFunc() + } + // Unless we follow, quit. + if !options.Follow || !containerCouldBeLogging { + return + } + // Sleep until something's happening on the journal. + journal.Wait(sdjournal.IndefiniteWait) + continue + } + lastReadCursor = cursor + + // Read the journal entry. + entry, err := journal.GetEntry() + if err != nil { + logrus.Errorf("Failed to get journal entry: %v", err) + return + } + + entryTime := time.Unix(0, int64(entry.RealtimeTimestamp)*int64(time.Microsecond)) + if (entryTime.Before(options.Since) && !options.Since.IsZero()) || (entryTime.After(options.Until) && !options.Until.IsZero()) { + continue + } + // If we're reading an event and the container exited/died, + // then we're done and can return. + event, ok := entry.Fields["PODMAN_EVENT"] + if ok { + status, err := events.StringToStatus(event) + if err != nil { + logrus.Errorf("Failed to translate event: %v", err) + return + } + switch status { + case events.History, events.Init, events.Start, events.Restart: + containerCouldBeLogging = true + case events.Exited: + containerCouldBeLogging = false + if doTail { + doTailFunc() + } + } + continue + } + + var message string + var formatError error + + if options.Multi { + message, formatError = journalFormatterWithID(entry) + } else { + message, formatError = journalFormatter(entry) + } + + if formatError != nil { + logrus.Errorf("Failed to parse journald log entry: %v", err) + return + } + + logLine, err := logs.NewJournaldLogLine(message, options.Multi) + if err != nil { + logrus.Errorf("Failed parse log line: %v", err) + return + } + if logLine.Partial() { + partial += logLine.Msg + continue + } + logLine.Msg = partial + logLine.Msg + partial = "" + if doTail { + tailQueue = append(tailQueue, logLine) continue } logChannel <- logLine - ec, err = r.Read(bytes) - } - if err != nil && err != io.EOF { - logrus.Error(err) } - r.Close() - options.WaitGroup.Done() }() + return nil } -func journalFormatterWithID(entry *journal.JournalEntry) (string, error) { +func journalFormatterWithID(entry *sdjournal.JournalEntry) (string, error) { output, err := formatterPrefix(entry) if err != nil { return "", err @@ -162,7 +276,7 @@ return output, nil } -func journalFormatter(entry *journal.JournalEntry) (string, error) { +func journalFormatter(entry *sdjournal.JournalEntry) (string, error) { output, err := formatterPrefix(entry) if err != nil { return "", err @@ -176,7 +290,7 @@ return output, nil } -func formatterPrefix(entry *journal.JournalEntry) (string, error) { +func formatterPrefix(entry *sdjournal.JournalEntry) (string, error) { usec := entry.RealtimeTimestamp tsString := time.Unix(0, int64(usec)*int64(time.Microsecond)).Format(logs.LogTimeFormat) output := fmt.Sprintf("%s ", tsString) @@ -202,7 +316,7 @@ return output, nil } -func formatterMessage(entry *journal.JournalEntry) (string, error) { +func formatterMessage(entry *sdjournal.JournalEntry) (string, error) { // Finally, append the message msg, ok := entry.Fields["MESSAGE"] if !ok { @@ -211,18 +325,3 @@ msg = strings.TrimSuffix(msg, "\n") return msg, nil } - -type journaldFollowBuffer struct { - logChannel chan *logs.LogLine - withID bool -} - -func (f journaldFollowBuffer) Write(p []byte) (int, error) { - bytestr := string(p) - logLine, err := logs.NewJournaldLogLine(bytestr, f.withID) - if err != nil { - return -1, err - } - f.logChannel <- logLine - return len(p), nil -} diff -Nru libpod-3.2.1+ds1/libpod/container_log_unsupported.go libpod-3.4.4+ds1/libpod/container_log_unsupported.go --- libpod-3.2.1+ds1/libpod/container_log_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_log_unsupported.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,3 +13,7 @@ func (c *Container) readFromJournal(_ context.Context, _ *logs.LogOptions, _ chan *logs.LogLine) error { return errors.Wrapf(define.ErrOSNotSupported, "Journald logging only enabled with systemd on linux") } + +func (c *Container) initializeJournal(ctx context.Context) error { + return errors.Wrapf(define.ErrOSNotSupported, "Journald logging only enabled with systemd on linux") +} diff -Nru libpod-3.2.1+ds1/libpod/container_path_resolution.go libpod-3.4.4+ds1/libpod/container_path_resolution.go --- libpod-3.2.1+ds1/libpod/container_path_resolution.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_path_resolution.go 2021-12-26 00:45:51.000000000 +0000 @@ -112,7 +112,7 @@ func findVolume(c *Container, containerPath string) (*Volume, error) { runtime := c.Runtime() cleanedContainerPath := filepath.Clean(containerPath) - for _, vol := range c.Config().NamedVolumes { + for _, vol := range c.config.NamedVolumes { if cleanedContainerPath == filepath.Clean(vol.Dest) { return runtime.GetVolume(vol.Name) } @@ -124,7 +124,7 @@ // Volume's destination. func isPathOnVolume(c *Container, containerPath string) bool { cleanedContainerPath := filepath.Clean(containerPath) - for _, vol := range c.Config().NamedVolumes { + for _, vol := range c.config.NamedVolumes { if cleanedContainerPath == filepath.Clean(vol.Dest) { return true } @@ -141,7 +141,7 @@ // path of a Mount. Returns a matching Mount or nil. func findBindMount(c *Container, containerPath string) *specs.Mount { cleanedPath := filepath.Clean(containerPath) - for _, m := range c.Config().Spec.Mounts { + for _, m := range c.config.Spec.Mounts { if m.Type != "bind" { continue } @@ -157,11 +157,11 @@ // Mount's destination. func isPathOnBindMount(c *Container, containerPath string) bool { cleanedContainerPath := filepath.Clean(containerPath) - for _, m := range c.Config().Spec.Mounts { + for _, m := range c.config.Spec.Mounts { if cleanedContainerPath == filepath.Clean(m.Destination) { return true } - for dest := m.Destination; dest != "/"; dest = filepath.Dir(dest) { + for dest := m.Destination; dest != "/" && dest != "."; dest = filepath.Dir(dest) { if cleanedContainerPath == dest { return true } diff -Nru libpod-3.2.1+ds1/libpod/container_stat_unsupported.go libpod-3.4.4+ds1/libpod/container_stat_unsupported.go --- libpod-3.2.1+ds1/libpod/container_stat_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_stat_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -// +build !linux - -package libpod - -import ( - "context" - - "github.com/containers/podman/v3/libpod/define" -) - -func (c *Container) stat(ctx context.Context, containerMountPoint string, containerPath string) (*define.FileInfo, string, string, error) { - return nil, "", "", nil -} diff -Nru libpod-3.2.1+ds1/libpod/container_top_linux.go libpod-3.4.4+ds1/libpod/container_top_linux.go --- libpod-3.2.1+ds1/libpod/container_top_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_top_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,6 +4,7 @@ import ( "bufio" + "fmt" "os" "strconv" "strings" @@ -11,6 +12,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/psgo" + "github.com/google/shlex" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -47,11 +49,25 @@ if psgoErr == nil { return output, nil } - if errors.Cause(psgoErr) != psgo.ErrUnknownDescriptor { + if !errors.Is(psgoErr, psgo.ErrUnknownDescriptor) { return nil, psgoErr } - output, err = c.execPS(descriptors) + // Note that the descriptors to ps(1) must be shlexed (see #12452). + psDescriptors := []string{} + for _, d := range descriptors { + shSplit, err := shlex.Split(d) + if err != nil { + return nil, fmt.Errorf("parsing ps args: %v", err) + } + for _, s := range shSplit { + if s != "" { + psDescriptors = append(psDescriptors, s) + } + } + } + + output, err = c.execPS(psDescriptors) if err != nil { return nil, errors.Wrapf(err, "error executing ps(1) in the container") } diff -Nru libpod-3.2.1+ds1/libpod/container_top_unsupported.go libpod-3.4.4+ds1/libpod/container_top_unsupported.go --- libpod-3.2.1+ds1/libpod/container_top_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_top_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -// +build !linux - -package libpod - -import "github.com/containers/podman/v3/libpod/define" - -// Top gathers statistics about the running processes in a container. It returns a -// []string for output -func (c *Container) Top(descriptors []string) ([]string, error) { - return nil, define.ErrNotImplemented -} - -// GetContainerPidInformation returns process-related data of all processes in -// the container. The output data can be controlled via the `descriptors` -// argument which expects format descriptors and supports all AIXformat -// descriptors of ps (1) plus some additional ones to for instance inspect the -// set of effective capabilities. Each element in the returned string slice -// is a tab-separated string. -// -// For more details, please refer to github.com/containers/psgo. -func (c *Container) GetContainerPidInformation(descriptors []string) ([]string, error) { - return nil, define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/container_unsupported.go libpod-3.4.4+ds1/libpod/container_unsupported.go --- libpod-3.2.1+ds1/libpod/container_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -// +build !linux - -package libpod - -type containerPlatformState struct{} diff -Nru libpod-3.2.1+ds1/libpod/container_validate.go libpod-3.4.4+ds1/libpod/container_validate.go --- libpod-3.2.1+ds1/libpod/container_validate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/container_validate.go 2021-12-26 00:45:51.000000000 +0000 @@ -132,5 +132,10 @@ return errors.Wrapf(define.ErrInvalidArg, "please set User explicitly via WithUser() instead of in OCI spec directly") } + // Init-ctrs must be used inside a Pod. Check if a init container type is + // passed and if no pod is passed + if len(c.config.InitContainerType) > 0 && len(c.config.Pod) < 1 { + return errors.Wrap(define.ErrInvalidArg, "init containers must be created in a pod") + } return nil } diff -Nru libpod-3.2.1+ds1/libpod/define/config.go libpod-3.4.4+ds1/libpod/define/config.go --- libpod-3.2.1+ds1/libpod/define/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -87,3 +87,6 @@ // DefaultRlimitValue is the value set by default for nofile and nproc const RLimitDefaultValue = uint64(1048576) + +// BindMountPrefix distinguishes its annotations from others +const BindMountPrefix = "bind-mount-options:" diff -Nru libpod-3.2.1+ds1/libpod/define/container.go libpod-3.4.4+ds1/libpod/define/container.go --- libpod-3.2.1+ds1/libpod/define/container.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/container.go 2021-12-26 00:45:51.000000000 +0000 @@ -26,3 +26,13 @@ RestartPolicyOnFailure: RestartPolicyOnFailure, RestartPolicyUnlessStopped: RestartPolicyUnlessStopped, } + +// InitContainerTypes +const ( + // AlwaysInitContainer is an init container than runs on each + // pod start (including restart) + AlwaysInitContainer = "always" + // OneShotInitContainer is a container that only runs as init once + // and is then deleted. + OneShotInitContainer = "once" +) diff -Nru libpod-3.2.1+ds1/libpod/define/container_inspect.go libpod-3.4.4+ds1/libpod/define/container_inspect.go --- libpod-3.2.1+ds1/libpod/define/container_inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/container_inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -189,20 +189,22 @@ // Docker, but here we see more fields that are unused (nonsensical in the // context of Libpod). type InspectContainerState struct { - OciVersion string `json:"OciVersion"` - Status string `json:"Status"` - Running bool `json:"Running"` - Paused bool `json:"Paused"` - Restarting bool `json:"Restarting"` // TODO - OOMKilled bool `json:"OOMKilled"` - Dead bool `json:"Dead"` - Pid int `json:"Pid"` - ConmonPid int `json:"ConmonPid,omitempty"` - ExitCode int32 `json:"ExitCode"` - Error string `json:"Error"` // TODO - StartedAt time.Time `json:"StartedAt"` - FinishedAt time.Time `json:"FinishedAt"` - Healthcheck HealthCheckResults `json:"Healthcheck,omitempty"` + OciVersion string `json:"OciVersion"` + Status string `json:"Status"` + Running bool `json:"Running"` + Paused bool `json:"Paused"` + Restarting bool `json:"Restarting"` // TODO + OOMKilled bool `json:"OOMKilled"` + Dead bool `json:"Dead"` + Pid int `json:"Pid"` + ConmonPid int `json:"ConmonPid,omitempty"` + ExitCode int32 `json:"ExitCode"` + Error string `json:"Error"` // TODO + StartedAt time.Time `json:"StartedAt"` + FinishedAt time.Time `json:"FinishedAt"` + Healthcheck HealthCheckResults `json:"Healthcheck,omitempty"` + Checkpointed bool `json:"Checkpointed,omitempty"` + CgroupPath string `json:"CgroupPath,omitempty"` } // HealthCheckResults describes the results/logs from a healthcheck @@ -713,13 +715,16 @@ Data map[string]string `json:"Data"` } -// InspectHostPort provides information on a port on the host that a container's -// port is bound to. +// InspectSecret contains information on secrets mounted inside the container type InspectSecret struct { - // IP on the host we are bound to. "" if not specified (binding to all - // IPs). + // Name is the name of the secret Name string `json:"Name"` - // Port on the host we are bound to. No special formatting - just an - // integer stuffed into a string. + // ID is the ID of the secret ID string `json:"ID"` + // ID is the UID of the mounted secret file + UID uint32 `json:"UID"` + // ID is the GID of the mounted secret file + GID uint32 `json:"GID"` + // ID is the ID of the mode of the mounted secret file + Mode uint32 `json:"Mode"` } diff -Nru libpod-3.2.1+ds1/libpod/define/containerstate.go libpod-3.4.4+ds1/libpod/define/containerstate.go --- libpod-3.2.1+ds1/libpod/define/containerstate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/containerstate.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,10 @@ package define -import "github.com/pkg/errors" +import ( + "time" + + "github.com/pkg/errors" +) // ContainerStatus represents the current state of a container type ContainerStatus int @@ -120,12 +124,14 @@ // ContainerStats contains the statistics information for a running container type ContainerStats struct { + AvgCPU float64 ContainerID string Name string PerCPU []uint64 CPU float64 CPUNano uint64 CPUSystemNano uint64 + DataPoints int64 SystemNano uint64 MemUsage uint64 MemLimit uint64 @@ -135,4 +141,6 @@ BlockInput uint64 BlockOutput uint64 PIDs uint64 + UpTime time.Duration + Duration uint64 } diff -Nru libpod-3.2.1+ds1/libpod/define/diff.go libpod-3.4.4+ds1/libpod/define/diff.go --- libpod-3.2.1+ds1/libpod/define/diff.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,26 @@ +package define + +// extra type to use as enum +type DiffType uint8 + +const ( + // only diff containers + DiffContainer DiffType = 1 << iota + // only diff images + DiffImage + // diff both containers and images + DiffAll DiffType = 0b11111111 +) + +func (d DiffType) String() string { + switch d { + case DiffAll: + return "all" + case DiffContainer: + return "container" + case DiffImage: + return "image" + default: + return "unknown" + } +} diff -Nru libpod-3.2.1+ds1/libpod/define/info.go libpod-3.4.4+ds1/libpod/define/info.go --- libpod-3.2.1+ds1/libpod/define/info.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/info.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ Host *HostInfo `json:"host"` Store *StoreInfo `json:"store"` Registries map[string]interface{} `json:"registries"` + Plugins Plugins `json:"plugins"` Version Version `json:"version"` } @@ -35,6 +36,7 @@ Hostname string `json:"hostname"` IDMappings IDMappings `json:"idMappings,omitempty"` Kernel string `json:"kernel"` + LogDriver string `json:"logDriver"` MemFree int64 `json:"memFree"` MemTotal int64 `json:"memTotal"` OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"` @@ -76,7 +78,9 @@ // for libpod type DistributionInfo struct { Distribution string `json:"distribution"` + Variant string `json:"variant,omitempty"` Version string `json:"version"` + Codename string `json:"codename,omitempty"` } // ConmonInfo describes the conmon executable being used @@ -123,3 +127,11 @@ Running int `json:"running"` Stopped int `json:"stopped"` } + +type Plugins struct { + Volume []string `json:"volume"` + Network []string `json:"network"` + Log []string `json:"log"` + // FIXME what should we do with Authorization, docker seems to return nothing by default + // Authorization []string `json:"authorization"` +} diff -Nru libpod-3.2.1+ds1/libpod/define/pod_inspect.go libpod-3.4.4+ds1/libpod/define/pod_inspect.go --- libpod-3.2.1+ds1/libpod/define/pod_inspect.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/define/pod_inspect.go 2021-12-26 00:45:51.000000000 +0000 @@ -91,6 +91,10 @@ Networks []string // NetworkOptions are additional options for each network NetworkOptions map[string][]string + // Pid is the PID namespace mode of the pod's infra container + PidNS string `json:"pid_ns,omitempty"` + // UserNS is the usernamespace that all the containers in the pod will join. + UserNS string `json:"userns,omitempty"` } // InspectPodContainerInfo contains information on a container in a pod. diff -Nru libpod-3.2.1+ds1/libpod/diff.go libpod-3.4.4+ds1/libpod/diff.go --- libpod-3.2.1+ds1/libpod/diff.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,33 +1,35 @@ package libpod import ( - "github.com/containers/common/libimage" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/layers" "github.com/containers/storage/pkg/archive" "github.com/pkg/errors" ) -var containerMounts = map[string]bool{ +var initInodes = map[string]bool{ "/dev": true, "/etc/hostname": true, "/etc/hosts": true, "/etc/resolv.conf": true, "/proc": true, "/run": true, + "/run/notify": true, "/run/.containerenv": true, "/run/secrets": true, "/sys": true, + "/etc/mtab": true, } // GetDiff returns the differences between the two images, layers, or containers -func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) { - toLayer, err := r.getLayerID(to) +func (r *Runtime) GetDiff(from, to string, diffType define.DiffType) ([]archive.Change, error) { + toLayer, err := r.getLayerID(to, diffType) if err != nil { return nil, err } fromLayer := "" if from != "" { - fromLayer, err = r.getLayerID(from) + fromLayer, err = r.getLayerID(from, diffType) if err != nil { return nil, err } @@ -36,7 +38,7 @@ changes, err := r.store.Changes(fromLayer, toLayer) if err == nil { for _, c := range changes { - if containerMounts[c.Path] { + if initInodes[c.Path] { continue } rchanges = append(rchanges, c) @@ -48,25 +50,30 @@ // GetLayerID gets a full layer id given a full or partial id // If the id matches a container or image, the id of the top layer is returned // If the id matches a layer, the top layer id is returned -func (r *Runtime) getLayerID(id string) (string, error) { - var toLayer string - toImage, _, err := r.libimageRuntime.LookupImage(id, &libimage.LookupImageOptions{IgnorePlatform: true}) - if err == nil { - return toImage.TopLayer(), nil +func (r *Runtime) getLayerID(id string, diffType define.DiffType) (string, error) { + var lastErr error + if diffType&define.DiffImage == define.DiffImage { + toImage, _, err := r.libimageRuntime.LookupImage(id, nil) + if err == nil { + return toImage.TopLayer(), nil + } + lastErr = err } - targetID, err := r.store.Lookup(id) - if err != nil { - targetID = id + if diffType&define.DiffContainer == define.DiffContainer { + toCtr, err := r.store.Container(id) + if err == nil { + return toCtr.LayerID, nil + } + lastErr = err } - toCtr, err := r.store.Container(targetID) - if err != nil { - toLayer, err = layers.FullID(r.store, targetID) - if err != nil { - return "", errors.Errorf("layer, image, or container %s does not exist", id) + + if diffType == define.DiffAll { + toLayer, err := layers.FullID(r.store, id) + if err == nil { + return toLayer, nil } - } else { - toLayer = toCtr.LayerID + lastErr = err } - return toLayer, nil + return "", errors.Wrapf(lastErr, "%s not found", id) } diff -Nru libpod-3.2.1+ds1/libpod/events/config.go libpod-3.4.4+ds1/libpod/events/config.go --- libpod-3.2.1+ds1/libpod/events/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/events/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -127,6 +127,8 @@ Create Status = "create" // Exec ... Exec Status = "exec" + // ExecDied indicates that an exec session in a container died. + ExecDied Status = "exec_died" // Exited indicates that a container's process died Exited Status = "died" // Export ... diff -Nru libpod-3.2.1+ds1/libpod/events/events.go libpod-3.4.4+ds1/libpod/events/events.go --- libpod-3.2.1+ds1/libpod/events/events.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/events/events.go 2021-12-26 00:45:51.000000000 +0000 @@ -149,6 +149,8 @@ return Create, nil case Exec.String(): return Exec, nil + case ExecDied.String(): + return ExecDied, nil case Exited.String(): return Exited, nil case Export.String(): diff -Nru libpod-3.2.1+ds1/libpod/events/filters.go libpod-3.4.4+ds1/libpod/events/filters.go --- libpod-3.2.1+ds1/libpod/events/filters.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/events/filters.go 2021-12-26 00:45:51.000000000 +0000 @@ -135,7 +135,7 @@ } if len(since) > 0 { - timeSince, err := util.ParseInputTime(since) + timeSince, err := util.ParseInputTime(since, true) if err != nil { return nil, errors.Wrapf(err, "unable to convert since time of %s", since) } @@ -144,7 +144,7 @@ } if len(until) > 0 { - timeUntil, err := util.ParseInputTime(until) + timeUntil, err := util.ParseInputTime(until, false) if err != nil { return nil, errors.Wrapf(err, "unable to convert until time of %s", until) } diff -Nru libpod-3.2.1+ds1/libpod/events/journal_linux.go libpod-3.4.4+ds1/libpod/events/journal_linux.go --- libpod-3.2.1+ds1/libpod/events/journal_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/events/journal_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -73,13 +73,15 @@ if err != nil { return errors.Wrapf(err, "failed to parse event filters") } + var untilTime time.Time if len(options.Until) > 0 { - untilTime, err = util.ParseInputTime(options.Until) + untilTime, err = util.ParseInputTime(options.Until, false) if err != nil { return err } } + j, err := sdjournal.NewJournal() if err != nil { return err diff -Nru libpod-3.2.1+ds1/libpod/events/logfile.go libpod-3.4.4+ds1/libpod/events/logfile.go --- libpod-3.2.1+ds1/libpod/events/logfile.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/events/logfile.go 2021-12-26 00:45:51.000000000 +0000 @@ -53,7 +53,7 @@ return err } if len(options.Until) > 0 { - untilTime, err := util.ParseInputTime(options.Until) + untilTime, err := util.ParseInputTime(options.Until, false) if err != nil { return err } diff -Nru libpod-3.2.1+ds1/libpod/events.go libpod-3.4.4+ds1/libpod/events.go --- libpod-3.2.1+ds1/libpod/events.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/events.go 2021-12-26 00:45:51.000000000 +0000 @@ -46,7 +46,22 @@ e.Type = events.Container e.ContainerExitCode = int(exitCode) if err := c.runtime.eventer.Write(e); err != nil { - logrus.Errorf("unable to write pod event: %q", err) + logrus.Errorf("unable to write container exited event: %q", err) + } +} + +// newExecDiedEvent creates a new event for an exec session's death +func (c *Container) newExecDiedEvent(sessionID string, exitCode int) { + e := events.NewEvent(events.ExecDied) + e.ID = c.ID() + e.Name = c.Name() + e.Image = c.config.RootfsImageName + e.Type = events.Container + e.ContainerExitCode = exitCode + e.Attributes = make(map[string]string) + e.Attributes["execID"] = sessionID + if err := c.runtime.eventer.Write(e); err != nil { + logrus.Errorf("unable to write exec died event: %q", err) } } @@ -154,3 +169,25 @@ // return the last element in the slice return containerEvents[len(containerEvents)-1], nil } + +// GetExecDiedEvent takes a container name or ID, exec session ID, and returns +// that exec session's Died event (if it has already occurred). +func (r *Runtime) GetExecDiedEvent(ctx context.Context, nameOrID, execSessionID string) (*events.Event, error) { + filters := []string{ + fmt.Sprintf("container=%s", nameOrID), + "event=exec_died", + "type=container", + fmt.Sprintf("label=execID=%s", execSessionID), + } + + containerEvents, err := r.GetEvents(ctx, filters) + if err != nil { + return nil, err + } + // There *should* only be one event maximum. + // But... just in case... let's not blow up if there's more than one. + if len(containerEvents) < 1 { + return nil, errors.Wrapf(events.ErrEventNotFound, "exec died event for session %s (container %s) not found", execSessionID, nameOrID) + } + return containerEvents[len(containerEvents)-1], nil +} diff -Nru libpod-3.2.1+ds1/libpod/healthcheck.go libpod-3.4.4+ds1/libpod/healthcheck.go --- libpod-3.2.1+ds1/libpod/healthcheck.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/healthcheck.go 2021-12-26 00:45:51.000000000 +0000 @@ -162,7 +162,7 @@ // updatedHealthCheckStatus updates the health status of the container // in the healthcheck log func (c *Container) updateHealthStatus(status string) error { - healthCheck, err := c.GetHealthCheckLog() + healthCheck, err := c.getHealthCheckLog() if err != nil { return err } @@ -176,7 +176,7 @@ // UpdateHealthCheckLog parses the health check results and writes the log func (c *Container) updateHealthCheckLog(hcl define.HealthCheckLog, inStartPeriod bool) error { - healthCheck, err := c.GetHealthCheckLog() + healthCheck, err := c.getHealthCheckLog() if err != nil { return err } @@ -213,10 +213,11 @@ return filepath.Join(filepath.Dir(c.state.RunDir), "healthcheck.log") } -// GetHealthCheckLog returns HealthCheck results by reading the container's +// getHealthCheckLog returns HealthCheck results by reading the container's // health check log file. If the health check log file does not exist, then // an empty healthcheck struct is returned -func (c *Container) GetHealthCheckLog() (define.HealthCheckResults, error) { +// The caller should lock the container before this function is called. +func (c *Container) getHealthCheckLog() (define.HealthCheckResults, error) { var healthCheck define.HealthCheckResults if _, err := os.Stat(c.healthCheckLogPath()); os.IsNotExist(err) { return healthCheck, nil @@ -236,7 +237,12 @@ if !c.HasHealthCheck() { return "", errors.Errorf("container %s has no defined healthcheck", c.ID()) } - results, err := c.GetHealthCheckLog() + c.lock.Lock() + defer c.lock.Unlock() + if err := c.syncContainer(); err != nil { + return "", err + } + results, err := c.getHealthCheckLog() if err != nil { return "", errors.Wrapf(err, "unable to get healthcheck log for %s", c.ID()) } diff -Nru libpod-3.2.1+ds1/libpod/healthcheck_unsupported.go libpod-3.4.4+ds1/libpod/healthcheck_unsupported.go --- libpod-3.2.1+ds1/libpod/healthcheck_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/healthcheck_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -// +build !linux - -package libpod - -import "github.com/containers/podman/v3/libpod/define" - -// createTimer systemd timers for healthchecks of a container -func (c *Container) createTimer() error { - return define.ErrNotImplemented -} - -// startTimer starts a systemd timer for the healthchecks -func (c *Container) startTimer() error { - return define.ErrNotImplemented -} - -// removeTimer removes the systemd timer and unit files -// for the container -func (c *Container) removeTimer() error { - return define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/info.go libpod-3.4.4+ds1/libpod/info.go --- libpod-3.2.1+ds1/libpod/info.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/info.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,10 +15,11 @@ "github.com/containers/buildah" "github.com/containers/common/pkg/apparmor" "github.com/containers/common/pkg/seccomp" + "github.com/containers/image/v5/pkg/sysregistriesv2" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/linkmode" + "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/pkg/cgroups" - registries2 "github.com/containers/podman/v3/pkg/registries" "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/storage" "github.com/containers/storage/pkg/system" @@ -49,20 +50,32 @@ } info.Store = storeInfo registries := make(map[string]interface{}) - data, err := registries2.GetRegistriesData() + + sys := r.SystemContext() + data, err := sysregistriesv2.GetRegistries(sys) if err != nil { return nil, errors.Wrapf(err, "error getting registries") } for _, reg := range data { registries[reg.Prefix] = reg } - regs, err := registries2.GetRegistries() + regs, err := sysregistriesv2.UnqualifiedSearchRegistries(sys) if err != nil { return nil, errors.Wrapf(err, "error getting registries") } if len(regs) > 0 { registries["search"] = regs } + volumePlugins := make([]string, 0, len(r.config.Engine.VolumePlugins)+1) + // the local driver always exists + volumePlugins = append(volumePlugins, "local") + for plugin := range r.config.Engine.VolumePlugins { + volumePlugins = append(volumePlugins, plugin) + } + info.Plugins.Volume = volumePlugins + // TODO move this into the new network interface + info.Plugins.Network = []string{network.BridgeNetworkDriver, network.MacVLANNetworkDriver} + info.Plugins.Log = logDrivers info.Registries = registries return &info, nil @@ -113,6 +126,7 @@ Linkmode: linkmode.Linkmode(), CPUs: runtime.NumCPU(), Distribution: hostDistributionInfo, + LogDriver: r.config.Containers.LogDriver, EventLogger: r.eventer.String(), Hostname: host, IDMappings: define.IDMappings{}, @@ -139,19 +153,24 @@ } info.CGroupsVersion = cgroupVersion - if rootless.IsRootless() { - if path, err := exec.LookPath("slirp4netns"); err == nil { - version, err := programVersion(path) - if err != nil { - logrus.Warnf("Failed to retrieve program version for %s: %v", path, err) - } - program := define.SlirpInfo{ - Executable: path, - Package: packageVersion(path), - Version: version, - } - info.Slirp4NetNS = program + slirp4netnsPath := r.config.Engine.NetworkCmdPath + if slirp4netnsPath == "" { + slirp4netnsPath, _ = exec.LookPath("slirp4netns") + } + if slirp4netnsPath != "" { + version, err := programVersion(slirp4netnsPath) + if err != nil { + logrus.Warnf("Failed to retrieve program version for %s: %v", slirp4netnsPath, err) } + program := define.SlirpInfo{ + Executable: slirp4netnsPath, + Package: packageVersion(slirp4netnsPath), + Version: version, + } + info.Slirp4NetNS = program + } + + if rootless.IsRootless() { uidmappings, err := rootless.ReadMappingsProc("/proc/self/uid_map") if err != nil { return nil, errors.Wrapf(err, "error reading uid mappings") @@ -314,7 +333,7 @@ return "", err } f := bytes.Fields(buf) - if len(f) < 2 { + if len(f) < 3 { return string(bytes.TrimSpace(buf)), nil } return string(f[2]), nil @@ -351,9 +370,15 @@ if strings.HasPrefix(l.Text(), "ID=") { dist.Distribution = strings.TrimPrefix(l.Text(), "ID=") } + if strings.HasPrefix(l.Text(), "VARIANT_ID=") { + dist.Variant = strings.Trim(strings.TrimPrefix(l.Text(), "VARIANT_ID="), "\"") + } if strings.HasPrefix(l.Text(), "VERSION_ID=") { dist.Version = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_ID="), "\"") } + if strings.HasPrefix(l.Text(), "VERSION_CODENAME=") { + dist.Codename = strings.Trim(strings.TrimPrefix(l.Text(), "VERSION_CODENAME="), "\"") + } } return dist } diff -Nru libpod-3.2.1+ds1/libpod/kube.go libpod-3.4.4+ds1/libpod/kube.go --- libpod-3.2.1+ds1/libpod/kube.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/kube.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,15 +1,21 @@ package libpod import ( + "context" "fmt" "math/rand" "os" + "reflect" + "sort" "strconv" "strings" "time" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/env" "github.com/containers/podman/v3/pkg/lookup" + "github.com/containers/podman/v3/pkg/namespaces" + "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/opencontainers/runtime-spec/specs-go" @@ -19,18 +25,19 @@ v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" v12 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" ) // GenerateForKube takes a slice of libpod containers and generates // one v1.Pod description that includes just a single container. -func GenerateForKube(ctrs []*Container) (*v1.Pod, error) { +func GenerateForKube(ctx context.Context, ctrs []*Container) (*v1.Pod, error) { // Generate the v1.Pod yaml description - return simplePodWithV1Containers(ctrs) + return simplePodWithV1Containers(ctx, ctrs) } // GenerateForKube takes a slice of libpod containers and generates // one v1.Pod description -func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) { +func (p *Pod) GenerateForKube(ctx context.Context) (*v1.Pod, []v1.ServicePort, error) { // Generate the v1.Pod yaml description var ( ports []v1.ContainerPort //nolint @@ -71,10 +78,14 @@ if err != nil { return nil, servicePorts, err } - servicePorts = containerPortsToServicePorts(ports) - hostNetwork = p.config.InfraContainer.HostNetwork + spState := newServicePortState() + servicePorts, err = spState.containerPortsToServicePorts(ports) + if err != nil { + return nil, servicePorts, err + } + hostNetwork = infraContainer.NetworkMode() == string(namespaces.NetworkMode(specgen.Host)) } - pod, err := p.podWithContainers(allContainers, ports, hostNetwork) + pod, err := p.podWithContainers(ctx, allContainers, ports, hostNetwork) if err != nil { return nil, servicePorts, err } @@ -84,7 +95,7 @@ // so set it at here for _, ctr := range allContainers { if !ctr.IsInfra() { - switch ctr.Config().RestartPolicy { + switch ctr.config.RestartPolicy { case define.RestartPolicyAlways: pod.Spec.RestartPolicy = v1.RestartPolicyAlways case define.RestartPolicyOnFailure: @@ -159,14 +170,92 @@ } } +// YAMLPodSpec represents the same k8s API core PodSpec struct with a small +// change and that is having Containers as a pointer to YAMLContainer. +// Because Go doesn't omit empty struct and we want to omit Status in YAML +// if it's empty. Fixes: GH-11998 +type YAMLPodSpec struct { + v1.PodSpec + Containers []*YAMLContainer `json:"containers"` +} + +// YAMLPod represents the same k8s API core Pod struct with a small +// change and that is having Spec as a pointer to YAMLPodSpec and +// Status as a pointer to k8s API core PodStatus. +// Because Go doesn't omit empty struct and we want to omit Status in YAML +// if it's empty. Fixes: GH-11998 +type YAMLPod struct { + v1.Pod + Spec *YAMLPodSpec `json:"spec,omitempty"` + Status *v1.PodStatus `json:"status,omitempty"` +} + +// YAMLService represents the same k8s API core Service struct with a small +// change and that is having Status as a pointer to k8s API core ServiceStatus. +// Because Go doesn't omit empty struct and we want to omit Status in YAML +// if it's empty. Fixes: GH-11998 +type YAMLService struct { + v1.Service + Status *v1.ServiceStatus `json:"status,omitempty"` +} + +// YAMLContainer represents the same k8s API core Container struct with a small +// change and that is having Resources as a pointer to k8s API core ResourceRequirements. +// Because Go doesn't omit empty struct and we want to omit Status in YAML +// if it's empty. Fixes: GH-11998 +type YAMLContainer struct { + v1.Container + Resources *v1.ResourceRequirements `json:"resources,omitempty"` +} + +// ConvertV1PodToYAMLPod takes k8s API core Pod and returns a pointer to YAMLPod +func ConvertV1PodToYAMLPod(pod *v1.Pod) *YAMLPod { + cs := []*YAMLContainer{} + for _, cc := range pod.Spec.Containers { + var res *v1.ResourceRequirements = nil + if len(cc.Resources.Limits) > 0 || len(cc.Resources.Requests) > 0 { + res = &cc.Resources + } + cs = append(cs, &YAMLContainer{Container: cc, Resources: res}) + } + mpo := &YAMLPod{Pod: *pod} + mpo.Spec = &YAMLPodSpec{PodSpec: (*pod).Spec, Containers: cs} + for _, ctr := range pod.Spec.Containers { + if ctr.SecurityContext == nil || ctr.SecurityContext.SELinuxOptions == nil { + continue + } + selinuxOpts := ctr.SecurityContext.SELinuxOptions + if selinuxOpts.User == "" && selinuxOpts.Role == "" && selinuxOpts.Type == "" && selinuxOpts.Level == "" { + ctr.SecurityContext.SELinuxOptions = nil + } + } + dnsCfg := pod.Spec.DNSConfig + if dnsCfg != nil && (len(dnsCfg.Nameservers)+len(dnsCfg.Searches)+len(dnsCfg.Options) > 0) { + mpo.Spec.DNSConfig = dnsCfg + } + status := pod.Status + if status.Phase != "" || len(status.Conditions) > 0 || + status.Message != "" || status.Reason != "" || + status.NominatedNodeName != "" || status.HostIP != "" || + status.PodIP != "" || status.StartTime != nil || + len(status.InitContainerStatuses) > 0 || len(status.ContainerStatuses) > 0 || status.QOSClass != "" || len(status.EphemeralContainerStatuses) > 0 { + mpo.Status = &status + } + return mpo +} + // GenerateKubeServiceFromV1Pod creates a v1 service object from a v1 pod object -func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) v1.Service { - service := v1.Service{} +func GenerateKubeServiceFromV1Pod(pod *v1.Pod, servicePorts []v1.ServicePort) (YAMLService, error) { + service := YAMLService{} selector := make(map[string]string) selector["app"] = pod.Labels["app"] ports := servicePorts if len(ports) == 0 { - ports = containersToServicePorts(pod.Spec.Containers) + p, err := containersToServicePorts(pod.Spec.Containers) + if err != nil { + return service, err + } + ports = p } serviceSpec := v1.ServiceSpec{ Ports: ports, @@ -180,46 +269,82 @@ APIVersion: pod.TypeMeta.APIVersion, } service.TypeMeta = tm - return service + return service, nil +} + +// servicePortState allows calling containerPortsToServicePorts for a single service +type servicePortState struct { + // A program using the shared math/rand state with the default seed will produce the same sequence of pseudo-random numbers + // for each execution. Use a private RNG state not to interfere with other users. + rng *rand.Rand + usedPorts map[int]struct{} +} + +func newServicePortState() servicePortState { + return servicePortState{ + rng: rand.New(rand.NewSource(time.Now().UnixNano())), + usedPorts: map[int]struct{}{}, + } } // containerPortsToServicePorts takes a slice of containerports and generates a // slice of service ports -func containerPortsToServicePorts(containerPorts []v1.ContainerPort) []v1.ServicePort { +func (state *servicePortState) containerPortsToServicePorts(containerPorts []v1.ContainerPort) ([]v1.ServicePort, error) { sps := make([]v1.ServicePort, 0, len(containerPorts)) for _, cp := range containerPorts { - nodePort := 30000 + rand.Intn(32767-30000+1) + var nodePort int + attempt := 0 + for { + // Legal nodeport range is 30000-32767 + nodePort = 30000 + state.rng.Intn(32767-30000+1) + if _, found := state.usedPorts[nodePort]; !found { + state.usedPorts[nodePort] = struct{}{} + break + } + attempt++ + if attempt >= 100 { + return nil, fmt.Errorf("too many attempts trying to generate a unique NodePort number") + } + } servicePort := v1.ServicePort{ - Protocol: cp.Protocol, - Port: cp.ContainerPort, - NodePort: int32(nodePort), - Name: strconv.Itoa(int(cp.ContainerPort)), + Protocol: cp.Protocol, + Port: cp.ContainerPort, + NodePort: int32(nodePort), + Name: strconv.Itoa(int(cp.ContainerPort)), + TargetPort: intstr.Parse(strconv.Itoa(int(cp.ContainerPort))), } sps = append(sps, servicePort) } - return sps + return sps, nil } // containersToServicePorts takes a slice of v1.Containers and generates an // inclusive list of serviceports to expose -func containersToServicePorts(containers []v1.Container) []v1.ServicePort { - // Without the call to rand.Seed, a program will produce the same sequence of pseudo-random numbers - // for each execution. Legal nodeport range is 30000-32767 - rand.Seed(time.Now().UnixNano()) - +func containersToServicePorts(containers []v1.Container) ([]v1.ServicePort, error) { + state := newServicePortState() sps := make([]v1.ServicePort, 0, len(containers)) for _, ctr := range containers { - sps = append(sps, containerPortsToServicePorts(ctr.Ports)...) + ports, err := state.containerPortsToServicePorts(ctr.Ports) + if err != nil { + return nil, err + } + sps = append(sps, ports...) } - return sps + return sps, nil } -func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort, hostNetwork bool) (*v1.Pod, error) { +func (p *Pod) podWithContainers(ctx context.Context, containers []*Container, ports []v1.ContainerPort, hostNetwork bool) (*v1.Pod, error) { deDupPodVolumes := make(map[string]*v1.Volume) first := true podContainers := make([]v1.Container, 0, len(containers)) + podInitCtrs := []v1.Container{} podAnnotations := make(map[string]string) dnsInfo := v1.PodDNSConfig{} + + // Let's sort the containers in order of created time + // This will ensure that the init containers are defined in the correct order in the kube yaml + sort.Slice(containers, func(i, j int) bool { return containers[i].CreatedTime().Before(containers[j].CreatedTime()) }) + for _, ctr := range containers { if !ctr.IsInfra() { // Convert auto-update labels into kube annotations @@ -227,11 +352,15 @@ podAnnotations[k] = v } - ctr, volumes, _, err := containerToV1Container(ctr) + isInit := ctr.IsInitCtr() + + ctr, volumes, _, annotations, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } - + for k, v := range annotations { + podAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) + } // Since port bindings for the pod are handled by the // infra container, wipe them here. ctr.Ports = nil @@ -239,10 +368,16 @@ // We add the original port declarations from the libpod infra container // to the first kubernetes container description because otherwise we loose // the original container/port bindings. - if first && len(ports) > 0 { + // Add the port configuration to the first regular container or the first + // init container if only init containers have been created in the pod. + if first && len(ports) > 0 && (!isInit || len(containers) == 2) { ctr.Ports = ports first = false } + if isInit { + podInitCtrs = append(podInitCtrs, ctr) + continue + } podContainers = append(podContainers, ctr) // Deduplicate volumes, so if containers in the pod share a volume, it's only // listed in the volumes section once @@ -251,7 +386,7 @@ deDupPodVolumes[vol.Name] = &vol } } else { - _, _, infraDNS, err := containerToV1Container(ctr) + _, _, infraDNS, _, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } @@ -276,13 +411,14 @@ return newPodObject( p.Name(), podAnnotations, + podInitCtrs, podContainers, podVolumes, &dnsInfo, hostNetwork), nil } -func newPodObject(podName string, annotations map[string]string, containers []v1.Container, volumes []v1.Volume, dnsOptions *v1.PodDNSConfig, hostNetwork bool) *v1.Pod { +func newPodObject(podName string, annotations map[string]string, initCtrs, containers []v1.Container, volumes []v1.Volume, dnsOptions *v1.PodDNSConfig, hostNetwork bool) *v1.Pod { tm := v12.TypeMeta{ Kind: "Pod", APIVersion: "v1", @@ -302,11 +438,12 @@ Annotations: annotations, } ps := v1.PodSpec{ - Containers: containers, - Volumes: volumes, - HostNetwork: hostNetwork, + Containers: containers, + HostNetwork: hostNetwork, + InitContainers: initCtrs, + Volumes: volumes, } - if dnsOptions != nil { + if dnsOptions != nil && (len(dnsOptions.Nameservers)+len(dnsOptions.Searches)+len(dnsOptions.Options) > 0) { ps.DNSConfig = dnsOptions } p := v1.Pod{ @@ -319,8 +456,9 @@ // simplePodWithV1Containers is a function used by inspect when kube yaml needs to be generated // for a single container. we "insert" that container description in a pod. -func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { +func simplePodWithV1Containers(ctx context.Context, ctrs []*Container) (*v1.Pod, error) { kubeCtrs := make([]v1.Container, 0, len(ctrs)) + kubeInitCtrs := []v1.Container{} kubeVolumes := make([]v1.Volume, 0) hostNetwork := true podDNS := v1.PodDNSConfig{} @@ -331,16 +469,24 @@ kubeAnnotations[k] = v } + isInit := ctr.IsInitCtr() + if !ctr.HostNetwork() { hostNetwork = false } - kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctr) + kubeCtr, kubeVols, ctrDNS, annotations, err := containerToV1Container(ctx, ctr) if err != nil { return nil, err } - kubeCtrs = append(kubeCtrs, kubeCtr) + for k, v := range annotations { + kubeAnnotations[define.BindMountPrefix+k] = strings.TrimSpace(v) + } + if isInit { + kubeInitCtrs = append(kubeInitCtrs, kubeCtr) + } else { + kubeCtrs = append(kubeCtrs, kubeCtr) + } kubeVolumes = append(kubeVolumes, kubeVols...) - // Combine DNS information in sum'd structure if ctrDNS != nil { // nameservers @@ -377,6 +523,7 @@ return newPodObject( strings.ReplaceAll(ctrs[0].Name(), "_", ""), kubeAnnotations, + kubeInitCtrs, kubeCtrs, kubeVolumes, &podDNS, @@ -385,42 +532,39 @@ // containerToV1Container converts information we know about a libpod container // to a V1.Container specification. -func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, error) { +func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, map[string]string, error) { kubeContainer := v1.Container{} kubeVolumes := []v1.Volume{} + annotations := make(map[string]string) kubeSec, err := generateKubeSecurityContext(c) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } // NOTE: a privileged container mounts all of /dev/*. if !c.Privileged() && len(c.config.Spec.Linux.Devices) > 0 { // TODO Enable when we can support devices and their names - kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) - return kubeContainer, kubeVolumes, nil, errors.Wrapf(define.ErrNotImplemented, "linux devices") + kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.config.Spec.Linux.Devices) + return kubeContainer, kubeVolumes, nil, annotations, errors.Wrapf(define.ErrNotImplemented, "linux devices") } if len(c.config.UserVolumes) > 0 { - volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c) + volumeMounts, volumes, localAnnotations, err := libpodMountsToKubeVolumeMounts(c) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, nil, err } + annotations = localAnnotations kubeContainer.VolumeMounts = volumeMounts kubeVolumes = append(kubeVolumes, volumes...) } - envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env) - if err != nil { - return kubeContainer, kubeVolumes, nil, err - } - portmappings, err := c.PortMappings() if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } ports, err := ocicniPortMappingToContainerPort(portmappings) if err != nil { - return kubeContainer, kubeVolumes, nil, err + return kubeContainer, kubeVolumes, nil, annotations, err } // Handle command and arguments. @@ -437,12 +581,37 @@ _, image := c.Image() kubeContainer.Image = image kubeContainer.Stdin = c.Stdin() + img, _, err := c.runtime.libimageRuntime.LookupImage(image, nil) + if err != nil { + return kubeContainer, kubeVolumes, nil, annotations, err + } + imgData, err := img.Inspect(ctx, false) + if err != nil { + return kubeContainer, kubeVolumes, nil, annotations, err + } + // If the user doesn't set a command/entrypoint when creating the container with podman and + // is using the image command or entrypoint from the image, don't add it to the generated kube yaml + if reflect.DeepEqual(imgData.Config.Cmd, kubeContainer.Command) || reflect.DeepEqual(imgData.Config.Entrypoint, kubeContainer.Command) { + kubeContainer.Command = nil + } + + if c.WorkingDir() != "/" && imgData.Config.WorkingDir != c.WorkingDir() { + kubeContainer.WorkingDir = c.WorkingDir() + } + + if imgData.User == c.User() { + kubeSec.RunAsGroup, kubeSec.RunAsUser = nil, nil + } + + envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env, imgData.Config.Env) + if err != nil { + return kubeContainer, kubeVolumes, nil, annotations, err + } + kubeContainer.Env = envVariables - kubeContainer.WorkingDir = c.WorkingDir() kubeContainer.Ports = ports // This should not be applicable //container.EnvFromSource = - kubeContainer.Env = envVariables kubeContainer.SecurityContext = kubeSec kubeContainer.StdinOnce = false kubeContainer.TTY = c.config.Spec.Process.Terminal @@ -514,7 +683,7 @@ } dns.Options = dnsOptions } - return kubeContainer, kubeVolumes, &dns, nil + return kubeContainer, kubeVolumes, &dns, annotations, nil } // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts @@ -525,7 +694,8 @@ var protocol v1.Protocol switch strings.ToUpper(p.Protocol) { case "TCP": - protocol = v1.ProtocolTCP + // do nothing as it is the default protocol in k8s, there is no need to explicitly + // add it to the generated yaml case "UDP": protocol = v1.ProtocolUDP default: @@ -544,13 +714,25 @@ } // libpodEnvVarsToKubeEnvVars converts a key=value string slice to []v1.EnvVar -func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) { +func libpodEnvVarsToKubeEnvVars(envs []string, imageEnvs []string) ([]v1.EnvVar, error) { + defaultEnv := env.DefaultEnvVariables() envVars := make([]v1.EnvVar, 0, len(envs)) + imageMap := make(map[string]string, len(imageEnvs)) + for _, ie := range envs { + split := strings.SplitN(ie, "=", 2) + imageMap[split[0]] = split[1] + } for _, e := range envs { split := strings.SplitN(e, "=", 2) if len(split) != 2 { return envVars, errors.Errorf("environment variable %s is malformed; should be key=value", e) } + if defaultEnv[split[0]] == split[1] { + continue + } + if imageMap[split[0]] == split[1] { + continue + } ev := v1.EnvVar{ Name: split[0], Value: split[1], @@ -561,16 +743,23 @@ } // libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands -func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, error) { +func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, map[string]string, error) { namedVolumes, mounts := c.sortUserVolumes(c.config.Spec) vms := make([]v1.VolumeMount, 0, len(mounts)) vos := make([]v1.Volume, 0, len(mounts)) + annotations := make(map[string]string) var suffix string for index, m := range mounts { + for _, opt := range m.Options { + if opt == "Z" || opt == "z" { + annotations[m.Source] = opt + break + } + } vm, vo, err := generateKubeVolumeMount(m) if err != nil { - return vms, vos, err + return vms, vos, annotations, err } // Name will be the same, so use the index as suffix suffix = fmt.Sprintf("-%d", index) @@ -584,7 +773,7 @@ vms = append(vms, vm) vos = append(vos, vo) } - return vms, vos, nil + return vms, vos, annotations, nil } // generateKubePersistentVolumeClaim converts a ContainerNamedVolume to a Kubernetes PersistentVolumeClaim @@ -741,33 +930,42 @@ capabilities = newCaps } + sc := v1.SecurityContext{ + // RunAsNonRoot is an optional parameter; our first implementations should be root only; however + // I'm leaving this as a bread-crumb for later + //RunAsNonRoot: &nonRoot, + } + if capabilities != nil { + sc.Capabilities = capabilities + } var selinuxOpts v1.SELinuxOptions opts := strings.SplitN(c.config.Spec.Annotations[define.InspectAnnotationLabel], ":", 2) - if len(opts) == 2 { + switch len(opts) { + case 2: switch opts[0] { case "type": selinuxOpts.Type = opts[1] + sc.SELinuxOptions = &selinuxOpts case "level": selinuxOpts.Level = opts[1] + sc.SELinuxOptions = &selinuxOpts } - } - if len(opts) == 1 { + case 1: if opts[0] == "disable" { selinuxOpts.Type = "spc_t" + sc.SELinuxOptions = &selinuxOpts } } - sc := v1.SecurityContext{ - Capabilities: capabilities, - Privileged: &privileged, - SELinuxOptions: &selinuxOpts, - // RunAsNonRoot is an optional parameter; our first implementations should be root only; however - // I'm leaving this as a bread-crumb for later - //RunAsNonRoot: &nonRoot, - ReadOnlyRootFilesystem: &ro, - AllowPrivilegeEscalation: &allowPrivEscalation, + if !allowPrivEscalation { + sc.AllowPrivilegeEscalation = &allowPrivEscalation + } + if privileged { + sc.Privileged = &privileged + } + if ro { + sc.ReadOnlyRootFilesystem = &ro } - if c.User() != "" { if !c.batched { c.lock.Lock() diff -Nru libpod-3.2.1+ds1/libpod/lock/shm/shm_lock.go libpod-3.4.4+ds1/libpod/lock/shm/shm_lock.go --- libpod-3.2.1+ds1/libpod/lock/shm/shm_lock.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/lock/shm/shm_lock.go 2021-12-26 00:45:51.000000000 +0000 @@ -130,8 +130,17 @@ // semaphore indexes, and can still return error codes. retCode := C.allocate_semaphore(locks.lockStruct) if retCode < 0 { + var err = syscall.Errno(-1 * retCode) // Negative errno returned - return 0, syscall.Errno(-1 * retCode) + if errors.Is(err, syscall.ENOSPC) { + // ENOSPC expands to "no space left on device". While it is technically true + // that there's no room in the SHM inn for this lock, this tends to send normal people + // down the path of checking disk-space which is not actually their problem. + // Give a clue that it's actually due to num_locks filling up. + var errFull = errors.Errorf("allocation failed; exceeded num_locks (%d)", locks.maxLocks) + return uint32(retCode), errFull + } + return uint32(retCode), syscall.Errno(-1 * retCode) } return uint32(retCode), nil diff -Nru libpod-3.2.1+ds1/libpod/logs/log.go libpod-3.4.4+ds1/libpod/logs/log.go --- libpod-3.2.1+ds1/libpod/logs/log.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/logs/log.go 2021-12-26 00:45:51.000000000 +0000 @@ -34,6 +34,7 @@ Details bool Follow bool Since time.Time + Until time.Time Tail int64 Timestamps bool Multi bool @@ -184,7 +185,12 @@ // Since returns a bool as to whether a log line occurred after a given time func (l *LogLine) Since(since time.Time) bool { - return l.Time.After(since) + return l.Time.After(since) || since.IsZero() +} + +// Until returns a bool as to whether a log line occurred before a given time +func (l *LogLine) Until(until time.Time) bool { + return l.Time.Before(until) || until.IsZero() } // NewLogLine creates a logLine struct from a container log string diff -Nru libpod-3.2.1+ds1/libpod/network/cni/cni_conversion.go libpod-3.4.4+ds1/libpod/network/cni/cni_conversion.go --- libpod-3.2.1+ds1/libpod/network/cni/cni_conversion.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/cni_conversion.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,375 @@ +// +build linux + +package cni + +import ( + "encoding/json" + "io/ioutil" + "net" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "github.com/containernetworking/cni/libcni" + "github.com/containernetworking/cni/pkg/version" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/libpod/network/util" + pkgutil "github.com/containers/podman/v3/pkg/util" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func createNetworkFromCNIConfigList(conf *libcni.NetworkConfigList, confPath string) (*types.Network, error) { + network := types.Network{ + Name: conf.Name, + ID: getNetworkIDFromName(conf.Name), + Labels: map[string]string{}, + Options: map[string]string{}, + IPAMOptions: map[string]string{}, + } + + cniJSON := make(map[string]interface{}) + err := json.Unmarshal(conf.Bytes, &cniJSON) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal network config %s", conf.Name) + } + if args, ok := cniJSON["args"]; ok { + if key, ok := args.(map[string]interface{}); ok { + // read network labels and options from the conf file + network.Labels = getNetworkArgsFromConfList(key, podmanLabelKey) + network.Options = getNetworkArgsFromConfList(key, podmanOptionsKey) + } + } + + f, err := os.Stat(confPath) + if err != nil { + return nil, err + } + stat := f.Sys().(*syscall.Stat_t) + network.Created = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) + + firstPlugin := conf.Plugins[0] + network.Driver = firstPlugin.Network.Type + + switch firstPlugin.Network.Type { + case types.BridgeNetworkDriver: + var bridge hostLocalBridge + err := json.Unmarshal(firstPlugin.Bytes, &bridge) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal the bridge plugin config in %s", confPath) + } + network.NetworkInterface = bridge.BrName + + // if isGateway is false we have an internal network + if !bridge.IsGW { + network.Internal = true + } + + // set network options + if bridge.MTU != 0 { + network.Options["mtu"] = strconv.Itoa(bridge.MTU) + } + if bridge.Vlan != 0 { + network.Options["vlan"] = strconv.Itoa(bridge.Vlan) + } + + err = convertIPAMConfToNetwork(&network, bridge.IPAM, confPath) + if err != nil { + return nil, err + } + + case types.MacVLANNetworkDriver: + var macvlan macVLANConfig + err := json.Unmarshal(firstPlugin.Bytes, &macvlan) + if err != nil { + return nil, errors.Wrapf(err, "failed to unmarshal the macvlan plugin config in %s", confPath) + } + network.NetworkInterface = macvlan.Master + + // set network options + if macvlan.MTU != 0 { + network.Options["mtu"] = strconv.Itoa(macvlan.MTU) + } + + err = convertIPAMConfToNetwork(&network, macvlan.IPAM, confPath) + if err != nil { + return nil, err + } + + default: + // A warning would be good but users would get this warning everytime so keep this at info level. + logrus.Infof("unsupported CNI config type %s in %s, this network can still be used but inspect or list cannot show all information", + firstPlugin.Network.Type, confPath) + } + + // check if the dnsname plugin is configured + network.DNSEnabled = findPluginByName(conf.Plugins, "dnsname") + + return &network, nil +} + +func findPluginByName(plugins []*libcni.NetworkConfig, name string) bool { + for _, plugin := range plugins { + if plugin.Network.Type == name { + return true + } + } + return false +} + +// convertIPAMConfToNetwork converts A cni IPAMConfig to libpod network subnets. +// It returns an array of subnets and an extra bool if dhcp is configured. +func convertIPAMConfToNetwork(network *types.Network, ipam ipamConfig, confPath string) error { + if ipam.PluginType == types.DHCPIPAMDriver { + network.IPAMOptions["driver"] = types.DHCPIPAMDriver + return nil + } + + if ipam.PluginType != types.HostLocalIPAMDriver { + return errors.Errorf("unsupported ipam plugin %s in %s", ipam.PluginType, confPath) + } + + network.IPAMOptions["driver"] = types.HostLocalIPAMDriver + for _, r := range ipam.Ranges { + for _, ipam := range r { + s := types.Subnet{} + + // Do not use types.ParseCIDR() because we want the ip to be + // the network address and not a random ip in the sub. + _, sub, err := net.ParseCIDR(ipam.Subnet) + if err != nil { + return err + } + s.Subnet = types.IPNet{IPNet: *sub} + + // gateway + var gateway net.IP + if ipam.Gateway != "" { + gateway = net.ParseIP(ipam.Gateway) + if gateway == nil { + return errors.Errorf("failed to parse gateway ip %s", ipam.Gateway) + } + // convert to 4 byte if ipv4 + ipv4 := gateway.To4() + if ipv4 != nil { + gateway = ipv4 + } + } else if !network.Internal { + // only add a gateway address if the network is not internal + gateway, err = util.FirstIPInSubnet(sub) + if err != nil { + return errors.Errorf("failed to get first ip in subnet %s", sub.String()) + } + } + s.Gateway = gateway + + var rangeStart net.IP + var rangeEnd net.IP + if ipam.RangeStart != "" { + rangeStart = net.ParseIP(ipam.RangeStart) + if rangeStart == nil { + return errors.Errorf("failed to parse range start ip %s", ipam.RangeStart) + } + } + if ipam.RangeEnd != "" { + rangeEnd = net.ParseIP(ipam.RangeEnd) + if rangeEnd == nil { + return errors.Errorf("failed to parse range end ip %s", ipam.RangeEnd) + } + } + if rangeStart != nil || rangeEnd != nil { + s.LeaseRange = &types.LeaseRange{} + s.LeaseRange.StartIP = rangeStart + s.LeaseRange.EndIP = rangeEnd + } + network.Subnets = append(network.Subnets, s) + } + } + return nil +} + +// getNetworkArgsFromConfList returns the map of args in a conflist, argType should be labels or options +func getNetworkArgsFromConfList(args map[string]interface{}, argType string) map[string]string { + if args, ok := args[argType]; ok { + if labels, ok := args.(map[string]interface{}); ok { + result := make(map[string]string, len(labels)) + for k, v := range labels { + if v, ok := v.(string); ok { + result[k] = v + } + } + return result + } + } + return nil +} + +// createCNIConfigListFromNetwork will create a cni config file from the given network. +// It returns the cni config and the path to the file where the config was written. +// Set writeToDisk to false to only add this network into memory. +func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writeToDisk bool) (*libcni.NetworkConfigList, string, error) { + var ( + routes []ipamRoute + ipamRanges [][]ipamLocalHostRangeConf + ipamConf ipamConfig + err error + ) + if len(network.Subnets) > 0 { + for _, subnet := range network.Subnets { + route, err := newIPAMDefaultRoute(util.IsIPv6(subnet.Subnet.IP)) + if err != nil { + return nil, "", err + } + routes = append(routes, route) + ipam := newIPAMLocalHostRange(subnet.Subnet, subnet.LeaseRange, subnet.Gateway) + ipamRanges = append(ipamRanges, []ipamLocalHostRangeConf{*ipam}) + } + ipamConf = newIPAMHostLocalConf(routes, ipamRanges) + } else { + ipamConf = ipamConfig{PluginType: "dhcp"} + } + + vlan := 0 + mtu := 0 + for k, v := range network.Options { + switch k { + case "mtu": + mtu, err = parseMTU(v) + if err != nil { + return nil, "", err + } + + case "vlan": + vlan, err = parseVlan(v) + if err != nil { + return nil, "", err + } + + default: + return nil, "", errors.Errorf("unsupported network option %s", k) + } + } + + isGateway := true + ipMasq := true + if network.Internal { + isGateway = false + ipMasq = false + } + // create CNI plugin configuration + ncList := newNcList(network.Name, version.Current(), network.Labels, network.Options) + var plugins []interface{} + + switch network.Driver { + case types.BridgeNetworkDriver: + bridge := newHostLocalBridge(network.NetworkInterface, isGateway, ipMasq, mtu, vlan, ipamConf) + plugins = append(plugins, bridge, newPortMapPlugin(), newFirewallPlugin(), newTuningPlugin()) + // if we find the dnsname plugin we add configuration for it + if hasDNSNamePlugin(n.cniPluginDirs) && network.DNSEnabled { + // Note: in the future we might like to allow for dynamic domain names + plugins = append(plugins, newDNSNamePlugin(defaultPodmanDomainName)) + } + // Add the podman-machine CNI plugin if we are in a machine + if n.isMachine { + plugins = append(plugins, newPodmanMachinePlugin()) + } + + case types.MacVLANNetworkDriver: + plugins = append(plugins, newMacVLANPlugin(network.NetworkInterface, mtu, ipamConf)) + + default: + return nil, "", errors.Errorf("driver %q is not supported by cni", network.Driver) + } + ncList["plugins"] = plugins + b, err := json.MarshalIndent(ncList, "", " ") + if err != nil { + return nil, "", err + } + cniPathName := "" + if writeToDisk { + cniPathName = filepath.Join(n.cniConfigDir, network.Name+".conflist") + err = ioutil.WriteFile(cniPathName, b, 0644) + if err != nil { + return nil, "", err + } + f, err := os.Stat(cniPathName) + if err != nil { + return nil, "", err + } + stat := f.Sys().(*syscall.Stat_t) + network.Created = time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)) + } else { + network.Created = time.Now() + } + config, err := libcni.ConfListFromBytes(b) + if err != nil { + return nil, "", err + } + return config, cniPathName, nil +} + +// parseMTU parses the mtu option +func parseMTU(mtu string) (int, error) { + if mtu == "" { + return 0, nil // default + } + m, err := strconv.Atoi(mtu) + if err != nil { + return 0, err + } + if m < 0 { + return 0, errors.Errorf("mtu %d is less than zero", m) + } + return m, nil +} + +// parseVlan parses the vlan option +func parseVlan(vlan string) (int, error) { + if vlan == "" { + return 0, nil // default + } + v, err := strconv.Atoi(vlan) + if err != nil { + return 0, err + } + if v < 0 || v > 4094 { + return 0, errors.Errorf("vlan ID %d must be between 0 and 4094", v) + } + return v, nil +} + +func convertSpecgenPortsToCNIPorts(ports []types.PortMapping) ([]cniPortMapEntry, error) { + cniPorts := make([]cniPortMapEntry, 0, len(ports)) + for _, port := range ports { + if port.Protocol == "" { + return nil, errors.New("port protocol should not be empty") + } + protocols := strings.Split(port.Protocol, ",") + + for _, protocol := range protocols { + if !pkgutil.StringInSlice(protocol, []string{"tcp", "udp", "sctp"}) { + return nil, errors.Errorf("unknown port protocol %s", protocol) + } + cniPort := cniPortMapEntry{ + HostPort: int(port.HostPort), + ContainerPort: int(port.ContainerPort), + HostIP: port.HostIP, + Protocol: protocol, + } + cniPorts = append(cniPorts, cniPort) + for i := 1; i < int(port.Range); i++ { + cniPort := cniPortMapEntry{ + HostPort: int(port.HostPort) + i, + ContainerPort: int(port.ContainerPort) + i, + HostIP: port.HostIP, + Protocol: protocol, + } + cniPorts = append(cniPorts, cniPort) + } + } + } + return cniPorts, nil +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/cni_exec.go libpod-3.4.4+ds1/libpod/network/cni/cni_exec.go --- libpod-3.2.1+ds1/libpod/network/cni/cni_exec.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/cni_exec.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,110 @@ +// Copyright 2016 CNI authors +// Copyright 2021 Podman authors +// +// This code has been originally copied from github.com/containernetworking/cni +// but has been changed to better fit the Podman use case. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build linux + +package cni + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "os/exec" + "path/filepath" + + "github.com/containernetworking/cni/pkg/invoke" + "github.com/containernetworking/cni/pkg/version" + "github.com/containers/podman/v3/pkg/rootless" +) + +type cniExec struct { + version.PluginDecoder +} + +type cniPluginError struct { + plugin string + Code uint `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` +} + +// Error returns a nicely formatted error message for the cni plugin errors. +func (e *cniPluginError) Error() string { + err := fmt.Sprintf("cni plugin %s failed", e.plugin) + if e.Msg != "" { + err = fmt.Sprintf("%s: %s", err, e.Msg) + } else if e.Code > 0 { + err = fmt.Sprintf("%s with error code %d", err, e.Code) + } + if e.Details != "" { + err = fmt.Sprintf("%s: %s", err, e.Details) + } + return err +} + +// ExecPlugin execute the cni plugin. Returns the stdout of the plugin or an error. +func (e *cniExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) { + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + c := exec.CommandContext(ctx, pluginPath) + c.Env = environ + c.Stdin = bytes.NewBuffer(stdinData) + c.Stdout = stdout + c.Stderr = stderr + + // The dnsname plugin tries to use XDG_RUNTIME_DIR to store files. + // podman run will have XDG_RUNTIME_DIR set and thus the cni plugin can use + // it. The problem is that XDG_RUNTIME_DIR is unset for the conmon process + // for rootful users. This causes issues since the cleanup process is spawned + // by conmon and thus not have XDG_RUNTIME_DIR set to same value as podman run. + // Because of it dnsname will not find the config files and cannot correctly cleanup. + // To fix this we should also unset XDG_RUNTIME_DIR for the cni plugins as rootful. + if !rootless.IsRootless() { + c.Env = append(c.Env, "XDG_RUNTIME_DIR=") + } + + err := c.Run() + if err != nil { + return nil, annotatePluginError(err, pluginPath, stdout.Bytes(), stderr.Bytes()) + } + return stdout.Bytes(), nil +} + +// annotatePluginError parses the common cni plugin error json. +func annotatePluginError(err error, plugin string, stdout []byte, stderr []byte) error { + pluginName := filepath.Base(plugin) + emsg := cniPluginError{ + plugin: pluginName, + } + if len(stdout) == 0 { + if len(stderr) == 0 { + emsg.Msg = err.Error() + } else { + emsg.Msg = string(stderr) + } + } else if perr := json.Unmarshal(stdout, &emsg); perr != nil { + emsg.Msg = fmt.Sprintf("failed to unmarshal error message %q: %v", string(stdout), perr) + } + return &emsg +} + +// FindInPath finds the plugin in the given paths. +func (e *cniExec) FindInPath(plugin string, paths []string) (string, error) { + return invoke.FindInPath(plugin, paths) +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/cni_suite_test.go libpod-3.4.4+ds1/libpod/network/cni/cni_suite_test.go --- libpod-3.2.1+ds1/libpod/network/cni/cni_suite_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/cni_suite_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,53 @@ +// +build linux + +package cni_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/containers/podman/v3/libpod/network/cni" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var cniPluginDirs = []string{ + "/usr/libexec/cni", + "/usr/lib/cni", + "/usr/local/lib/cni", + "/opt/cni/bin", +} + +func TestCni(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CNI Suite") +} + +func getNetworkInterface(cniConfDir string, machine bool) (types.ContainerNetwork, error) { + return cni.NewCNINetworkInterface(cni.InitConfig{ + CNIConfigDir: cniConfDir, + CNIPluginDirs: cniPluginDirs, + IsMachine: machine, + LockFile: filepath.Join(cniConfDir, "cni.lock"), + }) +} + +func SkipIfNoDnsname() { + for _, path := range cniPluginDirs { + f, err := os.Stat(filepath.Join(path, "dnsname")) + if err == nil && f.Mode().IsRegular() { + return + } + } + Skip("dnsname cni plugin needs to be installed for this test") +} + +func SkipIfNotFedora(msg string) { + info := utils.GetHostDistributionInfo() + if info.Distribution != "fedora" { + Skip("Test can only run on Fedora: " + msg) + } +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/cni_types.go libpod-3.4.4+ds1/libpod/network/cni/cni_types.go --- libpod-3.2.1+ds1/libpod/network/cni/cni_types.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/cni_types.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,292 @@ +// +build linux + +package cni + +import ( + "net" + "os" + "path/filepath" + + "github.com/containers/podman/v3/libpod/network/types" +) + +const ( + defaultIPv4Route = "0.0.0.0/0" + defaultIPv6Route = "::/0" + // defaultPodmanDomainName is used for the dnsname plugin to define + // a localized domain name for a created network + defaultPodmanDomainName = "dns.podman" + + // cniDeviceName is the default name for a new bridge, it should be suffixed with an integer + cniDeviceName = "cni-podman" + + // podmanLabelKey key used to store the podman network label in a cni config + podmanLabelKey = "podman_labels" + + // podmanOptionsKey key used to store the podman network options in a cni config + podmanOptionsKey = "podman_options" +) + +// cniPortMapEntry struct is used by the portmap plugin +// https://github.com/containernetworking/plugins/blob/649e0181fe7b3a61e708f3e4249a798f57f25cc5/plugins/meta/portmap/main.go#L43-L50 +type cniPortMapEntry struct { + HostPort int `json:"hostPort"` + ContainerPort int `json:"containerPort"` + Protocol string `json:"protocol"` + HostIP string `json:"hostIP,omitempty"` +} + +// hostLocalBridge describes a configuration for a bridge plugin +// https://github.com/containernetworking/plugins/tree/master/plugins/main/bridge#network-configuration-reference +type hostLocalBridge struct { + PluginType string `json:"type"` + BrName string `json:"bridge,omitempty"` + IsGW bool `json:"isGateway"` + IsDefaultGW bool `json:"isDefaultGateway,omitempty"` + ForceAddress bool `json:"forceAddress,omitempty"` + IPMasq bool `json:"ipMasq,omitempty"` + MTU int `json:"mtu,omitempty"` + HairpinMode bool `json:"hairpinMode,omitempty"` + PromiscMode bool `json:"promiscMode,omitempty"` + Vlan int `json:"vlan,omitempty"` + IPAM ipamConfig `json:"ipam"` + Capabilities map[string]bool `json:"capabilities"` +} + +// ipamConfig describes an IPAM configuration +// https://github.com/containernetworking/plugins/tree/master/plugins/ipam/host-local#network-configuration-reference +type ipamConfig struct { + PluginType string `json:"type"` + Routes []ipamRoute `json:"routes,omitempty"` + ResolveConf string `json:"resolveConf,omitempty"` + DataDir string `json:"dataDir,omitempty"` + Ranges [][]ipamLocalHostRangeConf `json:"ranges,omitempty"` +} + +// ipamLocalHostRangeConf describes the new style IPAM ranges +type ipamLocalHostRangeConf struct { + Subnet string `json:"subnet"` + RangeStart string `json:"rangeStart,omitempty"` + RangeEnd string `json:"rangeEnd,omitempty"` + Gateway string `json:"gateway,omitempty"` +} + +// ipamRoute describes a route in an ipam config +type ipamRoute struct { + Dest string `json:"dst"` +} + +// portMapConfig describes the default portmapping config +type portMapConfig struct { + PluginType string `json:"type"` + Capabilities map[string]bool `json:"capabilities"` +} + +// macVLANConfig describes the macvlan config +type macVLANConfig struct { + PluginType string `json:"type"` + Master string `json:"master"` + IPAM ipamConfig `json:"ipam"` + MTU int `json:"mtu,omitempty"` + Capabilities map[string]bool `json:"capabilities"` +} + +// firewallConfig describes the firewall plugin +type firewallConfig struct { + PluginType string `json:"type"` + Backend string `json:"backend"` +} + +// tuningConfig describes the tuning plugin +type tuningConfig struct { + PluginType string `json:"type"` +} + +// dnsNameConfig describes the dns container name resolution plugin config +type dnsNameConfig struct { + PluginType string `json:"type"` + DomainName string `json:"domainName"` + Capabilities map[string]bool `json:"capabilities"` +} + +// podmanMachineConfig enables port handling on the host OS +type podmanMachineConfig struct { + PluginType string `json:"type"` + Capabilities map[string]bool `json:"capabilities"` +} + +// ncList describes a generic map +type ncList map[string]interface{} + +// newNcList creates a generic map of values with string +// keys and adds in version and network name +func newNcList(name, version string, labels, options map[string]string) ncList { + n := ncList{} + n["cniVersion"] = version + n["name"] = name + args := map[string]map[string]string{} + if len(labels) > 0 { + args[podmanLabelKey] = labels + } + if len(options) > 0 { + args[podmanOptionsKey] = options + } + if len(args) > 0 { + n["args"] = args + } + return n +} + +// newHostLocalBridge creates a new LocalBridge for host-local +func newHostLocalBridge(name string, isGateWay, ipMasq bool, mtu int, vlan int, ipamConf ipamConfig) *hostLocalBridge { + caps := make(map[string]bool) + caps["ips"] = true + bridge := hostLocalBridge{ + PluginType: "bridge", + BrName: name, + IsGW: isGateWay, + IPMasq: ipMasq, + MTU: mtu, + HairpinMode: true, + Vlan: vlan, + IPAM: ipamConf, + } + // if we use host-local set the ips cap to ensure we can set static ips via runtime config + if ipamConf.PluginType == types.HostLocalIPAMDriver { + bridge.Capabilities = caps + } + return &bridge +} + +// newIPAMHostLocalConf creates a new IPAMHostLocal configuration +func newIPAMHostLocalConf(routes []ipamRoute, ipamRanges [][]ipamLocalHostRangeConf) ipamConfig { + ipamConf := ipamConfig{ + PluginType: "host-local", + Routes: routes, + } + + ipamConf.Ranges = ipamRanges + return ipamConf +} + +// newIPAMLocalHostRange create a new IPAM range +func newIPAMLocalHostRange(subnet types.IPNet, leaseRange *types.LeaseRange, gw net.IP) *ipamLocalHostRangeConf { + hostRange := &ipamLocalHostRangeConf{ + Subnet: subnet.String(), + } + + // a user provided a range, we add it here + if leaseRange != nil { + if leaseRange.StartIP != nil { + hostRange.RangeStart = leaseRange.StartIP.String() + } + if leaseRange.EndIP != nil { + hostRange.RangeStart = leaseRange.EndIP.String() + } + } + + if gw != nil { + hostRange.Gateway = gw.String() + } + return hostRange +} + +// newIPAMRoute creates a new IPAM route configuration +// nolint:interfacer +func newIPAMRoute(r *net.IPNet) ipamRoute { + return ipamRoute{Dest: r.String()} +} + +// newIPAMDefaultRoute creates a new IPAMDefault route of +// 0.0.0.0/0 for IPv4 or ::/0 for IPv6 +func newIPAMDefaultRoute(isIPv6 bool) (ipamRoute, error) { + route := defaultIPv4Route + if isIPv6 { + route = defaultIPv6Route + } + _, n, err := net.ParseCIDR(route) + if err != nil { + return ipamRoute{}, err + } + return newIPAMRoute(n), nil +} + +// newPortMapPlugin creates a predefined, default portmapping +// configuration +func newPortMapPlugin() portMapConfig { + caps := make(map[string]bool) + caps["portMappings"] = true + p := portMapConfig{ + PluginType: "portmap", + Capabilities: caps, + } + return p +} + +// newFirewallPlugin creates a generic firewall plugin +func newFirewallPlugin() firewallConfig { + return firewallConfig{ + PluginType: "firewall", + } +} + +// newTuningPlugin creates a generic tuning section +func newTuningPlugin() tuningConfig { + return tuningConfig{ + PluginType: "tuning", + } +} + +// newDNSNamePlugin creates the dnsname config with a given +// domainname +func newDNSNamePlugin(domainName string) dnsNameConfig { + caps := make(map[string]bool, 1) + caps["aliases"] = true + return dnsNameConfig{ + PluginType: "dnsname", + DomainName: domainName, + Capabilities: caps, + } +} + +// hasDNSNamePlugin looks to see if the dnsname cni plugin is present +func hasDNSNamePlugin(paths []string) bool { + for _, p := range paths { + if _, err := os.Stat(filepath.Join(p, "dnsname")); err == nil { + return true + } + } + return false +} + +// newMacVLANPlugin creates a macvlanconfig with a given device name +func newMacVLANPlugin(device string, mtu int, ipam ipamConfig) macVLANConfig { + m := macVLANConfig{ + PluginType: "macvlan", + IPAM: ipam, + } + if mtu > 0 { + m.MTU = mtu + } + // CNI is supposed to use the default route if a + // parent device is not provided + if len(device) > 0 { + m.Master = device + } + caps := make(map[string]bool) + caps["ips"] = true + // if we use host-local set the ips cap to ensure we can set static ips via runtime config + if ipam.PluginType == types.HostLocalIPAMDriver { + m.Capabilities = caps + } + return m +} + +func newPodmanMachinePlugin() podmanMachineConfig { + caps := make(map[string]bool, 1) + caps["portMappings"] = true + return podmanMachineConfig{ + PluginType: "podman-machine", + Capabilities: caps, + } +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/config.go libpod-3.4.4+ds1/libpod/network/cni/config.go --- libpod-3.2.1+ds1/libpod/network/cni/config.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,313 @@ +// +build linux + +package cni + +import ( + "net" + "os" + + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/libpod/network/util" + pkgutil "github.com/containers/podman/v3/pkg/util" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// NetworkCreate will take a partial filled Network and fill the +// missing fields. It creates the Network and returns the full Network. +func (n *cniNetwork) NetworkCreate(net types.Network) (types.Network, error) { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return types.Network{}, err + } + network, err := n.networkCreate(net, true) + if err != nil { + return types.Network{}, err + } + // add the new network to the map + n.networks[network.libpodNet.Name] = network + return *network.libpodNet, nil +} + +func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*network, error) { + // if no driver is set use the default one + if net.Driver == "" { + net.Driver = types.DefaultNetworkDriver + } + + // FIXME: Should we use a different type for network create without the ID field? + // the caller is not allowed to set a specific ID + if net.ID != "" { + return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create") + } + + if net.Labels == nil { + net.Labels = map[string]string{} + } + if net.Options == nil { + net.Options = map[string]string{} + } + if net.IPAMOptions == nil { + net.IPAMOptions = map[string]string{} + } + + var name string + var err error + // validate the name when given + if net.Name != "" { + if !define.NameRegex.MatchString(net.Name) { + return nil, errors.Wrapf(define.RegexError, "network name %s invalid", net.Name) + } + if _, ok := n.networks[net.Name]; ok { + return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", net.Name) + } + } else { + name, err = n.getFreeDeviceName() + if err != nil { + return nil, err + } + net.Name = name + } + + switch net.Driver { + case types.BridgeNetworkDriver: + // if the name was created with getFreeDeviceName set the interface to it as well + if name != "" && net.NetworkInterface == "" { + net.NetworkInterface = name + } + err = n.createBridge(&net) + if err != nil { + return nil, err + } + case types.MacVLANNetworkDriver: + err = createMacVLAN(&net) + if err != nil { + return nil, err + } + default: + return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", net.Driver) + } + + for i := range net.Subnets { + err := validateSubnet(&net.Subnets[i], !net.Internal) + if err != nil { + return nil, err + } + if util.IsIPv6(net.Subnets[i].Subnet.IP) { + net.IPv6Enabled = true + } + } + + // generate the network ID + net.ID = getNetworkIDFromName(net.Name) + + // FIXME: Should this be a hard error? + if net.DNSEnabled && net.Internal && hasDNSNamePlugin(n.cniPluginDirs) { + logrus.Warnf("dnsname and internal networks are incompatible. dnsname plugin not configured for network %s", net.Name) + net.DNSEnabled = false + } + + cniConf, path, err := n.createCNIConfigListFromNetwork(&net, writeToDisk) + if err != nil { + return nil, err + } + return &network{cniNet: cniConf, libpodNet: &net, filename: path}, nil +} + +// NetworkRemove will remove the Network with the given name or ID. +// It does not ensure that the network is unused. +func (n *cniNetwork) NetworkRemove(nameOrID string) error { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return err + } + + network, err := n.getNetwork(nameOrID) + if err != nil { + return err + } + + // Removing the default network is not allowed. + if network.libpodNet.Name == n.defaultNetwork { + return errors.Errorf("default network %s cannot be removed", n.defaultNetwork) + } + + // Remove the bridge network interface on the host. + if network.libpodNet.Driver == types.BridgeNetworkDriver { + link, err := netlink.LinkByName(network.libpodNet.NetworkInterface) + if err == nil { + err = netlink.LinkDel(link) + // only log the error, it is not fatal + if err != nil { + logrus.Infof("failed to remove network interface %s: %v", network.libpodNet.NetworkInterface, err) + } + } + } + + file := network.filename + delete(n.networks, network.libpodNet.Name) + + return os.Remove(file) +} + +// NetworkList will return all known Networks. Optionally you can +// supply a list of filter functions. Only if a network matches all +// functions it is returned. +func (n *cniNetwork) NetworkList(filters ...types.FilterFunc) ([]types.Network, error) { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return nil, err + } + + networks := make([]types.Network, 0, len(n.networks)) +outer: + for _, net := range n.networks { + for _, filter := range filters { + // All filters have to match, if one does not match we can skip to the next network. + if !filter(*net.libpodNet) { + continue outer + } + } + networks = append(networks, *net.libpodNet) + } + return networks, nil +} + +// NetworkInspect will return the Network with the given name or ID. +func (n *cniNetwork) NetworkInspect(nameOrID string) (types.Network, error) { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return types.Network{}, err + } + + network, err := n.getNetwork(nameOrID) + if err != nil { + return types.Network{}, err + } + return *network.libpodNet, nil +} + +func createMacVLAN(network *types.Network) error { + if network.Internal { + return errors.New("internal is not supported with macvlan") + } + if network.NetworkInterface != "" { + interfaceNames, err := util.GetLiveNetworkNames() + if err != nil { + return err + } + if !pkgutil.StringInSlice(network.NetworkInterface, interfaceNames) { + return errors.Errorf("parent interface %s does not exists", network.NetworkInterface) + } + } + if len(network.Subnets) == 0 { + network.IPAMOptions["driver"] = types.DHCPIPAMDriver + } else { + network.IPAMOptions["driver"] = types.HostLocalIPAMDriver + } + return nil +} + +func (n *cniNetwork) createBridge(network *types.Network) error { + if network.NetworkInterface != "" { + bridges := n.getBridgeInterfaceNames() + if pkgutil.StringInSlice(network.NetworkInterface, bridges) { + return errors.Errorf("bridge name %s already in use", network.NetworkInterface) + } + if !define.NameRegex.MatchString(network.NetworkInterface) { + return errors.Wrapf(define.RegexError, "bridge name %s invalid", network.NetworkInterface) + } + } else { + var err error + network.NetworkInterface, err = n.getFreeDeviceName() + if err != nil { + return err + } + } + + if len(network.Subnets) == 0 { + freeSubnet, err := n.getFreeIPv4NetworkSubnet() + if err != nil { + return err + } + network.Subnets = append(network.Subnets, *freeSubnet) + } + // ipv6 enabled means dual stack, check if we already have + // a ipv4 or ipv6 subnet and add one if not. + if network.IPv6Enabled { + ipv4 := false + ipv6 := false + for _, subnet := range network.Subnets { + if util.IsIPv6(subnet.Subnet.IP) { + ipv6 = true + } + if util.IsIPv4(subnet.Subnet.IP) { + ipv4 = true + } + } + if !ipv4 { + freeSubnet, err := n.getFreeIPv4NetworkSubnet() + if err != nil { + return err + } + network.Subnets = append(network.Subnets, *freeSubnet) + } + if !ipv6 { + freeSubnet, err := n.getFreeIPv6NetworkSubnet() + if err != nil { + return err + } + network.Subnets = append(network.Subnets, *freeSubnet) + } + } + network.IPAMOptions["driver"] = types.HostLocalIPAMDriver + return nil +} + +// validateSubnet will validate a given Subnet. It checks if the +// given gateway and lease range are part of this subnet. If the +// gateway is empty and addGateway is true it will get the first +// available ip in the subnet assigned. +func validateSubnet(s *types.Subnet, addGateway bool) error { + if s == nil { + return errors.New("subnet is nil") + } + // Reparse to ensure subnet is valid. + // Do not use types.ParseCIDR() because we want the ip to be + // the network address and not a random ip in the subnet. + _, net, err := net.ParseCIDR(s.Subnet.String()) + if err != nil { + return errors.Wrap(err, "subnet invalid") + } + s.Subnet = types.IPNet{IPNet: *net} + if s.Gateway != nil { + if !s.Subnet.Contains(s.Gateway) { + return errors.Errorf("gateway %s not in subnet %s", s.Gateway, &s.Subnet) + } + } else if addGateway { + ip, err := util.FirstIPInSubnet(net) + if err != nil { + return err + } + s.Gateway = ip + } + if s.LeaseRange != nil { + if s.LeaseRange.StartIP != nil && !s.Subnet.Contains(s.LeaseRange.StartIP) { + return errors.Errorf("lease range start ip %s not in subnet %s", s.LeaseRange.StartIP, &s.Subnet) + } + if s.LeaseRange.EndIP != nil && !s.Subnet.Contains(s.LeaseRange.EndIP) { + return errors.Errorf("lease range end ip %s not in subnet %s", s.LeaseRange.EndIP, &s.Subnet) + } + } + return nil +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/config_test.go libpod-3.4.4+ds1/libpod/network/cni/config_test.go --- libpod-3.2.1+ds1/libpod/network/cni/config_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/config_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,1241 @@ +// +build linux + +package cni_test + +import ( + "bytes" + "io/ioutil" + "net" + "os" + "path/filepath" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + gomegaTypes "github.com/onsi/gomega/types" + "github.com/sirupsen/logrus" + + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/libpod/network/util" +) + +var _ = Describe("Config", func() { + var ( + libpodNet types.ContainerNetwork + cniConfDir string + logBuffer bytes.Buffer + ) + + BeforeEach(func() { + var err error + cniConfDir, err = ioutil.TempDir("", "podman_cni_test") + if err != nil { + Fail("Failed to create tmpdir") + + } + logBuffer = bytes.Buffer{} + logrus.SetOutput(&logBuffer) + }) + + JustBeforeEach(func() { + var err error + libpodNet, err = getNetworkInterface(cniConfDir, false) + if err != nil { + Fail("Failed to create NewCNINetworkInterface") + } + }) + + AfterEach(func() { + os.RemoveAll(cniConfDir) + }) + + Context("basic network config tests", func() { + + It("check default network config exists", func() { + networks, err := libpodNet.NetworkList() + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(1)) + Expect(networks[0].Name).To(Equal("podman")) + Expect(networks[0].Driver).To(Equal("bridge")) + Expect(networks[0].NetworkInterface).To(Equal("cni-podman0")) + Expect(networks[0].Created.Before(time.Now())).To(BeTrue()) + Expect(networks[0].Subnets).To(HaveLen(1)) + Expect(networks[0].Subnets[0].Subnet.String()).To(Equal("10.88.0.0/16")) + Expect(networks[0].Subnets[0].Gateway.String()).To(Equal("10.88.0.1")) + Expect(networks[0].Subnets[0].LeaseRange).To(BeNil()) + Expect(networks[0].IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) + Expect(networks[0].Options).To(BeEmpty()) + Expect(networks[0].Labels).To(BeEmpty()) + Expect(networks[0].DNSEnabled).To(BeFalse()) + Expect(networks[0].Internal).To(BeFalse()) + }) + + It("basic network create, inspect and remove", func() { + // Because we get the time from the file create timestamp there is small precision + // loss so lets remove 500 milliseconds to make sure this test does not flake. + now := time.Now().Add(-500 * time.Millisecond) + network := types.Network{} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.IPAMOptions).ToNot(BeEmpty()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) + Expect(network1.Created.After(now)).To(BeTrue()) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.89.0.0/24")) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.89.0.1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + + // inspect by name + network2, err := libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network2).To(Equal(network1)) + + // inspect by ID + network2, err = libpodNet.NetworkInspect(network1.ID) + Expect(err).To(BeNil()) + Expect(network2).To(Equal(network1)) + + // inspect by partial ID + network2, err = libpodNet.NetworkInspect(network1.ID[:10]) + Expect(err).To(BeNil()) + Expect(network2).To(Equal(network1)) + + // create a new interface to force a config load from disk + libpodNet, err = getNetworkInterface(cniConfDir, false) + Expect(err).To(BeNil()) + + network2, err = libpodNet.NetworkInspect(network1.Name) + Expect(err).To(BeNil()) + Expect(network2).To(Equal(network1)) + + err = libpodNet.NetworkRemove(network1.Name) + Expect(err).To(BeNil()) + Expect(path).ToNot(BeARegularFile()) + + _, err = libpodNet.NetworkInspect(network1.Name) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("network not found")) + }) + + It("create two networks", func() { + network := types.Network{} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.Subnets).To(HaveLen(1)) + + network = types.Network{} + network2, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network2.Name).ToNot(Equal(network1.Name)) + Expect(network2.ID).ToNot(Equal(network1.ID)) + Expect(network2.NetworkInterface).ToNot(Equal(network1.NetworkInterface)) + Expect(network2.Subnets).To(HaveLen(1)) + Expect(network2.Subnets[0].Subnet.Contains(network1.Subnets[0].Subnet.IP)).To(BeFalse()) + }) + + It("create bridge config", func() { + network := types.Network{Driver: "bridge"} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.IPAMOptions).ToNot(BeEmpty()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.89.0.0/24")) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.89.0.1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + }) + + It("create bridge with same name should fail", func() { + network := types.Network{ + Driver: "bridge", + NetworkInterface: "cni-podman2", + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).To(Equal("cni-podman2")) + Expect(network1.Driver).To(Equal("bridge")) + + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("bridge name cni-podman2 already in use")) + }) + + It("create macvlan config", func() { + network := types.Network{Driver: "macvlan"} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("macvlan")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.IPAMOptions).ToNot(BeEmpty()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + Expect(network1.Subnets).To(HaveLen(0)) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + }) + + It("create macvlan config with device", func() { + network := types.Network{ + Driver: "macvlan", + NetworkInterface: "lo", + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("macvlan")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.Subnets).To(HaveLen(0)) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + grepInFile(path, `"type": "macvlan"`) + grepInFile(path, `"master": "lo"`) + grepInFile(path, `"type": "dhcp"`) + }) + + It("create macvlan config with subnet", func() { + subnet := "10.1.0.0/24" + n, _ := types.ParseCIDR(subnet) + network := types.Network{ + Driver: "macvlan", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("macvlan")) + Expect(network1.Labels).To(BeEmpty()) + Expect(network1.Options).To(BeEmpty()) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.1.0.1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.DNSEnabled).To(BeFalse()) + Expect(network1.Internal).To(BeFalse()) + Expect(network1.IPAMOptions).To(HaveKeyWithValue("driver", "host-local")) + grepInFile(path, `"type": "host-local"`) + }) + + It("create macvlan config with invalid device", func() { + network := types.Network{ + Driver: "macvlan", + NetworkInterface: "idonotexists", + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("parent interface idonotexists does not exists")) + }) + + It("create macvlan config with internal should fail", func() { + network := types.Network{ + Driver: "macvlan", + Internal: true, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("internal is not supported with macvlan")) + }) + + It("create bridge with subnet", func() { + subnet := "10.0.0.0/24" + n, _ := types.ParseCIDR(subnet) + + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + }) + + It("create bridge with ipv6 subnet", func() { + subnet := "fdcc::/64" + n, _ := types.ParseCIDR(subnet) + + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.IPv6Enabled).To(BeTrue()) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("fdcc::1")) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + }) + + It("create bridge with ipv6 enabled", func() { + network := types.Network{ + Driver: "bridge", + IPv6Enabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(2)) + Expect(network1.Subnets[0].Subnet.String()).To(ContainSubstring(".0/24")) + Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.Subnets[1].Subnet.String()).To(ContainSubstring("::/64")) + Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[1].LeaseRange).To(BeNil()) + }) + + It("create bridge with ipv6 enabled and ipv4 subnet", func() { + subnet := "10.100.0.0/24" + n, _ := types.ParseCIDR(subnet) + + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + IPv6Enabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(2)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.Subnets[1].Subnet.String()).To(ContainSubstring("::/64")) + Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[1].LeaseRange).To(BeNil()) + }) + + It("create bridge with ipv6 enabled and ipv6 subnet", func() { + subnet := "fd66::/64" + n, _ := types.ParseCIDR(subnet) + + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n}, + }, + IPv6Enabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(2)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.Subnets[1].Subnet.String()).To(ContainSubstring(".0/24")) + Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[1].LeaseRange).To(BeNil()) + }) + + It("create bridge with ipv6 enabled and ipv4+ipv6 subnet", func() { + subnet1 := "10.100.0.0/24" + n1, _ := types.ParseCIDR(subnet1) + subnet2 := "fd66::/64" + n2, _ := types.ParseCIDR(subnet2) + + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n1}, {Subnet: n2}, + }, + IPv6Enabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(2)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet1)) + Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.Subnets[1].Subnet.String()).To(Equal(subnet2)) + Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[1].LeaseRange).To(BeNil()) + }) + + It("create bridge with ipv6 enabled and two ipv4 subnets", func() { + subnet1 := "10.100.0.0/24" + n1, _ := types.ParseCIDR(subnet1) + subnet2 := "10.200.0.0/24" + n2, _ := types.ParseCIDR(subnet2) + + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n1}, {Subnet: n2}, + }, + IPv6Enabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(3)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet1)) + Expect(network1.Subnets[0].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + Expect(network1.Subnets[1].Subnet.String()).To(Equal(subnet2)) + Expect(network1.Subnets[1].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[1].LeaseRange).To(BeNil()) + Expect(network1.Subnets[2].Subnet.String()).To(ContainSubstring("::/64")) + Expect(network1.Subnets[2].Gateway).ToNot(BeNil()) + Expect(network1.Subnets[2].LeaseRange).To(BeNil()) + }) + + It("create bridge with subnet and gateway", func() { + subnet := "10.0.0.5/24" + n, _ := types.ParseCIDR(subnet) + gateway := "10.0.0.50" + g := net.ParseIP(gateway) + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, Gateway: g}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal("10.0.0.0/24")) + Expect(network1.Subnets[0].Gateway.String()).To(Equal(gateway)) + Expect(network1.Subnets[0].LeaseRange).To(BeNil()) + }) + + It("create bridge with subnet and gateway not in the same subnet", func() { + subnet := "10.0.0.0/24" + n, _ := types.ParseCIDR(subnet) + gateway := "10.10.0.50" + g := net.ParseIP(gateway) + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, Gateway: g}, + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("not in subnet")) + }) + + It("create bridge with subnet and lease range", func() { + subnet := "10.0.0.0/24" + n, _ := types.ParseCIDR(subnet) + startIP := "10.0.0.10" + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, LeaseRange: &types.LeaseRange{ + StartIP: net.ParseIP(startIP), + }}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) + Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) + + endIP := "10.0.0.10" + network = types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, LeaseRange: &types.LeaseRange{ + EndIP: net.ParseIP(endIP), + }}, + }, + } + network1, err = libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(filepath.Join(cniConfDir, network1.Name+".conflist")).To(BeARegularFile()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) + Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) + + network = types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, LeaseRange: &types.LeaseRange{ + StartIP: net.ParseIP(startIP), + EndIP: net.ParseIP(endIP), + }}, + }, + } + network1, err = libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(BeEmpty()) + Expect(network1.ID).ToNot(BeEmpty()) + Expect(network1.NetworkInterface).ToNot(BeEmpty()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).To(Equal(subnet)) + Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1")) + Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP)) + Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP)) + }) + + It("create bridge with subnet and invalid lease range", func() { + subnet := "10.0.0.0/24" + n, _ := types.ParseCIDR(subnet) + startIP := "10.0.1.2" + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, LeaseRange: &types.LeaseRange{ + StartIP: net.ParseIP(startIP), + }}, + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("not in subnet")) + + endIP := "10.1.1.1" + network = types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: n, LeaseRange: &types.LeaseRange{ + EndIP: net.ParseIP(endIP), + }}, + }, + } + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("not in subnet")) + }) + + It("create bridge with broken subnet", func() { + network := types.Network{ + Driver: "bridge", + Subnets: []types.Subnet{ + {Subnet: types.IPNet{}}, + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("subnet invalid")) + }) + + It("create network with name", func() { + name := "myname" + network := types.Network{ + Name: name, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).To(Equal(name)) + Expect(network1.NetworkInterface).ToNot(Equal(name)) + Expect(network1.Driver).To(Equal("bridge")) + }) + + It("create network with invalid name", func() { + name := "myname@some" + network := types.Network{ + Name: name, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + }) + + It("create network with name", func() { + name := "myname" + network := types.Network{ + Name: name, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).To(Equal(name)) + Expect(network1.NetworkInterface).ToNot(Equal(name)) + Expect(network1.Driver).To(Equal("bridge")) + }) + + It("create network with invalid name", func() { + name := "myname@some" + network := types.Network{ + Name: name, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + }) + + It("create network with interface name", func() { + name := "myname" + network := types.Network{ + NetworkInterface: name, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).ToNot(Equal(name)) + Expect(network1.NetworkInterface).To(Equal(name)) + Expect(network1.Driver).To(Equal("bridge")) + }) + + It("create network with invalid interface name", func() { + name := "myname@some" + network := types.Network{ + NetworkInterface: name, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + }) + + It("create network with ID should fail", func() { + id := "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121" + network := types.Network{ + ID: id, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("ID can not be set for network create")) + }) + + It("create bridge with dns", func() { + network := types.Network{ + Driver: "bridge", + DNSEnabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.DNSEnabled).To(BeTrue()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + grepInFile(path, `"type": "dnsname"`) + }) + + It("create bridge with internal", func() { + network := types.Network{ + Driver: "bridge", + Internal: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).ToNot(BeEmpty()) + Expect(network1.Subnets[0].Gateway).To(BeNil()) + Expect(network1.Internal).To(BeTrue()) + }) + + It("create network with labels", func() { + network := types.Network{ + Labels: map[string]string{ + "key": "value", + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Labels).ToNot(BeNil()) + Expect(network1.Labels).To(ContainElement("value")) + }) + + It("create network with mtu option", func() { + network := types.Network{ + Options: map[string]string{ + "mtu": "1500", + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Options).ToNot(BeNil()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + grepInFile(path, `"mtu": 1500,`) + Expect(network1.Options).To(HaveKeyWithValue("mtu", "1500")) + }) + + It("create network with invalid mtu option", func() { + network := types.Network{ + Options: map[string]string{ + "mtu": "abc", + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) + + network = types.Network{ + Options: map[string]string{ + "mtu": "-1", + }, + } + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`mtu -1 is less than zero`)) + }) + + It("create macvlan network with mtu option", func() { + network := types.Network{ + Driver: "macvlan", + Options: map[string]string{ + "mtu": "1500", + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("macvlan")) + Expect(network1.Options).ToNot(BeNil()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + grepInFile(path, `"mtu": 1500`) + Expect(network1.Options).To(HaveKeyWithValue("mtu", "1500")) + }) + + It("create network with vlan option", func() { + network := types.Network{ + Options: map[string]string{ + "vlan": "5", + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Options).ToNot(BeNil()) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + grepInFile(path, `"vlan": 5,`) + Expect(network1.Options).To(HaveKeyWithValue("vlan", "5")) + }) + + It("create network with invalid vlan option", func() { + network := types.Network{ + Options: map[string]string{ + "vlan": "abc", + }, + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`parsing "abc": invalid syntax`)) + + network = types.Network{ + Options: map[string]string{ + "vlan": "-1", + }, + } + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`vlan ID -1 must be between 0 and 4094`)) + }) + + It("network create unsupported option", func() { + network := types.Network{Options: map[string]string{ + "someopt": "", + }} + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("unsupported network option someopt")) + }) + + It("network create unsupported driver", func() { + network := types.Network{ + Driver: "someDriver", + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("unsupported driver someDriver")) + }) + + It("network create internal and dns", func() { + network := types.Network{ + Driver: "bridge", + Internal: true, + DNSEnabled: true, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + Expect(network1.Subnets).To(HaveLen(1)) + Expect(network1.Subnets[0].Subnet.String()).ToNot(BeEmpty()) + Expect(network1.Subnets[0].Gateway).To(BeNil()) + Expect(network1.Internal).To(BeTrue()) + // internal and dns does not work, dns should be disabled + Expect(network1.DNSEnabled).To(BeFalse()) + logString := logBuffer.String() + Expect(logString).To(ContainSubstring("dnsname and internal networks are incompatible")) + }) + + It("create config with podman machine plugin", func() { + libpodNet, err := getNetworkInterface(cniConfDir, true) + Expect(err).To(BeNil()) + + network := types.Network{} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Driver).To(Equal("bridge")) + path := filepath.Join(cniConfDir, network1.Name+".conflist") + Expect(path).To(BeARegularFile()) + grepInFile(path, `"type": "podman-machine",`) + }) + + It("network inspect partial ID", func() { + network := types.Network{Name: "net4"} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.ID).To(Equal("b44b7426c006839e7fe6f15d1faf64db58079d5233cba09b43be2257c1652cf5")) + network = types.Network{Name: "net5"} + network1, err = libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.ID).To(Equal("b67e86fb039828ad686aa13667975b9e51f192eb617044faf06cded9d31602af")) + + // Note ID is the sha256 from the name + // both net4 and net5 have an ID starting with b... + _, err = libpodNet.NetworkInspect("b") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("more than one result for network ID")) + }) + + It("network create two with same name", func() { + network := types.Network{Name: "net"} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + Expect(network1.Name).To(Equal("net")) + network = types.Network{Name: "net"} + _, err = libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("network name net already used")) + }) + + It("remove default network config should fail", func() { + err := libpodNet.NetworkRemove("podman") + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("default network podman cannot be removed")) + + network, err := libpodNet.NetworkInspect("podman") + Expect(err).To(BeNil()) + err = libpodNet.NetworkRemove(network.ID) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(Equal("default network podman cannot be removed")) + }) + + }) + + Context("network load valid existing ones", func() { + + BeforeEach(func() { + dir := "testfiles/valid" + files, err := ioutil.ReadDir(dir) + if err != nil { + Fail("Failed to read test directory") + } + for _, file := range files { + filename := file.Name() + data, err := ioutil.ReadFile(filepath.Join(dir, filename)) + if err != nil { + Fail("Failed to copy test files") + } + err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700) + if err != nil { + Fail("Failed to copy test files") + } + } + }) + + It("load networks from disk", func() { + nets, err := libpodNet.NetworkList() + Expect(err).To(BeNil()) + Expect(nets).To(HaveLen(9)) + // test the we do not show logrus warnings/errors + logString := logBuffer.String() + Expect(logString).To(BeEmpty()) + }) + + It("change network struct fields should not affect network struct in the backend", func() { + nets, err := libpodNet.NetworkList() + Expect(err).To(BeNil()) + Expect(nets).To(HaveLen(9)) + + nets[0].Name = "myname" + nets, err = libpodNet.NetworkList() + Expect(err).To(BeNil()) + Expect(nets).To(HaveLen(9)) + Expect(nets).ToNot(ContainElement(HaveNetworkName("myname"))) + + network, err := libpodNet.NetworkInspect("bridge") + Expect(err).To(BeNil()) + network.NetworkInterface = "abc" + + network, err = libpodNet.NetworkInspect("bridge") + Expect(err).To(BeNil()) + Expect(network.NetworkInterface).ToNot(Equal("abc")) + }) + + It("bridge network", func() { + network, err := libpodNet.NetworkInspect("bridge") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("bridge")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("cni-podman9")) + Expect(network.Driver).To(Equal("bridge")) + Expect(network.Subnets).To(HaveLen(1)) + Expect(network.Subnets[0].Subnet.String()).To(Equal("10.89.8.0/24")) + Expect(network.Subnets[0].Gateway.String()).To(Equal("10.89.8.1")) + Expect(network.Subnets[0].LeaseRange).ToNot(BeNil()) + Expect(network.Subnets[0].LeaseRange.StartIP.String()).To(Equal("10.89.8.20")) + Expect(network.Subnets[0].LeaseRange.EndIP.String()).To(Equal("10.89.8.50")) + Expect(network.Internal).To(BeFalse()) + }) + + It("macvlan network", func() { + network, err := libpodNet.NetworkInspect("macvlan") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("macvlan")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("lo")) + Expect(network.Driver).To(Equal("macvlan")) + Expect(network.Subnets).To(HaveLen(0)) + // DHCP + }) + + It("internal network", func() { + network, err := libpodNet.NetworkInspect("internal") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("internal")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("cni-podman8")) + Expect(network.Driver).To(Equal("bridge")) + Expect(network.Subnets).To(HaveLen(1)) + Expect(network.Subnets[0].Subnet.String()).To(Equal("10.89.7.0/24")) + Expect(network.Subnets[0].Gateway).To(BeNil()) + Expect(network.Internal).To(BeTrue()) + }) + + It("bridge network with mtu", func() { + network, err := libpodNet.NetworkInspect("mtu") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("mtu")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("cni-podman13")) + Expect(network.Driver).To(Equal("bridge")) + Expect(network.Subnets).To(HaveLen(1)) + Expect(network.Subnets[0].Subnet.String()).To(Equal("10.89.11.0/24")) + Expect(network.Subnets[0].Gateway.String()).To(Equal("10.89.11.1")) + Expect(network.Internal).To(BeFalse()) + Expect(network.Options).To(HaveLen(1)) + Expect(network.Options).To(HaveKeyWithValue("mtu", "1500")) + }) + + It("macvlan network with mtu", func() { + network, err := libpodNet.NetworkInspect("macvlan_mtu") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("macvlan_mtu")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("lo")) + Expect(network.Driver).To(Equal("macvlan")) + Expect(network.Subnets).To(HaveLen(0)) + Expect(network.Internal).To(BeFalse()) + Expect(network.Options).To(HaveLen(1)) + Expect(network.Options).To(HaveKeyWithValue("mtu", "1300")) + Expect(network.IPAMOptions).To(HaveLen(1)) + Expect(network.IPAMOptions).To(HaveKeyWithValue("driver", "dhcp")) + }) + + It("bridge network with vlan", func() { + network, err := libpodNet.NetworkInspect("vlan") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("vlan")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("cni-podman14")) + Expect(network.Driver).To(Equal("bridge")) + Expect(network.Subnets).To(HaveLen(1)) + Expect(network.Options).To(HaveLen(1)) + Expect(network.Options).To(HaveKeyWithValue("vlan", "5")) + }) + + It("bridge network with labels", func() { + network, err := libpodNet.NetworkInspect("label") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("label")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("cni-podman15")) + Expect(network.Driver).To(Equal("bridge")) + Expect(network.Subnets).To(HaveLen(1)) + Expect(network.Labels).To(HaveLen(1)) + Expect(network.Labels).To(HaveKeyWithValue("mykey", "value")) + }) + + It("dual stack network", func() { + network, err := libpodNet.NetworkInspect("dualstack") + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal("dualstack")) + Expect(network.ID).To(HaveLen(64)) + Expect(network.NetworkInterface).To(Equal("cni-podman21")) + Expect(network.Driver).To(Equal("bridge")) + Expect(network.Subnets).To(HaveLen(2)) + + sub1, _ := types.ParseCIDR("fd10:88:a::/64") + sub2, _ := types.ParseCIDR("10.89.19.0/24") + Expect(network.Subnets).To(ContainElements( + types.Subnet{Subnet: sub1, Gateway: net.ParseIP("fd10:88:a::1")}, + types.Subnet{Subnet: sub2, Gateway: net.ParseIP("10.89.19.10").To4()}, + )) + }) + + It("network list with filters (name)", func() { + filters := map[string][]string{ + "name": {"internal", "bridge"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(2)) + Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) + }) + + It("network list with filters (partial name)", func() { + filters := map[string][]string{ + "name": {"inte", "bri"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(2)) + Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) + }) + + It("network list with filters (id)", func() { + filters := map[string][]string{ + "id": {"3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f", "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(2)) + Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) + }) + + It("network list with filters (id)", func() { + filters := map[string][]string{ + "id": {"3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f", "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(2)) + Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) + }) + + It("network list with filters (partial id)", func() { + filters := map[string][]string{ + "id": {"3bed2cb3a3acf7b6a8ef408420", "17f29b073143d8cd97b5bbe492bde"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(2)) + Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"))) + }) + + It("network list with filters (driver)", func() { + filters := map[string][]string{ + "driver": {"bridge", "macvlan"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(9)) + Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"), + HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"), + HaveNetworkName("label"), HaveNetworkName("macvlan"), HaveNetworkName("macvlan_mtu"), HaveNetworkName("dualstack"))) + }) + + It("network list with filters (label)", func() { + filters := map[string][]string{ + "label": {"mykey"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(1)) + Expect(networks).To(ConsistOf(HaveNetworkName("label"))) + + filters = map[string][]string{ + "label": {"mykey=value"}, + } + filterFuncs, err = util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err = libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(1)) + Expect(networks).To(ConsistOf(HaveNetworkName("label"))) + }) + + It("network list with filters", func() { + filters := map[string][]string{ + "driver": {"bridge"}, + "label": {"mykey"}, + } + filterFuncs, err := util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + Expect(filterFuncs).To(HaveLen(2)) + + networks, err := libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(1)) + Expect(networks).To(ConsistOf(HaveNetworkName("label"))) + + filters = map[string][]string{ + "driver": {"macvlan"}, + "label": {"mykey"}, + } + filterFuncs, err = util.GenerateNetworkFilters(filters) + Expect(err).To(BeNil()) + + networks, err = libpodNet.NetworkList(filterFuncs...) + Expect(err).To(BeNil()) + Expect(networks).To(HaveLen(0)) + }) + + It("crate bridge network with used interface name", func() { + network := types.Network{ + NetworkInterface: "cni-podman9", + } + _, err := libpodNet.NetworkCreate(network) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("bridge name cni-podman9 already in use")) + }) + }) + + Context("network load invalid existing ones", func() { + + BeforeEach(func() { + dir := "testfiles/invalid" + files, err := ioutil.ReadDir(dir) + if err != nil { + Fail("Failed to read test directory") + } + for _, file := range files { + filename := file.Name() + data, err := ioutil.ReadFile(filepath.Join(dir, filename)) + if err != nil { + Fail("Failed to copy test files") + } + err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700) + if err != nil { + Fail("Failed to copy test files") + } + } + }) + + It("load invalid networks from disk", func() { + nets, err := libpodNet.NetworkList() + Expect(err).To(BeNil()) + Expect(nets).To(HaveLen(2)) + logString := logBuffer.String() + Expect(logString).To(ContainSubstring("noname.conflist: error parsing configuration list: no name")) + Expect(logString).To(ContainSubstring("noplugin.conflist: error parsing configuration list: no plugins in list")) + Expect(logString).To(ContainSubstring("invalidname.conflist has invalid name, skipping: names must match")) + Expect(logString).To(ContainSubstring("has the same network name as")) + Expect(logString).To(ContainSubstring("broken.conflist: error parsing configuration list")) + Expect(logString).To(ContainSubstring("invalid_gateway.conflist could not be converted to a libpod config, skipping: failed to parse gateway ip 10.89.8")) + }) + + }) + +}) + +func grepInFile(path string, match string) { + data, err := ioutil.ReadFile(path) + ExpectWithOffset(1, err).To(BeNil()) + ExpectWithOffset(1, string(data)).To(ContainSubstring(match)) +} + +// HaveNetworkName is a custom GomegaMatcher to match a network name +func HaveNetworkName(name string) gomegaTypes.GomegaMatcher { + return WithTransform(func(e types.Network) string { + return e.Name + }, Equal(name)) +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/network.go libpod-3.4.4+ds1/libpod/network/cni/network.go --- libpod-3.2.1+ds1/libpod/network/cni/network.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,340 @@ +// +build linux + +package cni + +import ( + "context" + "crypto/sha256" + "encoding/hex" + "fmt" + "net" + "os" + "strings" + + "github.com/containernetworking/cni/libcni" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/libpod/network/util" + pkgutil "github.com/containers/podman/v3/pkg/util" + "github.com/containers/storage/pkg/lockfile" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +type cniNetwork struct { + // cniConfigDir is directory where the cni config files are stored. + cniConfigDir string + // cniPluginDirs is a list of directories where cni should look for the plugins. + cniPluginDirs []string + + cniConf *libcni.CNIConfig + + // defaultNetwork is the name for the default network. + defaultNetwork string + // defaultSubnet is the default subnet for the default network. + defaultSubnet types.IPNet + + // isMachine describes whenever podman runs in a podman machine environment. + isMachine bool + + // lock is a internal lock for critical operations + lock lockfile.Locker + + // networks is a map with loaded networks, the key is the network name + networks map[string]*network +} + +type network struct { + // filename is the full path to the cni config file on disk + filename string + libpodNet *types.Network + cniNet *libcni.NetworkConfigList +} + +type InitConfig struct { + // CNIConfigDir is directory where the cni config files are stored. + CNIConfigDir string + // CNIPluginDirs is a list of directories where cni should look for the plugins. + CNIPluginDirs []string + + // DefaultNetwork is the name for the default network. + DefaultNetwork string + // DefaultSubnet is the default subnet for the default network. + DefaultSubnet string + + // IsMachine describes whenever podman runs in a podman machine environment. + IsMachine bool + + // LockFile is the path to lock file. + LockFile string +} + +// NewCNINetworkInterface creates the ContainerNetwork interface for the CNI backend. +// Note: The networks are not loaded from disk until a method is called. +func NewCNINetworkInterface(conf InitConfig) (types.ContainerNetwork, error) { + // TODO: consider using a shared memory lock + lock, err := lockfile.GetLockfile(conf.LockFile) + if err != nil { + return nil, err + } + + defaultNetworkName := conf.DefaultNetwork + if defaultNetworkName == "" { + defaultNetworkName = types.DefaultNetworkName + } + + defaultSubnet := conf.DefaultSubnet + if defaultSubnet == "" { + defaultSubnet = types.DefaultSubnet + } + defaultNet, err := types.ParseCIDR(defaultSubnet) + if err != nil { + return nil, errors.Wrap(err, "failed to parse default subnet") + } + + cni := libcni.NewCNIConfig(conf.CNIPluginDirs, &cniExec{}) + n := &cniNetwork{ + cniConfigDir: conf.CNIConfigDir, + cniPluginDirs: conf.CNIPluginDirs, + cniConf: cni, + defaultNetwork: defaultNetworkName, + defaultSubnet: defaultNet, + isMachine: conf.IsMachine, + lock: lock, + } + + return n, nil +} + +func (n *cniNetwork) loadNetworks() error { + // skip loading networks if they are already loaded + if n.networks != nil { + return nil + } + // FIXME: do we have to support other file types as well, e.g. .conf? + files, err := libcni.ConfFiles(n.cniConfigDir, []string{".conflist"}) + if err != nil { + return err + } + networks := make(map[string]*network, len(files)) + for _, file := range files { + conf, err := libcni.ConfListFromFile(file) + if err != nil { + // do not log ENOENT errors + if !os.IsNotExist(err) { + logrus.Warnf("Error loading CNI config file %s: %v", file, err) + } + continue + } + + if !define.NameRegex.MatchString(conf.Name) { + logrus.Warnf("CNI config list %s has invalid name, skipping: %v", file, define.RegexError) + continue + } + + if _, err := n.cniConf.ValidateNetworkList(context.Background(), conf); err != nil { + logrus.Warnf("Error validating CNI config file %s: %v", file, err) + continue + } + + if val, ok := networks[conf.Name]; ok { + logrus.Warnf("CNI config list %s has the same network name as %s, skipping", file, val.filename) + continue + } + + net, err := createNetworkFromCNIConfigList(conf, file) + if err != nil { + logrus.Errorf("CNI config list %s could not be converted to a libpod config, skipping: %v", file, err) + continue + } + logrus.Tracef("Successfully loaded network %s: %v", net.Name, net) + networkInfo := network{ + filename: file, + cniNet: conf, + libpodNet: net, + } + networks[net.Name] = &networkInfo + } + + // create the default network in memory if it did not exists on disk + if networks[n.defaultNetwork] == nil { + networkInfo, err := n.createDefaultNetwork() + if err != nil { + return errors.Wrapf(err, "failed to create default network %s", n.defaultNetwork) + } + networks[n.defaultNetwork] = networkInfo + } + + logrus.Debugf("Successfully loaded %d networks", len(networks)) + n.networks = networks + return nil +} + +func (n *cniNetwork) createDefaultNetwork() (*network, error) { + net := types.Network{ + Name: n.defaultNetwork, + NetworkInterface: "cni-podman0", + Driver: types.BridgeNetworkDriver, + Subnets: []types.Subnet{ + {Subnet: n.defaultSubnet}, + }, + } + return n.networkCreate(net, false) +} + +// getNetwork will lookup a network by name or ID. It returns an +// error when no network was found or when more than one network +// with the given (partial) ID exists. +// getNetwork will read from the networks map, therefore the caller +// must ensure that n.lock is locked before using it. +func (n *cniNetwork) getNetwork(nameOrID string) (*network, error) { + // fast path check the map key, this will only work for names + if val, ok := n.networks[nameOrID]; ok { + return val, nil + } + // If there was no match we might got a full or partial ID. + var net *network + for _, val := range n.networks { + // This should not happen because we already looked up the map by name but check anyway. + if val.libpodNet.Name == nameOrID { + return val, nil + } + + if strings.HasPrefix(val.libpodNet.ID, nameOrID) { + if net != nil { + return nil, errors.Errorf("more than one result for network ID %s", nameOrID) + } + net = val + } + } + if net != nil { + return net, nil + } + return nil, errors.Wrapf(define.ErrNoSuchNetwork, "unable to find network with name or ID %s", nameOrID) +} + +// getNetworkIDFromName creates a network ID from the name. It is just the +// sha256 hash so it is not safe but it should be safe enough for our use case. +func getNetworkIDFromName(name string) string { + hash := sha256.Sum256([]byte(name)) + return hex.EncodeToString(hash[:]) +} + +// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet +func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) { + networks, err := n.getUsedSubnets() + if err != nil { + return nil, err + } + + // the default podman network is 10.88.0.0/16 + // start locking for free /24 networks + network := &net.IPNet{ + IP: net.IP{10, 89, 0, 0}, + Mask: net.IPMask{255, 255, 255, 0}, + } + + // TODO: make sure to not use public subnets + for { + if intersectsConfig := util.NetworkIntersectsWithNetworks(network, networks); !intersectsConfig { + logrus.Debugf("found free ipv4 network subnet %s", network.String()) + return &types.Subnet{ + Subnet: types.IPNet{IPNet: *network}, + }, nil + } + network, err = util.NextSubnet(network) + if err != nil { + return nil, err + } + } +} + +// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet +func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) { + networks, err := n.getUsedSubnets() + if err != nil { + return nil, err + } + + // FIXME: Is 10000 fine as limit? We should prevent an endless loop. + for i := 0; i < 10000; i++ { + // RFC4193: Choose the ipv6 subnet random and NOT sequentially. + network, err := util.GetRandomIPv6Subnet() + if err != nil { + return nil, err + } + if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, networks); !intersectsConfig { + logrus.Debugf("found free ipv6 network subnet %s", network.String()) + return &types.Subnet{ + Subnet: types.IPNet{IPNet: network}, + }, nil + } + } + return nil, errors.New("failed to get random ipv6 subnet") +} + +// getUsedSubnets returns a list of all used subnets by network +// configs and interfaces on the host. +func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) { + // first, load all used subnets from network configs + subnets := make([]*net.IPNet, 0, len(n.networks)) + for _, val := range n.networks { + for _, subnet := range val.libpodNet.Subnets { + // nolint:exportloopref + subnets = append(subnets, &subnet.Subnet.IPNet) + } + } + // second, load networks from the current system + liveSubnets, err := util.GetLiveNetworkSubnets() + if err != nil { + return nil, err + } + return append(subnets, liveSubnets...), nil +} + +// getFreeDeviceName returns a free device name which can +// be used for new configs as name and bridge interface name +func (n *cniNetwork) getFreeDeviceName() (string, error) { + bridgeNames := n.getBridgeInterfaceNames() + netNames := n.getUsedNetworkNames() + liveInterfaces, err := util.GetLiveNetworkNames() + if err != nil { + return "", nil + } + names := make([]string, 0, len(bridgeNames)+len(netNames)+len(liveInterfaces)) + names = append(names, bridgeNames...) + names = append(names, netNames...) + names = append(names, liveInterfaces...) + // FIXME: Is a limit fine? + // Start by 1, 0 is reserved for the default network + for i := 1; i < 1000000; i++ { + deviceName := fmt.Sprintf("%s%d", cniDeviceName, i) + if !pkgutil.StringInSlice(deviceName, names) { + logrus.Debugf("found free device name %s", deviceName) + return deviceName, nil + } + } + return "", errors.New("could not find free device name, to many iterations") +} + +// getUsedNetworkNames returns all network names already used +// by network configs +func (n *cniNetwork) getUsedNetworkNames() []string { + names := make([]string, 0, len(n.networks)) + for _, val := range n.networks { + names = append(names, val.libpodNet.Name) + } + return names +} + +// getUsedNetworkNames returns all bridge device names already used +// by network configs +func (n *cniNetwork) getBridgeInterfaceNames() []string { + names := make([]string, 0, len(n.networks)) + for _, val := range n.networks { + if val.libpodNet.Driver == types.BridgeNetworkDriver { + names = append(names, val.libpodNet.NetworkInterface) + } + } + return names +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/README.md libpod-3.4.4+ds1/libpod/network/cni/README.md --- libpod-3.2.1+ds1/libpod/network/cni/README.md 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,10 @@ +This package abstracts CNI from libpod. +It implements the `ContainerNetwork` interface defined in [libpod/network/types/network.go](../types/network.go) for the CNI backend. + + +## Testing +Run the tests with: +``` +go test -v -mod=vendor -cover ./libpod/network/cni/ +``` +Run the tests as root to also test setup/teardown. This will execute CNI and therefore the cni plugins have to be installed. diff -Nru libpod-3.2.1+ds1/libpod/network/cni/run.go libpod-3.4.4+ds1/libpod/network/cni/run.go --- libpod-3.2.1+ds1/libpod/network/cni/run.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/run.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,307 @@ +// +build linux + +package cni + +import ( + "context" + "net" + "os" + "strings" + + "github.com/containernetworking/cni/libcni" + cnitypes "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/plugins/pkg/ns" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" +) + +// Setup will setup the container network namespace. It returns +// a map of StatusBlocks, the key is the network name. +func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (map[string]types.StatusBlock, error) { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return nil, err + } + + if namespacePath == "" { + return nil, errors.New("namespacePath is empty") + } + if options.ContainerID == "" { + return nil, errors.New("ContainerID is empty") + } + if len(options.Networks) == 0 { + return nil, errors.New("must specify at least one network") + } + for name, netOpts := range options.Networks { + network := n.networks[name] + if network == nil { + return nil, errors.Wrapf(define.ErrNoSuchNetwork, "network %s", name) + } + err := validatePerNetworkOpts(network, netOpts) + if err != nil { + return nil, err + } + } + + // set the loopback adapter up in the container netns + err = ns.WithNetNSPath(namespacePath, func(_ ns.NetNS) error { + link, err := netlink.LinkByName("lo") + if err == nil { + err = netlink.LinkSetUp(link) + } + return err + }) + if err != nil { + return nil, errors.Wrapf(err, "failed to set the loopback adapter up") + } + + var retErr error + teardownOpts := options + teardownOpts.Networks = map[string]types.PerNetworkOptions{} + // make sure to teardown the already connected networks on error + defer func() { + if retErr != nil { + if len(teardownOpts.Networks) > 0 { + err := n.teardown(namespacePath, types.TeardownOptions(teardownOpts)) + if err != nil { + logrus.Warn(err) + } + } + } + }() + + ports, err := convertSpecgenPortsToCNIPorts(options.PortMappings) + if err != nil { + return nil, err + } + + results := make(map[string]types.StatusBlock, len(options.Networks)) + for name, netOpts := range options.Networks { + network := n.networks[name] + rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, netOpts) + + // If we have more than one static ip we need parse the ips via runtime config, + // make sure to add the ips capability to the first plugin otherwise it doesn't get the ips + if len(netOpts.StaticIPs) > 0 && !network.cniNet.Plugins[0].Network.Capabilities["ips"] { + caps := make(map[string]interface{}) + caps["capabilities"] = map[string]bool{"ips": true} + network.cniNet.Plugins[0], retErr = libcni.InjectConf(network.cniNet.Plugins[0], caps) + if retErr != nil { + return nil, retErr + } + } + + var res cnitypes.Result + res, retErr = n.cniConf.AddNetworkList(context.Background(), network.cniNet, rt) + // Add this network to teardown opts since it is now connected. + // Also add this if an errors was returned since we want to call teardown on this regardless. + teardownOpts.Networks[name] = netOpts + if retErr != nil { + return nil, retErr + } + + var cnires *current.Result + cnires, retErr = current.GetResult(res) + if retErr != nil { + return nil, retErr + } + logrus.Debugf("cni result for container %s network %s: %v", options.ContainerID, name, cnires) + var status types.StatusBlock + status, retErr = cniResultToStatus(cnires) + if retErr != nil { + return nil, retErr + } + results[name] = status + } + return results, nil +} + +// cniResultToStatus convert the cni result to status block +func cniResultToStatus(cniResult *current.Result) (types.StatusBlock, error) { + result := types.StatusBlock{} + nameservers := make([]net.IP, 0, len(cniResult.DNS.Nameservers)) + for _, nameserver := range cniResult.DNS.Nameservers { + ip := net.ParseIP(nameserver) + if ip == nil { + return result, errors.Errorf("failed to parse cni nameserver ip %s", nameserver) + } + nameservers = append(nameservers, ip) + } + result.DNSServerIPs = nameservers + result.DNSSearchDomains = cniResult.DNS.Search + + interfaces := make(map[string]types.NetInterface) + for _, ip := range cniResult.IPs { + if ip.Interface == nil { + // we do no expect ips without an interface + continue + } + if len(cniResult.Interfaces) <= *ip.Interface { + return result, errors.Errorf("invalid cni result, interface index %d out of range", *ip.Interface) + } + cniInt := cniResult.Interfaces[*ip.Interface] + netInt, ok := interfaces[cniInt.Name] + if ok { + netInt.Networks = append(netInt.Networks, types.NetAddress{ + Subnet: types.IPNet{IPNet: ip.Address}, + Gateway: ip.Gateway, + }) + interfaces[cniInt.Name] = netInt + } else { + mac, err := net.ParseMAC(cniInt.Mac) + if err != nil { + return result, err + } + interfaces[cniInt.Name] = types.NetInterface{ + MacAddress: mac, + Networks: []types.NetAddress{{ + Subnet: types.IPNet{IPNet: ip.Address}, + Gateway: ip.Gateway, + }}, + } + } + } + result.Interfaces = interfaces + return result, nil +} + +// validatePerNetworkOpts checks that all given static ips are in a subnet on this network +func validatePerNetworkOpts(network *network, netOpts types.PerNetworkOptions) error { + if netOpts.InterfaceName == "" { + return errors.Errorf("interface name on network %s is empty", network.libpodNet.Name) + } +outer: + for _, ip := range netOpts.StaticIPs { + for _, s := range network.libpodNet.Subnets { + if s.Subnet.Contains(ip) { + continue outer + } + } + return errors.Errorf("requested static ip %s not in any subnet on network %s", ip.String(), network.libpodNet.Name) + } + if len(netOpts.Aliases) > 0 && !network.libpodNet.DNSEnabled { + return errors.New("cannot set aliases on a network without dns enabled") + } + return nil +} + +func getRuntimeConfig(netns, conName, conID, networkName string, ports []cniPortMapEntry, opts types.PerNetworkOptions) *libcni.RuntimeConf { + rt := &libcni.RuntimeConf{ + ContainerID: conID, + NetNS: netns, + IfName: opts.InterfaceName, + Args: [][2]string{ + {"IgnoreUnknown", "1"}, + // Do not set the K8S env vars, see https://github.com/containers/podman/issues/12083. + // Only K8S_POD_NAME is used by dnsname to get the container name. + {"K8S_POD_NAME", conName}, + }, + CapabilityArgs: map[string]interface{}{}, + } + + // Propagate environment CNI_ARGS + for _, kvpairs := range strings.Split(os.Getenv("CNI_ARGS"), ";") { + if keyval := strings.SplitN(kvpairs, "=", 2); len(keyval) == 2 { + rt.Args = append(rt.Args, [2]string{keyval[0], keyval[1]}) + } + } + + // Add mac address to cni args + if len(opts.StaticMAC) > 0 { + rt.Args = append(rt.Args, [2]string{"MAC", opts.StaticMAC.String()}) + } + + if len(opts.StaticIPs) == 1 { + // Add a single IP to the args field. CNI plugins < 1.0.0 + // do not support multiple ips via capability args. + rt.Args = append(rt.Args, [2]string{"IP", opts.StaticIPs[0].String()}) + } else if len(opts.StaticIPs) > 1 { + // Set the static ips in the capability args + // to support more than one static ip per network. + rt.CapabilityArgs["ips"] = opts.StaticIPs + } + + // Set network aliases for the dnsname plugin. + if len(opts.Aliases) > 0 { + rt.CapabilityArgs["aliases"] = map[string][]string{ + networkName: opts.Aliases, + } + } + + // Set PortMappings in Capabilities + if len(ports) > 0 { + rt.CapabilityArgs["portMappings"] = ports + } + + return rt +} + +// Teardown will teardown the container network namespace. +func (n *cniNetwork) Teardown(namespacePath string, options types.TeardownOptions) error { + n.lock.Lock() + defer n.lock.Unlock() + err := n.loadNetworks() + if err != nil { + return err + } + return n.teardown(namespacePath, options) +} + +func (n *cniNetwork) teardown(namespacePath string, options types.TeardownOptions) error { + // Note: An empty namespacePath is allowed because some plugins + // still need teardown, for example ipam should remove used ip allocations. + + ports, err := convertSpecgenPortsToCNIPorts(options.PortMappings) + if err != nil { + return err + } + + var multiErr *multierror.Error + for name, netOpts := range options.Networks { + rt := getRuntimeConfig(namespacePath, options.ContainerName, options.ContainerID, name, ports, netOpts) + + cniConfList, newRt, err := getCachedNetworkConfig(n.cniConf, name, rt) + if err == nil { + rt = newRt + } else { + logrus.Warnf("failed to load cached network config: %v, falling back to loading network %s from disk", err, name) + network := n.networks[name] + if network == nil { + multiErr = multierror.Append(multiErr, errors.Wrapf(define.ErrNoSuchNetwork, "network %s", name)) + continue + } + cniConfList = network.cniNet + } + + err = n.cniConf.DelNetworkList(context.Background(), cniConfList, rt) + if err != nil { + multiErr = multierror.Append(multiErr, err) + } + } + return multiErr.ErrorOrNil() +} + +func getCachedNetworkConfig(cniConf *libcni.CNIConfig, name string, rt *libcni.RuntimeConf) (*libcni.NetworkConfigList, *libcni.RuntimeConf, error) { + cniConfList := &libcni.NetworkConfigList{ + Name: name, + } + confBytes, rt, err := cniConf.GetNetworkListCachedConfig(cniConfList, rt) + if err != nil { + return nil, nil, err + } else if confBytes == nil { + return nil, nil, errors.Errorf("network %s not found in CNI cache", name) + } + + cniConfList, err = libcni.ConfListFromBytes(confBytes) + if err != nil { + return nil, nil, err + } + return cniConfList, rt, nil +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/run_test.go libpod-3.4.4+ds1/libpod/network/cni/run_test.go --- libpod-3.2.1+ds1/libpod/network/cni/run_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/run_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,1326 @@ +// +build linux + +package cni_test + +// The tests have to be run as root. +// For each test there will be two network namespaces created, +// netNSTest and netNSContainer. Each test must be run inside +// netNSTest to prevent leakage in the host netns, therefore +// it should use the following structure: +// It("test name", func() { +// runTest(func() { +// // add test logic here +// }) +// }) + +import ( + "bytes" + "io/ioutil" + "net" + "os" + "path/filepath" + "strconv" + "sync" + "time" + + "github.com/containernetworking/plugins/pkg/ns" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" + "golang.org/x/sys/unix" + + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/pkg/netns" + "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/storage/pkg/stringid" +) + +var _ = Describe("run CNI", func() { + var ( + libpodNet types.ContainerNetwork + cniConfDir string + logBuffer bytes.Buffer + netNSTest ns.NetNS + netNSContainer ns.NetNS + ) + const cniVarDir = "/var/lib/cni" + + // runTest is a helper function to run a test. It ensures that each test + // is run in its own netns. It also creates a mountns to mount a tmpfs to /var/lib/cni. + runTest := func(run func()) { + netNSTest.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + err := os.MkdirAll(cniVarDir, 0755) + Expect(err).To(BeNil(), "Failed to create cniVarDir") + err = unix.Unshare(unix.CLONE_NEWNS) + Expect(err).To(BeNil(), "Failed to create new mountns") + err = unix.Mount("tmpfs", cniVarDir, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, "") + Expect(err).To(BeNil(), "Failed to mount tmpfs for cniVarDir") + defer unix.Unmount(cniVarDir, 0) + + // we have to setup the loopback adapter in this netns to use port forwarding + link, err := netlink.LinkByName("lo") + Expect(err).To(BeNil(), "Failed to get loopback adapter") + err = netlink.LinkSetUp(link) + Expect(err).To(BeNil(), "Failed to set loopback adapter up") + run() + return nil + }) + } + + BeforeEach(func() { + // The tests need root privileges. + // Technically we could work around that by using user namespaces and + // the rootless cni code but this is to much work to get it right for a unit test. + if rootless.IsRootless() { + Skip("this test needs to be run as root") + } + + var err error + cniConfDir, err = ioutil.TempDir("", "podman_cni_test") + if err != nil { + Fail("Failed to create tmpdir") + } + logBuffer = bytes.Buffer{} + logrus.SetOutput(&logBuffer) + + netNSTest, err = netns.NewNS() + if err != nil { + Fail("Failed to create netns") + } + + netNSContainer, err = netns.NewNS() + if err != nil { + Fail("Failed to create netns") + } + }) + + JustBeforeEach(func() { + var err error + libpodNet, err = getNetworkInterface(cniConfDir, false) + if err != nil { + Fail("Failed to create NewCNINetworkInterface") + } + }) + + AfterEach(func() { + os.RemoveAll(cniConfDir) + + netns.UnmountNS(netNSTest) + netNSTest.Close() + + netns.UnmountNS(netNSContainer) + netNSContainer.Close() + }) + + Context("network setup test", func() { + + It("run with default config", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(defNet)) + Expect(res[defNet].Interfaces).To(HaveKey(intName)) + Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1)) + Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0.")) + Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) + // default network has no dns + Expect(res[defNet].DNSServerIPs).To(BeEmpty()) + Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + }) + }) + + It("run with default config and static ip", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + ip := net.ParseIP("10.88.5.5") + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: intName, + StaticIPs: []net.IP{ip}, + }, + }, + }, + } + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(defNet)) + Expect(res[defNet].Interfaces).To(HaveKey(intName)) + Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1)) + Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP).To(Equal(ip)) + Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) + // default network has no dns + Expect(res[defNet].DNSServerIPs).To(BeEmpty()) + Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + }) + }) + + for _, proto := range []string{"tcp", "udp"} { + // copy proto to extra var to keep correct references in the goroutines + protocol := proto + It("run with exposed ports protocol "+protocol, func() { + runTest(func() { + testdata := stringid.GenerateNonCryptoID() + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + PortMappings: []types.PortMapping{{ + Protocol: protocol, + HostIP: "127.0.0.1", + HostPort: 5000, + ContainerPort: 5000, + }}, + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(defNet)) + Expect(res[defNet].Interfaces).To(HaveKey(intName)) + Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1)) + Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0.")) + Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) + // default network has no dns + Expect(res[defNet].DNSServerIPs).To(BeEmpty()) + Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) + var wg sync.WaitGroup + wg.Add(1) + // start a listener in the container ns + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) + return nil + }) + Expect(err).To(BeNil()) + + conn, err := net.Dial(protocol, "127.0.0.1:5000") + Expect(err).To(BeNil()) + _, err = conn.Write([]byte(testdata)) + Expect(err).To(BeNil()) + conn.Close() + + // wait for the listener to finish + wg.Wait() + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + }) + }) + + It("run with range ports protocol "+protocol, func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + PortMappings: []types.PortMapping{{ + Protocol: protocol, + HostIP: "127.0.0.1", + HostPort: 5001, + ContainerPort: 5000, + Range: 3, + }}, + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(defNet)) + Expect(res[defNet].Interfaces).To(HaveKey(intName)) + Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1)) + containerIP := res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String() + Expect(containerIP).To(ContainSubstring("10.88.0.")) + Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) + // default network has no dns + Expect(res[defNet].DNSServerIPs).To(BeEmpty()) + Expect(res[defNet].DNSSearchDomains).To(BeEmpty()) + + // loop over all ports + for p := 5001; p < 5004; p++ { + port := p + var wg sync.WaitGroup + wg.Add(1) + testdata := stringid.GenerateNonCryptoID() + // start a listener in the container ns + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + runNetListener(&wg, protocol, containerIP, port-1, testdata) + return nil + }) + Expect(err).To(BeNil()) + + conn, err := net.Dial(protocol, net.JoinHostPort("127.0.0.1", strconv.Itoa(port))) + Expect(err).To(BeNil()) + _, err = conn.Write([]byte(testdata)) + Expect(err).To(BeNil()) + conn.Close() + + // wait for the listener to finish + wg.Wait() + } + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + }) + }) + } + + It("run with comma separated port protocol", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + PortMappings: []types.PortMapping{{ + Protocol: "tcp,udp", + HostIP: "127.0.0.1", + HostPort: 5000, + ContainerPort: 5000, + }}, + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(defNet)) + Expect(res[defNet].Interfaces).To(HaveKey(intName)) + Expect(res[defNet].Interfaces[intName].Networks).To(HaveLen(1)) + Expect(res[defNet].Interfaces[intName].Networks[0].Subnet.IP.String()).To(ContainSubstring("10.88.0.")) + Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6)) + + for _, proto := range []string{"tcp", "udp"} { + // copy proto to extra var to keep correct references in the goroutines + protocol := proto + + testdata := stringid.GenerateNonCryptoID() + var wg sync.WaitGroup + wg.Add(1) + // start tcp listener in the container ns + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + runNetListener(&wg, protocol, "0.0.0.0", 5000, testdata) + return nil + }) + Expect(err).To(BeNil()) + + conn, err := net.Dial(protocol, "127.0.0.1:5000") + Expect(err).To(BeNil()) + _, err = conn.Write([]byte(testdata)) + Expect(err).To(BeNil()) + conn.Close() + + // wait for the listener to finish + wg.Wait() + } + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + }) + }) + + It("call setup twice", func() { + runTest(func() { + network := types.Network{} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + intName1 := "eth0" + netName1 := network1.Name + + containerID := stringid.GenerateNonCryptoID() + + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: containerID, + Networks: map[string]types.PerNetworkOptions{ + netName1: { + InterfaceName: intName1, + }, + }, + }, + } + + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + + Expect(res).To(HaveKey(netName1)) + Expect(res[netName1].Interfaces).To(HaveKey(intName1)) + Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1)) + ipInt1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP + Expect(ipInt1).ToNot(BeEmpty()) + macInt1 := res[netName1].Interfaces[intName1].MacAddress + Expect(macInt1).To(HaveLen(6)) + + // check in the container namespace if the settings are applied + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + i, err := net.InterfaceByName(intName1) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(intName1)) + Expect(i.HardwareAddr).To(Equal(macInt1)) + addrs, err := i.Addrs() + Expect(err).To(BeNil()) + subnet := &net.IPNet{ + IP: ipInt1, + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet)) + + // check loopback adapter + i, err = net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + return nil + }) + Expect(err).To(BeNil()) + + network = types.Network{} + network2, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + intName2 := "eth1" + netName2 := network2.Name + + setupOpts.Networks = map[string]types.PerNetworkOptions{ + netName2: { + InterfaceName: intName2, + }, + } + + res, err = libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + + Expect(res).To(HaveKey(netName2)) + Expect(res[netName2].Interfaces).To(HaveKey(intName2)) + Expect(res[netName2].Interfaces[intName2].Networks).To(HaveLen(1)) + ipInt2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP + Expect(ipInt2).ToNot(BeEmpty()) + macInt2 := res[netName2].Interfaces[intName2].MacAddress + Expect(macInt2).To(HaveLen(6)) + + // check in the container namespace if the settings are applied + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + i, err := net.InterfaceByName(intName1) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(intName1)) + Expect(i.HardwareAddr).To(Equal(macInt1)) + addrs, err := i.Addrs() + Expect(err).To(BeNil()) + subnet := &net.IPNet{ + IP: ipInt1, + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet)) + + i, err = net.InterfaceByName(intName2) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(intName2)) + Expect(i.HardwareAddr).To(Equal(macInt2)) + addrs, err = i.Addrs() + Expect(err).To(BeNil()) + subnet = &net.IPNet{ + IP: ipInt2, + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet)) + + // check loopback adapter + i, err = net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + return nil + }) + Expect(err).To(BeNil()) + + teatdownOpts := types.TeardownOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: containerID, + Networks: map[string]types.PerNetworkOptions{ + netName1: { + InterfaceName: intName1, + }, + netName2: { + InterfaceName: intName2, + }, + }, + }, + } + + err = libpodNet.Teardown(netNSContainer.Path(), teatdownOpts) + Expect(err).To(BeNil()) + logString := logBuffer.String() + Expect(logString).To(BeEmpty()) + + // check in the container namespace that the interface is removed + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + _, err := net.InterfaceByName(intName1) + Expect(err).To(HaveOccurred()) + _, err = net.InterfaceByName(intName2) + Expect(err).To(HaveOccurred()) + + // check that only the loopback adapter is left + ints, err := net.Interfaces() + Expect(err).To(BeNil()) + Expect(ints).To(HaveLen(1)) + Expect(ints[0].Name).To(Equal("lo")) + Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + + return nil + }) + Expect(err).To(BeNil()) + + err = libpodNet.NetworkRemove(netName1) + Expect(err).To(BeNil()) + err = libpodNet.NetworkRemove(netName2) + Expect(err).To(BeNil()) + + // check that the interfaces are removed in the host ns + _, err = net.InterfaceByName(network1.NetworkInterface) + Expect(err).To(HaveOccurred()) + _, err = net.InterfaceByName(network2.NetworkInterface) + Expect(err).To(HaveOccurred()) + }) + }) + + It("setup two networks with one setup call", func() { + runTest(func() { + subnet1, _ := types.ParseCIDR("192.168.0.0/24") + subnet2, _ := types.ParseCIDR("192.168.1.0/24") + network := types.Network{ + Subnets: []types.Subnet{ + {Subnet: subnet1}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + network = types.Network{ + Subnets: []types.Subnet{ + {Subnet: subnet2}, + }, + } + network2, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + intName1 := "eth0" + intName2 := "eth1" + netName1 := network1.Name + netName2 := network2.Name + + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + netName1: { + InterfaceName: intName1, + }, + netName2: { + InterfaceName: intName2, + }, + }, + }, + } + + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(2)) + + Expect(res).To(HaveKey(netName1)) + Expect(res[netName1].Interfaces).To(HaveKey(intName1)) + Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1)) + ipInt1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP + Expect(ipInt1.String()).To(ContainSubstring("192.168.0.")) + macInt1 := res[netName1].Interfaces[intName1].MacAddress + Expect(macInt1).To(HaveLen(6)) + + Expect(res).To(HaveKey(netName2)) + Expect(res[netName2].Interfaces).To(HaveKey(intName2)) + Expect(res[netName2].Interfaces[intName2].Networks).To(HaveLen(1)) + ipInt2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP + Expect(ipInt2.String()).To(ContainSubstring("192.168.1.")) + macInt2 := res[netName2].Interfaces[intName2].MacAddress + Expect(macInt2).To(HaveLen(6)) + + // default network has no dns + Expect(res[netName1].DNSServerIPs).To(BeEmpty()) + Expect(res[netName1].DNSSearchDomains).To(BeEmpty()) + + // check in the container namespace if the settings are applied + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + i, err := net.InterfaceByName(intName1) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(intName1)) + Expect(i.HardwareAddr).To(Equal(macInt1)) + addrs, err := i.Addrs() + Expect(err).To(BeNil()) + subnet := &net.IPNet{ + IP: ipInt1, + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet)) + + i, err = net.InterfaceByName(intName2) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(intName2)) + Expect(i.HardwareAddr).To(Equal(macInt2)) + addrs, err = i.Addrs() + Expect(err).To(BeNil()) + subnet = &net.IPNet{ + IP: ipInt2, + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet)) + + // check loopback adapter + i, err = net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + return nil + }) + Expect(err).To(BeNil()) + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + logString := logBuffer.String() + Expect(logString).To(BeEmpty()) + + // check in the container namespace that the interface is removed + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + _, err := net.InterfaceByName(intName1) + Expect(err).To(HaveOccurred()) + _, err = net.InterfaceByName(intName2) + Expect(err).To(HaveOccurred()) + + // check that only the loopback adapter is left + ints, err := net.Interfaces() + Expect(err).To(BeNil()) + Expect(ints).To(HaveLen(1)) + Expect(ints[0].Name).To(Equal("lo")) + Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + + return nil + }) + Expect(err).To(BeNil()) + }) + + }) + + It("dual stack network with static ips", func() { + // Version checks for cni plugins are not possible, the plugins do not output + // version information and using the package manager does not work across distros. + // Fedora has the right version so we use this for now. + SkipIfNotFedora("requires cni plugins 1.0.0 or newer for multiple static ips") + runTest(func() { + subnet1, _ := types.ParseCIDR("192.168.0.0/24") + subnet2, _ := types.ParseCIDR("fd41:0a75:2ca0:48a9::/64") + network := types.Network{ + Subnets: []types.Subnet{ + {Subnet: subnet1}, {Subnet: subnet2}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + mac, _ := net.ParseMAC("40:15:2f:d8:42:36") + interfaceName := "eth0" + + ip1 := net.ParseIP("192.168.0.5") + ip2 := net.ParseIP("fd41:0a75:2ca0:48a9::5") + + netName := network1.Name + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerName: "mycon", + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: interfaceName, + StaticIPs: []net.IP{ip1, ip2}, + StaticMAC: mac, + }, + }, + }, + } + + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(netName)) + Expect(res[netName].Interfaces).To(HaveKey(interfaceName)) + Expect(res[netName].Interfaces[interfaceName].Networks).To(HaveLen(2)) + Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.IP.String()).To(Equal(ip1.String())) + Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.Mask).To(Equal(subnet1.Mask)) + Expect(res[netName].Interfaces[interfaceName].Networks[0].Gateway).To(Equal(net.ParseIP("192.168.0.1"))) + Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.IP.String()).To(Equal(ip2.String())) + Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.Mask).To(Equal(subnet2.Mask)) + Expect(res[netName].Interfaces[interfaceName].Networks[1].Gateway).To(Equal(net.ParseIP("fd41:0a75:2ca0:48a9::1"))) + Expect(res[netName].Interfaces[interfaceName].MacAddress).To(Equal(mac)) + // default network has no dns + Expect(res[netName].DNSServerIPs).To(BeEmpty()) + Expect(res[netName].DNSSearchDomains).To(BeEmpty()) + + // check in the container namespace if the settings are applied + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + i, err := net.InterfaceByName(interfaceName) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(interfaceName)) + Expect(i.HardwareAddr).To(Equal(mac)) + addrs, err := i.Addrs() + Expect(err).To(BeNil()) + subnet1 := &net.IPNet{ + IP: ip1, + Mask: net.CIDRMask(24, 32), + } + subnet2 := &net.IPNet{ + IP: ip2, + Mask: net.CIDRMask(64, 128), + } + Expect(addrs).To(ContainElements(subnet1, subnet2)) + + // check loopback adapter + i, err = net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + return nil + }) + Expect(err).To(BeNil()) + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + logString := logBuffer.String() + Expect(logString).To(BeEmpty()) + + // check in the container namespace that the interface is removed + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + _, err := net.InterfaceByName(interfaceName) + Expect(err).To(HaveOccurred()) + + // check that only the loopback adapter is left + ints, err := net.Interfaces() + Expect(err).To(BeNil()) + Expect(ints).To(HaveLen(1)) + Expect(ints[0].Name).To(Equal("lo")) + Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + + return nil + }) + Expect(err).To(BeNil()) + }) + }) + + It("CNI_ARGS from environment variable", func() { + runTest(func() { + subnet1, _ := types.ParseCIDR("172.16.1.0/24") + ip := "172.16.1.5" + network := types.Network{ + Subnets: []types.Subnet{ + {Subnet: subnet1}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + netName := network1.Name + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: intName, + }, + }, + }, + } + + os.Setenv("CNI_ARGS", "IP="+ip) + defer os.Unsetenv("CNI_ARGS") + + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(netName)) + Expect(res[netName].Interfaces).To(HaveKey(intName)) + Expect(res[netName].Interfaces[intName].Networks).To(HaveLen(1)) + Expect(res[netName].Interfaces[intName].Networks[0].Subnet.IP.String()).To(Equal(ip)) + Expect(res[netName].Interfaces[intName].Networks[0].Subnet.Mask).To(Equal(net.CIDRMask(24, 32))) + + // check in the container namespace if the settings are applied + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + i, err := net.InterfaceByName(intName) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(intName)) + addrs, err := i.Addrs() + Expect(err).To(BeNil()) + subnet := &net.IPNet{ + IP: net.ParseIP(ip), + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet)) + + // check loopback adapter + i, err = net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + return nil + }) + Expect(err).To(BeNil()) + }) + }) + }) + + Context("network setup test with networks from disk", func() { + + BeforeEach(func() { + dir := "testfiles/valid" + files, err := ioutil.ReadDir(dir) + if err != nil { + Fail("Failed to read test directory") + } + for _, file := range files { + filename := file.Name() + data, err := ioutil.ReadFile(filepath.Join(dir, filename)) + if err != nil { + Fail("Failed to copy test files") + } + err = ioutil.WriteFile(filepath.Join(cniConfDir, filename), data, 0700) + if err != nil { + Fail("Failed to copy test files") + } + } + }) + + It("dualstack setup with static ip and dns", func() { + SkipIfNoDnsname() + // Version checks for cni plugins are not possible, the plugins do not output + // version information and using the package manager does not work across distros. + // Fedora has the right version so we use this for now. + SkipIfNotFedora("requires cni plugins 1.0.0 or newer for multiple static ips") + runTest(func() { + interfaceName := "eth0" + + ip1 := net.ParseIP("fd10:88:a::11") + ip2 := net.ParseIP("10.89.19.15") + + containerName := "myname" + aliases := []string{"aliasname"} + + netName := "dualstack" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + ContainerName: containerName, + Networks: map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: interfaceName, + StaticIPs: []net.IP{ip1, ip2}, + Aliases: aliases, + }, + }, + }, + } + + network, err := libpodNet.NetworkInspect(netName) + Expect(err).To(BeNil()) + Expect(network.Name).To(Equal(netName)) + Expect(network.DNSEnabled).To(BeTrue()) + Expect(network.Subnets).To(HaveLen(2)) + gw1 := network.Subnets[0].Gateway + Expect(gw1).To(HaveLen(16)) + mask1 := network.Subnets[0].Subnet.Mask + Expect(mask1).To(HaveLen(16)) + gw2 := network.Subnets[1].Gateway + Expect(gw2).To(HaveLen(4)) + mask2 := network.Subnets[1].Subnet.Mask + Expect(mask2).To(HaveLen(4)) + + // because this net has dns we should always teardown otherwise we leak a dnsmasq process + defer libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(BeNil()) + Expect(res).To(HaveLen(1)) + Expect(res).To(HaveKey(netName)) + Expect(res[netName].Interfaces).To(HaveKey(interfaceName)) + Expect(res[netName].Interfaces[interfaceName].Networks).To(HaveLen(2)) + Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.IP.String()).To(Equal(ip1.String())) + Expect(res[netName].Interfaces[interfaceName].Networks[0].Subnet.Mask).To(Equal(mask1)) + Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.IP.String()).To(Equal(ip2.String())) + Expect(res[netName].Interfaces[interfaceName].Networks[1].Subnet.Mask).To(Equal(mask2)) + // dualstack network dns + Expect(res[netName].DNSServerIPs).To(HaveLen(2)) + Expect(res[netName].DNSSearchDomains).To(HaveLen(1)) + Expect(res[netName].DNSSearchDomains).To(ConsistOf("dns.podman")) + + // check in the container namespace if the settings are applied + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + i, err := net.InterfaceByName(interfaceName) + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal(interfaceName)) + addrs, err := i.Addrs() + Expect(err).To(BeNil()) + subnet1 := &net.IPNet{ + IP: ip1, + Mask: net.CIDRMask(64, 128), + } + subnet2 := &net.IPNet{ + IP: ip2, + Mask: net.CIDRMask(24, 32), + } + Expect(addrs).To(ContainElements(subnet1, subnet2)) + + // check loopback adapter + i, err = net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + + return nil + }) + Expect(err).To(BeNil()) + + err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts)) + Expect(err).To(BeNil()) + logString := logBuffer.String() + Expect(logString).To(BeEmpty()) + + // check in the container namespace that the interface is removed + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + _, err := net.InterfaceByName(interfaceName) + Expect(err).To(HaveOccurred()) + + // check that only the loopback adapter is left + ints, err := net.Interfaces() + Expect(err).To(BeNil()) + Expect(ints).To(HaveLen(1)) + Expect(ints[0].Name).To(Equal("lo")) + Expect(ints[0].Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(ints[0].Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + + return nil + }) + Expect(err).To(BeNil()) + }) + }) + + }) + + Context("invalid network setup test", func() { + + It("static ip not in subnet", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + ip := "1.1.1.1" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: intName, + StaticIPs: []net.IP{net.ParseIP(ip)}, + }, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("requested static ip %s not in any subnet on network %s", ip, defNet)) + }) + }) + + It("setup without namespace path", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: intName, + }, + }, + }, + } + _, err := libpodNet.Setup("", setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("namespacePath is empty")) + }) + }) + + It("setup with invalid namespace path", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: intName, + }, + }, + }, + } + _, err := libpodNet.Setup("some path", setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring(`"some path": no such file or directory`)) + }) + }) + + It("setup without container ID", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: "", + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: intName, + }, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("ContainerID is empty")) + }) + }) + + It("setup with aliases but dns disabled", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: intName, + Aliases: []string{"somealias"}, + }, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("cannot set aliases on a network without dns enabled")) + }) + }) + + It("setup without networks", func() { + runTest(func() { + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("must specify at least one network")) + }) + }) + + It("setup without interface name", func() { + runTest(func() { + defNet := types.DefaultNetworkName + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: { + InterfaceName: "", + }, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("interface name on network %s is empty", defNet)) + }) + }) + + It("setup does teardown on failure", func() { + runTest(func() { + subnet1, _ := types.ParseCIDR("192.168.0.0/24") + network := types.Network{ + Subnets: []types.Subnet{ + {Subnet: subnet1}, + }, + } + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + subnet2, _ := types.ParseCIDR("192.168.1.0/31") + network = types.Network{ + Subnets: []types.Subnet{ + {Subnet: subnet2}, + }, + } + network2, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + intName1 := "eth0" + intName2 := "eth1" + netName1 := network1.Name + netName2 := network2.Name + + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + netName1: { + InterfaceName: intName1, + }, + netName2: { + InterfaceName: intName2, + }, + }, + }, + } + _, err = libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("Network 192.168.1.0/31 too small to allocate from")) + // Note: we call teardown on the failing net and log the error, it should be the same. + logString := logBuffer.String() + Expect(logString).To(ContainSubstring("Network 192.168.1.0/31 too small to allocate from")) + + // check in the container namespace that no interface is there + err = netNSContainer.Do(func(_ ns.NetNS) error { + defer GinkgoRecover() + _, err := net.InterfaceByName(intName1) + Expect(err).To(HaveOccurred()) + + // Note: We can check if intName2 is removed because + // the cni plugin fails before it removes the interface + + // check loopback adapter + i, err := net.InterfaceByName("lo") + Expect(err).To(BeNil()) + Expect(i.Name).To(Equal("lo")) + Expect(i.Flags & net.FlagLoopback).To(Equal(net.FlagLoopback)) + Expect(i.Flags&net.FlagUp).To(Equal(net.FlagUp), "Loopback adapter should be up") + return nil + }) + Expect(err).To(BeNil()) + }) + }) + + It("setup with exposed invalid port protocol", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + PortMappings: []types.PortMapping{{ + Protocol: "someproto", + HostIP: "127.0.0.1", + HostPort: 5000, + ContainerPort: 5000, + }}, + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("unknown port protocol someproto")) + }) + }) + + It("setup with exposed empty port protocol", func() { + runTest(func() { + defNet := types.DefaultNetworkName + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + PortMappings: []types.PortMapping{{ + Protocol: "", + HostIP: "127.0.0.1", + HostPort: 5000, + ContainerPort: 5000, + }}, + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("port protocol should not be empty")) + }) + }) + + It("setup with unknown network", func() { + runTest(func() { + defNet := "somenet" + intName := "eth0" + setupOpts := types.SetupOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + defNet: {InterfaceName: intName}, + }, + }, + } + _, err := libpodNet.Setup(netNSContainer.Path(), setupOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("network somenet: network not found")) + }) + }) + + It("teardown with unknown network", func() { + runTest(func() { + interfaceName := "eth0" + netName := "somenet" + teardownOpts := types.TeardownOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: interfaceName, + }, + }, + }, + } + + err := libpodNet.Teardown(netNSContainer.Path(), teardownOpts) + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("network somenet: network not found")) + logString := logBuffer.String() + Expect(logString).To(ContainSubstring("failed to load cached network config")) + }) + }) + + It("teardown on not connected network", func() { + runTest(func() { + network := types.Network{} + network1, err := libpodNet.NetworkCreate(network) + Expect(err).To(BeNil()) + + interfaceName := "eth0" + netName := network1.Name + teardownOpts := types.TeardownOptions{ + NetworkOptions: types.NetworkOptions{ + ContainerID: stringid.GenerateNonCryptoID(), + Networks: map[string]types.PerNetworkOptions{ + netName: { + InterfaceName: interfaceName, + }, + }, + }, + } + + // Most CNI plugins do not error on teardown when there is nothing to do. + err = libpodNet.Teardown(netNSContainer.Path(), teardownOpts) + Expect(err).To(BeNil()) + logString := logBuffer.String() + Expect(logString).To(ContainSubstring("failed to load cached network config")) + }) + }) + }) +}) + +func runNetListener(wg *sync.WaitGroup, protocol, ip string, port int, expectedData string) { + switch protocol { + case "tcp": + ln, err := net.Listen(protocol, net.JoinHostPort(ip, strconv.Itoa(port))) + Expect(err).To(BeNil()) + // make sure to read in a separate goroutine to not block + go func() { + defer GinkgoRecover() + defer wg.Done() + conn, err := ln.Accept() + Expect(err).To(BeNil()) + conn.SetDeadline(time.Now().Add(1 * time.Second)) + data, err := ioutil.ReadAll(conn) + Expect(err).To(BeNil()) + Expect(string(data)).To(Equal(expectedData)) + conn.Close() + ln.Close() + }() + case "udp": + conn, err := net.ListenUDP("udp", &net.UDPAddr{ + IP: net.ParseIP(ip), + Port: port, + }) + Expect(err).To(BeNil()) + conn.SetDeadline(time.Now().Add(1 * time.Second)) + go func() { + defer GinkgoRecover() + defer wg.Done() + data := make([]byte, len(expectedData)) + i, err := conn.Read(data) + Expect(err).To(BeNil()) + Expect(i).To(Equal(len(expectedData))) + Expect(string(data)).To(Equal(expectedData)) + conn.Close() + }() + default: + Fail("unsupported protocol") + } +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/broken.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/broken.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/broken.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/broken.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,25 @@ +{ + "cniVersion": "0.4.0", + "name": "bridge", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman9", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8.1" + } + ] + ] diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/invalid_gateway.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,51 @@ +{ + "cniVersion": "0.4.0", + "name": "invalidgw", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman8", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8", + "rangeStart": "10.89.8.20", + "rangeEnd": "10.89.8.50" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/invalidname.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/invalidname.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/invalidname.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/invalidname.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,49 @@ +{ + "cniVersion": "0.4.0", + "name": "bridge@123", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman9", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/noname.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/noname.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/noname.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/noname.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,48 @@ +{ + "cniVersion": "0.4.0", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman9", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/noplugin.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/noplugin.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/noplugin.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/noplugin.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,5 @@ +{ + "cniVersion": "0.4.0", + "name": "bridge", + "plugins": [] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/samename1.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/samename1.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/samename1.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/samename1.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,49 @@ +{ + "cniVersion": "0.4.0", + "name": "bridge", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman9", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/samename2.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/samename2.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/invalid/samename2.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/invalid/samename2.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,49 @@ +{ + "cniVersion": "0.4.0", + "name": "bridge", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman9", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/87-podman.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/87-podman.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/87-podman.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/87-podman.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,37 @@ +{ + "cniVersion": "0.4.0", + "name": "podman", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman0", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [{ "dst": "0.0.0.0/0" }], + "ranges": [ + [ + { + "subnet": "10.88.0.0/16", + "gateway": "10.88.0.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall" + }, + { + "type": "tuning" + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/bridge.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/bridge.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/bridge.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/bridge.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,51 @@ +{ + "cniVersion": "0.4.0", + "name": "bridge", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman9", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.8.0/24", + "gateway": "10.89.8.1", + "rangeStart": "10.89.8.20", + "rangeEnd": "10.89.8.50" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/dualstack.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/dualstack.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/dualstack.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/dualstack.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,58 @@ +{ + "cniVersion": "0.4.0", + "name": "dualstack", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman21", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "::/0" + }, + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "fd10:88:a::/64", + "gateway": "fd10:88:a::1" + } + ], + [ + { + "subnet": "10.89.19.0/24", + "gateway": "10.89.19.10" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/internal.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/internal.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/internal.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/internal.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,40 @@ +{ + "cniVersion": "0.4.0", + "name": "internal", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman8", + "isGateway": false, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.7.0/24" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/label.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/label.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/label.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/label.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,54 @@ +{ + "args": { + "podman_labels": { + "mykey": "value" + } + }, + "cniVersion": "0.4.0", + "name": "label", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman15", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.13.0/24", + "gateway": "10.89.13.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/macvlan.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/macvlan.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/macvlan.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/macvlan.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,13 @@ +{ + "cniVersion": "0.4.0", + "name": "macvlan", + "plugins": [ + { + "type": "macvlan", + "master": "lo", + "ipam": { + "type": "dhcp" + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/macvlan_mtu.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/macvlan_mtu.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/macvlan_mtu.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/macvlan_mtu.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,14 @@ +{ + "cniVersion": "0.4.0", + "name": "macvlan_mtu", + "plugins": [ + { + "type": "macvlan", + "master": "lo", + "ipam": { + "type": "dhcp" + }, + "mtu": 1300 + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/mtu.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/mtu.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/mtu.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/mtu.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,49 @@ +{ + "cniVersion": "0.4.0", + "name": "mtu", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman13", + "isGateway": true, + "ipMasq": true, + "mtu": 1500, + "hairpinMode": true, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.11.0/24" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/vlan.conflist libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/vlan.conflist --- libpod-3.2.1+ds1/libpod/network/cni/testfiles/valid/vlan.conflist 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/cni/testfiles/valid/vlan.conflist 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,50 @@ +{ + "cniVersion": "0.4.0", + "name": "vlan", + "plugins": [ + { + "type": "bridge", + "bridge": "cni-podman14", + "isGateway": true, + "ipMasq": true, + "hairpinMode": true, + "vlan": 5, + "ipam": { + "type": "host-local", + "routes": [ + { + "dst": "0.0.0.0/0" + } + ], + "ranges": [ + [ + { + "subnet": "10.89.12.0/24", + "gateway": "10.89.12.1" + } + ] + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + }, + { + "type": "firewall", + "backend": "" + }, + { + "type": "tuning" + }, + { + "type": "dnsname", + "domainName": "dns.podman", + "capabilities": { + "aliases": true + } + } + ] +} diff -Nru libpod-3.2.1+ds1/libpod/network/devices.go libpod-3.4.4+ds1/libpod/network/devices.go --- libpod-3.2.1+ds1/libpod/network/devices.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/devices.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,12 +2,11 @@ import ( "fmt" - "os/exec" "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/pkg/util" - "github.com/containers/podman/v3/utils" "github.com/sirupsen/logrus" + "github.com/vishvananda/netlink" ) // GetFreeDeviceName returns a device name that is unused; used when no network @@ -52,12 +51,9 @@ // RemoveInterface removes an interface by the given name func RemoveInterface(interfaceName string) error { - // Make sure we have the ip command on the system - ipPath, err := exec.LookPath("ip") + link, err := netlink.LinkByName(interfaceName) if err != nil { return err } - // Delete the network interface - _, err = utils.ExecCmd(ipPath, []string{"link", "del", interfaceName}...) - return err + return netlink.LinkDel(link) } diff -Nru libpod-3.2.1+ds1/libpod/network/network.go libpod-3.4.4+ds1/libpod/network/network.go --- libpod-3.2.1+ds1/libpod/network/network.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -111,8 +111,10 @@ if len(network.IPAM.Ranges) > 0 { // this is the new IPAM range style // append each subnet from ipam the rangeset - for _, r := range network.IPAM.Ranges[0] { - nets = append(nets, newIPNetFromSubnet(r.Subnet)) + for _, allocatorRange := range network.IPAM.Ranges { + for _, r := range allocatorRange { + nets = append(nets, newIPNetFromSubnet(r.Subnet)) + } } } else { // looks like the old, deprecated style @@ -192,8 +194,9 @@ return errors.Wrapf(err, "failed to get live network names") } if util.StringInSlice(interfaceName, liveNetworkNames) { - if err := RemoveInterface(interfaceName); err != nil { - return errors.Wrapf(err, "failed to delete the network interface %q", interfaceName) + if err = RemoveInterface(interfaceName); err != nil { + // only log the error, it is not fatal + logrus.Infof("failed to remove network interface %s: %v", interfaceName, err) } } } diff -Nru libpod-3.2.1+ds1/libpod/network/types/const.go libpod-3.4.4+ds1/libpod/network/types/const.go --- libpod-3.2.1+ds1/libpod/network/types/const.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/types/const.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,21 @@ +package types + +const ( + // BridgeNetworkDriver defines the bridge driver + BridgeNetworkDriver = "bridge" + // DefaultNetworkDriver is the default network type used + DefaultNetworkDriver = BridgeNetworkDriver + // MacVLANNetworkDriver defines the macvlan driver + MacVLANNetworkDriver = "macvlan" + + // IPAM drivers + // HostLocalIPAMDriver store the ip + HostLocalIPAMDriver = "host-local" + // DHCPIPAMDriver get subnet and ip from dhcp server + DHCPIPAMDriver = "dhcp" + + // DefaultSubnet is the name that will be used for the default CNI network. + DefaultNetworkName = "podman" + // DefaultSubnet is the subnet that will be used for the default CNI network. + DefaultSubnet = "10.88.0.0/16" +) diff -Nru libpod-3.2.1+ds1/libpod/network/types/network.go libpod-3.4.4+ds1/libpod/network/types/network.go --- libpod-3.2.1+ds1/libpod/network/types/network.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/types/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,208 @@ +package types + +import ( + "net" + "time" +) + +type ContainerNetwork interface { + // NetworkCreate will take a partial filled Network and fill the + // missing fields. It creates the Network and returns the full Network. + NetworkCreate(Network) (Network, error) + // NetworkRemove will remove the Network with the given name or ID. + NetworkRemove(nameOrID string) error + // NetworkList will return all known Networks. Optionally you can + // supply a list of filter functions. Only if a network matches all + // functions it is returned. + NetworkList(...FilterFunc) ([]Network, error) + // NetworkInspect will return the Network with the given name or ID. + NetworkInspect(nameOrID string) (Network, error) + + // Setup will setup the container network namespace. It returns + // a map of StatusBlocks, the key is the network name. + Setup(namespacePath string, options SetupOptions) (map[string]StatusBlock, error) + // Teardown will teardown the container network namespace. + Teardown(namespacePath string, options TeardownOptions) error +} + +// Network describes the Network attributes. +type Network struct { + // Name of the Network. + Name string `json:"name,omitempty"` + // ID of the Network. + ID string `json:"id,omitempty"` + // Driver for this Network, e.g. bridge, macvlan... + Driver string `json:"driver,omitempty"` + // InterfaceName is the network interface name on the host. + NetworkInterface string `json:"network_interface,omitempty"` + // Created contains the timestamp when this network was created. + // This is not guaranteed to stay exactly the same. + Created time.Time + // Subnets to use. + Subnets []Subnet `json:"subnets,omitempty"` + // IPv6Enabled if set to true an ipv6 subnet should be created for this net. + IPv6Enabled bool `json:"ipv6_enabled"` + // Internal is whether the Network should not have external routes + // to public or other Networks. + Internal bool `json:"internal"` + // DNSEnabled is whether name resolution is active for container on + // this Network. + DNSEnabled bool `json:"dns_enabled"` + // Labels is a set of key-value labels that have been applied to the + // Network. + Labels map[string]string `json:"labels,omitempty"` + // Options is a set of key-value options that have been applied to + // the Network. + Options map[string]string `json:"options,omitempty"` + // IPAMOptions contains options used for the ip assignment. + IPAMOptions map[string]string `json:"ipam_options,omitempty"` +} + +// IPNet is used as custom net.IPNet type to add Marshal/Unmarshal methods. +type IPNet struct { + net.IPNet +} + +// ParseCIDR parse a string to IPNet +func ParseCIDR(cidr string) (IPNet, error) { + ip, net, err := net.ParseCIDR(cidr) + if err != nil { + return IPNet{}, err + } + // convert to 4 bytes if ipv4 + ipv4 := ip.To4() + if ipv4 != nil { + ip = ipv4 + } + net.IP = ip + return IPNet{*net}, err +} + +func (n *IPNet) MarshalText() ([]byte, error) { + return []byte(n.String()), nil +} + +func (n *IPNet) UnmarshalText(text []byte) error { + net, err := ParseCIDR(string(text)) + if err != nil { + return err + } + *n = net + return nil +} + +type Subnet struct { + // Subnet for this Network. + Subnet IPNet `json:"subnet,omitempty"` + // Gateway IP for this Network. + Gateway net.IP `json:"gateway,omitempty"` + // LeaseRange contains the range where IP are leased. Optional. + LeaseRange *LeaseRange `json:"lease_range,omitempty"` +} + +// LeaseRange contains the range where IP are leased. +type LeaseRange struct { + // StartIP first IP in the subnet which should be used to assign ips. + StartIP net.IP `json:"start_ip,omitempty"` + // EndIP last IP in the subnet which should be used to assign ips. + EndIP net.IP `json:"end_ip,omitempty"` +} + +// StatusBlock contains the network information about a container +// connected to one Network. +type StatusBlock struct { + // Interfaces contains the created network interface in the container. + // The map key is the interface name. + Interfaces map[string]NetInterface `json:"interfaces,omitempty"` + // DNSServerIPs nameserver addresses which should be added to + // the containers resolv.conf file. + DNSServerIPs []net.IP `json:"dns_server_ips,omitempty"` + // DNSSearchDomains search domains which should be added to + // the containers resolv.conf file. + DNSSearchDomains []string `json:"dns_search_domains,omitempty"` +} + +// NetInterface contains the settings for a given network interface. +type NetInterface struct { + // Networks list of assigned subnets with their gateway. + Networks []NetAddress `json:"networks,omitempty"` + // MacAddress for this Interface. + MacAddress net.HardwareAddr `json:"mac_address,omitempty"` +} + +// NetAddress contains the subnet and gatway. +type NetAddress struct { + // Subnet of this NetAddress. Note that the subnet contains the + // actual ip of the net interface and not the network address. + Subnet IPNet `json:"subnet,omitempty"` + // Gateway for the Subnet. This can be nil if there is no gateway, e.g. internal network. + Gateway net.IP `json:"gateway,omitempty"` +} + +// PerNetworkOptions are options which should be set on a per network basis. +type PerNetworkOptions struct { + // StaticIPv4 for this container. Optional. + StaticIPs []net.IP `json:"static_ips,omitempty"` + // Aliases contains a list of names which the dns server should resolve + // to this container. Can only be set when DNSEnabled is true on the Network. + // Optional. + Aliases []string `json:"aliases,omitempty"` + // StaticMac for this container. Optional. + StaticMAC net.HardwareAddr `json:"static_mac,omitempty"` + // InterfaceName for this container. Required. + InterfaceName string `json:"interface_name,omitempty"` +} + +// NetworkOptions for a given container. +type NetworkOptions struct { + // ContainerID is the container id, used for iptables comments and ipam allocation. + ContainerID string `json:"container_id,omitempty"` + // ContainerName is the container name, used as dns name. + ContainerName string `json:"container_name,omitempty"` + // PortMappings contains the port mappings for this container + PortMappings []PortMapping `json:"port_mappings,omitempty"` + // Networks contains all networks with the PerNetworkOptions. + // The map should contain at least one element. + Networks map[string]PerNetworkOptions `json:"networks,omitempty"` +} + +// PortMapping is one or more ports that will be mapped into the container. +type PortMapping struct { + // HostIP is the IP that we will bind to on the host. + // If unset, assumed to be 0.0.0.0 (all interfaces). + HostIP string `json:"host_ip,omitempty"` + // ContainerPort is the port number that will be exposed from the + // container. + // Mandatory. + ContainerPort uint16 `json:"container_port"` + // HostPort is the port number that will be forwarded from the host into + // the container. + // If omitted, a random port on the host (guaranteed to be over 1024) + // will be assigned. + HostPort uint16 `json:"host_port,omitempty"` + // Range is the number of ports that will be forwarded, starting at + // HostPort and ContainerPort and counting up. + // This is 1-indexed, so 1 is assumed to be a single port (only the + // Hostport:Containerport mapping will be added), 2 is two ports (both + // Hostport:Containerport and Hostport+1:Containerport+1), etc. + // If unset, assumed to be 1 (a single port). + // Both hostport + range and containerport + range must be less than + // 65536. + Range uint16 `json:"range,omitempty"` + // Protocol is the protocol forward. + // Must be either "tcp", "udp", and "sctp", or some combination of these + // separated by commas. + // If unset, assumed to be TCP. + Protocol string `json:"protocol,omitempty"` +} + +type SetupOptions struct { + NetworkOptions +} + +type TeardownOptions struct { + NetworkOptions +} + +// FilterFunc can be passed to NetworkList to filter the networks. +type FilterFunc func(Network) bool diff -Nru libpod-3.2.1+ds1/libpod/network/util/filters.go libpod-3.4.4+ds1/libpod/network/util/filters.go --- libpod-3.2.1+ds1/libpod/network/util/filters.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/util/filters.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,55 @@ +package util + +import ( + "strings" + + "github.com/containers/podman/v3/libpod/network/types" + "github.com/containers/podman/v3/pkg/util" + "github.com/pkg/errors" +) + +func GenerateNetworkFilters(filters map[string][]string) ([]types.FilterFunc, error) { + filterFuncs := make([]types.FilterFunc, 0, len(filters)) + for key, filterValues := range filters { + filterFunc, err := createFilterFuncs(key, filterValues) + if err != nil { + return nil, err + } + filterFuncs = append(filterFuncs, filterFunc) + } + return filterFuncs, nil +} + +func createFilterFuncs(key string, filterValues []string) (types.FilterFunc, error) { + switch strings.ToLower(key) { + case "name": + // matches one name, regex allowed + return func(net types.Network) bool { + return util.StringMatchRegexSlice(net.Name, filterValues) + }, nil + + case "label": + // matches all labels + return func(net types.Network) bool { + return util.MatchLabelFilters(filterValues, net.Labels) + }, nil + + case "driver": + // matches network driver + return func(net types.Network) bool { + return util.StringInSlice(net.Driver, filterValues) + }, nil + + case "id": + // matches part of one id + return func(net types.Network) bool { + return util.StringMatchRegexSlice(net.ID, filterValues) + }, nil + + // FIXME: What should we do with the old plugin filter + // TODO: add dangling, dns enabled, internal filter + + default: + return nil, errors.Errorf("invalid filter %q", key) + } +} diff -Nru libpod-3.2.1+ds1/libpod/network/util/interfaces.go libpod-3.4.4+ds1/libpod/network/util/interfaces.go --- libpod-3.2.1+ds1/libpod/network/util/interfaces.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/util/interfaces.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,34 @@ +package util + +import "net" + +// GetLiveNetworkSubnets returns a slice of subnets representing what the system +// has defined as network interfaces +func GetLiveNetworkSubnets() ([]*net.IPNet, error) { + addrs, err := net.InterfaceAddrs() + if err != nil { + return nil, err + } + nets := make([]*net.IPNet, 0, len(addrs)) + for _, address := range addrs { + _, n, err := net.ParseCIDR(address.String()) + if err != nil { + return nil, err + } + nets = append(nets, n) + } + return nets, nil +} + +// GetLiveNetworkNames returns a list of network interface names on the system +func GetLiveNetworkNames() ([]string, error) { + liveInterfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + interfaceNames := make([]string, 0, len(liveInterfaces)) + for _, i := range liveInterfaces { + interfaceNames = append(interfaceNames, i.Name) + } + return interfaceNames, nil +} diff -Nru libpod-3.2.1+ds1/libpod/network/util/ip.go libpod-3.4.4+ds1/libpod/network/util/ip.go --- libpod-3.2.1+ds1/libpod/network/util/ip.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/util/ip.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,113 @@ +package util + +import ( + "crypto/rand" + "net" + + "github.com/pkg/errors" +) + +// IsIPv6 returns true if netIP is IPv6. +func IsIPv6(netIP net.IP) bool { + return netIP != nil && netIP.To4() == nil +} + +// IsIPv4 returns true if netIP is IPv4. +func IsIPv4(netIP net.IP) bool { + return netIP != nil && netIP.To4() != nil +} + +func incByte(subnet *net.IPNet, idx int, shift uint) error { + if idx < 0 { + return errors.New("no more subnets left") + } + if subnet.IP[idx] == 255 { + subnet.IP[idx] = 0 + return incByte(subnet, idx-1, 0) + } + subnet.IP[idx] += 1 << shift + return nil +} + +// NextSubnet returns subnet incremented by 1 +func NextSubnet(subnet *net.IPNet) (*net.IPNet, error) { + newSubnet := &net.IPNet{ + IP: subnet.IP, + Mask: subnet.Mask, + } + ones, bits := newSubnet.Mask.Size() + if ones == 0 { + return nil, errors.Errorf("%s has only one subnet", subnet.String()) + } + zeroes := uint(bits - ones) + shift := zeroes % 8 + idx := ones/8 - 1 + if idx < 0 { + idx = 0 + } + if err := incByte(newSubnet, idx, shift); err != nil { + return nil, err + } + return newSubnet, nil +} + +// LastIPInSubnet gets the last IP in a subnet +func LastIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer + // re-parse to ensure clean network address + _, cidr, err := net.ParseCIDR(addr.String()) + if err != nil { + return nil, err + } + + ones, bits := cidr.Mask.Size() + if ones == bits { + return cidr.IP, nil + } + for i := range cidr.IP { + cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i] + } + return cidr.IP, nil +} + +// FirstIPInSubnet gets the first IP in a subnet +func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer + // re-parse to ensure clean network address + _, cidr, err := net.ParseCIDR(addr.String()) + if err != nil { + return nil, err + } + ones, bits := cidr.Mask.Size() + if ones == bits { + return cidr.IP, nil + } + cidr.IP[len(cidr.IP)-1]++ + return cidr.IP, nil +} + +func NetworkIntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) bool { + for _, nw := range networklist { + if networkIntersect(n, nw) { + return true + } + } + return false +} + +func networkIntersect(n1, n2 *net.IPNet) bool { + return n2.Contains(n1.IP) || n1.Contains(n2.IP) +} + +// GetRandomIPv6Subnet returns a random internal ipv6 subnet as described in RFC3879. +func GetRandomIPv6Subnet() (net.IPNet, error) { + ip := make(net.IP, 8, net.IPv6len) + // read 8 random bytes + _, err := rand.Read(ip) + if err != nil { + return net.IPNet{}, nil + } + // first byte must be FD as per RFC3879 + ip[0] = 0xfd + // add 8 zero bytes + ip = append(ip, make([]byte, 8)...) + return net.IPNet{IP: ip, Mask: net.CIDRMask(64, 128)}, nil +} diff -Nru libpod-3.2.1+ds1/libpod/network/util/ip_test.go libpod-3.4.4+ds1/libpod/network/util/ip_test.go --- libpod-3.2.1+ds1/libpod/network/util/ip_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/network/util/ip_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,125 @@ +package util + +import ( + "fmt" + "net" + "reflect" + "testing" +) + +func parseCIDR(n string) *net.IPNet { + _, parsedNet, _ := net.ParseCIDR(n) + return parsedNet +} + +func TestNextSubnet(t *testing.T) { + type args struct { + subnet *net.IPNet + } + tests := []struct { + name string + args args + want *net.IPNet + wantErr bool + }{ + {"class b", args{subnet: parseCIDR("192.168.0.0/16")}, parseCIDR("192.169.0.0/16"), false}, + {"class c", args{subnet: parseCIDR("192.168.1.0/24")}, parseCIDR("192.168.2.0/24"), false}, + } + for _, tt := range tests { + test := tt + t.Run(test.name, func(t *testing.T) { + got, err := NextSubnet(test.args.subnet) + if (err != nil) != test.wantErr { + t.Errorf("NextSubnet() error = %v, wantErr %v", err, test.wantErr) + return + } + if !reflect.DeepEqual(got, test.want) { + t.Errorf("NextSubnet() got = %v, want %v", got, test.want) + } + }) + } +} + +func TestFirstIPInSubnet(t *testing.T) { + tests := []struct { + name string + args *net.IPNet + want net.IP + wantErr bool + }{ + {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.0.1"), false}, + {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.1"), false}, + {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.0.1"), false}, + {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.1"), false}, + {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.129"), false}, + {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.0.0.1"), false}, + {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, + {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, + } + for _, tt := range tests { + test := tt + t.Run(test.name, func(t *testing.T) { + got, err := FirstIPInSubnet(test.args) + if (err != nil) != test.wantErr { + t.Errorf("FirstIPInSubnet() error = %v, wantErr %v", err, test.wantErr) + return + } + if !got.Equal(test.want) { + t.Errorf("FirstIPInSubnet() got = %v, want %v", got, test.want) + } + }) + } +} + +func TestLastIPInSubnet(t *testing.T) { + tests := []struct { + name string + args *net.IPNet + want net.IP + wantErr bool + }{ + {"class b", parseCIDR("192.168.0.0/16"), net.ParseIP("192.168.255.255"), false}, + {"class c", parseCIDR("192.168.1.0/24"), net.ParseIP("192.168.1.255"), false}, + {"cidr /23", parseCIDR("192.168.0.0/23"), net.ParseIP("192.168.1.255"), false}, + {"cidr /25", parseCIDR("192.168.1.0/25"), net.ParseIP("192.168.1.127"), false}, + {"cidr /26", parseCIDR("172.16.1.128/26"), net.ParseIP("172.16.1.191"), false}, + {"class a", parseCIDR("10.0.0.0/8"), net.ParseIP("10.255.255.255"), false}, + {"cidr /32", parseCIDR("192.168.255.4/32"), net.ParseIP("192.168.255.4"), false}, + {"cidr /31", parseCIDR("192.168.255.4/31"), net.ParseIP("192.168.255.5"), false}, + } + for _, tt := range tests { + test := tt + t.Run(test.name, func(t *testing.T) { + got, err := LastIPInSubnet(test.args) + if (err != nil) != test.wantErr { + t.Errorf("LastIPInSubnet() error = %v, wantErr %v", err, test.wantErr) + return + } + if !got.Equal(test.want) { + t.Errorf("LastIPInSubnet() got = %v, want %v", got, test.want) + } + }) + } +} + +func TestGetRandomIPv6Subnet(t *testing.T) { + for i := 0; i < 1000; i++ { + t.Run(fmt.Sprintf("GetRandomIPv6Subnet %d", i), func(t *testing.T) { + sub, err := GetRandomIPv6Subnet() + if err != nil { + t.Errorf("GetRandomIPv6Subnet() error should be nil: %v", err) + return + } + if sub.IP.To4() != nil { + t.Errorf("ip %s is not an ipv6 address", sub.IP) + } + if sub.IP[0] != 0xfd { + t.Errorf("ipv6 %s does not start with fd", sub.IP) + } + ones, bytes := sub.Mask.Size() + if ones != 64 || bytes != 128 { + t.Errorf("wrong network mask %v, it should be /64", sub.Mask) + } + }) + } +} diff -Nru libpod-3.2.1+ds1/libpod/networking_linux.go libpod-3.4.4+ds1/libpod/networking_linux.go --- libpod-3.2.1+ds1/libpod/networking_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/networking_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -46,6 +46,9 @@ // rootlessCNINSName is the file name for the rootless network namespace bind mount rootlessCNINSName = "rootless-cni-ns" + + // persistentCNIDir is the directory where the CNI files are stored + persistentCNIDir = "/var/lib/cni" ) // Get an OCICNI network config @@ -105,16 +108,36 @@ type RootlessCNI struct { ns ns.NetNS dir string - lock lockfile.Locker + Lock lockfile.Locker +} + +// getPath will join the given path to the rootless cni dir +func (r *RootlessCNI) getPath(path string) string { + return filepath.Join(r.dir, path) } +// Do - run the given function in the rootless cni ns. +// It does not lock the rootlessCNI lock, the caller +// should only lock when needed, e.g. for cni operations. func (r *RootlessCNI) Do(toRun func() error) error { err := r.ns.Do(func(_ ns.NetNS) error { - // before we can run the given function - // we have to setup all mounts correctly + // Before we can run the given function, + // we have to setup all mounts correctly. - // create a new mount namespace - // this should happen inside the netns thread + // The order of the mounts is IMPORTANT. + // The idea of the extra mount ns is to make /run and /var/lib/cni writeable + // for the cni plugins but not affecting the podman user namespace. + // Because the plugins also need access to XDG_RUNTIME_DIR/netns some special setup is needed. + + // The following bind mounts are needed + // 1. XDG_RUNTIME_DIR/netns -> XDG_RUNTIME_DIR/rootless-cni/XDG_RUNTIME_DIR/netns + // 2. /run/systemd -> XDG_RUNTIME_DIR/rootless-cni/run/systemd (only if it exists) + // 3. XDG_RUNTIME_DIR/rootless-cni/resolv.conf -> /etc/resolv.conf or XDG_RUNTIME_DIR/rootless-cni/run/symlink/target + // 4. XDG_RUNTIME_DIR/rootless-cni/var/lib/cni -> /var/lib/cni (if /var/lib/cni does not exists use the parent dir) + // 5. XDG_RUNTIME_DIR/rootless-cni/run -> /run + + // Create a new mount namespace, + // this must happen inside the netns thread. err := unix.Unshare(unix.CLONE_NEWNS) if err != nil { return errors.Wrapf(err, "cannot create a new mount namespace") @@ -124,41 +147,118 @@ if err != nil { return errors.Wrap(err, "could not get network namespace directory") } - newNetNsDir := filepath.Join(r.dir, netNsDir) - // mount the netns into the new run to keep them accessible - // otherwise cni setup will fail because it cannot access the netns files + newNetNsDir := r.getPath(netNsDir) + // 1. Mount the netns into the new run to keep them accessible. + // Otherwise cni setup will fail because it cannot access the netns files. err = unix.Mount(netNsDir, newNetNsDir, "none", unix.MS_BIND|unix.MS_SHARED|unix.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount netns directory for rootless cni") } - // mount resolv.conf to make use of the host dns - err = unix.Mount(filepath.Join(r.dir, "resolv.conf"), "/etc/resolv.conf", "none", unix.MS_BIND, "") - if err != nil { - return errors.Wrap(err, "failed to mount resolv.conf for rootless cni") - } - - // also keep /run/systemd if it exists - // many files are symlinked into this dir, for example /dev/log + // 2. Also keep /run/systemd if it exists. + // Many files are symlinked into this dir, for example /dev/log. runSystemd := "/run/systemd" _, err = os.Stat(runSystemd) if err == nil { - newRunSystemd := filepath.Join(r.dir, runSystemd[1:]) + newRunSystemd := r.getPath(runSystemd) err = unix.Mount(runSystemd, newRunSystemd, "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount /run/systemd directory for rootless cni") } } - // cni plugins need access to /var and /run - runDir := filepath.Join(r.dir, "run") - varDir := filepath.Join(r.dir, "var") + // 3. On some distros /etc/resolv.conf is symlinked to somewhere under /run. + // Because the kernel will follow the symlink before mounting, it is not + // possible to mount a file at /etc/resolv.conf. We have to ensure that + // the link target will be available in the mount ns. + // see: https://github.com/containers/podman/issues/10855 + resolvePath := "/etc/resolv.conf" + for i := 0; i < 255; i++ { + // Do not use filepath.EvalSymlinks, we only want the first symlink under /run. + // If /etc/resolv.conf has more than one symlink under /run, e.g. + // -> /run/systemd/resolve/stub-resolv.conf -> /run/systemd/resolve/resolv.conf + // we would put the netns resolv.conf file to the last path. However this will + // break dns because the second link does not exists in the mount ns. + // see https://github.com/containers/podman/issues/11222 + link, err := os.Readlink(resolvePath) + if err != nil { + // if there is no symlink exit + break + } + if filepath.IsAbs(link) { + // link is as an absolute path + resolvePath = link + } else { + // link is as a relative, join it with the previous path + resolvePath = filepath.Join(filepath.Dir(resolvePath), link) + } + if strings.HasPrefix(resolvePath, "/run/") { + break + } + if i == 254 { + return errors.New("too many symlinks while resolving /etc/resolv.conf") + } + } + logrus.Debugf("The path of /etc/resolv.conf in the mount ns is %q", resolvePath) + // When /etc/resolv.conf on the host is a symlink to /run/systemd/resolve/stub-resolv.conf, + // we have to mount an empty filesystem on /run/systemd/resolve in the child namespace, + // so as to isolate the directory from the host mount namespace. + // + // Otherwise our bind-mount for /run/systemd/resolve/stub-resolv.conf is unmounted + // when systemd-resolved unlinks and recreates /run/systemd/resolve/stub-resolv.conf on the host. + // see: https://github.com/containers/podman/issues/10929 + if strings.HasPrefix(resolvePath, "/run/systemd/resolve/") { + rsr := r.getPath("/run/systemd/resolve") + err = unix.Mount("", rsr, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, "") + if err != nil { + return errors.Wrapf(err, "failed to mount tmpfs on %q for rootless cni", rsr) + } + } + if strings.HasPrefix(resolvePath, "/run/") { + resolvePath = r.getPath(resolvePath) + err = os.MkdirAll(filepath.Dir(resolvePath), 0700) + if err != nil { + return errors.Wrap(err, "failed to create rootless-cni resolv.conf directory") + } + // we want to bind mount on this file so we have to create the file first + _, err = os.OpenFile(resolvePath, os.O_CREATE|os.O_RDONLY, 0700) + if err != nil { + return errors.Wrap(err, "failed to create rootless-cni resolv.conf file") + } + } + // mount resolv.conf to make use of the host dns + err = unix.Mount(r.getPath("resolv.conf"), resolvePath, "none", unix.MS_BIND, "") + if err != nil { + return errors.Wrap(err, "failed to mount resolv.conf for rootless cni") + } + + // 4. CNI plugins need access to /var/lib/cni and /run + varDir := "" + varTarget := persistentCNIDir + // we can only mount to a target dir which exists, check /var/lib/cni recursively + // while we could always use /var there are cases where a user might store the cni + // configs under /var/custom and this would break + for { + if _, err := os.Stat(varTarget); err == nil { + varDir = r.getPath(varTarget) + break + } + varTarget = filepath.Dir(varTarget) + if varTarget == "/" { + break + } + } + if varDir == "" { + return errors.New("failed to stat /var directory") + } // make sure to mount var first - err = unix.Mount(varDir, "/var", "none", unix.MS_BIND, "") + err = unix.Mount(varDir, varTarget, "none", unix.MS_BIND, "") if err != nil { - return errors.Wrap(err, "failed to mount /var for rootless cni") + return errors.Wrapf(err, "failed to mount %s for rootless cni", varTarget) } - // recursive mount to keep the netns mount + + // 5. Mount the new prepared run dir to /run, it has to be recursive to keep the other bind mounts. + runDir := r.getPath("run") err = unix.Mount(runDir, "/run", "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount /run for rootless cni") @@ -171,16 +271,17 @@ return err } -// Cleanup the rootless cni namespace if needed -// check if we have running containers with the bridge network mode +// Cleanup the rootless cni namespace if needed. +// It checks if we have running containers with the bridge network mode. +// Cleanup() will try to lock RootlessCNI, therefore you have to call it with an unlocked func (r *RootlessCNI) Cleanup(runtime *Runtime) error { _, err := os.Stat(r.dir) if os.IsNotExist(err) { // the directory does not exists no need for cleanup return nil } - r.lock.Lock() - defer r.lock.Unlock() + r.Lock.Lock() + defer r.Lock.Unlock() running := func(c *Container) bool { // we cannot use c.state() because it will try to lock the container // using c.state.State directly should be good enough for this use case @@ -201,7 +302,7 @@ // make sure the the cni results (cache) dir is empty // libpod instances with another root dir are not covered by the check above // this allows several libpod instances to use the same rootless cni ns - contents, err := ioutil.ReadDir(filepath.Join(r.dir, "var/lib/cni/results")) + contents, err := ioutil.ReadDir(r.getPath("var/lib/cni/results")) if (err == nil && len(contents) == 0) || os.IsNotExist(err) { logrus.Debug("Cleaning up rootless cni namespace") err = netns.UnmountNS(r.ns) @@ -213,7 +314,7 @@ if err != nil { logrus.Error(err) } - b, err := ioutil.ReadFile(filepath.Join(r.dir, "rootless-cni-slirp4netns.pid")) + b, err := ioutil.ReadFile(r.getPath("rootless-cni-slirp4netns.pid")) if err == nil { var i int i, err = strconv.Atoi(string(b)) @@ -238,208 +339,246 @@ // GetRootlessCNINetNs returns the rootless cni object. If create is set to true // the rootless cni namespace will be created if it does not exists already. +// If called as root it returns always nil. +// On success the returned RootlessCNI lock is locked and must be unlocked by the caller. func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) { + if !rootless.IsRootless() { + return nil, nil + } var rootlessCNINS *RootlessCNI - if rootless.IsRootless() { - runDir, err := util.GetRuntimeDir() - if err != nil { - return nil, err + runDir, err := util.GetRuntimeDir() + if err != nil { + return nil, err + } + + lfile := filepath.Join(runDir, "rootless-cni.lock") + lock, err := lockfile.GetLockfile(lfile) + if err != nil { + return nil, errors.Wrap(err, "failed to get rootless-cni lockfile") + } + lock.Lock() + defer func() { + // In case of an error (early exit) rootlessCNINS will be nil. + // Make sure to unlock otherwise we could deadlock. + if rootlessCNINS == nil { + lock.Unlock() } - cniDir := filepath.Join(runDir, "rootless-cni") - err = os.MkdirAll(cniDir, 0700) + }() + + cniDir := filepath.Join(runDir, "rootless-cni") + err = os.MkdirAll(cniDir, 0700) + if err != nil { + return nil, errors.Wrap(err, "could not create rootless-cni directory") + } + + nsDir, err := netns.GetNSRunDir() + if err != nil { + return nil, err + } + path := filepath.Join(nsDir, rootlessCNINSName) + ns, err := ns.GetNS(path) + if err != nil { + if !new { + // return a error if we could not get the namespace and should no create one + return nil, errors.Wrap(err, "error getting rootless cni network namespace") + } + // create a new namespace + logrus.Debug("creating rootless cni network namespace") + ns, err = netns.NewNSWithName(rootlessCNINSName) if err != nil { - return nil, errors.Wrap(err, "could not create rootless-cni directory") + return nil, errors.Wrap(err, "error creating rootless cni network namespace") + } + // setup slirp4netns here + path := r.config.Engine.NetworkCmdPath + if path == "" { + var err error + path, err = exec.LookPath("slirp4netns") + if err != nil { + return nil, err + } } - lfile := filepath.Join(cniDir, "rootless-cni.lck") - lock, err := lockfile.GetLockfile(lfile) + syncR, syncW, err := os.Pipe() if err != nil { - return nil, errors.Wrap(err, "failed to get rootless-cni lockfile") + return nil, errors.Wrapf(err, "failed to open pipe") } - lock.Lock() - defer lock.Unlock() + defer errorhandling.CloseQuiet(syncR) + defer errorhandling.CloseQuiet(syncW) - nsDir, err := netns.GetNSRunDir() + netOptions, err := parseSlirp4netnsNetworkOptions(r, nil) if err != nil { return nil, err } - path := filepath.Join(nsDir, rootlessCNINSName) - ns, err := ns.GetNS(path) + slirpFeatures, err := checkSlirpFlags(path) if err != nil { - if new { - // create a new namespace - logrus.Debug("creating rootless cni network namespace") - ns, err = netns.NewNSWithName(rootlessCNINSName) - if err != nil { - return nil, errors.Wrap(err, "error creating rootless cni network namespace") - } - - // setup slirp4netns here - path := r.config.Engine.NetworkCmdPath - if path == "" { - var err error - path, err = exec.LookPath("slirp4netns") - if err != nil { - return nil, err - } - } - - syncR, syncW, err := os.Pipe() - if err != nil { - return nil, errors.Wrapf(err, "failed to open pipe") - } - defer errorhandling.CloseQuiet(syncR) - defer errorhandling.CloseQuiet(syncW) - - netOptions, err := parseSlirp4netnsNetworkOptions(r, nil) - if err != nil { - return nil, err - } - slirpFeatures, err := checkSlirpFlags(path) - if err != nil { - return nil, errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err) - } - cmdArgs, err := createBasicSlirp4netnsCmdArgs(netOptions, slirpFeatures) - if err != nil { - return nil, err - } - // Note we do not use --exit-fd, we kill this process by pid - cmdArgs = append(cmdArgs, "-c", "-r", "3") - cmdArgs = append(cmdArgs, "--netns-type=path", ns.Path(), "tap0") - - cmd := exec.Command(path, cmdArgs...) - logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " ")) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - } - - // workaround for https://github.com/rootless-containers/slirp4netns/pull/153 - if !netOptions.noPivotRoot && slirpFeatures.HasEnableSandbox { - cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS - cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS - } + return nil, errors.Wrapf(err, "error checking slirp4netns binary %s: %q", path, err) + } + cmdArgs, err := createBasicSlirp4netnsCmdArgs(netOptions, slirpFeatures) + if err != nil { + return nil, err + } + // Note we do not use --exit-fd, we kill this process by pid + cmdArgs = append(cmdArgs, "-c", "-r", "3") + cmdArgs = append(cmdArgs, "--netns-type=path", ns.Path(), "tap0") - // Leak one end of the pipe in slirp4netns - cmd.ExtraFiles = append(cmd.ExtraFiles, syncW) + cmd := exec.Command(path, cmdArgs...) + logrus.Debugf("slirp4netns command: %s", strings.Join(cmd.Args, " ")) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } - logPath := filepath.Join(r.config.Engine.TmpDir, "slirp4netns-rootless-cni.log") - logFile, err := os.Create(logPath) - if err != nil { - return nil, errors.Wrapf(err, "failed to open slirp4netns log file %s", logPath) - } - defer logFile.Close() - // Unlink immediately the file so we won't need to worry about cleaning it up later. - // It is still accessible through the open fd logFile. - if err := os.Remove(logPath); err != nil { - return nil, errors.Wrapf(err, "delete file %s", logPath) - } - cmd.Stdout = logFile - cmd.Stderr = logFile - if err := cmd.Start(); err != nil { - return nil, errors.Wrapf(err, "failed to start slirp4netns process") - } - // create pid file for the slirp4netns process - // this is need to kill the process in the cleanup - pid := strconv.Itoa(cmd.Process.Pid) - err = ioutil.WriteFile(filepath.Join(cniDir, "rootless-cni-slirp4netns.pid"), []byte(pid), 0700) - if err != nil { - errors.Wrap(err, "unable to write rootless-cni slirp4netns pid file") - } + // workaround for https://github.com/rootless-containers/slirp4netns/pull/153 + if !netOptions.noPivotRoot && slirpFeatures.HasEnableSandbox { + cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNS + cmd.SysProcAttr.Unshareflags = syscall.CLONE_NEWNS + } - defer func() { - if err := cmd.Process.Release(); err != nil { - logrus.Errorf("unable to release command process: %q", err) - } - }() + // Leak one end of the pipe in slirp4netns + cmd.ExtraFiles = append(cmd.ExtraFiles, syncW) - if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil { - return nil, err - } + logPath := filepath.Join(r.config.Engine.TmpDir, "slirp4netns-rootless-cni.log") + logFile, err := os.Create(logPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to open slirp4netns log file %s", logPath) + } + defer logFile.Close() + // Unlink immediately the file so we won't need to worry about cleaning it up later. + // It is still accessible through the open fd logFile. + if err := os.Remove(logPath); err != nil { + return nil, errors.Wrapf(err, "delete file %s", logPath) + } + cmd.Stdout = logFile + cmd.Stderr = logFile + if err := cmd.Start(); err != nil { + return nil, errors.Wrapf(err, "failed to start slirp4netns process") + } + // create pid file for the slirp4netns process + // this is need to kill the process in the cleanup + pid := strconv.Itoa(cmd.Process.Pid) + err = ioutil.WriteFile(filepath.Join(cniDir, "rootless-cni-slirp4netns.pid"), []byte(pid), 0700) + if err != nil { + errors.Wrap(err, "unable to write rootless-cni slirp4netns pid file") + } - // build a new resolv.conf file which uses the slirp4netns dns server address - resolveIP, err := GetSlirp4netnsDNS(nil) - if err != nil { - return nil, errors.Wrap(err, "failed to determine default slirp4netns DNS address") - } + defer func() { + if err := cmd.Process.Release(); err != nil { + logrus.Errorf("unable to release command process: %q", err) + } + }() - if netOptions.cidr != "" { - _, cidr, err := net.ParseCIDR(netOptions.cidr) - if err != nil { - return nil, errors.Wrap(err, "failed to parse slirp4netns cidr") - } - resolveIP, err = GetSlirp4netnsDNS(cidr) - if err != nil { - return nil, errors.Wrapf(err, "failed to determine slirp4netns DNS address from cidr: %s", cidr.String()) - } - } - conf, err := resolvconf.Get() - if err != nil { - return nil, err - } - searchDomains := resolvconf.GetSearchDomains(conf.Content) - dnsOptions := resolvconf.GetOptions(conf.Content) + if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil { + return nil, err + } - _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP.String()}, searchDomains, dnsOptions) - if err != nil { - return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf") - } + // build a new resolv.conf file which uses the slirp4netns dns server address + resolveIP, err := GetSlirp4netnsDNS(nil) + if err != nil { + return nil, errors.Wrap(err, "failed to determine default slirp4netns DNS address") + } - // create cni directories to store files - // they will be bind mounted to the correct location in a extra mount ns - err = os.MkdirAll(filepath.Join(cniDir, "var"), 0700) - if err != nil { - return nil, errors.Wrap(err, "could not create rootless-cni var directory") - } - runDir := filepath.Join(cniDir, "run") - err = os.MkdirAll(runDir, 0700) - if err != nil { - return nil, errors.Wrap(err, "could not create rootless-cni run directory") - } - // relabel the new run directory to the iptables /run label - // this is important, otherwise the iptables command will fail - err = label.Relabel(runDir, "system_u:object_r:iptables_var_run_t:s0", false) - if err != nil { - return nil, errors.Wrap(err, "could not create relabel rootless-cni run directory") - } - // create systemd run directory - err = os.MkdirAll(filepath.Join(runDir, "systemd"), 0700) - if err != nil { - return nil, errors.Wrap(err, "could not create rootless-cni systemd directory") - } - // create the directory for the netns files at the same location - // relative to the rootless-cni location - err = os.MkdirAll(filepath.Join(cniDir, nsDir), 0700) - if err != nil { - return nil, errors.Wrap(err, "could not create rootless-cni netns directory") - } - } else { - // return a error if we could not get the namespace and should no create one - return nil, errors.Wrap(err, "error getting rootless cni network namespace") + if netOptions.cidr != "" { + _, cidr, err := net.ParseCIDR(netOptions.cidr) + if err != nil { + return nil, errors.Wrap(err, "failed to parse slirp4netns cidr") } + resolveIP, err = GetSlirp4netnsDNS(cidr) + if err != nil { + return nil, errors.Wrapf(err, "failed to determine slirp4netns DNS address from cidr: %s", cidr.String()) + } + } + conf, err := resolvconf.Get() + if err != nil { + return nil, err } + conf, err = resolvconf.FilterResolvDNS(conf.Content, netOptions.enableIPv6, true) + if err != nil { + return nil, err + } + searchDomains := resolvconf.GetSearchDomains(conf.Content) + dnsOptions := resolvconf.GetOptions(conf.Content) + nameServers := resolvconf.GetNameservers(conf.Content) - // The CNI plugins need access to iptables in $PATH. As it turns out debian doesn't put - // /usr/sbin in $PATH for rootless users. This will break rootless cni completely. - // We might break existing users and we cannot expect everyone to change their $PATH so - // lets add /usr/sbin to $PATH ourselves. - path = os.Getenv("PATH") - if !strings.Contains(path, "/usr/sbin") { - path = path + ":/usr/sbin" - os.Setenv("PATH", path) + _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), append([]string{resolveIP.String()}, nameServers...), searchDomains, dnsOptions) + if err != nil { + return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf") } - rootlessCNINS = &RootlessCNI{ - ns: ns, - dir: cniDir, - lock: lock, + // create cni directories to store files + // they will be bind mounted to the correct location in a extra mount ns + err = os.MkdirAll(filepath.Join(cniDir, strings.TrimPrefix(persistentCNIDir, "/")), 0700) + if err != nil { + return nil, errors.Wrap(err, "could not create rootless-cni var directory") + } + runDir := filepath.Join(cniDir, "run") + err = os.MkdirAll(runDir, 0700) + if err != nil { + return nil, errors.Wrap(err, "could not create rootless-cni run directory") + } + // relabel the new run directory to the iptables /run label + // this is important, otherwise the iptables command will fail + err = label.Relabel(runDir, "system_u:object_r:iptables_var_run_t:s0", false) + if err != nil { + return nil, errors.Wrap(err, "could not create relabel rootless-cni run directory") + } + // create systemd run directory + err = os.MkdirAll(filepath.Join(runDir, "systemd"), 0700) + if err != nil { + return nil, errors.Wrap(err, "could not create rootless-cni systemd directory") + } + // create the directory for the netns files at the same location + // relative to the rootless-cni location + err = os.MkdirAll(filepath.Join(cniDir, nsDir), 0700) + if err != nil { + return nil, errors.Wrap(err, "could not create rootless-cni netns directory") } } + + // The CNI plugins need access to iptables in $PATH. As it turns out debian doesn't put + // /usr/sbin in $PATH for rootless users. This will break rootless cni completely. + // We might break existing users and we cannot expect everyone to change their $PATH so + // lets add /usr/sbin to $PATH ourselves. + path = os.Getenv("PATH") + if !strings.Contains(path, "/usr/sbin") { + path = path + ":/usr/sbin" + os.Setenv("PATH", path) + } + + // Important set rootlessCNINS as last step. + // Do not return any errors after this. + rootlessCNINS = &RootlessCNI{ + ns: ns, + dir: cniDir, + Lock: lock, + } return rootlessCNINS, nil } +// setPrimaryMachineIP is used for podman-machine and it sets +// and environment variable with the IP address of the podman-machine +// host. +func setPrimaryMachineIP() error { + // no connection is actually made here + conn, err := net.Dial("udp", "8.8.8.8:80") + if err != nil { + return err + } + defer func() { + if err := conn.Close(); err != nil { + logrus.Error(err) + } + }() + addr := conn.LocalAddr().(*net.UDPAddr) + return os.Setenv("PODMAN_MACHINE_HOST", addr.IP.String()) +} + // setUpOCICNIPod will set up the cni networks, on error it will also tear down the cni // networks. If rootless it will join/create the rootless cni namespace. func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResult, error) { + if r.config.MachineEnabled() { + if err := setPrimaryMachineIP(); err != nil { + return nil, err + } + } rootlessCNINS, err := r.GetRootlessCNINetNs(true) if err != nil { return nil, err @@ -459,6 +598,7 @@ if rootlessCNINS != nil { // execute the cni setup in the rootless net ns err = rootlessCNINS.Do(setUpPod) + rootlessCNINS.Lock.Unlock() } else { err = setUpPod() } @@ -498,7 +638,6 @@ } podName := getCNIPodName(ctr) - networks, _, err := ctr.networks() if err != nil { return nil, err @@ -579,6 +718,7 @@ // set up port forwarder for CNI-in-slirp4netns netnsPath := ctr.state.NetNS.Path() // TODO: support slirp4netns port forwarder as well + // make sure to fix this container.handleRestartPolicy() as well return r.setupRootlessPortMappingViaRLK(ctr, netnsPath) } return nil @@ -671,6 +811,7 @@ if rootlessCNINS != nil { // execute the cni setup in the rootless net ns err = rootlessCNINS.Do(tearDownPod) + rootlessCNINS.Lock.Unlock() if err == nil { err = rootlessCNINS.Cleanup(r) } @@ -866,6 +1007,10 @@ if err != nil { return nil, err } + // see https://github.com/containers/podman/issues/10090 + // the container has to be locked for syncContainer() + netNsCtr.lock.Lock() + defer netNsCtr.lock.Unlock() // Have to sync to ensure that state is populated if err := netNsCtr.syncContainer(); err != nil { return nil, err @@ -876,7 +1021,7 @@ } settings := new(define.InspectNetworkSettings) - settings.Ports = makeInspectPortBindings(c.config.PortMappings) + settings.Ports = makeInspectPortBindings(c.config.PortMappings, c.config.ExposedPorts) networks, isDefault, err := c.networks() if err != nil { @@ -1021,7 +1166,7 @@ // after itself on an unclean reboot. Return what we're pretty sure is the path // to CNI's internal files (it's not really exposed to us). func getCNINetworksDir() (string, error) { - return "/var/lib/cni/networks", nil + return filepath.Join(persistentCNIDir, "networks"), nil } type logrusDebugWriter struct { @@ -1091,7 +1236,29 @@ } } c.state.NetworkStatus = tmpNetworkStatus - return c.save() + err = c.save() + if err != nil { + return err + } + + // OCICNI will set the loopback adapter down on teardown so we should set it up again + err = c.state.NetNS.Do(func(_ ns.NetNS) error { + link, err := netlink.LinkByName("lo") + if err != nil { + return err + } + err = netlink.LinkSetUp(link) + return err + }) + if err != nil { + logrus.Warnf("failed to set loopback adapter up in the container: %v", err) + } + // Reload ports when there are still connected networks, maybe we removed the network interface with the child ip. + // Reloading without connected networks does not make sense, so we can skip this step. + if rootless.IsRootless() && len(tmpNetworkStatus) > 0 { + return c.reloadRootlessRLKPortMapping() + } + return nil } // ConnectNetwork connects a container to a given network @@ -1183,7 +1350,16 @@ networkStatus[index] = networkResults[0] c.state.NetworkStatus = networkStatus } - return c.save() + err = c.save() + if err != nil { + return err + } + // The first network needs a port reload to set the correct child ip for the rootlessport process. + // Adding a second network does not require a port reload because the child ip is still valid. + if rootless.IsRootless() && len(networks) == 0 { + return c.reloadRootlessRLKPortMapping() + } + return nil } // DisconnectContainerFromNetwork removes a container from its CNI network diff -Nru libpod-3.2.1+ds1/libpod/networking_slirp4netns.go libpod-3.4.4+ds1/libpod/networking_slirp4netns.go --- libpod-3.2.1+ds1/libpod/networking_slirp4netns.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/networking_slirp4netns.go 2021-12-26 00:45:51.000000000 +0000 @@ -16,8 +16,11 @@ "syscall" "time" + "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/podman/v3/pkg/errorhandling" + "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/pkg/rootlessport" + "github.com/containers/podman/v3/pkg/servicereaper" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -56,6 +59,8 @@ outboundAddr6 string } +const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/default/accept_dad" + func checkSlirpFlags(path string) (*slirpFeatures, error) { cmd := exec.Command(path, "--help") out, err := cmd.CombinedOutput() @@ -220,7 +225,7 @@ defer errorhandling.CloseQuiet(syncR) defer errorhandling.CloseQuiet(syncW) - havePortMapping := len(ctr.Config().PortMappings) > 0 + havePortMapping := len(ctr.config.PortMappings) > 0 logPath := filepath.Join(ctr.runtime.config.Engine.TmpDir, fmt.Sprintf("slirp4netns-%s.log", ctr.config.ID)) ctrNetworkSlipOpts := []string{} @@ -295,10 +300,44 @@ } cmd.Stdout = logFile cmd.Stderr = logFile + + var slirpReadyChan (chan struct{}) + + if netOptions.enableIPv6 { + slirpReadyChan = make(chan struct{}) + defer close(slirpReadyChan) + go func() { + err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error { + // Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds. + // Since slirp4netns is run it is own namespace and not directly routed + // we can skip this to make the ipv6 address immediately available. + // We change the default to make sure the slirp tap interface gets the + // correct value assigned so DAD is disabled for it + // Also make sure to change this value back to the original after slirp4netns + // is ready in case users rely on this sysctl. + orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl) + if err != nil { + return err + } + err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644) + if err != nil { + return err + } + // wait for slirp to finish setup + <-slirpReadyChan + return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644) + }) + if err != nil { + logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err) + } + }() + } + if err := cmd.Start(); err != nil { return errors.Wrapf(err, "failed to start slirp4netns process") } defer func() { + servicereaper.AddPID(cmd.Process.Pid) if err := cmd.Process.Release(); err != nil { logrus.Errorf("unable to release command process: %q", err) } @@ -307,6 +346,9 @@ if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil { return err } + if slirpReadyChan != nil { + slirpReadyChan <- struct{}{} + } // Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself _, ctr.slirp4netnsSubnet, _ = net.ParseCIDR(defaultSlirp4netnsSubnet) @@ -464,29 +506,16 @@ } } - slirp4netnsIP, err := GetSlirp4netnsIP(ctr.slirp4netnsSubnet) - if err != nil { - return errors.Wrapf(err, "failed to get slirp4ns ip") - } - childIP := slirp4netnsIP.String() -outer: - for _, r := range ctr.state.NetworkStatus { - for _, i := range r.IPs { - ipv4 := i.Address.IP.To4() - if ipv4 != nil { - childIP = ipv4.String() - break outer - } - } - } - + childIP := getRootlessPortChildIP(ctr) cfg := rootlessport.Config{ - Mappings: ctr.config.PortMappings, - NetNSPath: netnsPath, - ExitFD: 3, - ReadyFD: 4, - TmpDir: ctr.runtime.config.Engine.TmpDir, - ChildIP: childIP, + Mappings: ctr.config.PortMappings, + NetNSPath: netnsPath, + ExitFD: 3, + ReadyFD: 4, + TmpDir: ctr.runtime.config.Engine.TmpDir, + ChildIP: childIP, + ContainerID: ctr.config.ID, + RootlessCNI: ctr.config.NetMode.IsBridge() && rootless.IsRootless(), } cfgJSON, err := json.Marshal(cfg) if err != nil { @@ -514,6 +543,7 @@ return errors.Wrapf(err, "failed to start rootlessport process") } defer func() { + servicereaper.AddPID(cmd.Process.Pid) if err := cmd.Process.Release(); err != nil { logrus.Errorf("unable to release rootlessport process: %q", err) } @@ -614,3 +644,56 @@ logrus.Debug("slirp4netns port-forwarding setup via add_hostfwd is ready") return nil } + +func getRootlessPortChildIP(c *Container) string { + if c.config.NetMode.IsSlirp4netns() { + slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet) + if err != nil { + return "" + } + return slirp4netnsIP.String() + } + + for _, r := range c.state.NetworkStatus { + for _, i := range r.IPs { + ipv4 := i.Address.IP.To4() + if ipv4 != nil { + return ipv4.String() + } + } + } + return "" +} + +// reloadRootlessRLKPortMapping will trigger a reload for the port mappings in the rootlessport process. +// This should only be called by network connect/disconnect and only as rootless. +func (c *Container) reloadRootlessRLKPortMapping() error { + if len(c.config.PortMappings) == 0 { + return nil + } + childIP := getRootlessPortChildIP(c) + logrus.Debugf("reloading rootless ports for container %s, childIP is %s", c.config.ID, childIP) + + conn, err := openUnixSocket(filepath.Join(c.runtime.config.Engine.TmpDir, "rp", c.config.ID)) + if err != nil { + // This is not a hard error for backwards compatibility. A container started + // with an old version did not created the rootlessport socket. + logrus.Warnf("Could not reload rootless port mappings, port forwarding may no longer work correctly: %v", err) + return nil + } + defer conn.Close() + enc := json.NewEncoder(conn) + err = enc.Encode(childIP) + if err != nil { + return errors.Wrap(err, "port reloading failed") + } + b, err := ioutil.ReadAll(conn) + if err != nil { + return errors.Wrap(err, "port reloading failed") + } + data := string(b) + if data != "OK" { + return errors.Errorf("port reloading failed: %s", data) + } + return nil +} diff -Nru libpod-3.2.1+ds1/libpod/networking_unsupported.go libpod-3.4.4+ds1/libpod/networking_unsupported.go --- libpod-3.2.1+ds1/libpod/networking_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/networking_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -// +build !linux - -package libpod - -import ( - cnitypes "github.com/containernetworking/cni/pkg/types/current" - "github.com/containers/podman/v3/libpod/define" -) - -func (r *Runtime) setupRootlessNetNS(ctr *Container) error { - return define.ErrNotImplemented -} - -func (r *Runtime) setupSlirp4netns(ctr *Container) error { - return define.ErrNotImplemented -} - -func (r *Runtime) setupNetNS(ctr *Container) error { - return define.ErrNotImplemented -} - -func (r *Runtime) teardownNetNS(ctr *Container) error { - return define.ErrNotImplemented -} - -func (r *Runtime) createNetNS(ctr *Container) error { - return define.ErrNotImplemented -} - -func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, error) { - return nil, define.ErrNotImplemented -} - -func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) { - return nil, define.ErrNotImplemented -} - -func getCNINetworksDir() (string, error) { - return "", define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/oci_attach_linux.go libpod-3.4.4+ds1/libpod/oci_attach_linux.go --- libpod-3.2.1+ds1/libpod/oci_attach_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_attach_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -84,7 +84,7 @@ if attachRdy != nil { attachRdy <- true } - return readStdio(streams, receiveStdoutError, stdinDone) + return readStdio(conn, streams, receiveStdoutError, stdinDone) } // Attach to the given container's exec session @@ -94,17 +94,18 @@ // this ensures attachToExec gets all of the output of the called process // conmon will then send the exit code of the exec process, or an error in the exec session // startFd must be the input side of the fd. +// newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty // conmon will wait to start the exec session until the parent process has setup the console socket. // Once attachToExec successfully attaches to the console socket, the child conmon process responsible for calling runtime exec // will read from the output side of start fd, thus learning to start the child process. // Thus, the order goes as follow: // 1. conmon parent process sets up its console socket. sends on attachFd -// 2. attachToExec attaches to the console socket after reading on attachFd +// 2. attachToExec attaches to the console socket after reading on attachFd and resizes the tty // 3. child waits on startFd for attachToExec to attach to said console socket // 4. attachToExec sends on startFd, signalling it has attached to the socket and child is ready to go // 5. child receives on startFd, runs the runtime exec command // attachToExec is responsible for closing startFd and attachFd -func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File) error { +func (c *Container) attachToExec(streams *define.AttachStreams, keys *string, sessionID string, startFd, attachFd *os.File, newSize *define.TerminalSize) error { if !streams.AttachOutput && !streams.AttachError && !streams.AttachInput { return errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to") } @@ -137,6 +138,14 @@ return err } + // resize before we start the container process + if newSize != nil { + err = c.ociRuntime.ExecAttachResize(c, sessionID, *newSize) + if err != nil { + logrus.Warn("resize failed", err) + } + } + // 2: then attach conn, err := openUnixSocket(sockPath) if err != nil { @@ -156,7 +165,7 @@ return err } - return readStdio(streams, receiveStdoutError, stdinDone) + return readStdio(conn, streams, receiveStdoutError, stdinDone) } func processDetachKeys(keys string) ([]byte, error) { @@ -199,11 +208,6 @@ var err error if streams.AttachInput { _, err = utils.CopyDetachable(conn, streams.InputStream, detachKeys) - if err == nil { - if connErr := conn.CloseWrite(); connErr != nil { - logrus.Errorf("unable to close conn: %q", connErr) - } - } } stdinDone <- err }() @@ -256,7 +260,7 @@ return err } -func readStdio(streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { +func readStdio(conn *net.UnixConn, streams *define.AttachStreams, receiveStdoutError, stdinDone chan error) error { var err error select { case err = <-receiveStdoutError: @@ -265,6 +269,12 @@ if err == define.ErrDetach { return err } + if err == nil { + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } + } if streams.AttachOutput || streams.AttachError { return <-receiveStdoutError } diff -Nru libpod-3.2.1+ds1/libpod/oci_attach_unsupported.go libpod-3.4.4+ds1/libpod/oci_attach_unsupported.go --- libpod-3.2.1+ds1/libpod/oci_attach_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_attach_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -//+build !linux - -package libpod - -import ( - "os" - - "github.com/containers/podman/v3/libpod/define" -) - -func (c *Container) attach(streams *define.AttachStreams, keys string, resize <-chan define.TerminalSize, startContainer bool, started chan bool, attachRdy chan<- bool) error { - return define.ErrNotImplemented -} - -func (c *Container) attachToExec(streams *define.AttachStreams, keys string, resize <-chan define.TerminalSize, sessionID string, startFd *os.File, attachFd *os.File) error { - return define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/oci_conmon_exec_linux.go libpod-3.4.4+ds1/libpod/oci_conmon_exec_linux.go --- libpod-3.2.1+ds1/libpod/oci_conmon_exec_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_conmon_exec_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,7 +25,7 @@ ) // ExecContainer executes a command in a running container -func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams) (int, chan error, error) { +func (r *ConmonOCIRuntime) ExecContainer(c *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) { if options == nil { return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide an ExecOptions struct to ExecContainer") } @@ -68,7 +68,7 @@ attachChan := make(chan error) go func() { // attachToExec is responsible for closing pipes - attachChan <- c.attachToExec(streams, options.DetachKeys, sessionID, pipes.startPipe, pipes.attachPipe) + attachChan <- c.attachToExec(streams, options.DetachKeys, sessionID, pipes.startPipe, pipes.attachPipe, newSize) close(attachChan) }() @@ -83,7 +83,8 @@ // ExecContainerHTTP executes a new command in an existing container and // forwards its standard streams over an attach -func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (int, chan error, error) { +func (r *ConmonOCIRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, + streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) { if streams != nil { if !streams.Stdin && !streams.Stdout && !streams.Stderr { return -1, nil, errors.Wrapf(define.ErrInvalidArg, "must provide at least one stream to attach to") @@ -133,7 +134,7 @@ conmonPipeDataChan := make(chan conmonPipeData) go func() { // attachToExec is responsible for closing pipes - attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog) + attachChan <- attachExecHTTP(ctr, sessionID, req, w, streams, pipes, detachKeys, options.Terminal, cancel, hijackDone, holdConnOpen, execCmd, conmonPipeDataChan, ociLog, newSize) close(attachChan) }() @@ -437,7 +438,7 @@ // } // } - conmonEnv, extraFiles := r.configureConmonEnv(c, runtimeDir) + conmonEnv := r.configureConmonEnv(c, runtimeDir) var filesToClose []*os.File if options.PreserveFDs > 0 { @@ -455,13 +456,12 @@ execCmd.Env = append(execCmd.Env, conmonEnv...) execCmd.ExtraFiles = append(execCmd.ExtraFiles, childSyncPipe, childStartPipe, childAttachPipe) - execCmd.ExtraFiles = append(execCmd.ExtraFiles, extraFiles...) execCmd.Dir = c.execBundlePath(sessionID) execCmd.SysProcAttr = &syscall.SysProcAttr{ Setpgid: true, } - err = startCommandGivenSelinux(execCmd) + err = startCommandGivenSelinux(execCmd, c) // We don't need children pipes on the parent side errorhandling.CloseQuiet(childSyncPipe) @@ -486,7 +486,7 @@ } // Attach to a container over HTTP -func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string) (deferredErr error) { +func attachExecHTTP(c *Container, sessionID string, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, pipes *execPipes, detachKeys []byte, isTerminal bool, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, execCmd *exec.Cmd, conmonPipeDataChan chan<- conmonPipeData, ociLog string, newSize *define.TerminalSize) (deferredErr error) { // NOTE: As you may notice, the attach code is quite complex. // Many things happen concurrently and yet are interdependent. // If you ever change this function, make sure to write to the @@ -524,6 +524,14 @@ return err } + // resize before we start the container process + if newSize != nil { + err = c.ociRuntime.ExecAttachResize(c, sessionID, *newSize) + if err != nil { + logrus.Warn("resize failed", err) + } + } + // 2: then attach conn, err := openUnixSocket(sockPath) if err != nil { @@ -643,6 +651,10 @@ if err != nil { return err } + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } case <-cancel: return nil } @@ -673,6 +685,19 @@ pspec.Env = append(pspec.Env, env...) } + // Add secret envs if they exist + manager, err := c.runtime.SecretsManager() + if err != nil { + return nil, err + } + for name, secr := range c.config.EnvSecrets { + _, data, err := manager.LookupSecretData(secr.Name) + if err != nil { + return nil, err + } + pspec.Env = append(pspec.Env, fmt.Sprintf("%s=%s", name, string(data))) + } + if options.Cwd != "" { pspec.Cwd = options.Cwd } diff -Nru libpod-3.2.1+ds1/libpod/oci_conmon_linux.go libpod-3.4.4+ds1/libpod/oci_conmon_linux.go --- libpod-3.2.1+ds1/libpod/oci_conmon_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_conmon_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -34,7 +34,6 @@ "github.com/containers/podman/v3/utils" "github.com/containers/storage/pkg/homedir" pmount "github.com/containers/storage/pkg/mount" - "github.com/coreos/go-systemd/v22/activation" "github.com/coreos/go-systemd/v22/daemon" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/selinux/go-selinux" @@ -47,7 +46,9 @@ const ( // This is Conmon's STDIO_BUF_SIZE. I don't believe we have access to it // directly from the Go code, so const it here - bufferSize = conmonConfig.BufSize + // Important: The conmon attach socket uses an extra byte at the beginning of each + // message to specify the STREAM so we have to increase the buffer size by one + bufferSize = conmonConfig.BufSize + 1 ) // ConmonOCIRuntime is an OCI runtime managed by Conmon. @@ -66,7 +67,6 @@ supportsJSON bool supportsKVM bool supportsNoCgroups bool - sdNotify bool enableKeyring bool } @@ -105,7 +105,6 @@ runtime.logSizeMax = runtimeCfg.Containers.LogSizeMax runtime.noPivot = runtimeCfg.Engine.NoPivotRoot runtime.reservePorts = runtimeCfg.Engine.EnablePortReservation - runtime.sdNotify = runtimeCfg.Engine.SDNotify runtime.enableKeyring = runtimeCfg.Containers.EnableKeyring // TODO: probe OCI runtime for feature and enable automatically if @@ -290,7 +289,7 @@ if err2 != nil { return errors.Wrapf(err, "error getting container %s state", ctr.ID()) } - if strings.Contains(string(out), "does not exist") { + if strings.Contains(string(out), "does not exist") || strings.Contains(string(out), "No such file") { if err := ctr.removeConmonFiles(); err != nil { logrus.Debugf("unable to remove conmon files for container %s", ctr.ID()) } @@ -352,6 +351,12 @@ return ctr.handleExitFile(exitFile, fi) } + // Handle ContainerStateStopping - keep it unless the container + // transitioned to no longer running. + if oldState == define.ContainerStateStopping && (ctr.state.State == define.ContainerStatePaused || ctr.state.State == define.ContainerStateRunning) { + ctr.state.State = define.ContainerStateStopping + } + return nil } @@ -364,11 +369,6 @@ return err } env := []string{fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)} - if ctr.config.SdNotifyMode == define.SdNotifyModeContainer { - if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { - env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify)) - } - } if path, ok := os.LookupEnv("PATH"); ok { env = append(env, fmt.Sprintf("PATH=%s", path)) } @@ -399,6 +399,11 @@ args = append(args, "kill", ctr.ID(), fmt.Sprintf("%d", signal)) } if err := utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, env, r.path, args...); err != nil { + // try updating container state but ignore errors we cant do anything if this fails. + r.UpdateContainerStatus(ctr) + if ctr.state.State == define.ContainerStateExited { + return nil + } return errors.Wrapf(err, "error sending signal to container %s", ctr.ID()) } @@ -705,6 +710,10 @@ if err != nil { return err } + // copy stdin is done, close it + if connErr := conn.CloseWrite(); connErr != nil { + logrus.Errorf("Unable to close conn: %v", connErr) + } case <-cancel: return nil } @@ -1014,12 +1023,6 @@ } } - if ctr.config.SdNotifyMode == define.SdNotifyModeIgnore { - if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil { - logrus.Warnf("Error unsetting NOTIFY_SOCKET %v", err) - } - } - pidfile := ctr.config.PidFile if pidfile == "" { pidfile = filepath.Join(ctr.state.RunDir, "pidfile") @@ -1027,6 +1030,10 @@ args := r.sharedConmonArgs(ctr, ctr.ID(), ctr.bundlePath(), pidfile, ctr.LogPath(), r.exitsDir, ociLog, ctr.LogDriver(), logTag) + if ctr.config.SdNotifyMode == define.SdNotifyModeContainer && ctr.notifySocket != "" { + args = append(args, fmt.Sprintf("--sdnotify-socket=%s", ctr.notifySocket)) + } + if ctr.config.Spec.Process.Terminal { args = append(args, "-t") } else if ctr.config.Stdin { @@ -1055,8 +1062,22 @@ } } - if ctr.config.PreserveFDs > 0 { - args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", ctr.config.PreserveFDs))...) + // Pass down the LISTEN_* environment (see #10443). + preserveFDs := ctr.config.PreserveFDs + if val := os.Getenv("LISTEN_FDS"); val != "" { + if ctr.config.PreserveFDs > 0 { + logrus.Warnf("Ignoring LISTEN_FDS to preserve custom user-specified FDs") + } else { + fds, err := strconv.Atoi(val) + if err != nil { + return fmt.Errorf("converting LISTEN_FDS=%s: %w", val, err) + } + preserveFDs = uint(fds) + } + } + + if preserveFDs > 0 { + args = append(args, formatRuntimeOpts("--preserve-fds", fmt.Sprintf("%d", preserveFDs))...) } if restoreOptions != nil { @@ -1064,6 +1085,30 @@ if restoreOptions.TCPEstablished { args = append(args, "--runtime-opt", "--tcp-established") } + if restoreOptions.Pod != "" { + mountLabel := ctr.config.MountLabel + processLabel := ctr.config.ProcessLabel + if mountLabel != "" { + args = append( + args, + "--runtime-opt", + fmt.Sprintf( + "--lsm-mount-context=%s", + mountLabel, + ), + ) + } + if processLabel != "" { + args = append( + args, + "--runtime-opt", + fmt.Sprintf( + "--lsm-profile=selinux:%s", + processLabel, + ), + ) + } + } } logrus.WithFields(logrus.Fields{ @@ -1085,11 +1130,11 @@ } // 0, 1 and 2 are stdin, stdout and stderr - conmonEnv, envFiles := r.configureConmonEnv(ctr, runtimeDir) + conmonEnv := r.configureConmonEnv(ctr, runtimeDir) var filesToClose []*os.File - if ctr.config.PreserveFDs > 0 { - for fd := 3; fd < int(3+ctr.config.PreserveFDs); fd++ { + if preserveFDs > 0 { + for fd := 3; fd < int(3+preserveFDs); fd++ { f := os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)) filesToClose = append(filesToClose, f) cmd.ExtraFiles = append(cmd.ExtraFiles, f) @@ -1099,16 +1144,16 @@ cmd.Env = r.conmonEnv // we don't want to step on users fds they asked to preserve // Since 0-2 are used for stdio, start the fds we pass in at preserveFDs+3 - cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_SYNCPIPE=%d", ctr.config.PreserveFDs+3), fmt.Sprintf("_OCI_STARTPIPE=%d", ctr.config.PreserveFDs+4)) + cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_SYNCPIPE=%d", preserveFDs+3), fmt.Sprintf("_OCI_STARTPIPE=%d", preserveFDs+4)) cmd.Env = append(cmd.Env, conmonEnv...) cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe) - cmd.ExtraFiles = append(cmd.ExtraFiles, envFiles...) if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() { ports, err := bindPorts(ctr.config.PortMappings) if err != nil { return err } + filesToClose = append(filesToClose, ports...) // Leak the port we bound in the conmon process. These fd's won't be used // by the container and conmon will keep the ports busy so that another @@ -1118,7 +1163,7 @@ if ctr.config.NetMode.IsSlirp4netns() || rootless.IsRootless() { if ctr.config.PostConfigureNetNS { - havePortMapping := len(ctr.Config().PortMappings) > 0 + havePortMapping := len(ctr.config.PortMappings) > 0 if havePortMapping { ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe() if err != nil { @@ -1147,7 +1192,8 @@ } } - err = startCommandGivenSelinux(cmd) + err = startCommandGivenSelinux(cmd, ctr) + // regardless of whether we errored or not, we no longer need the children pipes childSyncPipe.Close() childStartPipe.Close() @@ -1179,7 +1225,13 @@ // conmon not having a pid file is a valid state, so don't set it if we don't have it logrus.Infof("Got Conmon PID as %d", conmonPID) ctr.state.ConmonPID = conmonPID - if ctr.config.SdNotifyMode != define.SdNotifyModeIgnore { + + // Send the MAINPID via sdnotify if needed. + switch ctr.config.SdNotifyMode { + case define.SdNotifyModeContainer, define.SdNotifyModeIgnore: + // Nothing to do or conmon takes care of it already. + + default: if sent, err := daemon.SdNotify(false, fmt.Sprintf("MAINPID=%d", conmonPID)); err != nil { logrus.Errorf("Error notifying systemd of Conmon PID: %v", err) } else if sent { @@ -1199,7 +1251,7 @@ // configureConmonEnv gets the environment values to add to conmon's exec struct // TODO this may want to be less hardcoded/more configurable in the future -func (r *ConmonOCIRuntime) configureConmonEnv(ctr *Container, runtimeDir string) ([]string, []*os.File) { +func (r *ConmonOCIRuntime) configureConmonEnv(ctr *Container, runtimeDir string) []string { var env []string for _, e := range os.Environ() { if strings.HasPrefix(e, "LC_") { @@ -1214,22 +1266,7 @@ env = append(env, fmt.Sprintf("HOME=%s", home)) } - extraFiles := make([]*os.File, 0) - if ctr.config.SdNotifyMode == define.SdNotifyModeContainer { - if notify, ok := os.LookupEnv("NOTIFY_SOCKET"); ok { - env = append(env, fmt.Sprintf("NOTIFY_SOCKET=%s", notify)) - } - } - if !r.sdNotify { - if listenfds, ok := os.LookupEnv("LISTEN_FDS"); ok { - env = append(env, fmt.Sprintf("LISTEN_FDS=%s", listenfds), "LISTEN_PID=1") - fds := activation.Files(false) - extraFiles = append(extraFiles, fds...) - } - } else { - logrus.Debug("disabling SD notify") - } - return env, extraFiles + return env } // sharedConmonArgs takes common arguments for exec and create/restore and formats them for the conmon CLI @@ -1311,7 +1348,23 @@ // startCommandGivenSelinux starts a container ensuring to set the labels of // the process to make sure SELinux doesn't block conmon communication, if SELinux is enabled -func startCommandGivenSelinux(cmd *exec.Cmd) error { +func startCommandGivenSelinux(cmd *exec.Cmd, ctr *Container) error { + // Make sure to unset the NOTIFY_SOCKET and reset if afterwards if needed. + switch ctr.config.SdNotifyMode { + case define.SdNotifyModeContainer, define.SdNotifyModeIgnore: + if ctr.notifySocket != "" { + if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil { + logrus.Warnf("Error unsetting NOTIFY_SOCKET %v", err) + } + + defer func() { + if err := os.Setenv("NOTIFY_SOCKET", ctr.notifySocket); err != nil { + logrus.Errorf("Error resetting NOTIFY_SOCKET=%s", ctr.notifySocket) + } + }() + } + } + if !selinux.GetEnabled() { return cmd.Start() } diff -Nru libpod-3.2.1+ds1/libpod/oci_conmon_unsupported.go libpod-3.4.4+ds1/libpod/oci_conmon_unsupported.go --- libpod-3.2.1+ds1/libpod/oci_conmon_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_conmon_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -// +build !linux - -package libpod - -import ( - "github.com/containers/common/pkg/config" - - "github.com/containers/podman/v3/libpod/define" -) - -const ( - osNotSupported = "Not supported on this OS" -) - -// ConmonOCIRuntime is not supported on this OS. -type ConmonOCIRuntime struct { -} - -// newConmonOCIRuntime is not supported on this OS. -func newConmonOCIRuntime(name string, paths []string, conmonPath string, runtimeFlags []string, runtimeCfg *config.Config) (OCIRuntime, error) { - return nil, define.ErrNotImplemented -} - -// Name is not supported on this OS. -func (r *ConmonOCIRuntime) Name() string { - return osNotSupported -} - -// Path is not supported on this OS. -func (r *ConmonOCIRuntime) Path() string { - return osNotSupported -} - -// CreateContainer is not supported on this OS. -func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error { - return define.ErrNotImplemented -} - -// UpdateContainerStatus is not supported on this OS. -func (r *ConmonOCIRuntime) UpdateContainerStatus(ctr *Container, useRuntime bool) error { - return define.ErrNotImplemented -} - -// StartContainer is not supported on this OS. -func (r *ConmonOCIRuntime) StartContainer(ctr *Container) error { - return define.ErrNotImplemented -} - -// KillContainer is not supported on this OS. -func (r *ConmonOCIRuntime) KillContainer(ctr *Container, signal uint, all bool) error { - return define.ErrNotImplemented -} - -// StopContainer is not supported on this OS. -func (r *ConmonOCIRuntime) StopContainer(ctr *Container, timeout uint, all bool) error { - return define.ErrNotImplemented -} - -// DeleteContainer is not supported on this OS. -func (r *ConmonOCIRuntime) DeleteContainer(ctr *Container) error { - return define.ErrNotImplemented -} - -// PauseContainer is not supported on this OS. -func (r *ConmonOCIRuntime) PauseContainer(ctr *Container) error { - return define.ErrNotImplemented -} - -// UnpauseContainer is not supported on this OS. -func (r *ConmonOCIRuntime) UnpauseContainer(ctr *Container) error { - return define.ErrNotImplemented -} - -// ExecContainer is not supported on this OS. -func (r *ConmonOCIRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions) (int, chan error, error) { - return -1, nil, define.ErrNotImplemented -} - -// ExecStopContainer is not supported on this OS. -func (r *ConmonOCIRuntime) ExecStopContainer(ctr *Container, sessionID string, timeout uint) error { - return define.ErrNotImplemented -} - -// CheckpointContainer is not supported on this OS. -func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error { - return define.ErrNotImplemented -} - -// SupportsCheckpoint is not supported on this OS. -func (r *ConmonOCIRuntime) SupportsCheckpoint() bool { - return false -} - -// SupportsJSONErrors is not supported on this OS. -func (r *ConmonOCIRuntime) SupportsJSONErrors() bool { - return false -} - -// SupportsNoCgroups is not supported on this OS. -func (r *ConmonOCIRuntime) SupportsNoCgroups() bool { - return false -} - -// AttachSocketPath is not supported on this OS. -func (r *ConmonOCIRuntime) AttachSocketPath(ctr *Container) (string, error) { - return "", define.ErrNotImplemented -} - -// ExecAttachSocketPath is not supported on this OS. -func (r *ConmonOCIRuntime) ExecAttachSocketPath(ctr *Container, sessionID string) (string, error) { - return "", define.ErrNotImplemented -} - -// ExitFilePath is not supported on this OS. -func (r *ConmonOCIRuntime) ExitFilePath(ctr *Container) (string, error) { - return "", define.ErrNotImplemented -} - -// RuntimeInfo is not supported on this OS. -func (r *ConmonOCIRuntime) RuntimeInfo() (*define.ConmonInfo, *define.OCIRuntimeInfo, error) { - return nil, nil, define.ErrNotImplemented -} - -// Package is not supported on this OS. -func (r *ConmonOCIRuntime) Package() string { - return osNotSupported -} - -// ConmonPackage is not supported on this OS. -func (r *ConmonOCIRuntime) ConmonPackage() string { - return osNotSupported -} diff -Nru libpod-3.2.1+ds1/libpod/oci.go libpod-3.4.4+ds1/libpod/oci.go --- libpod-3.2.1+ds1/libpod/oci.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci.go 2021-12-26 00:45:51.000000000 +0000 @@ -72,13 +72,16 @@ // has completed, as one might expect. The attach session will remain // running, in a goroutine that will return via the chan error in the // return signature. - ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams) (int, chan error, error) + // newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty + ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) // ExecContainerHTTP executes a command in a running container and // attaches its standard streams to a provided hijacked HTTP session. // Maintains the same invariants as ExecContainer (returns on session // start, with a goroutine running in the background to handle attach). // The HTTP attach itself maintains the same invariants as HTTPAttach. - ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, r *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (int, chan error, error) + // newSize resizes the tty to this size before the process is started, must be nil if the exec session has no tty + ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, r *http.Request, w http.ResponseWriter, + streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) // ExecContainerDetached executes a command in a running container, but // does not attach to it. Returns the PID of the exec session and an // error (if starting the exec session failed) diff -Nru libpod-3.2.1+ds1/libpod/oci_missing.go libpod-3.4.4+ds1/libpod/oci_missing.go --- libpod-3.2.1+ds1/libpod/oci_missing.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_missing.go 2021-12-26 00:45:51.000000000 +0000 @@ -119,12 +119,13 @@ } // ExecContainer is not available as the runtime is missing -func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams) (int, chan error, error) { +func (r *MissingRuntime) ExecContainer(ctr *Container, sessionID string, options *ExecOptions, streams *define.AttachStreams, newSize *define.TerminalSize) (int, chan error, error) { return -1, nil, r.printError() } // ExecContainerHTTP is not available as the runtime is missing -func (r *MissingRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool) (int, chan error, error) { +func (r *MissingRuntime) ExecContainerHTTP(ctr *Container, sessionID string, options *ExecOptions, req *http.Request, w http.ResponseWriter, + streams *HTTPAttachStreams, cancel <-chan bool, hijackDone chan<- bool, holdConnOpen <-chan bool, newSize *define.TerminalSize) (int, chan error, error) { return -1, nil, r.printError() } diff -Nru libpod-3.2.1+ds1/libpod/oci_util.go libpod-3.4.4+ds1/libpod/oci_util.go --- libpod-3.2.1+ds1/libpod/oci_util.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/oci_util.go 2021-12-26 00:45:51.000000000 +0000 @@ -68,6 +68,12 @@ return nil, errors.Wrapf(err, "cannot get file for UDP socket") } files = append(files, f) + // close the listener + // note that this does not affect the fd, see the godoc for server.File() + err = server.Close() + if err != nil { + logrus.Warnf("failed to close connection: %v", err) + } case "tcp": var ( @@ -96,6 +102,13 @@ return nil, errors.Wrapf(err, "cannot get file for TCP socket") } files = append(files, f) + // close the listener + // note that this does not affect the fd, see the godoc for server.File() + err = server.Close() + if err != nil { + logrus.Warnf("failed to close connection: %v", err) + } + case "sctp": if !notifySCTP { notifySCTP = true diff -Nru libpod-3.2.1+ds1/libpod/options.go libpod-3.4.4+ds1/libpod/options.go --- libpod-3.2.1+ds1/libpod/options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/options.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,14 +7,17 @@ "strings" "syscall" + "github.com/containers/buildah/pkg/parse" "github.com/containers/common/pkg/config" "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" + netTypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/namespaces" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" @@ -265,11 +268,14 @@ logrus.Debugf("Setting custom registries.conf: %q", path) return func(rt *Runtime) error { if _, err := os.Stat(path); err != nil { - return errors.Wrap(err, "error locating specified registries.conf") + return errors.Wrap(err, "locating specified registries.conf") } if rt.imageContext == nil { - rt.imageContext = &types.SystemContext{} + rt.imageContext = &types.SystemContext{ + BigFilesTemporaryDir: parse.GetTempDir(), + } } + rt.imageContext.SystemRegistriesConfPath = path return nil } @@ -453,6 +459,19 @@ } } +// WithDefaultInfraName sets the infra container name for a single pod. +func WithDefaultInfraName(name string) RuntimeOption { + return func(rt *Runtime) error { + if rt.valid { + return define.ErrRuntimeFinalized + } + + rt.config.Engine.InfraImage = name + + return nil + } +} + // WithRenumber instructs libpod to perform a lock renumbering while // initializing. This will handle migrations from early versions of libpod with // file locks to newer versions with SHM locking, as well as changes in the @@ -555,7 +574,6 @@ if ctr.valid { return define.ErrRuntimeFinalized } - ctr.config.LogSize = limit return nil @@ -695,7 +713,6 @@ if pod == nil { return define.ErrInvalidArg } - ctr.config.Pod = pod.ID() return nil @@ -863,7 +880,6 @@ if err := checkDependencyContainer(nsCtr, ctr); err != nil { return err } - ctr.config.MountNsCtr = nsCtr.ID() return nil @@ -939,8 +955,9 @@ } ctr.config.UserNsCtr = nsCtr.ID() - ctr.config.IDMappings = nsCtr.config.IDMappings - + if err := JSONDeepCopy(nsCtr.IDMappings(), &ctr.config.IDMappings); err != nil { + return err + } g := generate.Generator{Config: ctr.config.Spec} g.ClearLinuxUIDMappings() @@ -951,7 +968,6 @@ for _, gidmap := range nsCtr.config.IDMappings.GIDMap { g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) } - ctr.config.IDMappings = nsCtr.config.IDMappings return nil } } @@ -1024,7 +1040,7 @@ // namespace with a minimal configuration. // An optional array of port mappings can be provided. // Conflicts with WithNetNSFrom(). -func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption { +func WithNetNS(portMappings []ocicni.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return define.ErrCtrFinalized @@ -1034,6 +1050,7 @@ ctr.config.NetMode = namespaces.NetworkMode(netmode) ctr.config.CreateNetNS = true ctr.config.PortMappings = portMappings + ctr.config.ExposedPorts = exposedPorts ctr.config.Networks = networks @@ -1412,20 +1429,6 @@ } } -// withIsInfra sets the container to be an infra container. This means the container will be sometimes hidden -// and expected to be the first container in the pod. -func withIsInfra() CtrCreateOption { - return func(ctr *Container) error { - if ctr.valid { - return define.ErrCtrFinalized - } - - ctr.config.IsInfra = true - - return nil - } -} - // WithNamedVolumes adds the given named volumes to the container. func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption { return func(ctr *Container) error { @@ -1436,7 +1439,7 @@ for _, vol := range volumes { mountOpts, err := util.ProcessOptions(vol.Options, false, "") if err != nil { - return errors.Wrapf(err, "error processing options for named volume %q mounted at %q", vol.Name, vol.Dest) + return errors.Wrapf(err, "processing options for named volume %q mounted at %q", vol.Name, vol.Dest) } ctr.config.NamedVolumes = append(ctr.config.NamedVolumes, &ContainerNamedVolume{ @@ -1523,6 +1526,20 @@ } } +// withIsInfra allows us to dfferentiate between infra containers and regular containers +// within the container config +func withIsInfra() CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized + } + + ctr.config.IsInfra = true + + return nil + } +} + // WithCreateWorkingDir tells Podman to create the container's working directory // if it does not exist. func WithCreateWorkingDir() CtrCreateOption { @@ -1628,6 +1645,32 @@ } } +// WithVolumeSize sets the maximum size of the volume +func WithVolumeSize(size uint64) VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.config.Size = size + + return nil + } +} + +// WithVolumeInodes sets the maximum inodes of the volume +func WithVolumeInodes(inodes uint64) VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.config.Inodes = inodes + + return nil + } +} + // WithVolumeGID sets the GID that the volume will be created as. func WithVolumeGID(gid int) VolumeCreateOption { return func(volume *Volume) error { @@ -1641,6 +1684,19 @@ } } +// WithVolumeNoChown prevents the volume from being chowned to the process uid at first use. +func WithVolumeNoChown() VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return define.ErrVolumeFinalized + } + + volume.state.NeedsChown = false + + return nil + } +} + // withSetAnon sets a bool notifying libpod that this volume is anonymous and // should be removed when containers using it are removed and volumes are // specified for removal. @@ -1695,23 +1751,12 @@ } // WithSecrets adds secrets to the container -func WithSecrets(secretNames []string) CtrCreateOption { +func WithSecrets(containerSecrets []*ContainerSecret) CtrCreateOption { return func(ctr *Container) error { if ctr.valid { return define.ErrCtrFinalized } - manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir()) - if err != nil { - return err - } - for _, name := range secretNames { - secr, err := manager.Lookup(name) - if err != nil { - return err - } - ctr.config.Secrets = append(ctr.config.Secrets, secr) - } - + ctr.config.Secrets = containerSecrets return nil } } @@ -1723,7 +1768,7 @@ if ctr.valid { return define.ErrCtrFinalized } - manager, err := secrets.NewManager(ctr.runtime.GetSecretsStorageDir()) + manager, err := ctr.runtime.SecretsManager() if err != nil { return err } @@ -1749,34 +1794,31 @@ } } -// Pod Creation Options - -// WithInfraImage sets the infra image for libpod. -// An infra image is used for inter-container kernel -// namespace sharing within a pod. Typically, an infra -// container is lightweight and is there to reap -// zombie processes within its pid namespace. -func WithInfraImage(img string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized +// WithInitCtrType indicates the container is a initcontainer +func WithInitCtrType(containerType string) CtrCreateOption { + return func(ctr *Container) error { + if ctr.valid { + return define.ErrCtrFinalized } - - pod.config.InfraContainer.InfraImage = img - - return nil + // Make sure the type is valid + if containerType == define.OneShotInitContainer || containerType == define.AlwaysInitContainer { + ctr.config.InitContainerType = containerType + return nil + } + return errors.Errorf("%s is invalid init container type", containerType) } } -// WithInfraCommand sets the command to -// run on pause container start up. -func WithInfraCommand(cmd []string) PodCreateOption { +// Pod Creation Options + +// WithPodCreateCommand adds the full command plus arguments of the current +// process to the pod config. +func WithPodCreateCommand(createCmd []string) PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized } - - pod.config.InfraContainer.InfraCommand = cmd + pod.config.CreateCommand = createCmd return nil } } @@ -1817,26 +1859,14 @@ } } -// WithPodCreateCommand adds the full command plus arguments of the current -// process to the pod config. -func WithPodCreateCommand(createCmd []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - pod.config.CreateCommand = createCmd - return nil - } -} - // WithInfraConmonPidFile sets the path to a custom conmon PID file for the // infra container. -func WithInfraConmonPidFile(path string) PodCreateOption { +func WithInfraConmonPidFile(path string, infraSpec *specgen.SpecGenerator) PodCreateOption { return func(pod *Pod) error { if pod.valid { return define.ErrPodFinalized } - pod.config.InfraContainer.ConmonPidFile = path + infraSpec.ConmonPidFile = path return nil } } @@ -2025,320 +2055,25 @@ if pod.valid { return define.ErrPodFinalized } - - pod.config.InfraContainer.HasInfraContainer = true + pod.config.HasInfra = true return nil } } // WithInfraContainerPorts tells the pod to add port bindings to the pause container -func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot set pod ports as no infra container is being created") - } - pod.config.InfraContainer.PortBindings = bindings - return nil - } -} - -// WithPodStaticIP sets a static IP for the pod. -func WithPodStaticIP(ip net.IP) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot set pod static IP as no infra container is being created") - } - - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP if host network is specified") - } - - if len(pod.config.InfraContainer.Networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static IP if joining more than 1 CNI network") - } - - pod.config.InfraContainer.StaticIP = ip - - return nil - } -} - -// WithPodStaticMAC sets a static MAC address for the pod. -func WithPodStaticMAC(mac net.HardwareAddr) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot set pod static MAC as no infra container is being created") - } - - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set static MAC if host network is specified") - } - - if len(pod.config.InfraContainer.Networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot set a static MAC if joining more than 1 CNI network") - } - - pod.config.InfraContainer.StaticMAC = mac - - return nil - } -} - -// WithPodUseImageResolvConf sets a pod to use an image's resolv.conf and not -// create its own. -func WithPodUseImageResolvConf() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if len(pod.config.InfraContainer.DNSServer) != 0 || - len(pod.config.InfraContainer.DNSSearch) != 0 || - len(pod.config.InfraContainer.DNSOption) != 0 { - return errors.Wrapf(define.ErrInvalidArg, "requested use of image resolv.conf conflicts with already-configured DNS settings") - } - - pod.config.InfraContainer.UseImageResolvConf = true - - return nil - } -} - -// WithPodDNS sets the DNS Servers for a pod. -func WithPodDNS(dnsServer []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS servers if pod will not create /etc/resolv.conf") - } - - pod.config.InfraContainer.DNSServer = dnsServer - - return nil - } -} - -// WithPodDNSSearch sets the DNS Search domains for a pod. -func WithPodDNSSearch(dnsSearch []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS search domains if pod will not create /etc/resolv.conf") - } - - pod.config.InfraContainer.DNSSearch = dnsSearch - - return nil - } -} - -// WithPodDNSOption sets DNS Options for a pod. -func WithPodDNSOption(dnsOption []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageResolvConf { - return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS options if pod will not create /etc/resolv.conf") - } - - pod.config.InfraContainer.DNSOption = dnsOption - - return nil - } -} - -// WithPodUseImageHosts tells the pod not to create /etc/hosts and instead to -// use the one provided by the image. -func WithPodUseImageHosts() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod hosts as no infra container is being created") - } - - if len(pod.config.InfraContainer.HostAdd) != 0 { - return errors.Wrapf(define.ErrInvalidArg, "not creating /etc/hosts conflicts with adding to the hosts file") - } - - pod.config.InfraContainer.UseImageHosts = true - - return nil - } -} - -// WithPodHosts adds additional entries to the pod's /etc/hosts -func WithPodHosts(hosts []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod hosts as no infra container is being created") - } - - if pod.config.InfraContainer.UseImageHosts { - return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if container is using image hosts") - } - - pod.config.InfraContainer.HostAdd = hosts - - return nil - } -} - -// WithPodNetworks sets additional CNI networks for the pod to join. -func WithPodNetworks(networks []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod CNI networks as no infra container is being created") - } - - if (pod.config.InfraContainer.StaticIP != nil || pod.config.InfraContainer.StaticMAC != nil) && - len(networks) > 1 { - return errors.Wrapf(define.ErrInvalidArg, "cannot join more than one CNI network if setting a static IP or MAC address") - } - - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot join pod to CNI networks if host network is specified") - } - - pod.config.InfraContainer.Networks = networks - - return nil - } -} - -// WithPodNoNetwork tells the pod to disable external networking. -func WithPodNoNetwork() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot disable pod networking as no infra container is being created") - } - - if len(pod.config.InfraContainer.PortBindings) > 0 || - pod.config.InfraContainer.StaticIP != nil || - pod.config.InfraContainer.StaticMAC != nil || - len(pod.config.InfraContainer.Networks) > 0 || - pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") - } - - pod.config.InfraContainer.NoNetwork = true - - return nil - } -} - -// WithPodHostNetwork tells the pod to use the host's network namespace. -func WithPodHostNetwork() PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod host networking as no infra container is being created") - } - - if len(pod.config.InfraContainer.PortBindings) > 0 || - pod.config.InfraContainer.StaticIP != nil || - pod.config.InfraContainer.StaticMAC != nil || - len(pod.config.InfraContainer.Networks) > 0 || - pod.config.InfraContainer.NoNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") - } - - pod.config.InfraContainer.HostNetwork = true - - return nil - } -} - -// WithPodInfraExitCommand sets an exit command for the pod's infra container. -// Semantics are identical to WithExitCommand() above - the ID of the container -// will be appended to the end of the provided command (note that this will -// specifically be the ID of the infra container *and not the pod's id*. -func WithPodInfraExitCommand(exitCmd []string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod infra container exit command as no infra container is being created") - } - - pod.config.InfraContainer.ExitCommand = exitCmd - - return nil - } -} - -// WithPodSlirp4netns tells the pod to use slirp4netns. -func WithPodSlirp4netns(networkOptions map[string][]string) PodCreateOption { - return func(pod *Pod) error { - if pod.valid { - return define.ErrPodFinalized - } - - if !pod.config.InfraContainer.HasInfraContainer { - return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod networking as no infra container is being created") - } - if pod.config.InfraContainer.HostNetwork { - return errors.Wrapf(define.ErrInvalidArg, "cannot set both HostNetwork and Slirp4netns") - } - pod.config.InfraContainer.Slirp4netns = true - pod.config.InfraContainer.NetworkOptions = networkOptions - - return nil +func WithInfraContainerPorts(bindings []ocicni.PortMapping, infraSpec *specgen.SpecGenerator) []netTypes.PortMapping { + bindingSpec := []netTypes.PortMapping{} + for _, bind := range bindings { + currBind := netTypes.PortMapping{} + currBind.ContainerPort = uint16(bind.ContainerPort) + currBind.HostIP = bind.HostIP + currBind.HostPort = uint16(bind.HostPort) + currBind.Protocol = bind.Protocol + bindingSpec = append(bindingSpec, currBind) } + infraSpec.PortMappings = bindingSpec + return infraSpec.PortMappings } // WithVolatile sets the volatile flag for the container storage. @@ -2350,6 +2085,7 @@ } ctr.config.Volatile = true + return nil } } diff -Nru libpod-3.2.1+ds1/libpod/pod_api.go libpod-3.4.4+ds1/libpod/pod_api.go --- libpod-3.2.1+ds1/libpod/pod_api.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/pod_api.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,45 @@ "github.com/sirupsen/logrus" ) +// startInitContainers starts a pod's init containers. +func (p *Pod) startInitContainers(ctx context.Context) error { + initCtrs, err := p.initContainers() + if err != nil { + return err + } + // Now iterate init containers + for _, initCon := range initCtrs { + if err := initCon.Start(ctx, true); err != nil { + return err + } + // Check that the init container waited correctly and the exit + // code is good + rc, err := initCon.Wait(ctx) + if err != nil { + return err + } + if rc != 0 { + return errors.Errorf("init container %s exited with code %d", initCon.ID(), rc) + } + // If the container is a once init container, we need to remove it + // after it runs + if initCon.config.InitContainerType == define.OneShotInitContainer { + icLock := initCon.lock + icLock.Lock() + if err := p.runtime.removeContainer(ctx, initCon, false, false, true); err != nil { + icLock.Unlock() + return errors.Wrapf(err, "failed to remove once init container %s", initCon.ID()) + } + // Removing a container this way requires an explicit call to clean up the db + if err := p.runtime.state.RemoveContainerFromPod(p, initCon); err != nil { + logrus.Errorf("Error removing container %s from database: %v", initCon.ID(), err) + } + icLock.Unlock() + } + } + return nil +} + // Start starts all containers within a pod. // It combines the effects of Init() and Start() on a container. // If a container has already been initialized it will be started, @@ -34,26 +73,29 @@ return nil, define.ErrPodRemoved } + // Before "regular" containers start in the pod, all init containers + // must have run and exited successfully. + if err := p.startInitContainers(ctx); err != nil { + return nil, err + } allCtrs, err := p.runtime.state.PodContainers(p) if err != nil { return nil, err } - // Build a dependency graph of containers in the pod graph, err := BuildContainerGraph(allCtrs) if err != nil { return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID()) } - - ctrErrors := make(map[string]error) - ctrsVisited := make(map[string]bool) - // If there are no containers without dependencies, we can't start // Error out if len(graph.noDepNodes) == 0 { return nil, errors.Wrapf(define.ErrNoSuchCtr, "no containers in pod %s have no dependencies, cannot start pod", p.ID()) } + ctrErrors := make(map[string]error) + ctrsVisited := make(map[string]bool) + // Traverse the graph beginning at nodes with no dependencies for _, node := range graph.noDepNodes { startNode(ctx, node, false, ctrErrors, ctrsVisited, false) @@ -449,12 +491,18 @@ if !p.valid { return nil, define.ErrPodRemoved } - allCtrs, err := p.runtime.state.PodContainers(p) if err != nil { return nil, err } - return containerStatusFromContainers(allCtrs) + noInitCtrs := make([]*Container, 0) + // Do not add init containers into status + for _, ctr := range allCtrs { + if ctrType := ctr.config.InitContainerType; len(ctrType) < 1 { + noInitCtrs = append(noInitCtrs, ctr) + } + } + return containerStatusFromContainers(noInitCtrs) } func containerStatusFromContainers(allCtrs []*Container) (map[string]define.ContainerStatus, error) { @@ -504,7 +552,10 @@ Name: c.Name(), State: containerStatus, }) - ctrStatuses[c.ID()] = c.state.State + // Do not add init containers fdr status + if len(c.config.InitContainerType) < 1 { + ctrStatuses[c.ID()] = c.state.State + } } podState, err := createPodStatusResults(ctrStatuses) if err != nil { @@ -531,36 +582,43 @@ // Infra config contains detailed information on the pod's infra // container. var infraConfig *define.InspectPodInfraConfig - if p.config.InfraContainer != nil && p.config.InfraContainer.HasInfraContainer { + if p.state.InfraContainerID != "" { + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return nil, err + } infraConfig = new(define.InspectPodInfraConfig) - infraConfig.HostNetwork = p.config.InfraContainer.HostNetwork - infraConfig.StaticIP = p.config.InfraContainer.StaticIP - infraConfig.StaticMAC = p.config.InfraContainer.StaticMAC.String() - infraConfig.NoManageResolvConf = p.config.InfraContainer.UseImageResolvConf - infraConfig.NoManageHosts = p.config.InfraContainer.UseImageHosts - - if len(p.config.InfraContainer.DNSServer) > 0 { - infraConfig.DNSServer = make([]string, 0, len(p.config.InfraContainer.DNSServer)) - infraConfig.DNSServer = append(infraConfig.DNSServer, p.config.InfraContainer.DNSServer...) - } - if len(p.config.InfraContainer.DNSSearch) > 0 { - infraConfig.DNSSearch = make([]string, 0, len(p.config.InfraContainer.DNSSearch)) - infraConfig.DNSSearch = append(infraConfig.DNSSearch, p.config.InfraContainer.DNSSearch...) - } - if len(p.config.InfraContainer.DNSOption) > 0 { - infraConfig.DNSOption = make([]string, 0, len(p.config.InfraContainer.DNSOption)) - infraConfig.DNSOption = append(infraConfig.DNSOption, p.config.InfraContainer.DNSOption...) - } - if len(p.config.InfraContainer.HostAdd) > 0 { - infraConfig.HostAdd = make([]string, 0, len(p.config.InfraContainer.HostAdd)) - infraConfig.HostAdd = append(infraConfig.HostAdd, p.config.InfraContainer.HostAdd...) - } - if len(p.config.InfraContainer.Networks) > 0 { - infraConfig.Networks = make([]string, 0, len(p.config.InfraContainer.Networks)) - infraConfig.Networks = append(infraConfig.Networks, p.config.InfraContainer.Networks...) + infraConfig.HostNetwork = !infra.config.ContainerNetworkConfig.UseImageHosts + infraConfig.StaticIP = infra.config.ContainerNetworkConfig.StaticIP + infraConfig.NoManageResolvConf = infra.config.UseImageResolvConf + infraConfig.NoManageHosts = infra.config.UseImageHosts + infraConfig.PidNS = p.PidMode() + infraConfig.UserNS = p.UserNSMode() + + if len(infra.config.ContainerNetworkConfig.DNSServer) > 0 { + infraConfig.DNSServer = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSServer)) + for _, entry := range infra.config.ContainerNetworkConfig.DNSServer { + infraConfig.DNSServer = append(infraConfig.DNSServer, entry.String()) + } + } + if len(infra.config.ContainerNetworkConfig.DNSSearch) > 0 { + infraConfig.DNSSearch = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSSearch)) + infraConfig.DNSSearch = append(infraConfig.DNSSearch, infra.config.ContainerNetworkConfig.DNSSearch...) + } + if len(infra.config.ContainerNetworkConfig.DNSOption) > 0 { + infraConfig.DNSOption = make([]string, 0, len(infra.config.ContainerNetworkConfig.DNSOption)) + infraConfig.DNSOption = append(infraConfig.DNSOption, infra.config.ContainerNetworkConfig.DNSOption...) + } + if len(infra.config.HostAdd) > 0 { + infraConfig.HostAdd = make([]string, 0, len(infra.config.HostAdd)) + infraConfig.HostAdd = append(infraConfig.HostAdd, infra.config.HostAdd...) + } + if len(infra.config.ContainerNetworkConfig.Networks) > 0 { + infraConfig.Networks = make([]string, 0, len(infra.config.ContainerNetworkConfig.Networks)) + infraConfig.Networks = append(infraConfig.Networks, infra.config.ContainerNetworkConfig.Networks...) } - infraConfig.NetworkOptions = p.config.InfraContainer.NetworkOptions - infraConfig.PortBindings = makeInspectPortBindings(p.config.InfraContainer.PortBindings) + infraConfig.NetworkOptions = infra.config.ContainerNetworkConfig.NetworkOptions + infraConfig.PortBindings = makeInspectPortBindings(infra.config.ContainerNetworkConfig.PortMappings, nil) } inspectData := define.InspectPodData{ diff -Nru libpod-3.2.1+ds1/libpod/pod.go libpod-3.4.4+ds1/libpod/pod.go --- libpod-3.2.1+ds1/libpod/pod.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/pod.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,12 +1,13 @@ package libpod import ( - "net" + "fmt" + "sort" "time" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/lock" - "github.com/cri-o/ocicni/pkg/ocicni" + "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" ) @@ -59,7 +60,7 @@ UsePodUTS bool `json:"sharesUts,omitempty"` UsePodCgroupNS bool `json:"sharesCgroupNS,omitempty"` - InfraContainer *InfraContainerConfig `json:"infraConfig"` + HasInfra bool `json:"hasInfra,omitempty"` // Time pod was created CreatedTime time.Time `json:"created"` @@ -81,37 +82,6 @@ InfraContainerID string } -// InfraContainerConfig is the configuration for the pod's infra container. -// Generally speaking, these are equivalent to container configuration options -// you will find in container_config.go (and even named identically), save for -// HasInfraContainer (which determines if an infra container is even created - -// if it is false, no other options in this struct will be used) and HostNetwork -// (this involves the created OCI spec, and as such is not represented directly -// in container_config.go). -// Generally speaking, aside from those two exceptions, these options will set -// the equivalent field in the container's configuration. -type InfraContainerConfig struct { - ConmonPidFile string `json:"conmonPidFile"` - HasInfraContainer bool `json:"makeInfraContainer"` - NoNetwork bool `json:"noNetwork,omitempty"` - HostNetwork bool `json:"infraHostNetwork,omitempty"` - PortBindings []ocicni.PortMapping `json:"infraPortBindings"` - StaticIP net.IP `json:"staticIP,omitempty"` - StaticMAC net.HardwareAddr `json:"staticMAC,omitempty"` - UseImageResolvConf bool `json:"useImageResolvConf,omitempty"` - DNSServer []string `json:"dnsServer,omitempty"` - DNSSearch []string `json:"dnsSearch,omitempty"` - DNSOption []string `json:"dnsOption,omitempty"` - UseImageHosts bool `json:"useImageHosts,omitempty"` - HostAdd []string `json:"hostsAdd,omitempty"` - Networks []string `json:"networks,omitempty"` - ExitCommand []string `json:"exitCommand,omitempty"` - InfraImage string `json:"infraImage,omitempty"` - InfraCommand []string `json:"infraCommand,omitempty"` - Slirp4netns bool `json:"slirp4netns,omitempty"` - NetworkOptions map[string][]string `json:"network_options,omitempty"` -} - // ID retrieves the pod's ID func (p *Pod) ID() string { return p.config.ID @@ -128,6 +98,48 @@ return p.config.Namespace } +// PidMode returns the PID mode given by the user ex: pod, private... +func (p *Pod) PidMode() string { + infra, err := p.runtime.GetContainer(p.state.InfraContainerID) + if err != nil { + return "" + } + ctrSpec := infra.config.Spec + if ctrSpec != nil && ctrSpec.Linux != nil { + for _, ns := range ctrSpec.Linux.Namespaces { + if ns.Type == specs.PIDNamespace { + if ns.Path != "" { + return fmt.Sprintf("ns:%s", ns.Path) + } + return "private" + } + } + return "host" + } + return "" +} + +// PidMode returns the PID mode given by the user ex: pod, private... +func (p *Pod) UserNSMode() string { + infra, err := p.infraContainer() + if err != nil { + return "" + } + ctrSpec := infra.config.Spec + if ctrSpec != nil && ctrSpec.Linux != nil { + for _, ns := range ctrSpec.Linux.Namespaces { + if ns.Type == specs.UserNamespace { + if ns.Path != "" { + return fmt.Sprintf("ns:%s", ns.Path) + } + return "private" + } + } + return "host" + } + return "" +} + // Labels returns the pod's labels func (p *Pod) Labels() map[string]string { labels := make(map[string]string) @@ -208,7 +220,6 @@ if err := p.updatePod(); err != nil { return "", err } - return p.state.CgroupPath, nil } @@ -249,7 +260,7 @@ // HasInfraContainer returns whether the pod will create an infra container func (p *Pod) HasInfraContainer() bool { - return p.config.InfraContainer.HasInfraContainer + return p.config.HasInfra } // SharesNamespaces checks if the pod has any kernel namespaces set as shared. An infra container will not be @@ -258,33 +269,42 @@ return p.SharesPID() || p.SharesIPC() || p.SharesNet() || p.SharesMount() || p.SharesUser() || p.SharesUTS() } -// InfraContainerID returns the infra container ID for a pod. -// If the container returned is "", the pod has no infra container. -func (p *Pod) InfraContainerID() (string, error) { - p.lock.Lock() - defer p.lock.Unlock() - +// infraContainerID returns the infra ID without a lock +func (p *Pod) infraContainerID() (string, error) { if err := p.updatePod(); err != nil { return "", err } - return p.state.InfraContainerID, nil } -// InfraContainer returns the infra container. -func (p *Pod) InfraContainer() (*Container, error) { - if !p.HasInfraContainer() { - return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") - } +// InfraContainerID returns the infra container ID for a pod. +// If the container returned is "", the pod has no infra container. +func (p *Pod) InfraContainerID() (string, error) { + p.lock.Lock() + defer p.lock.Unlock() + return p.infraContainerID() +} - id, err := p.InfraContainerID() +// infraContainer is the unlocked versio of InfraContainer which returns the infra container +func (p *Pod) infraContainer() (*Container, error) { + id, err := p.infraContainerID() if err != nil { return nil, err } + if id == "" { + return nil, errors.Wrap(define.ErrNoSuchCtr, "pod has no infra container") + } return p.runtime.state.Container(id) } +// InfraContainer returns the infra container. +func (p *Pod) InfraContainer() (*Container, error) { + p.lock.Lock() + defer p.lock.Unlock() + return p.infraContainer() +} + // TODO add pod batching // Lock pod to avoid lock contention // Store and lock all containers (no RemoveContainer in batch guarantees cache will not become stale) @@ -334,15 +354,29 @@ if !p.HasInfraContainer() { return "", nil } - - id, err := p.InfraContainerID() + ctr, err := p.infraContainer() if err != nil { return "", err } + return ctr.ProcessLabel(), nil +} - ctr, err := p.runtime.state.Container(id) +// initContainers returns the list of initcontainers +// in a pod sorted by create time +func (p *Pod) initContainers() ([]*Container, error) { + initCons := make([]*Container, 0) + // the pod is already locked when this is called + cons, err := p.allContainers() if err != nil { - return "", err + return nil, err } - return ctr.ProcessLabel(), nil + // Sort the pod containers by created time + sort.Slice(cons, func(i, j int) bool { return cons[i].CreatedTime().Before(cons[j].CreatedTime()) }) + // Iterate sorted containers and add ids for any init containers + for _, c := range cons { + if len(c.config.InitContainerType) > 0 { + initCons = append(initCons, c) + } + } + return initCons, nil } diff -Nru libpod-3.2.1+ds1/libpod/pod_internal.go libpod-3.4.4+ds1/libpod/pod_internal.go --- libpod-3.2.1+ds1/libpod/pod_internal.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/pod_internal.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,7 +20,7 @@ pod.config.ID = stringid.GenerateNonCryptoID() pod.config.Labels = make(map[string]string) pod.config.CreatedTime = time.Now() - pod.config.InfraContainer = new(InfraContainerConfig) + // pod.config.InfraContainer = new(ContainerConfig) pod.state = new(podState) pod.runtime = runtime diff -Nru libpod-3.2.1+ds1/libpod/pod_top_unsupported.go libpod-3.4.4+ds1/libpod/pod_top_unsupported.go --- libpod-3.2.1+ds1/libpod/pod_top_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/pod_top_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -// +build !linux - -package libpod - -import "github.com/containers/podman/v3/libpod/define" - -// GetPodPidInformation is exclusive to linux -func (p *Pod) GetPodPidInformation(descriptors []string) ([]string, error) { - return nil, define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/runtime_cstorage.go libpod-3.4.4+ds1/libpod/runtime_cstorage.go --- libpod-3.2.1+ds1/libpod/runtime_cstorage.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_cstorage.go 2021-12-26 00:45:51.000000000 +0000 @@ -106,18 +106,18 @@ logrus.Infof("Storage for container %s already removed", ctr.ID) return nil } - return errors.Wrapf(err, "error looking up container %q mounts", idOrName) + logrus.Warnf("Checking if container %q is mounted, attempting to delete: %v", idOrName, err) } if timesMounted > 0 { return errors.Wrapf(define.ErrCtrStateInvalid, "container %q is mounted and cannot be removed without using force", idOrName) } } else if _, err := r.store.Unmount(ctr.ID, true); err != nil { - if errors.Cause(err) == storage.ErrContainerUnknown { + if errors.Is(err, storage.ErrContainerUnknown) { // Container again gone, no error logrus.Infof("Storage for container %s already removed", ctr.ID) return nil } - return errors.Wrapf(err, "error unmounting container %q", idOrName) + logrus.Warnf("Unmounting container %q while attempting to delete storage: %v", idOrName, err) } if err := r.store.DeleteContainer(ctr.ID); err != nil { diff -Nru libpod-3.2.1+ds1/libpod/runtime_ctr.go libpod-3.4.4+ds1/libpod/runtime_ctr.go --- libpod-3.2.1+ds1/libpod/runtime_ctr.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_ctr.go 2021-12-26 00:45:51.000000000 +0000 @@ -17,6 +17,7 @@ "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/storage" "github.com/containers/storage/pkg/stringid" "github.com/docker/go-units" @@ -38,15 +39,44 @@ type ContainerFilter func(*Container) bool // NewContainer creates a new container from a given OCI config. -func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) { +func (r *Runtime) NewContainer(ctx context.Context, rSpec *spec.Spec, spec *specgen.SpecGenerator, infra bool, options ...CtrCreateOption) (*Container, error) { r.lock.Lock() defer r.lock.Unlock() if !r.valid { return nil, define.ErrRuntimeStopped } + if infra { + options = append(options, withIsInfra()) + } return r.newContainer(ctx, rSpec, options...) } +func (r *Runtime) PrepareVolumeOnCreateContainer(ctx context.Context, ctr *Container) error { + // Copy the content from the underlying image into the newly created + // volume if configured to do so. + if !r.config.Containers.PrepareVolumeOnCreate { + return nil + } + + defer func() { + if err := ctr.cleanupStorage(); err != nil { + logrus.Errorf("error cleaning up container storage %s: %v", ctr.ID(), err) + } + }() + + mountPoint, err := ctr.mountStorage() + if err == nil { + // Finish up mountStorage + ctr.state.Mounted = true + ctr.state.Mountpoint = mountPoint + if err = ctr.save(); err != nil { + logrus.Errorf("Error saving container %s state: %v", ctr.ID(), err) + } + } + + return err +} + // RestoreContainer re-creates a container from an imported checkpoint func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config *ContainerConfig) (*Container, error) { r.lock.Lock() @@ -146,6 +176,7 @@ } ctr.config.ShmSize = size ctr.config.StopSignal = 15 + ctr.config.StopTimeout = r.config.Engine.StopTimeout } else { // This is a restore from an imported checkpoint @@ -185,7 +216,11 @@ } func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options ...CtrCreateOption) (*Container, error) { - ctr, err := r.initContainerVariables(rSpec, nil) + var ctr *Container + var err error + + ctr, err = r.initContainerVariables(rSpec, nil) + if err != nil { return nil, errors.Wrapf(err, "error initializing container variables") } @@ -204,7 +239,9 @@ if err := ctr.validate(); err != nil { return nil, err } - + if ctr.config.IsInfra { + ctr.config.StopTimeout = 10 + } // normalize the networks to names // ocicni only knows about cni names so we have to make // sure we do not use ids internally @@ -220,6 +257,20 @@ ctr.config.Networks = netNames } + // https://github.com/containers/podman/issues/11285 + // normalize the networks aliases to use network names and never ids + if len(ctr.config.NetworkAliases) > 0 { + netAliases := make(map[string][]string, len(ctr.config.NetworkAliases)) + for nameOrID, aliases := range ctr.config.NetworkAliases { + netName, err := network.NormalizeName(r.config, nameOrID) + if err != nil { + return nil, err + } + netAliases[netName] = aliases + } + ctr.config.NetworkAliases = netAliases + } + // Inhibit shutdown until creation succeeds shutdown.Inhibit() defer shutdown.Uninhibit() @@ -271,15 +322,6 @@ } } - if ctr.config.Name == "" { - name, err := r.generateName() - if err != nil { - return nil, err - } - - ctr.config.Name = name - } - // Check CGroup parent sanity, and set it if it was not set. // Only if we're actually configuring CGroups. if !ctr.config.NoCgroups { @@ -287,7 +329,7 @@ switch r.config.Engine.CgroupManager { case config.CgroupfsCgroupsManager: if ctr.config.CgroupParent == "" { - if pod != nil && pod.config.UsePodCgroup { + if pod != nil && pod.config.UsePodCgroup && !ctr.IsInfra() { podCgroup, err := pod.CgroupPath() if err != nil { return nil, errors.Wrapf(err, "error retrieving pod %s cgroup", pod.ID()) @@ -308,7 +350,7 @@ case config.SystemdCgroupsManager: if ctr.config.CgroupParent == "" { switch { - case pod != nil && pod.config.UsePodCgroup: + case pod != nil && pod.config.UsePodCgroup && !ctr.IsInfra(): podCgroup, err := pod.CgroupPath() if err != nil { return nil, errors.Wrapf(err, "error retrieving pod %s cgroup", pod.ID()) @@ -327,6 +369,10 @@ } } + if ctr.config.Timezone == "" { + ctr.config.Timezone = r.config.Containers.TZ + } + if ctr.restoreFromCheckpoint { // Remove information about bind mount // for new container from imported checkpoint @@ -366,7 +412,7 @@ return nil, err } for _, secr := range ctr.config.Secrets { - err = ctr.extractSecretToCtrStorage(secr.Name) + err = ctr.extractSecretToCtrStorage(secr) if err != nil { return nil, err } @@ -418,8 +464,15 @@ ctrNamedVolumes = append(ctrNamedVolumes, newVol) } - if ctr.config.LogPath == "" && ctr.config.LogDriver != define.JournaldLogging && ctr.config.LogDriver != define.NoLogging { - ctr.config.LogPath = filepath.Join(ctr.config.StaticDir, "ctr.log") + switch ctr.config.LogDriver { + case define.NoLogging: + break + case define.JournaldLogging: + ctr.initializeJournal(ctx) + default: + if ctr.config.LogPath == "" { + ctr.config.LogPath = filepath.Join(ctr.config.StaticDir, "ctr.log") + } } if !MountExists(ctr.config.Spec.Mounts, "/dev/shm") && ctr.config.ShmDir == "" { @@ -581,6 +634,15 @@ if err := c.stop(c.StopTimeout()); err != nil && errors.Cause(err) != define.ErrConmonDead { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } + + // We unlocked as part of stop() above - there's a chance someone + // else got in and removed the container before we reacquired the + // lock. + // Do a quick ping of the database to check if the container + // still exists. + if ok, _ := r.state.HasContainer(c.ID()); !ok { + return nil + } } // Remove all active exec sessions @@ -773,7 +835,10 @@ return id, err } - infraID := pod.state.InfraContainerID + infraID, err := pod.infraContainerID() + if err != nil { + return "", err + } if c.ID() == infraID { return id, errors.Errorf("container %s is the infra container of pod %s and cannot be removed without removing the pod", c.ID(), pod.ID()) } @@ -859,6 +924,18 @@ return r.state.LookupContainer(idOrName) } +// LookupContainerId looks up a container id by its name or a partial ID +// If a partial ID is not unique, an error will be returned +func (r *Runtime) LookupContainerID(idOrName string) (string, error) { + r.lock.RLock() + defer r.lock.RUnlock() + + if !r.valid { + return "", define.ErrRuntimeStopped + } + return r.state.LookupContainerID(idOrName) +} + // GetContainers retrieves all containers from the state // Filters can be provided which will determine what containers are included in // the output. Multiple filters are handled by ANDing their output, so only diff -Nru libpod-3.2.1+ds1/libpod/runtime.go libpod-3.4.4+ds1/libpod/runtime.go --- libpod-3.2.1+ds1/libpod/runtime.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,8 +15,13 @@ "syscall" "time" + "golang.org/x/sys/unix" + + "github.com/containers/buildah/pkg/parse" "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/defaultnet" + "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/pkg/sysregistriesv2" is "github.com/containers/image/v5/storage" "github.com/containers/image/v5/types" @@ -26,9 +31,10 @@ "github.com/containers/podman/v3/libpod/plugin" "github.com/containers/podman/v3/libpod/shutdown" "github.com/containers/podman/v3/pkg/cgroups" - "github.com/containers/podman/v3/pkg/registries" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/systemd" "github.com/containers/podman/v3/pkg/util" + "github.com/containers/podman/v3/utils" "github.com/containers/storage" "github.com/containers/storage/pkg/unshare" "github.com/cri-o/ocicni/pkg/ocicni" @@ -105,6 +111,8 @@ // noStore indicates whether we need to interact with a store or not noStore bool + // secretsManager manages secrets + secretsManager *secrets.SecretsManager } // SetXdgDirs ensures the XDG_RUNTIME_DIR env and XDG_CONFIG_HOME variables are set. @@ -214,8 +222,6 @@ return nil, err } - runtime.libimageEventsShutdown = make(chan bool) - return runtime, nil } @@ -325,6 +331,24 @@ runtime.mergeDBConfig(dbConfig) + unified, _ := cgroups.IsCgroup2UnifiedMode() + if unified && rootless.IsRootless() && !systemd.IsSystemdSessionValid(rootless.GetRootlessUID()) { + // If user is rootless and XDG_RUNTIME_DIR is found, podman will not proceed with /tmp directory + // it will try to use existing XDG_RUNTIME_DIR + // if current user has no write access to XDG_RUNTIME_DIR we will fail later + if err := unix.Access(runtime.storageConfig.RunRoot, unix.W_OK); err != nil { + msg := "XDG_RUNTIME_DIR is pointing to a path which is not writable. Most likely podman will fail." + if errors.Is(err, os.ErrNotExist) { + // if dir does not exists try to create it + if err := os.MkdirAll(runtime.storageConfig.RunRoot, 0700); err != nil { + logrus.Warn(msg) + } + } else { + logrus.Warn(msg) + } + } + } + logrus.Debugf("Using graph driver %s", runtime.storageConfig.GraphDriverName) logrus.Debugf("Using graph root %s", runtime.storageConfig.GraphRoot) logrus.Debugf("Using run root %s", runtime.storageConfig.RunRoot) @@ -379,7 +403,9 @@ // Set up containers/image if runtime.imageContext == nil { - runtime.imageContext = &types.SystemContext{} + runtime.imageContext = &types.SystemContext{ + BigFilesTemporaryDir: parse.GetTempDir(), + } } runtime.imageContext.SignaturePolicyPath = runtime.config.Engine.SignaturePolicyPath @@ -457,8 +483,13 @@ } } + // If we need to make a default network - do so now. + if err := defaultnet.Create(runtime.config.Network.DefaultNetwork, runtime.config.Network.DefaultSubnet, runtime.config.Network.NetworkConfigDir, runtime.config.Engine.StaticDir, runtime.config.Engine.MachineEnabled); err != nil { + logrus.Errorf("Failed to created default CNI network: %v", err) + } + // Set up the CNI net plugin - netPlugin, err := ocicni.InitCNI(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, runtime.config.Network.CNIPluginDirs...) + netPlugin, err := ocicni.InitCNINoInotify(runtime.config.Network.DefaultNetwork, runtime.config.Network.NetworkConfigDir, "", runtime.config.Network.CNIPluginDirs...) if err != nil { return errors.Wrapf(err, "error configuring CNI network plugin") } @@ -491,6 +522,15 @@ // no containers running. Create immediately a namespace, as // we will need to access the storage. if needsUserns { + // warn users if mode is rootless and cgroup manager is systemd + // and no valid systemd session is present + // warn only whenever new namespace is created + if runtime.config.Engine.CgroupManager == config.SystemdCgroupsManager { + unified, _ := cgroups.IsCgroup2UnifiedMode() + if unified && rootless.IsRootless() && !systemd.IsSystemdSessionValid(rootless.GetRootlessUID()) { + logrus.Debug("Invalid systemd user session for current user") + } + } aliveLock.Unlock() // Unlock to avoid deadlock as BecomeRootInUserNS will reexec. pausePid, err := util.GetRootlessPauseProcessPidPathGivenDir(runtime.config.Engine.TmpDir) if err != nil { @@ -501,6 +541,9 @@ return err } if became { + // Check if the pause process was created. If it was created, then + // move it to its own systemd scope. + utils.MovePauseProcessToScope(pausePid) os.Exit(ret) } } @@ -698,6 +741,8 @@ // events on the libimage.Runtime. The gourtine will be cleaned up implicitly // when the main() exists. func (r *Runtime) libimageEvents() { + r.libimageEventsShutdown = make(chan bool) + toLibpodEventStatus := func(e *libimage.Event) events.Status { status, found := libimageEventsMap[e.Type] if !found { @@ -706,9 +751,8 @@ return status } + eventChannel := r.libimageRuntime.EventChannel() go func() { - eventChannel := r.libimageRuntime.EventChannel() - for { // Make sure to read and write all events before // checking if we're about to shutdown. @@ -777,7 +821,9 @@ // attempt to shut it down if r.store != nil { // Wait for the events to be written. - r.libimageEventsShutdown <- true + if r.libimageEventsShutdown != nil { + r.libimageEventsShutdown <- true + } // Note that the libimage runtime shuts down the store. if err := r.libimageRuntime.Shutdown(force); err != nil { @@ -919,7 +965,9 @@ // SystemContext returns the imagecontext func (r *Runtime) SystemContext() *types.SystemContext { - return r.imageContext + // Return the context from the libimage runtime. libimage is sensitive + // to a number of env vars. + return r.libimageRuntime.SystemContext() } // GetOCIRuntimePath retrieves the path of the default OCI runtime. @@ -927,14 +975,22 @@ return r.defaultOCIRuntime.Path() } +// DefaultOCIRuntime return copy of Default OCI Runtime +func (r *Runtime) DefaultOCIRuntime() OCIRuntime { + return r.defaultOCIRuntime +} + // StorageConfig retrieves the storage options for the container runtime func (r *Runtime) StorageConfig() storage.StoreOptions { return r.storageConfig } -// GetStore returns the runtime stores -func (r *Runtime) GetStore() storage.Store { - return r.store +// RunRoot retrieves the current c/storage temporary directory in use by Libpod. +func (r *Runtime) RunRoot() string { + if r.store == nil { + return "" + } + return r.store.RunRoot() } // GetName retrieves the name associated with a given full ID. @@ -1029,9 +1085,9 @@ if err := r.reloadStorageConf(); err != nil { return err } - if err := reloadRegistriesConf(); err != nil { - return err - } + // Invalidate the registries.conf cache. The next invocation will + // reload all data. + sysregistriesv2.InvalidateCache() return nil } @@ -1046,17 +1102,6 @@ return nil } -// reloadRegistries reloads the registries.conf -func reloadRegistriesConf() error { - sysregistriesv2.InvalidateCache() - registries, err := sysregistriesv2.GetRegistries(&types.SystemContext{SystemRegistriesConfPath: registries.SystemRegistriesConfPath()}) - if err != nil { - return err - } - logrus.Infof("applied new registry configuration: %+v", registries) - return nil -} - // reloadStorageConf reloads the storage.conf func (r *Runtime) reloadStorageConf() error { configFile, err := storage.DefaultConfigFile(rootless.IsRootless()) @@ -1088,6 +1133,18 @@ return filepath.Join(r.store.GraphRoot(), "secrets") } +// SecretsManager returns the directory that the secrets manager should take +func (r *Runtime) SecretsManager() (*secrets.SecretsManager, error) { + if r.secretsManager == nil { + manager, err := secrets.NewManager(r.GetSecretsStorageDir()) + if err != nil { + return nil, err + } + r.secretsManager = manager + } + return r.secretsManager, nil +} + func graphRootMounted() bool { f, err := os.OpenFile("/run/.containerenv", os.O_RDONLY, os.ModePerm) if err != nil { diff -Nru libpod-3.2.1+ds1/libpod/runtime_img_test.go libpod-3.4.4+ds1/libpod/runtime_img_test.go --- libpod-3.2.1+ds1/libpod/runtime_img_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_img_test.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -package libpod - -import ( - "io/ioutil" - "os" - "reflect" - "testing" - - sysreg "github.com/containers/podman/v3/pkg/registries" - "github.com/stretchr/testify/assert" -) - -var ( - registry = `[registries.search] -registries = ['one'] - -[registries.insecure] -registries = ['two']` -) - -func createTmpFile(content []byte) (string, error) { - tmpfile, err := ioutil.TempFile(os.TempDir(), "unittest") - if err != nil { - return "", err - } - - if _, err := tmpfile.Write(content); err != nil { - return "", err - } - if err := tmpfile.Close(); err != nil { - return "", err - } - return tmpfile.Name(), nil -} - -func TestGetRegistries(t *testing.T) { - registryPath, err := createTmpFile([]byte(registry)) - assert.NoError(t, err) - defer os.Remove(registryPath) - os.Setenv("CONTAINERS_REGISTRIES_CONF", registryPath) - registries, err := sysreg.GetRegistries() - assert.NoError(t, err) - assert.True(t, reflect.DeepEqual(registries, []string{"one"})) -} - -func TestGetInsecureRegistries(t *testing.T) { - registryPath, err := createTmpFile([]byte(registry)) - assert.NoError(t, err) - os.Setenv("CONTAINERS_REGISTRIES_CONF", registryPath) - defer os.Remove(registryPath) - registries, err := sysreg.GetInsecureRegistries() - assert.NoError(t, err) - assert.True(t, reflect.DeepEqual(registries, []string{"two"})) -} diff -Nru libpod-3.2.1+ds1/libpod/runtime_migrate_unsupported.go libpod-3.4.4+ds1/libpod/runtime_migrate_unsupported.go --- libpod-3.2.1+ds1/libpod/runtime_migrate_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_migrate_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -// +build !linux - -package libpod - -import ( - "context" -) - -func (r *Runtime) migrate(ctx context.Context) error { - return nil -} - -func (r *Runtime) stopPauseProcess() error { - return nil -} diff -Nru libpod-3.2.1+ds1/libpod/runtime_pod_infra_linux.go libpod-3.4.4+ds1/libpod/runtime_pod_infra_linux.go --- libpod-3.2.1+ds1/libpod/runtime_pod_infra_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_pod_infra_linux.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,238 +0,0 @@ -// +build linux - -package libpod - -import ( - "context" - "strings" - - "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod/define" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/containers/podman/v3/pkg/util" - v1 "github.com/opencontainers/image-spec/specs-go/v1" - spec "github.com/opencontainers/runtime-spec/specs-go" - "github.com/opencontainers/runtime-tools/generate" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" -) - -const ( - // IDTruncLength is the length of the pod's id that will be used to make the - // infra container name - IDTruncLength = 12 -) - -func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, rawImageName, imgID string, config *v1.ImageConfig) (*Container, error) { - // Set up generator for infra container defaults - g, err := generate.New("linux") - if err != nil { - return nil, err - } - - // Set Pod hostname - g.Config.Hostname = p.config.Hostname - - var options []CtrCreateOption - - // Command: If user-specified, use that preferentially. - // If not set and the config file is set, fall back to that. - var infraCtrCommand []string - if p.config.InfraContainer.InfraCommand != nil { - logrus.Debugf("User-specified infra container entrypoint %v", p.config.InfraContainer.InfraCommand) - infraCtrCommand = p.config.InfraContainer.InfraCommand - } else if r.config.Engine.InfraCommand != "" { - logrus.Debugf("Config-specified infra container entrypoint %s", r.config.Engine.InfraCommand) - infraCtrCommand = []string{r.config.Engine.InfraCommand} - } - // Only if set by the user or containers.conf, we set entrypoint for the - // infra container. - // This is only used by commit, so it shouldn't matter... But someone - // may eventually want to commit an infra container? - // TODO: Should we actually do this if set by containers.conf? - if infraCtrCommand != nil { - // Need to duplicate the array - we are going to add Cmd later - // so the current array will be changed. - newArr := make([]string, 0, len(infraCtrCommand)) - newArr = append(newArr, infraCtrCommand...) - options = append(options, WithEntrypoint(newArr)) - } - - isRootless := rootless.IsRootless() - - // I've seen circumstances where config is being passed as nil. - // Let's err on the side of safety and make sure it's safe to use. - if config != nil { - if infraCtrCommand == nil { - // If we have no entrypoint and command from the image, - // we can't go on - the infra container has no command. - if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { - return nil, errors.Errorf("infra container has no command") - } - if len(config.Entrypoint) > 0 { - infraCtrCommand = config.Entrypoint - } else { - // Use the Docker default "/bin/sh -c" - // entrypoint, as we're overriding command. - // If an image doesn't want this, it can - // override entrypoint too. - infraCtrCommand = []string{"/bin/sh", "-c"} - } - } - if len(config.Cmd) > 0 { - infraCtrCommand = append(infraCtrCommand, config.Cmd...) - } - - if len(config.Env) > 0 { - for _, nameValPair := range config.Env { - nameValSlice := strings.Split(nameValPair, "=") - if len(nameValSlice) < 2 { - return nil, errors.Errorf("Invalid environment variable structure in pause image") - } - g.AddProcessEnv(nameValSlice[0], nameValSlice[1]) - } - } - - switch { - case p.config.InfraContainer.HostNetwork: - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) - } - case p.config.InfraContainer.NoNetwork: - // Do nothing - we have a network namespace by default, - // but should not configure slirp. - default: - // Since user namespace sharing is not implemented, we only need to check if it's rootless - netmode := "bridge" - if p.config.InfraContainer.Slirp4netns { - netmode = "slirp4netns" - if len(p.config.InfraContainer.NetworkOptions) != 0 { - options = append(options, WithNetworkOptions(p.config.InfraContainer.NetworkOptions)) - } - } - // PostConfigureNetNS should not be set since user namespace sharing is not implemented - // and rootless networking no longer supports post configuration setup - options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, p.config.InfraContainer.Networks)) - } - - // For each option in InfraContainerConfig - if set, pass into - // the infra container we're creating with the appropriate - // With... option. - if p.config.InfraContainer.StaticIP != nil { - options = append(options, WithStaticIP(p.config.InfraContainer.StaticIP)) - } - if p.config.InfraContainer.StaticMAC != nil { - options = append(options, WithStaticMAC(p.config.InfraContainer.StaticMAC)) - } - if p.config.InfraContainer.UseImageResolvConf { - options = append(options, WithUseImageResolvConf()) - } - if len(p.config.InfraContainer.DNSServer) > 0 { - options = append(options, WithDNS(p.config.InfraContainer.DNSServer)) - } - if len(p.config.InfraContainer.DNSSearch) > 0 { - options = append(options, WithDNSSearch(p.config.InfraContainer.DNSSearch)) - } - if len(p.config.InfraContainer.DNSOption) > 0 { - options = append(options, WithDNSOption(p.config.InfraContainer.DNSOption)) - } - if p.config.InfraContainer.UseImageHosts { - options = append(options, WithUseImageHosts()) - } - if len(p.config.InfraContainer.HostAdd) > 0 { - options = append(options, WithHosts(p.config.InfraContainer.HostAdd)) - } - if len(p.config.InfraContainer.ExitCommand) > 0 { - options = append(options, WithExitCommand(p.config.InfraContainer.ExitCommand)) - } - } - - g.SetRootReadonly(true) - g.SetProcessArgs(infraCtrCommand) - - logrus.Debugf("Using %q as infra container command", infraCtrCommand) - - g.RemoveMount("/dev/shm") - if isRootless { - g.RemoveMount("/dev/pts") - devPts := spec.Mount{ - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"private", "nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}, - } - g.AddMount(devPts) - } - - // Add default sysctls from containers.conf - defaultSysctls, err := util.ValidateSysctls(r.config.Sysctls()) - if err != nil { - return nil, err - } - for sysctlKey, sysctlVal := range defaultSysctls { - // Ignore mqueue sysctls if not sharing IPC - if !p.config.UsePodIPC && strings.HasPrefix(sysctlKey, "fs.mqueue.") { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since IPC Namespace for pod is unused", sysctlKey, sysctlVal) - - continue - } - - // Ignore net sysctls if host network or not sharing network - if (p.config.InfraContainer.HostNetwork || !p.config.UsePodNet) && strings.HasPrefix(sysctlKey, "net.") { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since Network Namespace for pod is unused", sysctlKey, sysctlVal) - continue - } - - // Ignore uts sysctls if not sharing UTS - if !p.config.UsePodUTS && (strings.HasPrefix(sysctlKey, "kernel.domainname") || strings.HasPrefix(sysctlKey, "kernel.hostname")) { - logrus.Infof("Sysctl %s=%s ignored in containers.conf, since UTS Namespace for pod is unused", sysctlKey, sysctlVal) - continue - } - - g.AddLinuxSysctl(sysctlKey, sysctlVal) - } - - containerName := p.ID()[:IDTruncLength] + "-infra" - options = append(options, r.WithPod(p)) - options = append(options, WithRootFSFromImage(imgID, imgName, rawImageName)) - options = append(options, WithName(containerName)) - options = append(options, withIsInfra()) - if len(p.config.InfraContainer.ConmonPidFile) > 0 { - options = append(options, WithConmonPidFile(p.config.InfraContainer.ConmonPidFile)) - } - - return r.newContainer(ctx, g.Config, options...) -} - -// createInfraContainer wrap creates an infra container for a pod. -// An infra container becomes the basis for kernel namespace sharing between -// containers in the pod. -func (r *Runtime) createInfraContainer(ctx context.Context, p *Pod) (*Container, error) { - if !r.valid { - return nil, define.ErrRuntimeStopped - } - - imageName := p.config.InfraContainer.InfraImage - if imageName == "" { - imageName = r.config.Engine.InfraImage - } - - pulledImages, err := r.LibimageRuntime().Pull(ctx, imageName, config.PullPolicyMissing, nil) - if err != nil { - return nil, errors.Wrap(err, "error pulling infra-container image") - } - - newImage := pulledImages[0] - data, err := newImage.Inspect(ctx, false) - if err != nil { - return nil, err - } - - imageName = "none" - if len(newImage.Names()) > 0 { - imageName = newImage.Names()[0] - } - imageID := data.ID - - return r.makeInfraContainer(ctx, p, imageName, r.config.Engine.InfraImage, imageID, data.Config) -} diff -Nru libpod-3.2.1+ds1/libpod/runtime_pod_linux.go libpod-3.4.4+ds1/libpod/runtime_pod_linux.go --- libpod-3.2.1+ds1/libpod/runtime_pod_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_pod_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -14,13 +14,14 @@ "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // NewPod makes a new, empty pod -func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (_ *Pod, deferredErr error) { +func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, options ...PodCreateOption) (_ *Pod, deferredErr error) { r.lock.Lock() defer r.lock.Unlock() @@ -42,18 +43,6 @@ } } - if pod.config.Name == "" { - name, err := r.generateName() - if err != nil { - return nil, err - } - pod.config.Name = name - } - - if pod.config.Hostname == "" { - pod.config.Hostname = pod.config.Name - } - // Allocate a lock for the pod lock, err := r.lockManager.AllocateLock() if err != nil { @@ -88,6 +77,9 @@ // 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: @@ -108,6 +100,9 @@ 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) @@ -116,6 +111,7 @@ if pod.config.UsePodCgroup { logrus.Debugf("Got pod cgroup as %s", pod.state.CgroupPath) } + if !pod.HasInfraContainer() && pod.SharesNamespaces() { return nil, errors.Errorf("Pods must have an infra container to share namespaces") } @@ -123,31 +119,67 @@ logrus.Infof("Pod has an infra container, but shares no namespaces") } - if err := r.state.AddPod(pod); err != nil { - return nil, errors.Wrapf(err, "error adding pod to state") - } - defer func() { - if deferredErr != nil { - if err := r.removePod(ctx, pod, true, true); err != nil { - logrus.Errorf("Error removing pod after pause container creation failure: %v", err) + // Unless the user has specified a name, use a randomly generated one. + // Note that name conflicts may occur (see #11735), so we need to loop. + generateName := pod.config.Name == "" + var addPodErr error + for { + if generateName { + name, err := r.generateName() + if err != nil { + return nil, err } + pod.config.Name = name } - }() - if pod.HasInfraContainer() { - ctr, err := r.createInfraContainer(ctx, pod) - if err != nil { - return nil, errors.Wrapf(err, "error adding Infra Container") - } - pod.state.InfraContainerID = ctr.ID() - if err := pod.save(); err != nil { - return nil, err + if p.InfraContainerSpec != nil && p.InfraContainerSpec.Hostname == "" { + p.InfraContainerSpec.Hostname = pod.config.Name + } + if addPodErr = r.state.AddPod(pod); addPodErr == nil { + return pod, nil } + if !generateName || (errors.Cause(addPodErr) != define.ErrPodExists && errors.Cause(addPodErr) != define.ErrCtrExists) { + break + } + } + if addPodErr != nil { + return nil, errors.Wrapf(addPodErr, "error adding pod to state") + } + + return pod, nil +} + +// AddInfra adds the created infra container to the pod state +func (r *Runtime) AddInfra(ctx context.Context, pod *Pod, infraCtr *Container) (*Pod, error) { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return nil, define.ErrRuntimeStopped + } + pod.state.InfraContainerID = infraCtr.ID() + if err := pod.save(); err != nil { + return nil, err } pod.newPodEvent(events.Create) return pod, nil } +// SavePod is a helper function to save the pod state from outside of libpod +func (r *Runtime) SavePod(pod *Pod) error { + r.lock.Lock() + defer r.lock.Unlock() + + if !r.valid { + return define.ErrRuntimeStopped + } + if err := pod.save(); err != nil { + return err + } + pod.newPodEvent(events.Create) + return nil +} + func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { if err := p.updatePod(); err != nil { return err @@ -157,10 +189,9 @@ if err != nil { return err } - numCtrs := len(ctrs) - // If the only container in the pod is the pause container, remove the pod and container unconditionally. + // If the only running container in the pod is the pause container, remove the pod and container unconditionally. pauseCtrID := p.state.InfraContainerID if numCtrs == 1 && ctrs[0].ID() == pauseCtrID { removeCtrs = true @@ -244,6 +275,15 @@ } } + // Clear infra container ID before we remove the infra container. + // There is a potential issue if we don't do that, and removal is + // interrupted between RemoveAllContainers() below and the pod's removal + // later - we end up with a reference to a nonexistent infra container. + p.state.InfraContainerID = "" + if err := p.save(); err != nil { + return err + } + // Remove all containers in the pod from the state. if err := r.state.RemovePodContainers(p); err != nil { // If this fails, there isn't much more we can do. diff -Nru libpod-3.2.1+ds1/libpod/runtime_pod_unsupported.go libpod-3.4.4+ds1/libpod/runtime_pod_unsupported.go --- libpod-3.2.1+ds1/libpod/runtime_pod_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_pod_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -// +build !linux - -package libpod - -import ( - "context" - - "github.com/containers/podman/v3/libpod/define" -) - -// NewPod makes a new, empty pod -func (r *Runtime) NewPod(ctx context.Context, options ...PodCreateOption) (*Pod, error) { - return nil, define.ErrOSNotSupported -} - -func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { - return define.ErrOSNotSupported -} diff -Nru libpod-3.2.1+ds1/libpod/runtime_volume_linux.go libpod-3.4.4+ds1/libpod/runtime_volume_linux.go --- libpod-3.2.1+ds1/libpod/runtime_volume_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_volume_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" volplugin "github.com/containers/podman/v3/libpod/plugin" + "github.com/containers/storage/drivers/quota" "github.com/containers/storage/pkg/stringid" pluginapi "github.com/docker/go-plugins-helpers/volume" "github.com/pkg/errors" @@ -68,7 +69,7 @@ // Validate options for key := range volume.config.Options { switch key { - case "device", "o", "type", "UID", "GID": + case "device", "o", "type", "UID", "GID", "SIZE", "INODES": // Do nothing, valid keys default: return nil, errors.Wrapf(define.ErrInvalidArg, "invalid mount option %s for driver 'local'", key) @@ -106,6 +107,26 @@ if err := LabelVolumePath(fullVolPath); err != nil { return nil, err } + projectQuotaSupported := false + + q, err := quota.NewControl(r.config.Engine.VolumePath) + if err == nil { + projectQuotaSupported = true + } + quota := quota.Quota{} + if volume.config.Size > 0 || volume.config.Inodes > 0 { + if !projectQuotaSupported { + return nil, errors.New("Volume options size and inodes not supported. Filesystem does not support Project Quota") + } + quota.Size = volume.config.Size + quota.Inodes = volume.config.Inodes + } + if projectQuotaSupported { + if err := q.SetQuota(fullVolPath, quota); err != nil { + return nil, errors.Wrapf(err, "failed to set size quota size=%d inodes=%d for volume directory %q", volume.config.Size, volume.config.Inodes, fullVolPath) + } + } + volume.config.MountPoint = fullVolPath } @@ -234,11 +255,6 @@ // Set volume as invalid so it can no longer be used v.valid = false - // Remove the volume from the state - if err := r.state.RemoveVolume(v); err != nil { - return errors.Wrapf(err, "error removing volume %s", v.Name()) - } - var removalErr error // If we use a volume plugin, we need to remove from the plugin. @@ -266,11 +282,19 @@ req := new(pluginapi.RemoveRequest) req.Name = v.Name() if err := v.plugin.RemoveVolume(req); err != nil { - removalErr = errors.Wrapf(err, "volume %s could not be removed from plugin %s, but it has been removed from Podman", v.Name(), v.Driver()) + return errors.Wrapf(err, "volume %s could not be removed from plugin %s", v.Name(), v.Driver()) } } } + // Remove the volume from the state + if err := r.state.RemoveVolume(v); err != nil { + if removalErr != nil { + logrus.Errorf("Error removing volume %s from plugin %s: %v", v.Name(), v.Driver(), removalErr) + } + return errors.Wrapf(err, "error removing volume %s", v.Name()) + } + // Free the volume's lock if err := v.lock.Free(); err != nil { if removalErr == nil { diff -Nru libpod-3.2.1+ds1/libpod/runtime_volume_unsupported.go libpod-3.4.4+ds1/libpod/runtime_volume_unsupported.go --- libpod-3.2.1+ds1/libpod/runtime_volume_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/runtime_volume_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -// +build !linux - -package libpod - -import ( - "context" - - "github.com/containers/podman/v3/libpod/define" -) - -func (r *Runtime) removeVolume(ctx context.Context, v *Volume, force bool) error { - return define.ErrNotImplemented -} - -func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) (*Volume, error) { - return nil, define.ErrNotImplemented -} - -func (r *Runtime) NewVolume(ctx context.Context, options ...VolumeCreateOption) (*Volume, error) { - return nil, define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/shutdown/handler.go libpod-3.4.4+ds1/libpod/shutdown/handler.go --- libpod-3.2.1+ds1/libpod/shutdown/handler.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/shutdown/handler.go 2021-12-26 00:45:51.000000000 +0000 @@ -35,7 +35,7 @@ return nil } - sigChan = make(chan os.Signal, 1) + sigChan = make(chan os.Signal, 2) cancelChan = make(chan bool, 1) stopped = false diff -Nru libpod-3.2.1+ds1/libpod/stats.go libpod-3.4.4+ds1/libpod/stats.go --- libpod-3.2.1+ds1/libpod/stats.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/stats.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ package libpod import ( + "math" "strings" "syscall" "time" @@ -30,7 +31,7 @@ } } - if c.state.State != define.ContainerStateRunning { + if c.state.State != define.ContainerStateRunning && c.state.State != define.ContainerStatePaused { return stats, define.ErrCtrStateInvalid } @@ -54,14 +55,24 @@ return nil, err } + // If the current total usage in the cgroup is less than what was previously + // recorded then it means the container was restarted and runs in a new cgroup + if previousStats.Duration > cgroupStats.CPU.Usage.Total { + previousStats = &define.ContainerStats{} + } + previousCPU := previousStats.CPUNano now := uint64(time.Now().UnixNano()) + stats.Duration = cgroupStats.CPU.Usage.Total + stats.UpTime = time.Duration(stats.Duration) stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano) + stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints) + stats.DataPoints = previousStats.DataPoints + 1 stats.MemUsage = cgroupStats.Memory.Usage.Usage - stats.MemLimit = getMemLimit(cgroupStats.Memory.Usage.Limit) + stats.MemLimit = c.getMemLimit() stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100 stats.PIDs = 0 - if conState == define.ContainerStateRunning { + if conState == define.ContainerStateRunning || conState == define.ContainerStatePaused { stats.PIDs = cgroupStats.Pids.Current } stats.BlockInput, stats.BlockOutput = calculateBlockIO(cgroupStats) @@ -81,22 +92,29 @@ return stats, nil } -// getMemory limit returns the memory limit for a given cgroup -// If the configured memory limit is larger than the total memory on the sys, the -// physical system memory size is returned -func getMemLimit(cgroupLimit uint64) uint64 { +// getMemory limit returns the memory limit for a container +func (c *Container) getMemLimit() uint64 { + memLimit := uint64(math.MaxUint64) + + if c.config.Spec.Linux != nil && c.config.Spec.Linux.Resources != nil && + c.config.Spec.Linux.Resources.Memory != nil && c.config.Spec.Linux.Resources.Memory.Limit != nil { + memLimit = uint64(*c.config.Spec.Linux.Resources.Memory.Limit) + } + si := &syscall.Sysinfo_t{} err := syscall.Sysinfo(si) if err != nil { - return cgroupLimit + return memLimit } //nolint:unconvert physicalLimit := uint64(si.Totalram) - if cgroupLimit > physicalLimit { + + if memLimit <= 0 || memLimit > physicalLimit { return physicalLimit } - return cgroupLimit + + return memLimit } // calculateCPUPercent calculates the cpu usage using the latest measurement in stats. @@ -127,3 +145,9 @@ } return } + +// calculateAvgCPU calculates the avg CPU percentage given the previous average and the number of data points. +func calculateAvgCPU(statsCPU float64, prevAvg float64, prevData int64) float64 { + avgPer := ((prevAvg * float64(prevData)) + statsCPU) / (float64(prevData) + 1) + return avgPer +} diff -Nru libpod-3.2.1+ds1/libpod/stats_unsupported.go libpod-3.4.4+ds1/libpod/stats_unsupported.go --- libpod-3.2.1+ds1/libpod/stats_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/stats_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -// +build !linux - -package libpod - -import "github.com/containers/podman/v3/libpod/define" - -// GetContainerStats gets the running stats for a given container -func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) { - return nil, define.ErrOSNotSupported -} diff -Nru libpod-3.2.1+ds1/libpod/util.go libpod-3.4.4+ds1/libpod/util.go --- libpod-3.2.1+ds1/libpod/util.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/util.go 2021-12-26 00:45:51.000000000 +0000 @@ -153,33 +153,22 @@ return strings.Trim(output, "\n") } -func equeryVersion(path string) string { - return queryPackageVersion("/usr/bin/equery", "b", path) -} - -func pacmanVersion(path string) string { - return queryPackageVersion("/usr/bin/pacman", "-Qo", path) -} - -func dpkgVersion(path string) string { - return queryPackageVersion("/usr/bin/dpkg", "-S", path) -} - -func rpmVersion(path string) string { - return queryPackageVersion("/usr/bin/rpm", "-q", "-f", path) -} - -func packageVersion(program string) string { - if out := rpmVersion(program); out != unknownPackage { - return out - } - if out := dpkgVersion(program); out != unknownPackage { - return out +func packageVersion(program string) string { // program is full path + packagers := [][]string{ + {"/usr/bin/rpm", "-q", "-f"}, + {"/usr/bin/dpkg", "-S"}, // Debian, Ubuntu + {"/usr/bin/pacman", "-Qo"}, // Arch + {"/usr/bin/qfile", "-qv"}, // Gentoo (quick) + {"/usr/bin/equery", "b"}, // Gentoo (slow) } - if out := pacmanVersion(program); out != unknownPackage { - return out + + for _, cmd := range packagers { + cmd = append(cmd, program) + if out := queryPackageVersion(cmd...); out != unknownPackage { + return out + } } - return equeryVersion(program) + return unknownPackage } func programVersion(mountProgram string) (string, error) { @@ -306,8 +295,8 @@ } // Convert OCICNI port bindings into Inspect-formatted port bindings. -func makeInspectPortBindings(bindings []ocicni.PortMapping) map[string][]define.InspectHostPort { - portBindings := make(map[string][]define.InspectHostPort) +func makeInspectPortBindings(bindings []ocicni.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort { + portBindings := make(map[string][]define.InspectHostPort, len(bindings)) for _, port := range bindings { key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol) hostPorts := portBindings[key] @@ -320,6 +309,15 @@ }) portBindings[key] = hostPorts } + // add exposed ports without host port information to match docker + for port, protocols := range expose { + for _, protocol := range protocols { + key := fmt.Sprintf("%d/%s", port, protocol) + if _, ok := portBindings[key]; !ok { + portBindings[key] = nil + } + } + } return portBindings } diff -Nru libpod-3.2.1+ds1/libpod/util_unsupported.go libpod-3.4.4+ds1/libpod/util_unsupported.go --- libpod-3.2.1+ds1/libpod/util_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/util_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -// +build !linux - -package libpod - -import ( - "github.com/containers/podman/v3/libpod/define" - "github.com/pkg/errors" -) - -func systemdSliceFromPath(parent, name string) (string, error) { - return "", errors.Wrapf(define.ErrOSNotSupported, "cgroups are not supported on non-linux OSes") -} - -func makeSystemdCgroup(path string) error { - return errors.Wrapf(define.ErrOSNotSupported, "cgroups are not supported on non-linux OSes") -} - -func deleteSystemdCgroup(path string) error { - return errors.Wrapf(define.ErrOSNotSupported, "cgroups are not supported on non-linux OSes") -} - -func assembleSystemdCgroupName(baseSlice, newSlice string) (string, error) { - return "", errors.Wrapf(define.ErrOSNotSupported, "cgroups are not supported on non-linux OSes") -} - -// LabelVolumePath takes a mount path for a volume and gives it an -// selinux label of either shared or not -func LabelVolumePath(path string) error { - return define.ErrNotImplemented -} - -func Unmount(mount string) error { - return define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/libpod/volume.go libpod-3.4.4+ds1/libpod/volume.go --- libpod-3.2.1+ds1/libpod/volume.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/volume.go 2021-12-26 00:45:51.000000000 +0000 @@ -49,6 +49,10 @@ UID int `json:"uid"` // GID the volume will be created as. GID int `json:"gid"` + // Size maximum of the volume. + Size uint64 `json:"size"` + // Inodes maximum of the volume. + Inodes uint64 `json:"inodes"` } // VolumeState holds the volume's mutable state. @@ -135,6 +139,17 @@ return v.mountPoint(), nil } +// MountCount returns the volume's mountcount on the host from state +// Useful in determining if volume is using plugin or a filesystem mount and its mount +func (v *Volume) MountCount() (uint, error) { + v.lock.Lock() + defer v.lock.Unlock() + if err := v.update(); err != nil { + return 0, err + } + return v.state.MountCount, nil +} + // Internal-only helper for volume mountpoint func (v *Volume) mountPoint() string { if v.UsesVolumeDriver() { diff -Nru libpod-3.2.1+ds1/libpod/volume_internal.go libpod-3.4.4+ds1/libpod/volume_internal.go --- libpod-3.2.1+ds1/libpod/volume_internal.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/volume_internal.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,8 +39,26 @@ return true } - // Local driver with options needs mount - return len(v.config.Options) > 0 + // Commit 28138dafcc added the UID and GID options to this map + // However we should only mount when options other than uid and gid are set. + // see https://github.com/containers/podman/issues/10620 + index := 0 + if _, ok := v.config.Options["UID"]; ok { + index++ + } + if _, ok := v.config.Options["GID"]; ok { + index++ + } + if _, ok := v.config.Options["SIZE"]; ok { + index++ + } + // when uid or gid is set there is also the "o" option + // set so we have to ignore this one as well + if index > 0 { + index++ + } + // Local driver with options other than uid,gid needs mount + return len(v.config.Options) > index } // update() updates the volume state from the DB. diff -Nru libpod-3.2.1+ds1/libpod/volume_internal_linux.go libpod-3.4.4+ds1/libpod/volume_internal_linux.go --- libpod-3.2.1+ds1/libpod/volume_internal_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/volume_internal_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -104,7 +104,7 @@ logrus.Debugf("Running mount command: %s %s", mountPath, strings.Join(mountArgs, " ")) if output, err := mountCmd.CombinedOutput(); err != nil { - logrus.Debugf("Mount failed with %v", err) + logrus.Debugf("Mount %v failed with %v", mountCmd, err) return errors.Wrapf(errors.Errorf(string(output)), "error mounting volume %s", v.Name()) } diff -Nru libpod-3.2.1+ds1/libpod/volume_internal_unsupported.go libpod-3.4.4+ds1/libpod/volume_internal_unsupported.go --- libpod-3.2.1+ds1/libpod/volume_internal_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/libpod/volume_internal_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -// +build !linux - -package libpod - -import ( - "github.com/containers/podman/v3/libpod/define" -) - -func (v *Volume) mount() error { - return define.ErrNotImplemented -} - -func (v *Volume) unmount(force bool) error { - return define.ErrNotImplemented -} diff -Nru libpod-3.2.1+ds1/LICENSE libpod-3.4.4+ds1/LICENSE --- libpod-3.2.1+ds1/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/LICENSE 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,6 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +192,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff -Nru libpod-3.2.1+ds1/Makefile libpod-3.4.4+ds1/Makefile --- libpod-3.2.1+ds1/Makefile 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/Makefile 2021-12-26 00:45:51.000000000 +0000 @@ -25,12 +25,12 @@ GO ?= go COVERAGE_PATH ?= .coverage DESTDIR ?= -EPOCH_TEST_COMMIT ?= $(shell git merge-base $${DEST_BRANCH:-master} HEAD) +EPOCH_TEST_COMMIT ?= $(shell git merge-base $${DEST_BRANCH:-main} HEAD) HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD PROJECT := github.com/containers/podman -GIT_BASE_BRANCH ?= origin/master +GIT_BASE_BRANCH ?= origin/main GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null) GIT_BRANCH_CLEAN ?= $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g") LIBPOD_INSTANCE := libpod_dev @@ -39,7 +39,7 @@ LIBEXECDIR ?= ${PREFIX}/libexec MANDIR ?= ${PREFIX}/share/man SHAREDIR_CONTAINERS ?= ${PREFIX}/share/containers -ETCDIR ?= /etc +ETCDIR ?= ${PREFIX}/etc TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system USERSYSTEMDDIR ?= ${PREFIX}/lib/systemd/user @@ -50,6 +50,7 @@ $(shell hack/btrfs_tag.sh) \ $(shell hack/selinux_tag.sh) \ $(shell hack/systemd_tag.sh) \ + $(shell hack/libsubid_tag.sh) \ exclude_graphdriver_devicemapper \ seccomp PYTHON ?= $(shell command -v python3 python|head -n1) @@ -58,10 +59,11 @@ PRE_COMMIT = $(shell command -v bin/venv/bin/pre-commit ~/.local/bin/pre-commit pre-commit | head -n1) # This isn't what we actually build; it's a superset, used for target -# dependencies. Basically: all *.go files, except *_test.go, and except -# anything in a dot subdirectory. If any of these files is newer than -# our target (bin/podman{,-remote}), a rebuild is triggered. -SOURCES = $(shell find . -path './.*' -prune -o \( -name '*.go' -a ! -name '*_test.go' \) -print) +# dependencies. Basically: all *.go and *.c files, except *_test.go, +# and except anything in a dot subdirectory. If any of these files is +# newer than our target (bin/podman{,-remote}), a rebuild is +# triggered. +SOURCES = $(shell find . -path './.*' -prune -o \( \( -name '*.go' -o -name '*.c' \) -a ! -name '*_test.go' \) -print) BUILDFLAGS := -mod=vendor $(BUILDFLAGS) @@ -90,14 +92,16 @@ ISODATE ?= $(shell date --iso-8601) endif LIBPOD := ${PROJECT}/v3/libpod -GCFLAGS ?= all=-trimpath=$(CURDIR) -ASMFLAGS ?= all=-trimpath=$(CURDIR) +GOFLAGS ?= -trimpath LDFLAGS_PODMAN ?= \ - -X $(LIBPOD)/define.gitCommit=$(GIT_COMMIT) \ - -X $(LIBPOD)/define.buildInfo=$(BUILD_INFO) \ - -X $(LIBPOD)/config._installPrefix=$(PREFIX) \ - -X $(LIBPOD)/config._etcDir=$(ETCDIR) \ - $(EXTRA_LDFLAGS) + -X $(LIBPOD)/define.gitCommit=$(GIT_COMMIT) \ + -X $(LIBPOD)/define.buildInfo=$(BUILD_INFO) \ + -X $(LIBPOD)/config._installPrefix=$(PREFIX) \ + -X $(LIBPOD)/config._etcDir=$(ETCDIR) \ + $(EXTRA_LDFLAGS) +LDFLAGS_PODMAN_STATIC ?= \ + $(LDFLAGS_PODMAN) \ + -extldflags=-static #Update to LIBSECCOMP_COMMIT should reflect in Dockerfile too. LIBSECCOMP_COMMIT := v2.3.3 # Rarely if ever should integration tests take more than 50min, @@ -169,6 +173,15 @@ $(GO) get -u ${1} endef +# Need to use CGO for mDNS resolution, but cross builds need CGO disabled +# See https://github.com/golang/go/issues/12524 for details +DARWIN_GCO := 0 +ifeq ($(NATIVE_GOOS),darwin) +ifdef HOMEBREW_PREFIX + DARWIN_GCO := 1 +endif +endif + ### ### Primary entry-point targets ### @@ -211,7 +224,7 @@ .PHONY: .gitvalidation .gitvalidation: .gopathok @echo "Validating vs commit '$(call err_if_empty,EPOCH_TEST_COMMIT)'" - GIT_CHECK_EXCLUDE="./vendor:docs/make.bat" $(GOBIN)/git-validation -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) + GIT_CHECK_EXCLUDE="./vendor:docs/make.bat:test/buildah-bud/buildah-tests.diff" $(GOBIN)/git-validation -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) .PHONY: lint lint: golangci-lint @@ -255,10 +268,10 @@ .PHONY: codespell codespell: - codespell -S bin,vendor,.git,go.sum,changelog.txt,.cirrus.yml,"RELEASE_NOTES.md,*.xz,*.gz,*.tar,*.tgz,bin2img,*ico,*.png,*.1,*.5,copyimg,*.orig,apidoc.go" -L uint,iff,od,seeked,splitted,marge,ERRO,hist,ether -w + codespell -S bin,vendor,.git,go.sum,.cirrus.yml,"RELEASE_NOTES.md,*.xz,*.gz,*.ps1,*.tar,*.tgz,bin2img,*ico,*.png,*.1,*.5,copyimg,*.orig,apidoc.go" -L uint,iff,od,seeked,splitted,marge,ERRO,hist,ether -w .PHONY: validate -validate: gofmt lint .gitvalidation validate.completions man-page-check swagger-check tests-included +validate: gofmt lint .gitvalidation validate.completions man-page-check swagger-check tests-included tests-expect-exit .PHONY: build-all-new-commits build-all-new-commits: @@ -292,8 +305,6 @@ CGO_ENABLED=$(CGO_ENABLED) \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN)' \ -tags "$(BUILDTAGS)" \ -o $@ ./cmd/podman @@ -307,19 +318,15 @@ GOOS=$(GOOS) \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN)' \ -tags "${REMOTETAGS}" \ -o $@ ./cmd/podman $(SRCBINDIR)/podman-remote-static: $(SRCBINDIR) .gopathok $(SOURCES) go.mod go.sum - CGO_ENABLED=$(CGO_ENABLED) \ + CGO_ENABLED=0 \ GOOS=$(GOOS) \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN_STATIC)' \ -tags "${REMOTETAGS}" \ -o $@ ./cmd/podman @@ -351,7 +358,7 @@ .PHONY: podman-remote-darwin podman-remote-darwin: ## Build podman-remote for macOS $(MAKE) \ - CGO_ENABLED=0 \ + CGO_ENABLED=$(DARWIN_GCO) \ GOOS=darwin \ bin/darwin/podman @@ -373,8 +380,6 @@ CGO_ENABLED=0 \ $(GO) build \ $(BUILDFLAGS) \ - -gcflags '$(GCFLAGS)' \ - -asmflags '$(ASMFLAGS)' \ -ldflags '$(LDFLAGS_PODMAN)' \ -tags '$(BUILDTAGS_CROSS)' \ -o "$@" ./cmd/podman @@ -389,10 +394,10 @@ .PHONY: nixpkgs nixpkgs: @nix run \ - -f channel:nixos-20.09 nix-prefetch-git \ + -f channel:nixos-21.05 nix-prefetch-git \ -c nix-prefetch-git \ --no-deepClone \ - https://github.com/nixos/nixpkgs refs/heads/nixos-20.09 > nix/nixpkgs.json + https://github.com/nixos/nixpkgs refs/heads/nixos-21.05 > nix/nixpkgs.json # Build statically linked binary .PHONY: static @@ -428,8 +433,16 @@ make -C pkg/api $(MANPAGES): %: %.md .install.md2man docdir - @sed -e 's/\((podman[^)]*\.md)\)//g' -e 's/\[\(podman[^]]*\)\]/\1/g' \ - -e 's;<\(/\)\?\(a\|a\s\+[^>]*\|sup\)>;;g' $< | \ + +### sed is used to filter http/s links as well as relative links +### replaces "\" at the end of a line with two spaces +### this ensures that manpages are renderd correctly + + @sed -e 's/\((podman[^)]*\.md\(#.*\)\?)\)//g' \ + -e 's/\[\(podman[^]]*\)\]/\1/g' \ + -e 's/\[\([^]]*\)](http[^)]\+)/\1/g' \ + -e 's;<\(/\)\?\(a\|a\s\+[^>]*\|sup\)>;;g' \ + -e 's/\\$$/ /g' $< | \ $(GOMD2MAN) -in /dev/stdin -out $(subst source/markdown,build/man,$@) .PHONY: docdir @@ -466,17 +479,6 @@ docker-docs: docs (cd docs; ./dckrman.sh ./build/man/*.1) -.PHONY: changelog -changelog: ## Generate updated changelog.txt from git logs - @echo "Creating changelog from $(CHANGELOG_BASE) to $(CHANGELOG_TARGET)" - $(eval TMPFILE := $(shell mktemp podman_tmp_XXXX)) - $(shell cat changelog.txt > $(TMPFILE)) - $(shell echo "- Changelog for $(CHANGELOG_TARGET) ($(ISODATE)):" > changelog.txt) - $(shell git log --no-merges --format=" * %s" $(CHANGELOG_BASE)..$(CHANGELOG_TARGET) >> changelog.txt) - $(shell echo "" >> changelog.txt) - $(shell cat $(TMPFILE) >> changelog.txt) - $(shell rm $(TMPFILE)) - # Workaround vim syntax highlighting bug: " ### @@ -491,14 +493,15 @@ if [ -x /bin/zsh ]; then /bin/zsh completions/zsh/_podman; fi if [ -x /bin/fish ]; then /bin/fish completions/fish/podman.fish; fi +# Note: Assumes test/python/requirements.txt is installed & available .PHONY: run-docker-py-tests run-docker-py-tests: - $(eval testLogs=$(shell mktemp podman_tmp_XXXX)) - ./bin/podman run --rm --security-opt label=disable --privileged -v $(testLogs):/testLogs --net=host -e DOCKER_HOST=tcp://localhost:8080 $(DOCKERPY_IMAGE) sh -c "pytest $(DOCKERPY_TEST) " + touch test/__init__.py + pytest test/python/docker/ + -rm test/__init__.py .PHONY: localunit localunit: test/goecho/goecho - hack/check_root.sh make localunit rm -rf ${COVERAGE_PATH} && mkdir -p ${COVERAGE_PATH} $(GOBIN)/ginkgo \ -r \ @@ -576,7 +579,7 @@ .PHONY: localapiv2 localapiv2: env PODMAN=./bin/podman ./test/apiv2/test-apiv2 - env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/rest_api/ + env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/apiv2/python env PODMAN=./bin/podman ${PYTHON} -m unittest discover -v ./test/python/docker .PHONY: remoteapiv2 @@ -594,6 +597,16 @@ tests-included: contrib/cirrus/pr-should-include-tests +.PHONY: tests-expect-exit +tests-expect-exit: + @if egrep --line-number 'Expect.*ExitCode' test/e2e/*.go | egrep -v ', ".*"\)'; then \ + echo "^^^ Unhelpful use of Expect(ExitCode())"; \ + echo " Please use '.Should(Exit(...))' pattern instead."; \ + echo " If that's not possible, please add an annotation (description) to your assertion:"; \ + echo " Expect(...).To(..., \"Friendly explanation of this check\")"; \ + exit 1; \ + fi + ### ### Release/Packaging targets ### @@ -602,7 +615,7 @@ $(eval TMPDIR := $(shell mktemp -d podman_tmp_XXXX)) $(eval SUBDIR := podman-v$(RELEASE_NUMBER)) mkdir -p "$(TMPDIR)/$(SUBDIR)" - $(MAKE) install.bin install.man install.cni \ + $(MAKE) install.bin install.man \ install.systemd "DESTDIR=$(TMPDIR)/$(SUBDIR)" "PREFIX=/usr" tar -czvf $@ --xattrs -C "$(TMPDIR)" "./$(SUBDIR)" -rm -rf "$(TMPDIR)" @@ -655,7 +668,7 @@ /usr/bin/podman info # will catch a broken conmon .PHONY: install -install: .gopathok install.bin install.remote install.man install.cni install.systemd ## Install binaries to system locations +install: .gopathok install.bin install.remote install.man install.systemd ## Install binaries to system locations .PHONY: install.catatonit install.catatonit: @@ -708,11 +721,6 @@ install ${SELINUXOPT} -m 644 completions/fish/podman-remote.fish ${DESTDIR}${FISHINSTALLDIR} # There is no common location for powershell files so do not install them. Users have to source the file from their powershell profile. -.PHONY: install.cni -install.cni: - install ${SELINUXOPT} -d -m 755 ${DESTDIR}${ETCDIR}/cni/net.d/ - install ${SELINUXOPT} -m 644 cni/87-podman-bridge.conflist ${DESTDIR}${ETCDIR}/cni/net.d/87-podman-bridge.conflist - .PHONY: install.docker install.docker: install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR) @@ -740,11 +748,13 @@ install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.timer ${DESTDIR}${USERSYSTEMDDIR}/podman-auto-update.timer install ${SELINUXOPT} -m 644 contrib/systemd/user/podman.socket ${DESTDIR}${USERSYSTEMDDIR}/podman.socket install ${SELINUXOPT} -m 644 contrib/systemd/user/podman.service ${DESTDIR}${USERSYSTEMDDIR}/podman.service + install ${SELINUXOPT} -m 644 contrib/systemd/user/podman-restart.service ${DESTDIR}${USERSYSTEMDDIR}/podman-restart.service # System services install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.service ${DESTDIR}${SYSTEMDDIR}/podman-auto-update.service install ${SELINUXOPT} -m 644 contrib/systemd/auto-update/podman-auto-update.timer ${DESTDIR}${SYSTEMDDIR}/podman-auto-update.timer install ${SELINUXOPT} -m 644 contrib/systemd/system/podman.socket ${DESTDIR}${SYSTEMDDIR}/podman.socket install ${SELINUXOPT} -m 644 contrib/systemd/system/podman.service ${DESTDIR}${SYSTEMDDIR}/podman.service + install ${SELINUXOPT} -m 644 contrib/systemd/system/podman-restart.service ${DESTDIR}${SYSTEMDDIR}/podman-restart.service else install.systemd: endif @@ -832,11 +842,13 @@ build \ test/checkseccomp/checkseccomp \ test/goecho/goecho \ + test/__init__.py \ test/testdata/redis-image \ libpod/container_ffjson.go \ libpod/pod_ffjson.go \ libpod/container_easyjson.go \ libpod/pod_easyjson.go \ .install.goimports \ - docs/build + docs/build \ + venv make -C docs clean diff -Nru libpod-3.2.1+ds1/nix/default-arm64.nix libpod-3.4.4+ds1/nix/default-arm64.nix --- libpod-3.2.1+ds1/nix/default-arm64.nix 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/nix/default-arm64.nix 2021-12-26 00:45:51.000000000 +0000 @@ -25,6 +25,23 @@ -i "$dev"/include/glib-2.0/gobject/gobjectnotifyqueue.c ''; }); + pcsclite = (static pkg.pcsclite).overrideAttrs (x: { + configureFlags = [ + "--enable-confdir=/etc" + "--enable-usbdropdir=/var/lib/pcsc/drivers" + "--disable-libsystemd" + "--disable-libudev" + "--disable-libusb" + ]; + buildInputs = [ pkgs.python3 pkgs.dbus ]; + }); + systemd = (static pkg.systemd).overrideAttrs (x: { + outputs = [ "out" "dev" ]; + mesonFlags = x.mesonFlags ++ [ + "-Dglib=false" + "-Dstatic-libsystemd=true" + ]; + }); }; }; }); @@ -42,18 +59,20 @@ self = with pkgs; buildGoModule rec { name = "podman"; - src = ./..; + src = builtins.filterSource + (path: type: !(type == "directory" && baseNameOf path == "bin")) ./..; vendorSha256 = null; doCheck = false; enableParallelBuilding = true; outputs = [ "out" ]; - nativeBuildInputs = [ bash gitMinimal go-md2man installShellFiles makeWrapper pkg-config which ]; - buildInputs = [ glibc glibc.static gpgme libassuan libgpgerror libseccomp libapparmor libselinux ]; + nativeBuildInputs = [ bash gitMinimal go-md2man pkg-config which ]; + buildInputs = [ glibc glibc.static glib gpgme libassuan libgpgerror libseccomp libapparmor libselinux ]; prePatch = '' export CFLAGS='-static -pthread' export LDFLAGS='-s -w -static-libgcc -static' export EXTRA_LDFLAGS='-s -w -linkmode external -extldflags "-static -lm"' export BUILDTAGS='static netgo osusergo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper seccomp apparmor selinux' + export CGO_ENABLED=1 ''; buildPhase = '' patchShebangs . diff -Nru libpod-3.2.1+ds1/nix/default.nix libpod-3.4.4+ds1/nix/default.nix --- libpod-3.2.1+ds1/nix/default.nix 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/nix/default.nix 2021-12-26 00:45:51.000000000 +0000 @@ -23,6 +23,23 @@ -i "$dev"/include/glib-2.0/gobject/gobjectnotifyqueue.c ''; }); + pcsclite = (static pkg.pcsclite).overrideAttrs (x: { + configureFlags = [ + "--enable-confdir=/etc" + "--enable-usbdropdir=/var/lib/pcsc/drivers" + "--disable-libsystemd" + "--disable-libudev" + "--disable-libusb" + ]; + buildInputs = [ pkgs.python3 pkgs.dbus ]; + }); + systemd = (static pkg.systemd).overrideAttrs (x: { + outputs = [ "out" "dev" ]; + mesonFlags = x.mesonFlags ++ [ + "-Dglib=false" + "-Dstatic-libsystemd=true" + ]; + }); }; }; }); @@ -40,18 +57,20 @@ self = with pkgs; buildGoModule rec { name = "podman"; - src = ./..; + src = builtins.filterSource + (path: type: !(type == "directory" && baseNameOf path == "bin")) ./..; vendorSha256 = null; doCheck = false; enableParallelBuilding = true; outputs = [ "out" ]; - nativeBuildInputs = [ bash gitMinimal go-md2man installShellFiles makeWrapper pkg-config which ]; - buildInputs = [ glibc glibc.static gpgme libassuan libgpgerror libseccomp libapparmor libselinux ]; + nativeBuildInputs = [ bash gitMinimal go-md2man pkg-config which ]; + buildInputs = [ glibc glibc.static glib gpgme libassuan libgpgerror libseccomp libapparmor libselinux ]; prePatch = '' export CFLAGS='-static -pthread' export LDFLAGS='-s -w -static-libgcc -static' export EXTRA_LDFLAGS='-s -w -linkmode external -extldflags "-static -lm"' export BUILDTAGS='static netgo osusergo exclude_graphdriver_btrfs exclude_graphdriver_devicemapper seccomp apparmor selinux' + export CGO_ENABLED=1 ''; buildPhase = '' patchShebangs . diff -Nru libpod-3.2.1+ds1/nix/nixpkgs.json libpod-3.4.4+ds1/nix/nixpkgs.json --- libpod-3.2.1+ds1/nix/nixpkgs.json 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/nix/nixpkgs.json 2021-12-26 00:45:51.000000000 +0000 @@ -1,9 +1,9 @@ { "url": "https://github.com/nixos/nixpkgs", - "rev": "eb7e1ef185f6c990cda5f71fdc4fb02e76ab06d5", - "date": "2021-05-05T23:16:00+02:00", - "path": "/nix/store/a98lkhjlsqh32ic2kkrv5kkik6jy25wh-nixpkgs", - "sha256": "1ibz204c41g7baqga2iaj11yz9l75cfdylkiqjnk5igm81ivivxg", + "rev": "2a96414d7e350160a33ed0978449c9ff5b5a6eb3", + "date": "2021-07-13T18:21:47+02:00", + "path": "/nix/store/2ai9q8ac6vxb2rrngdz82y8jxnk15cvm-nixpkgs", + "sha256": "1dzrfqdjq3yq5jjskiqflzy58l2xx6059gay9p1k07zrlm1wigy5", "fetchSubmodules": false, "deepClone": false, "leaveDotGit": false diff -Nru libpod-3.2.1+ds1/nix/nixpkgs.nix libpod-3.4.4+ds1/nix/nixpkgs.nix --- libpod-3.2.1+ds1/nix/nixpkgs.nix 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/nix/nixpkgs.nix 2021-12-26 00:45:51.000000000 +0000 @@ -5,4 +5,5 @@ url = "${json.url}/archive/${json.rev}.tar.gz"; inherit (json) sha256; }); -in nixpkgs +in +nixpkgs diff -Nru libpod-3.2.1+ds1/OWNERS libpod-3.4.4+ds1/OWNERS --- libpod-3.2.1+ds1/OWNERS 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/OWNERS 2021-12-26 00:45:51.000000000 +0000 @@ -5,6 +5,7 @@ - jwhonce - Luap99 - mheon + - mtrmac - rhatdan - saschagrunert - TomSweeneyRedHat @@ -19,6 +20,7 @@ - jwhonce - Luap99 - mheon + - mtrmac - QiWang19 - rhatdan - saschagrunert diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/auth.go libpod-3.4.4+ds1/pkg/api/handlers/compat/auth.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/auth.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/auth.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,9 +9,10 @@ DockerClient "github.com/containers/image/v5/docker" "github.com/containers/image/v5/types" + "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/registries" docker "github.com/docker/docker/api/types" "github.com/pkg/errors" ) @@ -37,23 +38,31 @@ skipTLS = types.NewOptionalBool(true) } + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + sysCtx := runtime.SystemContext() + sysCtx.DockerInsecureSkipTLSVerify = skipTLS + fmt.Println("Authenticating with existing credentials...") - sysCtx := types.SystemContext{ - AuthFilePath: "", - DockerCertPath: "", - DockerInsecureSkipTLSVerify: skipTLS, - SystemRegistriesConfPath: registries.SystemRegistriesConfPath(), - } registry := stripAddressOfScheme(authConfig.ServerAddress) - if err := DockerClient.CheckAuth(context.Background(), &sysCtx, authConfig.Username, authConfig.Password, registry); err == nil { + if err := DockerClient.CheckAuth(context.Background(), sysCtx, authConfig.Username, authConfig.Password, registry); err == nil { utils.WriteResponse(w, http.StatusOK, entities.AuthReport{ IdentityToken: "", Status: "Login Succeeded", }) } else { - utils.WriteResponse(w, http.StatusBadRequest, entities.AuthReport{ - IdentityToken: "", - Status: "login attempt to " + authConfig.ServerAddress + " failed with status: " + err.Error(), + var msg string + + var unauthErr DockerClient.ErrUnauthorizedForCredentials + if errors.As(err, &unauthErr) { + msg = "401 Unauthorized" + } else { + msg = err.Error() + } + + utils.WriteResponse(w, http.StatusInternalServerError, struct { + Message string `json:"message"` + }{ + Message: "login attempt to " + authConfig.ServerAddress + " failed with status: " + msg, }) } } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/changes.go libpod-3.4.4+ds1/pkg/api/handlers/compat/changes.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/changes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/changes.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,14 +4,40 @@ "net/http" "github.com/containers/podman/v3/libpod" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" + "github.com/gorilla/schema" + "github.com/pkg/errors" ) func Changes(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + + query := struct { + Parent string `schema:"parent"` + DiffType string `schema:"diffType"` + }{} + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } + var diffType define.DiffType + switch query.DiffType { + case "", "all": + diffType = define.DiffAll + case "container": + diffType = define.DiffContainer + case "image": + diffType = define.DiffImage + default: + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Errorf("invalid diffType value %q", query.DiffType)) + return + } id := utils.GetName(r) - changes, err := runtime.GetDiff("", id) + changes, err := runtime.GetDiff(query.Parent, id, diffType) if err != nil { utils.InternalServerError(w, err) return diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_archive.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_archive.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_archive.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_archive.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package compat import ( + "encoding/json" "fmt" "net/http" "os" @@ -8,7 +9,9 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/copy" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -16,8 +19,8 @@ ) func Archive(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) switch r.Method { case http.MethodPut: @@ -92,11 +95,14 @@ func handlePut(w http.ResponseWriter, r *http.Request, decoder *schema.Decoder, runtime *libpod.Runtime) { query := struct { - Path string `schema:"path"` + Path string `schema:"path"` + Chown bool `schema:"copyUIDGID"` + Rename string `schema:"rename"` // TODO handle params below NoOverwriteDirNonDir bool `schema:"noOverwriteDirNonDir"` - CopyUIDGID bool `schema:"copyUIDGID"` - }{} + }{ + Chown: utils.IsLibpodRequest(r), // backward compatibility + } err := decoder.Decode(&query, r.URL.Query()) if err != nil { @@ -104,10 +110,19 @@ return } + var rename map[string]string + if query.Rename != "" { + if err := json.Unmarshal([]byte(query.Rename), &rename); err != nil { + utils.Error(w, "Bad Request.", http.StatusBadRequest, errors.Wrap(err, "couldn't decode the query")) + return + } + } + containerName := utils.GetName(r) containerEngine := abi.ContainerEngine{Libpod: runtime} - copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body) + copyOptions := entities.CopyOptions{Chown: query.Chown, Rename: rename} + copyFunc, err := containerEngine.ContainerCopyFromArchive(r.Context(), containerName, query.Path, r.Body, copyOptions) if errors.Cause(err) == define.ErrNoSuchCtr || os.IsNotExist(err) { // 404 is returned for an absent container and path. The // clients must deal with it accordingly. @@ -118,8 +133,10 @@ return } - w.WriteHeader(http.StatusOK) if err := copyFunc(); err != nil { logrus.Error(err.Error()) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) + return } + w.WriteHeader(http.StatusOK) } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_attach.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_attach.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_attach.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_attach.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,14 +7,15 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/api/server/idle" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) func AttachContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { DetachKeys string `schema:"detachKeys"` @@ -104,7 +105,7 @@ if <-hijackChan { // If connection was Hijacked, we have to signal it's being closed - t := r.Context().Value("idletracker").(*idle.Tracker) + t := r.Context().Value(api.IdleTrackerKey).(*idle.Tracker) defer t.Close() if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_create.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_create.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_create.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,17 +8,19 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/storage" "github.com/gorilla/schema" "github.com/pkg/errors" ) func CreateContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Name string `schema:"name"` }{ @@ -53,7 +55,7 @@ newImage, resolvedName, err := runtime.LibimageRuntime().LookupImage(body.Config.Image, nil) if err != nil { if errors.Cause(err) == storage.ErrImageUnknown { - utils.Error(w, "No such image", http.StatusNotFound, err) + utils.Error(w, "No such image", http.StatusNotFound, errors.Wrap(err, "No such image")) return } @@ -62,7 +64,7 @@ } // Take body structure and convert to cliopts - cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(body, rtc.Engine.CgroupManager) + cliOpts, args, err := common.ContainerCreateToContainerCLIOpts(body, rtc) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "make cli opts()")) return @@ -71,20 +73,21 @@ imgNameOrID := newImage.ID() // if the img had multi names with the same sha256 ID, should use the InputName, not the ID if len(newImage.Names()) > 1 { - imageRef, err := utils.ParseDockerReference(resolvedName) - if err != nil { + if err := utils.IsRegistryReference(resolvedName); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } // maybe the InputName has no tag, so use full name to display - imgNameOrID = imageRef.DockerReference().String() + imgNameOrID = resolvedName } sg := specgen.NewSpecGenerator(imgNameOrID, cliOpts.RootFS) - if err := common.FillOutSpecGen(sg, cliOpts, args); err != nil { + if err := specgenutil.FillOutSpecGen(sg, cliOpts, args); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "fill out specgen")) return } + // moby always create the working directory + sg.CreateWorkingDir = true ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.ContainerCreate(r.Context(), sg) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_export.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_export.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_export.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_export.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,11 +7,12 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/pkg/errors" ) func ExportContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) con, err := runtime.LookupContainer(name) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -14,6 +14,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/filters" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -31,7 +32,7 @@ ) func RemoveContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Force bool `schema:"force"` Ignore bool `schema:"ignore"` @@ -63,7 +64,7 @@ options.Volumes = query.DockerVolumes } - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} @@ -92,8 +93,8 @@ } func ListContainers(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { All bool `schema:"all"` Limit int `schema:"limit"` @@ -103,8 +104,12 @@ } filterMap, err := util.PrepareFilters(r) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to decode filter parameters for %s", r.URL.String())) + return + } - if dErr := decoder.Decode(&query, r.URL.Query()); dErr != nil || err != nil { + if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } @@ -168,8 +173,8 @@ } func GetContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Size bool `schema:"size"` }{ @@ -197,8 +202,8 @@ func KillContainer(w http.ResponseWriter, r *http.Request) { // /{version}/containers/(name)/kill - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Signal string `schema:"signal"` }{ @@ -403,6 +408,26 @@ state.Status = define.ContainerStateCreated.String() } + if l.HasHealthCheck() && state.Status != "created" { + state.Health = &types.Health{ + Status: inspect.State.Healthcheck.Status, + FailingStreak: inspect.State.Healthcheck.FailingStreak, + } + + log := inspect.State.Healthcheck.Log + + for _, item := range log { + res := &types.HealthcheckResult{} + s, _ := time.Parse(time.RFC3339Nano, item.Start) + e, _ := time.Parse(time.RFC3339Nano, item.End) + res.Start = s + res.End = e + res.ExitCode = item.ExitCode + res.Output = item.Output + state.Health.Log = append(state.Health.Log, res) + } + } + formatCapabilities(inspect.HostConfig.CapDrop) formatCapabilities(inspect.HostConfig.CapAdd) @@ -477,6 +502,17 @@ exposedPorts[exposedPort] = struct{}{} } + var healthcheck *container.HealthConfig + if inspect.Config.Healthcheck != nil { + healthcheck = &container.HealthConfig{ + Test: inspect.Config.Healthcheck.Test, + Interval: inspect.Config.Healthcheck.Interval, + Timeout: inspect.Config.Healthcheck.Timeout, + StartPeriod: inspect.Config.Healthcheck.StartPeriod, + Retries: inspect.Config.Healthcheck.Retries, + } + } + config := container.Config{ Hostname: l.Hostname(), Domainname: inspect.Config.DomainName, @@ -490,7 +526,7 @@ StdinOnce: inspect.Config.StdinOnce, Env: inspect.Config.Env, Cmd: l.Command(), - Healthcheck: nil, + Healthcheck: healthcheck, ArgsEscaped: false, Image: imageName, Volumes: nil, @@ -553,8 +589,8 @@ } func RenameContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) name := utils.GetName(r) query := struct { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_logs.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_logs.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_logs.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_logs.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,6 +13,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/logs" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -20,8 +21,8 @@ ) func LogsFromContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Follow bool `schema:"follow"` @@ -63,7 +64,7 @@ var since time.Time if _, found := r.URL.Query()["since"]; found { - since, err = util.ParseInputTime(query.Since) + since, err = util.ParseInputTime(query.Since, true) if err != nil { utils.BadRequest(w, "since", query.Since, err) return @@ -72,11 +73,12 @@ var until time.Time if _, found := r.URL.Query()["until"]; found { - // FIXME: until != since but the logs backend does not yet support until. - since, err = util.ParseInputTime(query.Until) - if err != nil { - utils.BadRequest(w, "until", query.Until, err) - return + if query.Until != "0" { + until, err = util.ParseInputTime(query.Until, false) + if err != nil { + utils.BadRequest(w, "until", query.Until, err) + return + } } } @@ -84,6 +86,7 @@ Details: true, Follow: query.Follow, Since: since, + Until: until, Tail: tail, Timestamps: query.Timestamps, } @@ -119,7 +122,7 @@ for line := range logChannel { if _, found := r.URL.Query()["until"]; found { - if line.Time.After(until) { + if line.Time.After(until) && !until.IsZero() { break } } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_pause.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_pause.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_pause.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_pause.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,10 +5,11 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" ) func PauseContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // /{version}/containers/(name)/pause name := utils.GetName(r) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_prune.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_prune.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/domain/filters" "github.com/containers/podman/v3/pkg/util" @@ -14,7 +15,7 @@ ) func PruneContainers(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filtersMap, err := util.PrepareFilters(r) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) @@ -65,7 +66,7 @@ } func PruneContainersHelper(r *http.Request, filterFuncs []libpod.ContainerFilter) ([]*reports.PruneReport, error) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) report, err := runtime.PruneContainers(filterFuncs) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_restart.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_restart.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_restart.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_restart.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/gorilla/schema" @@ -13,8 +14,8 @@ ) func RestartContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_start.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_start.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_start.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_start.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ import ( "net/http" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/sirupsen/logrus" "github.com/containers/podman/v3/libpod" @@ -12,7 +13,7 @@ ) func StartContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { DetachKeys string `schema:"detachKeys"` }{ @@ -26,7 +27,7 @@ // TODO - start does not support adding detach keys logrus.Info("the detach keys parameter is not supported on start container") } - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) con, err := runtime.LookupContainer(name) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_stats.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_stats.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_stats.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_stats.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/cgroups" docker "github.com/docker/docker/api/types" "github.com/gorilla/schema" @@ -18,11 +19,12 @@ const DefaultStatsPeriod = 5 * time.Second func StatsContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Stream bool `schema:"stream"` + Stream bool `schema:"stream"` + OneShot bool `schema:"one-shot"` // added schema for one shot }{ Stream: true, } @@ -30,6 +32,10 @@ utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } + if query.Stream && query.OneShot { // mismatch. one-shot can only be passed with stream=false + utils.Error(w, "invalid combination of stream and one-shot", http.StatusBadRequest, define.ErrInvalidArg) + return + } name := utils.GetName(r) ctnr, err := runtime.LookupContainer(name) @@ -56,6 +62,16 @@ return } + coder := json.NewEncoder(w) + // Write header and content type. + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + if flusher, ok := w.(http.Flusher); ok { + flusher.Flush() + } + + // Setup JSON encoder for streaming. + coder.SetEscapeHTML(true) var preRead time.Time var preCPUStats CPUStats if query.Stream { @@ -75,17 +91,6 @@ } } - // Write header and content type. - w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") - if flusher, ok := w.(http.Flusher); ok { - flusher.Flush() - } - - // Setup JSON encoder for streaming. - coder := json.NewEncoder(w) - coder.SetEscapeHTML(true) - streamLabel: // A label to flatten the scope select { case <-r.Context().Done(): @@ -93,7 +98,7 @@ default: // Container stats - stats, err := ctnr.GetContainerStats(stats) + stats, err = ctnr.GetContainerStats(stats) if err != nil { logrus.Errorf("Unable to get container stats: %v", err) return @@ -199,7 +204,7 @@ flusher.Flush() } - if !query.Stream { + if !query.Stream || query.OneShot { return } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_stop.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_stop.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_stop.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_stop.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/gorilla/schema" @@ -13,8 +14,8 @@ ) func StopContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_top.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_top.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_top.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_top.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,13 +7,14 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/gorilla/schema" "github.com/pkg/errors" ) func TopContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) defaultValue := "-ef" if utils.IsLibpodRequest(r) { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_unpause.go libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_unpause.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/containers_unpause.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/containers_unpause.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,10 +5,11 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" ) func UnpauseContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // /{version}/containers/(name)/unpause name := utils.GetName(r) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/events.go libpod-3.4.4+ds1/pkg/api/handlers/compat/events.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/events.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/events.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/util" "github.com/gorilla/schema" @@ -14,13 +15,12 @@ "github.com/sirupsen/logrus" ) -// NOTE: this endpoint serves both the docker-compatible one and the new libpod -// one. +// GetEvents endpoint serves both the docker-compatible one and the new libpod one func GetEvents(w http.ResponseWriter, r *http.Request) { var ( fromStart bool - decoder = r.Context().Value("decoder").(*schema.Decoder) - runtime = r.Context().Value("runtime").(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) json = jsoniter.ConfigCompatibleWithStandardLibrary // FIXME: this should happen on the package level ) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/exec.go libpod-3.4.4+ds1/pkg/api/handlers/compat/exec.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/exec.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/exec.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" "github.com/containers/podman/v3/pkg/api/server/idle" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/gorilla/mux" "github.com/pkg/errors" @@ -19,7 +20,7 @@ // ExecCreateHandler creates an exec session for a given container. func ExecCreateHandler(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) input := new(handlers.ExecCreateConfig) if err := json.NewDecoder(r.Body).Decode(&input); err != nil { @@ -101,7 +102,7 @@ // ExecInspectHandler inspects a given exec session. func ExecInspectHandler(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) sessionID := mux.Vars(r)["id"] sessionCtr, err := runtime.GetExecSessionContainer(sessionID) @@ -129,7 +130,7 @@ // ExecStartHandler runs a given exec session. func ExecStartHandler(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) sessionID := mux.Vars(r)["id"] @@ -178,12 +179,20 @@ logrus.Error(errors.Wrapf(e, "error attaching to container %s exec session %s", sessionCtr.ID(), sessionID)) } + var size *define.TerminalSize + if bodyParams.Tty && (bodyParams.Height > 0 || bodyParams.Width > 0) { + size = &define.TerminalSize{ + Height: bodyParams.Height, + Width: bodyParams.Width, + } + } + hijackChan := make(chan bool, 1) - err = sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan) + err = sessionCtr.ExecHTTPStartAndAttach(sessionID, r, w, nil, nil, nil, hijackChan, size) if <-hijackChan { // If connection was Hijacked, we have to signal it's being closed - t := r.Context().Value("idletracker").(*idle.Tracker) + t := r.Context().Value(api.IdleTrackerKey).(*idle.Tracker) defer t.Close() if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_build.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_build.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_build.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_build.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,9 +20,11 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/channel" "github.com/containers/storage/pkg/archive" + "github.com/docker/docker/pkg/jsonmessage" "github.com/gorilla/schema" "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -34,13 +36,16 @@ contentType := hdr[0] switch contentType { case "application/tar": - logrus.Warnf("tar file content type is %s, should use \"application/x-tar\" content type", contentType) + logrus.Infof("tar file content type is %s, should use \"application/x-tar\" content type", contentType) case "application/x-tar": break default: - utils.BadRequest(w, "Content-Type", hdr[0], - fmt.Errorf("Content-Type: %s is not supported. Should be \"application/x-tar\"", hdr[0])) - return + if utils.IsLibpodRequest(r) { + utils.BadRequest(w, "Content-Type", hdr[0], + fmt.Errorf("Content-Type: %s is not supported. Should be \"application/x-tar\"", hdr[0])) + return + } + logrus.Infof("tar file content type is %s, should use \"application/x-tar\" content type", contentType) } } @@ -73,10 +78,12 @@ CacheFrom string `schema:"cachefrom"` Compression uint64 `schema:"compression"` ConfigureNetwork string `schema:"networkmode"` - CpuPeriod uint64 `schema:"cpuperiod"` // nolint - CpuQuota int64 `schema:"cpuquota"` // nolint - CpuSetCpus string `schema:"cpusetcpus"` // nolint - CpuShares uint64 `schema:"cpushares"` // nolint + CpuPeriod uint64 `schema:"cpuperiod"` // nolint + CpuQuota int64 `schema:"cpuquota"` // nolint + CpuSetCpus string `schema:"cpusetcpus"` // nolint + CpuSetMems string `schema:"cpusetmems"` // nolint + CpuShares uint64 `schema:"cpushares"` // nolint + CgroupParent string `schema:"cgroupparent"` // nolint DNSOptions string `schema:"dnsoptions"` DNSSearch string `schema:"dnssearch"` DNSServers string `schema:"dnsservers"` @@ -100,12 +107,13 @@ NamespaceOptions string `schema:"nsoptions"` NoCache bool `schema:"nocache"` OutputFormat string `schema:"outputformat"` - Platform string `schema:"platform"` + Platform []string `schema:"platform"` Pull bool `schema:"pull"` PullPolicy string `schema:"pullpolicy"` Quiet bool `schema:"q"` Registry string `schema:"registry"` Rm bool `schema:"rm"` + RusageLogFile string `schema:"rusagelogfile"` Seccomp string `schema:"seccomp"` SecurityOpt string `schema:"securityopt"` ShmSize int `schema:"shmsize"` @@ -119,15 +127,23 @@ Registry: "docker.io", Rm: true, ShmSize: 64 * 1024 * 1024, - Tag: []string{}, } - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } + // if layers field not set assume its not from a valid podman-client + // could be a docker client, set `layers=true` since that is the default + // expected behviour + if !utils.IsLibpodRequest(r) { + if _, found := r.URL.Query()["layers"]; !found { + query.Layers = true + } + } + // convert addcaps formats var addCaps = []string{} if _, found := r.URL.Query()["addcaps"]; found { @@ -145,22 +161,19 @@ var m = []string{} if err := json.Unmarshal([]byte(query.Dockerfile), &m); err != nil { // it's not json, assume just a string - m = append(m, query.Dockerfile) + m = []string{filepath.Join(contextDirectory, query.Dockerfile)} } containerFiles = m } else { - containerFiles = []string{"Dockerfile"} + containerFiles = []string{filepath.Join(contextDirectory, "Dockerfile")} if utils.IsLibpodRequest(r) { - containerFiles = []string{"Containerfile"} - if _, err = os.Stat(filepath.Join(contextDirectory, "Containerfile")); err != nil { - if _, err1 := os.Stat(filepath.Join(contextDirectory, "Dockerfile")); err1 == nil { - containerFiles = []string{"Dockerfile"} - } else { + containerFiles = []string{filepath.Join(contextDirectory, "Containerfile")} + if _, err = os.Stat(containerFiles[0]); err != nil { + containerFiles = []string{filepath.Join(contextDirectory, "Dockerfile")} + if _, err1 := os.Stat(containerFiles[0]); err1 != nil { utils.BadRequest(w, "dockerfile", query.Dockerfile, err) } } - } else { - containerFiles = []string{"Dockerfile"} } } @@ -189,8 +202,8 @@ var devices = []string{} if _, found := r.URL.Query()["devices"]; found { var m = []string{} - if err := json.Unmarshal([]byte(query.DropCapabilities), &m); err != nil { - utils.BadRequest(w, "devices", query.DropCapabilities, err) + if err := json.Unmarshal([]byte(query.Devices), &m); err != nil { + utils.BadRequest(w, "devices", query.Devices, err) return } devices = m @@ -392,19 +405,19 @@ defer auth.RemoveAuthfile(authfile) // Channels all mux'ed in select{} below to follow API build protocol - stdout := channel.NewWriter(make(chan []byte, 1)) + stdout := channel.NewWriter(make(chan []byte)) defer stdout.Close() - auxout := channel.NewWriter(make(chan []byte, 1)) + auxout := channel.NewWriter(make(chan []byte)) defer auxout.Close() - stderr := channel.NewWriter(make(chan []byte, 1)) + stderr := channel.NewWriter(make(chan []byte)) defer stderr.Close() - reporter := channel.NewWriter(make(chan []byte, 1)) + reporter := channel.NewWriter(make(chan []byte)) defer reporter.Close() - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) rtc, err := runtime.GetConfig() if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) @@ -421,7 +434,9 @@ CPUPeriod: query.CpuPeriod, CPUQuota: query.CpuQuota, CPUSetCPUs: query.CpuSetCpus, + CPUSetMems: query.CpuSetMems, CPUShares: query.CpuShares, + CgroupParent: query.CgroupParent, DNSOptions: dnsoptions, DNSSearch: dnssearch, DNSServers: dnsservers, @@ -463,6 +478,7 @@ Registry: registry, RemoveIntermediateCtrs: query.Rm, ReportWriter: reporter, + RusageLogFile: query.RusageLogFile, Squash: query.Squash, Target: query.Target, SystemContext: &types.SystemContext{ @@ -471,16 +487,17 @@ }, } - if len(query.Platform) > 0 { - variant := "" - buildOptions.OS, buildOptions.Architecture, variant, err = parse.Platform(query.Platform) + for _, platformSpec := range query.Platform { + os, arch, variant, err := parse.Platform(platformSpec) if err != nil { - utils.BadRequest(w, "platform", query.Platform, err) + utils.BadRequest(w, "platform", platformSpec, err) return } - buildOptions.SystemContext.OSChoice = buildOptions.OS - buildOptions.SystemContext.ArchitectureChoice = buildOptions.Architecture - buildOptions.SystemContext.VariantChoice = variant + buildOptions.Platforms = append(buildOptions.Platforms, struct{ OS, Arch, Variant string }{ + OS: os, + Arch: arch, + Variant: variant, + }) } if _, found := r.URL.Query()["timestamp"]; found { ts := time.Unix(query.Timestamp, 0) @@ -511,7 +528,7 @@ // Send headers and prime client for stream to come w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") flush() body := w.(io.Writer) @@ -527,11 +544,13 @@ enc := json.NewEncoder(body) enc.SetEscapeHTML(true) -loop: + for { m := struct { - Stream string `json:"stream,omitempty"` - Error string `json:"error,omitempty"` + Stream string `json:"stream,omitempty"` + Error *jsonmessage.JSONError `json:"errorDetail,omitempty"` + // NOTE: `error` is being deprecated check https://github.com/moby/moby/blob/master/pkg/jsonmessage/jsonmessage.go#L148 + ErrorMessage string `json:"error,omitempty"` // deprecate this slowly }{} select { @@ -541,26 +560,29 @@ stderr.Write([]byte(err.Error())) } flush() - case e := <-auxout.Chan(): + case e := <-reporter.Chan(): m.Stream = string(e) if err := enc.Encode(m); err != nil { stderr.Write([]byte(err.Error())) } flush() - case e := <-reporter.Chan(): + case e := <-auxout.Chan(): m.Stream = string(e) if err := enc.Encode(m); err != nil { stderr.Write([]byte(err.Error())) } flush() case e := <-stderr.Chan(): - m.Error = string(e) + m.ErrorMessage = string(e) + m.Error = &jsonmessage.JSONError{ + Message: m.ErrorMessage, + } if err := enc.Encode(m); err != nil { logrus.Warnf("Failed to json encode error %v", err) } flush() + return case <-runCtx.Done(): - flush() if success { if !utils.IsLibpodRequest(r) { m.Stream = fmt.Sprintf("Successfully built %12.12s\n", imageID) @@ -577,7 +599,8 @@ } } } - break loop + flush() + return case <-r.Context().Done(): cancel() logrus.Infof("Client disconnect reported for build %q / %q.", registry, query.Dockerfile) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,6 @@ package compat import ( - "context" "encoding/json" "fmt" "io/ioutil" @@ -13,12 +12,13 @@ "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" + "github.com/containers/image/v5/pkg/shortnames" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" - "github.com/containers/podman/v3/pkg/channel" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/storage" @@ -46,7 +46,7 @@ func ExportImage(w http.ResponseWriter, r *http.Request) { // 200 ok // 500 server - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) tmpfile, err := ioutil.TempFile("", "api.tar") if err != nil { @@ -90,8 +90,8 @@ var ( destImage string ) - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Author string `schema:"author"` @@ -163,12 +163,15 @@ // 200 no error // 404 repo does not exist or no read access // 500 internal - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { - FromSrc string `schema:"fromSrc"` - Changes []string `schema:"changes"` + Changes []string `schema:"changes"` + FromSrc string `schema:"fromSrc"` + Message string `schema:"message"` + Platform string `schema:"platform"` + Repo string `shchema:"repo"` }{ // This is where you can override the golang default value for one of fields } @@ -185,14 +188,27 @@ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to create tempfile")) return } + source = f.Name() if err := SaveFromBody(f, r); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to write temporary file")) } } + platformSpecs := strings.Split(query.Platform, "/") + opts := entities.ImageImportOptions{ + Source: source, + Changes: query.Changes, + Message: query.Message, + Reference: query.Repo, + OS: platformSpecs[0], + } + if len(platformSpecs) > 1 { + opts.Architecture = platformSpecs[1] + } + imageEngine := abi.ImageEngine{Libpod: runtime} - report, err := imageEngine.Import(r.Context(), entities.ImageImportOptions{Source: source, Changes: query.Changes}) + report, err := imageEngine.Import(r.Context(), opts) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to import tarball")) return @@ -210,20 +226,25 @@ }) } +type pullResult struct { + images []*libimage.Image + err error +} + func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { // 200 no error // 404 repo does not exist or no read access // 500 internal - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { FromImage string `schema:"fromImage"` Tag string `schema:"tag"` + Platform string `schema:"platform"` }{ // This is where you can override the golang default value for one of fields } - if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return @@ -231,6 +252,14 @@ fromImage := mergeNameAndTagOrDigest(query.FromImage, query.Tag) + // without this early check this function would return 200 but reported error via body stream soon after + // it's better to let caller know early via HTTP status code that request cannot be processed + _, err := shortnames.Resolve(runtime.SystemContext(), fromImage) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrap(err, "failed to resolve image name")) + return + } + authConf, authfile, key, err := auth.GetCredentials(r) if err != nil { utils.Error(w, "failed to retrieve repository credentials", http.StatusBadRequest, errors.Wrapf(err, "failed to parse %q header for %s", key, r.URL.String())) @@ -247,26 +276,23 @@ } pullOptions.Writer = os.Stderr // allows for debugging on the server - stderr := channel.NewWriter(make(chan []byte)) - defer stderr.Close() + // Handle the platform. + platformSpecs := strings.Split(query.Platform, "/") + pullOptions.OS = platformSpecs[0] // may be empty + if len(platformSpecs) > 1 { + pullOptions.Architecture = platformSpecs[1] + if len(platformSpecs) > 2 { + pullOptions.Variant = platformSpecs[2] + } + } progress := make(chan types.ProgressProperties) pullOptions.Progress = progress - var img string - runCtx, cancel := context.WithCancel(context.Background()) + pullResChan := make(chan pullResult) go func() { - defer cancel() - pulledImages, err := runtime.LibimageRuntime().Pull(runCtx, fromImage, config.PullPolicyAlways, pullOptions) - if err != nil { - stderr.Write([]byte(err.Error() + "\n")) - } else { - if len(pulledImages) == 0 { - utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.New("internal error: no images pulled")) - return - } - img = pulledImages[0].ID() - } + pulledImages, err := runtime.LibimageRuntime().Pull(r.Context(), fromImage, config.PullPolicyAlways, pullOptions) + pullResChan <- pullResult{images: pulledImages, err: err} }() flush := func() { @@ -276,12 +302,11 @@ } w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") flush() enc := json.NewEncoder(w) enc.SetEscapeHTML(true) - var failed bool loop: // break out of for/select infinite loop for { @@ -295,7 +320,6 @@ Error string `json:"error,omitempty"` Id string `json:"id,omitempty"` // nolint } - select { case e := <-progress: switch e.Event { @@ -312,32 +336,31 @@ } report.Id = e.Artifact.Digest.Encoded()[0:12] if err := enc.Encode(report); err != nil { - stderr.Write([]byte(err.Error())) - } - flush() - case e := <-stderr.Chan(): - failed = true - report.Error = string(e) - if err := enc.Encode(report); err != nil { logrus.Warnf("Failed to json encode error %q", err.Error()) } flush() - case <-runCtx.Done(): - if !failed { - if utils.IsLibpodRequest(r) { - report.Status = "Pull complete" + case pullRes := <-pullResChan: + err := pullRes.err + pulledImages := pullRes.images + if err != nil { + report.Error = err.Error() + } else { + if len(pulledImages) > 0 { + img := pulledImages[0].ID() + if utils.IsLibpodRequest(r) { + report.Status = "Pull complete" + } else { + report.Status = "Download complete" + } + report.Id = img[0:12] } else { - report.Status = "Download complete" - } - report.Id = img[0:12] - if err := enc.Encode(report); err != nil { - logrus.Warnf("Failed to json encode error %q", err.Error()) + report.Error = "internal error: no images pulled" } - flush() } - break loop // break out of for/select infinite loop - case <-r.Context().Done(): - // Client has closed connection + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to json encode error %q", err.Error()) + } + flush() break loop // break out of for/select infinite loop } } @@ -370,14 +393,20 @@ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images")) return } - var summaries = make([]*entities.ImageSummary, len(images)) - for j, img := range images { + + summaries := make([]*entities.ImageSummary, 0, len(images)) + for _, img := range images { + // If the image is a manifest list, extract as much as we can. + if isML, _ := img.IsManifestList(r.Context()); isML { + continue + } + is, err := handlers.ImageToImageSummary(img) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed transform image summaries")) return } - summaries[j] = is + summaries = append(summaries, is) } utils.WriteResponse(w, http.StatusOK, summaries) } @@ -385,8 +414,8 @@ func LoadImages(w http.ResponseWriter, r *http.Request) { // TODO this is basically wrong // TODO ... improve these ^ messages to something useful - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Changes map[string]string `json:"changes"` // Ignored @@ -443,8 +472,8 @@ func ExportImages(w http.ResponseWriter, r *http.Request) { // 200 OK // 500 Error - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Names []string `schema:"names"` diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_history.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_history.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_history.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_history.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,19 +3,18 @@ import ( "net/http" - "github.com/containers/common/libimage" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/pkg/errors" ) func HistoryImage(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) - lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} - newImage, _, err := runtime.LibimageRuntime().LookupImage(name, lookupOptions) + newImage, _, err := runtime.LibimageRuntime().LookupImage(name, nil) if err != nil { utils.Error(w, "Something went wrong.", http.StatusNotFound, errors.Wrapf(err, "failed to find image %s", name)) return diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_prune.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_prune.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_prune.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_prune.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/util" @@ -19,7 +20,7 @@ var ( filters []string ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_push.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_push.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_push.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_push.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -22,8 +23,8 @@ // PushImage is the handler for the compat http endpoint for pushing images. func PushImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) digestFile, err := ioutil.TempFile("", "digest.txt") if err != nil { @@ -105,7 +106,7 @@ } w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") flush() var report jsonmessage.JSONMessage @@ -151,7 +152,7 @@ case err := <-pushErrChan: if err != nil { var msg string - if errors.Cause(err) != storage.ErrImageUnknown { + if errors.Is(err, storage.ErrImageUnknown) { msg = "An image does not exist locally with the tag: " + imageName } else { msg = err.Error() diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_remove.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_remove.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_remove.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_remove.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,6 +5,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/storage" @@ -13,8 +14,8 @@ ) func RemoveImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Force bool `schema:"force"` diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_search.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_search.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_search.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_search.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -16,8 +17,8 @@ ) func SearchImages(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Term string `json:"term"` Limit int `json:"limit"` diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/images_tag.go libpod-3.4.4+ds1/pkg/api/handlers/compat/images_tag.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/images_tag.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/images_tag.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,16 +7,18 @@ "github.com/containers/common/libimage" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/pkg/errors" ) func TagImage(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // /v1.xx/images/(name)/tag name := utils.GetName(r) - lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} + // Allow tagging manifest list instead of resolving instances from manifest + lookupOptions := &libimage.LookupImageOptions{ManifestList: true} newImage, _, err := runtime.LibimageRuntime().LookupImage(name, lookupOptions) if err != nil { utils.ImageNotFound(w, name, errors.Wrapf(err, "failed to find image %s", name)) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/info.go libpod-3.4.4+ds1/pkg/api/handlers/compat/info.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/info.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/info.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,6 +15,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/rootless" docker "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/registry" @@ -27,7 +28,7 @@ func GetInfo(w http.ResponseWriter, r *http.Request) { // 200 ok // 500 internal - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) infoData, err := runtime.Info() if err != nil { @@ -102,14 +103,18 @@ OomKillDisable: sysInfo.OomKillDisable, OperatingSystem: infoData.Host.Distribution.Distribution, PidsLimit: sysInfo.PidsLimit, - Plugins: docker.PluginsInfo{}, - ProductLicense: "Apache-2.0", - RegistryConfig: new(registry.ServiceConfig), - RuncCommit: docker.Commit{}, - Runtimes: getRuntimes(configInfo), - SecurityOptions: getSecOpts(sysInfo), - ServerVersion: versionInfo.Version, - SwapLimit: sysInfo.SwapLimit, + Plugins: docker.PluginsInfo{ + Volume: infoData.Plugins.Volume, + Network: infoData.Plugins.Network, + Log: infoData.Plugins.Log, + }, + ProductLicense: "Apache-2.0", + RegistryConfig: new(registry.ServiceConfig), + RuncCommit: docker.Commit{}, + Runtimes: getRuntimes(configInfo), + SecurityOptions: getSecOpts(sysInfo), + ServerVersion: versionInfo.Version, + SwapLimit: sysInfo.SwapLimit, Swarm: swarm.Info{ LocalNodeState: swarm.LocalNodeStateInactive, }, diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/networks.go libpod-3.4.4+ds1/pkg/api/handlers/compat/networks.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/networks.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/networks.go 2021-12-26 00:45:51.000000000 +0000 @@ -14,33 +14,46 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" networkid "github.com/containers/podman/v3/pkg/network" "github.com/containers/podman/v3/pkg/util" "github.com/docker/docker/api/types" + dockerNetwork "github.com/docker/docker/api/types/network" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +type pluginInterface struct { + PluginType string `json:"type"` + IPAM network.IPAMConfig `json:"ipam"` + IsGW bool `json:"isGateway"` +} + func InspectNetwork(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + + // scope is only used to see if the user passes any illegal value, verbose is not used but implemented + // for compatibility purposes only. + query := struct { + scope string `schema:"scope"` + verbose bool `schema:"verbose"` + }{ + scope: "local", + } + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + if err := decoder.Decode(&query, r.URL.Query()); err != nil { + utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) + return + } - // FYI scope and version are currently unused but are described by the API - // Leaving this for if/when we have to enable these - // query := struct { - // scope string - // verbose bool - // }{ - // // override any golang type defaults - // } - // decoder := r.Context().Value("decoder").(*schema.Decoder) - // if err := decoder.Decode(&query, r.URL.Query()); err != nil { - // utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) - // return - // } + if query.scope != "local" { + utils.Error(w, "Invalid scope value. Can only be local.", http.StatusBadRequest, define.ErrInvalidArg) + return + } config, err := runtime.GetConfig() if err != nil { utils.InternalServerError(w, err) @@ -98,12 +111,12 @@ } } - // No Bridge plugin means we bail - bridge, err := genericPluginsToBridge(conf.Plugins, network.DefaultNetworkDriver) + plugin, err := getPlugin(conf.Plugins) if err != nil { return nil, err } - for _, outer := range bridge.IPAM.Ranges { + + for _, outer := range plugin.IPAM.Ranges { for _, n := range outer { ipamConfig := dockerNetwork.IPAMConfig{ Subnet: n.Subnet, @@ -135,19 +148,26 @@ labels = map[string]string{} } + isInternal := false + dockerDriver := plugin.PluginType + if plugin.PluginType == network.DefaultNetworkDriver { + isInternal = !plugin.IsGW + dockerDriver = "default" + } + report := types.NetworkResource{ Name: conf.Name, ID: networkid.GetNetworkID(conf.Name), Created: time.Unix(int64(stat.Ctim.Sec), int64(stat.Ctim.Nsec)), // nolint: unconvert Scope: "local", - Driver: network.DefaultNetworkDriver, + Driver: plugin.PluginType, EnableIPv6: false, IPAM: dockerNetwork.IPAM{ - Driver: "default", + Driver: dockerDriver, Options: map[string]string{}, Config: ipamConfigs, }, - Internal: !bridge.IsGW, + Internal: isInternal, Attachable: false, Ingress: false, ConfigFrom: dockerNetwork.ConfigReference{}, @@ -161,27 +181,23 @@ return &report, nil } -func genericPluginsToBridge(plugins []*libcni.NetworkConfig, pluginType string) (network.HostLocalBridge, error) { - var bridge network.HostLocalBridge - generic, err := findPluginByName(plugins, pluginType) - if err != nil { - return bridge, err - } - err = json.Unmarshal(generic, &bridge) - return bridge, err -} +func getPlugin(plugins []*libcni.NetworkConfig) (pluginInterface, error) { + var plugin pluginInterface -func findPluginByName(plugins []*libcni.NetworkConfig, pluginType string) ([]byte, error) { for _, p := range plugins { - if pluginType == p.Network.Type { - return p.Bytes, nil + for _, pluginType := range network.SupportedNetworkDrivers { + if pluginType == p.Network.Type { + err := json.Unmarshal(p.Bytes, &plugin) + return plugin, err + } } } - return nil, errors.New("unable to find bridge plugin") + + return plugin, errors.New("unable to find supported plugin") } func ListNetworks(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) @@ -220,7 +236,7 @@ name string networkCreate types.NetworkCreateRequest ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) if err := json.NewDecoder(r.Body).Decode(&networkCreate); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return @@ -290,7 +306,7 @@ } func RemoveNetwork(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ic := abi.ContainerEngine{Libpod: runtime} query := struct { @@ -299,7 +315,7 @@ // This is where you can override the golang default value for one of fields } - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return @@ -334,7 +350,7 @@ // Connect adds a container to a network func Connect(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var ( aliases []string @@ -368,7 +384,7 @@ // Disconnect removes a container from a network func Disconnect(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var netDisconnect types.NetworkDisconnect if err := json.NewDecoder(r.Body).Decode(&netDisconnect); err != nil { @@ -395,7 +411,7 @@ // Prune removes unused networks func Prune(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/ping.go libpod-3.4.4+ds1/pkg/api/handlers/compat/ping.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/ping.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/ping.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,7 +13,7 @@ // Clients will use the Header availability to test which backend engine is in use. // Note: Additionally handler supports GET and HEAD methods func Ping(w http.ResponseWriter, r *http.Request) { - // Note API-Version and Libpod-API-Version are set in handler_api.go + // Note: API-Version and Libpod-API-Version are set in handler_api.go w.Header().Set("BuildKit-Version", "") w.Header().Set("Builder-Version", "") w.Header().Set("Docker-Experimental", "true") diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/resize.go libpod-3.4.4+ds1/pkg/api/handlers/compat/resize.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/resize.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/resize.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,14 +8,15 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/gorilla/mux" "github.com/gorilla/schema" "github.com/pkg/errors" ) func ResizeTTY(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) // /containers/{id}/resize query := struct { @@ -73,7 +74,7 @@ return } if err := ctnr.ExecResize(name, sz); err != nil { - if errors.Cause(err) != define.ErrCtrStateInvalid || !query.IgnoreNotRunning { + if errors.Cause(err) != define.ErrExecSessionStateInvalid || !query.IgnoreNotRunning { utils.InternalServerError(w, errors.Wrapf(err, "cannot resize session")) return } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/secrets.go libpod-3.4.4+ds1/pkg/api/handlers/compat/secrets.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,33 +9,28 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" - "github.com/gorilla/schema" + "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) func ListSecrets(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) - query := struct { - Filters map[string][]string `schema:"filters"` - }{} - - if err := decoder.Decode(&query, r.URL.Query()); err != nil { - utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, + filtersMap, err := util.PrepareFilters(r) + if err != nil { + utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if len(query.Filters) > 0 { - utils.Error(w, "filters not supported", http.StatusBadRequest, - errors.Wrapf(errors.New("bad parameter"), "filters not supported")) - return - } ic := abi.ContainerEngine{Libpod: runtime} - reports, err := ic.SecretList(r.Context()) + listOptions := entities.SecretListRequest{ + Filters: *filtersMap, + } + reports, err := ic.SecretList(r.Context(), listOptions) if err != nil { utils.InternalServerError(w, err) return @@ -59,7 +54,7 @@ func InspectSecret(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) name := utils.GetName(r) names := []string{name} @@ -92,7 +87,7 @@ func RemoveSecret(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) opts := entities.SecretRmOptions{} @@ -112,7 +107,7 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) opts := entities.SecretCreateOptions{} createParams := struct { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/swagger.go libpod-3.4.4+ds1/pkg/api/handlers/compat/swagger.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/swagger.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/swagger.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,7 +2,6 @@ import ( "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/storage/pkg/archive" "github.com/docker/docker/api/types" ) @@ -28,15 +27,6 @@ } } -// Object Changes -// swagger:response Changes -type swagChangesResponse struct { - // in:body - Body struct { - Changes []archive.Change - } -} - // Network inspect // swagger:response CompatNetworkInspect type swagCompatNetworkInspect struct { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/system.go libpod-3.4.4+ds1/pkg/api/handlers/compat/system.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" docker "github.com/docker/docker/api/types" @@ -14,7 +15,7 @@ func GetDiskUsage(w http.ResponseWriter, r *http.Request) { options := entities.SystemDfOptions{} - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ic := abi.ContainerEngine{Libpod: runtime} df, err := ic.SystemDf(r.Context(), options) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/version.go libpod-3.4.4+ds1/pkg/api/handlers/compat/version.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/version.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/version.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,24 +9,24 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/entities/types" "github.com/containers/podman/v3/version" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func VersionHandler(w http.ResponseWriter, r *http.Request) { - // 200 ok - // 500 internal - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) - versionInfo, err := define.GetVersion() + running, err := define.GetVersion() if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) return } - infoData, err := runtime.Info() + info, err := runtime.Info() if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrapf(err, "failed to obtain system memory info")) return @@ -34,20 +34,40 @@ components := []types.ComponentVersion{{ Name: "Podman Engine", - Version: versionInfo.Version, + Version: running.Version, Details: map[string]string{ "APIVersion": version.APIVersion[version.Libpod][version.CurrentAPI].String(), "Arch": goRuntime.GOARCH, - "BuildTime": time.Unix(versionInfo.Built, 0).Format(time.RFC3339), - "Experimental": "true", - "GitCommit": versionInfo.GitCommit, - "GoVersion": versionInfo.GoVersion, - "KernelVersion": infoData.Host.Kernel, + "BuildTime": time.Unix(running.Built, 0).Format(time.RFC3339), + "Experimental": "false", + "GitCommit": running.GitCommit, + "GoVersion": running.GoVersion, + "KernelVersion": info.Host.Kernel, "MinAPIVersion": version.APIVersion[version.Libpod][version.MinimalAPI].String(), "Os": goRuntime.GOOS, }, }} + if conmon, oci, err := runtime.DefaultOCIRuntime().RuntimeInfo(); err != nil { + logrus.Warnf("Failed to retrieve Conmon and OCI Information: %q", err.Error()) + } else { + additional := []types.ComponentVersion{ + { + Name: "Conmon", + Version: conmon.Version, + Details: map[string]string{ + "Package": conmon.Package, + }}, + { + Name: fmt.Sprintf("OCI Runtime (%s)", oci.Name), + Version: oci.Version, + Details: map[string]string{ + "Package": oci.Package, + }}, + } + components = append(components, additional...) + } + apiVersion := version.APIVersion[version.Compat][version.CurrentAPI] minVersion := version.APIVersion[version.Compat][version.MinimalAPI] @@ -56,13 +76,13 @@ Platform: struct { Name string }{ - Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, infoData.Host.Distribution.Distribution, infoData.Host.Distribution.Version), + Name: fmt.Sprintf("%s/%s/%s-%s", goRuntime.GOOS, goRuntime.GOARCH, info.Host.Distribution.Distribution, info.Host.Distribution.Version), }, APIVersion: fmt.Sprintf("%d.%d", apiVersion.Major, apiVersion.Minor), Arch: components[0].Details["Arch"], BuildTime: components[0].Details["BuildTime"], Components: components, - Experimental: true, + Experimental: false, GitCommit: components[0].Details["GitCommit"], GoVersion: components[0].Details["GoVersion"], KernelVersion: components[0].Details["KernelVersion"], diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/compat/volumes.go libpod-3.4.4+ds1/pkg/api/handlers/compat/volumes.go --- libpod-3.2.1+ds1/pkg/api/handlers/compat/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/compat/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/filters" "github.com/containers/podman/v3/pkg/domain/infra/abi/parse" "github.com/containers/podman/v3/pkg/util" @@ -21,9 +22,8 @@ ) func ListVolumes(w http.ResponseWriter, r *http.Request) { - var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - ) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + filtersMap, err := util.PrepareFilters(r) if err != nil { utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, @@ -79,8 +79,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { var ( volumeOptions []libpod.VolumeCreateOption - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) /* No query string data*/ query := struct{}{} @@ -181,7 +181,7 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) name := utils.GetName(r) vol, err := runtime.GetVolume(name) @@ -209,8 +209,8 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { Force bool `schema:"force"` @@ -225,7 +225,7 @@ } /* The implications for `force` differ between Docker and us, so we can't - * simply pass the `force` parameter to `runeimt.RemoveVolume()`. + * simply pass the `force` parameter to `runtime.RemoveVolume()`. * Specifically, Docker's behavior seems to be that `force` means "do not * error on missing volume"; ours means "remove any not-running containers * using the volume at the same time". @@ -263,7 +263,7 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) filterMap, err := util.PrepareFilters(r) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/containers_create.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/containers_create.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/containers_create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/containers_create.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" @@ -16,18 +17,24 @@ // CreateContainer takes a specgenerator and makes a container. It returns // the new container ID on success along with any warnings. func CreateContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var sg specgen.SpecGenerator if err := json.NewDecoder(r.Body).Decode(&sg); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) return } + warn, err := generate.CompleteSpec(r.Context(), runtime, &sg) if err != nil { utils.InternalServerError(w, err) return } - ctr, err := generate.MakeContainer(context.Background(), runtime, &sg) + rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), runtime, &sg) + if err != nil { + utils.InternalServerError(w, err) + return + } + ctr, err := generate.ExecuteCreate(context.Background(), runtime, rtSpec, spec, false, opts...) if err != nil { utils.InternalServerError(w, err) return diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/containers.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/containers.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/compat" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/util" @@ -18,8 +19,8 @@ ) func ContainerExists(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} @@ -58,7 +59,7 @@ } func ListContainers(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { All bool `schema:"all"` External bool `schema:"external"` @@ -72,8 +73,13 @@ } filterMap, err := util.PrepareFilters(r) + if err != nil { + utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, + errors.Wrapf(err, "failed to decode filter parameters for %s", r.URL.String())) + return + } - if dErr := decoder.Decode(&query, r.URL.Query()); dErr != nil || err != nil { + if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return @@ -89,7 +95,7 @@ limit = query.Last } - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} @@ -118,7 +124,7 @@ } func GetContainer(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Size bool `schema:"size"` }{ @@ -130,7 +136,7 @@ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) container, err := runtime.LookupContainer(name) if err != nil { @@ -150,7 +156,7 @@ } func UnmountContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) conn, err := runtime.LookupContainer(name) if err != nil { @@ -165,7 +171,7 @@ utils.WriteResponse(w, http.StatusNoContent, "") } func MountContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) conn, err := runtime.LookupContainer(name) if err != nil { @@ -181,7 +187,7 @@ func ShowMountedContainers(w http.ResponseWriter, r *http.Request) { response := make(map[string]string) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) conns, err := runtime.GetAllContainers() if err != nil { utils.InternalServerError(w, err) @@ -201,7 +207,7 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) { var targetFile string - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Keep bool `schema:"keep"` LeaveRunning bool `schema:"leaveRunning"` @@ -218,7 +224,7 @@ return } name := utils.GetName(r) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ctr, err := runtime.LookupContainer(name) if err != nil { utils.ContainerNotFound(w, name, err) @@ -268,7 +274,7 @@ var ( targetFile string ) - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Keep bool `schema:"keep"` TCPEstablished bool `schema:"tcpEstablished"` @@ -287,7 +293,7 @@ return } name := utils.GetName(r) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ctr, err := runtime.LookupContainer(name) if err != nil { utils.ContainerNotFound(w, name, err) @@ -328,7 +334,7 @@ func InitContainer(w http.ResponseWriter, r *http.Request) { name := utils.GetName(r) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ctr, err := runtime.LookupContainer(name) if err != nil { utils.ContainerNotFound(w, name, err) @@ -347,7 +353,7 @@ } func ShouldRestart(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) // Now use the ABI implementation to prevent us from having duplicate // code. containerEngine := abi.ContainerEngine{Libpod: runtime} diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/containers_stats.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/containers_stats.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/containers_stats.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/containers_stats.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,28 +3,40 @@ import ( "encoding/json" "net/http" - "time" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" + "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" + "github.com/containers/podman/v3/pkg/rootless" "github.com/gorilla/schema" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -const DefaultStatsPeriod = 5 * time.Second - func StatsContainer(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + + // Check if service is running rootless (cheap check) + if rootless.IsRootless() { + // if so, then verify cgroup v2 available (more expensive check) + if isV2, _ := cgroups.IsCgroup2UnifiedMode(); !isV2 { + msg := "Container stats resource only available for cgroup v2" + utils.Error(w, msg, http.StatusConflict, errors.New(msg)) + return + } + } query := struct { Containers []string `schema:"containers"` Stream bool `schema:"stream"` + Interval int `schema:"interval"` }{ - Stream: true, + Stream: true, + Interval: 5, } if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) @@ -36,7 +48,8 @@ containerEngine := abi.ContainerEngine{Libpod: runtime} statsOptions := entities.ContainerStatsOptions{ - Stream: query.Stream, + Stream: query.Stream, + Interval: query.Interval, } // Stats will stop if the connection is closed. @@ -48,7 +61,7 @@ // Write header and content type. w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") if flusher, ok := w.(http.Flusher); ok { flusher.Flush() } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/generate.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/generate.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/generate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/generate.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,6 +5,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/util" @@ -13,19 +14,18 @@ ) func GenerateSystemd(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Name bool `schema:"useName"` - New bool `schema:"new"` - NoHeader bool `schema:"noHeader"` - RestartPolicy string `schema:"restartPolicy"` - StopTimeout uint `schema:"stopTimeout"` - ContainerPrefix string `schema:"containerPrefix"` - PodPrefix string `schema:"podPrefix"` - Separator string `schema:"separator"` + Name bool `schema:"useName"` + New bool `schema:"new"` + NoHeader bool `schema:"noHeader"` + RestartPolicy *string `schema:"restartPolicy"` + StopTimeout uint `schema:"stopTimeout"` + ContainerPrefix string `schema:"containerPrefix"` + PodPrefix string `schema:"podPrefix"` + Separator string `schema:"separator"` }{ - RestartPolicy: "on-failure", StopTimeout: util.DefaultContainerConfig().Engine.StopTimeout, ContainerPrefix: "container", PodPrefix: "pod", @@ -49,6 +49,7 @@ PodPrefix: query.PodPrefix, Separator: query.Separator, } + report, err := containerEngine.GenerateSystemd(r.Context(), utils.GetName(r), options) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error generating systemd units")) @@ -59,8 +60,8 @@ } func GenerateKube(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Names []string `schema:"names"` Service bool `schema:"service"` diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/healthcheck.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/healthcheck.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/healthcheck.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/healthcheck.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,10 +6,11 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" ) func RunHealthCheck(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) status, err := runtime.HealthCheck(name) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/images.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/images.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,7 +11,6 @@ "strings" "github.com/containers/buildah" - "github.com/containers/common/libimage" "github.com/containers/common/pkg/filters" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" @@ -19,6 +18,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -42,7 +42,7 @@ // create func ImageExists(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) ir := abi.ImageEngine{Libpod: runtime} @@ -59,9 +59,9 @@ } func ImageTree(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { WhatRequires bool `schema:"whatrequires"` }{ @@ -102,8 +102,8 @@ } func GetImages(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { All bool Digests bool @@ -147,8 +147,8 @@ var ( err error ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { All bool `schema:"all"` }{ @@ -156,8 +156,14 @@ } filterMap, err := util.PrepareFilters(r) + if err != nil { + utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, + errors. + Wrapf(err, "failed to decode filter parameters for %s", r.URL.String())) + return + } - if dErr := decoder.Decode(&query, r.URL.Query()); dErr != nil || err != nil { + if err := decoder.Decode(&query, r.URL.Query()); err != nil { utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, errors. Wrapf(err, "failed to parse parameters for %s", r.URL.String())) @@ -199,8 +205,8 @@ var ( output string ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Compress bool `schema:"compress"` Format string `schema:"format"` @@ -215,8 +221,7 @@ } name := utils.GetName(r) - lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} - if _, _, err := runtime.LibimageRuntime().LookupImage(name, lookupOptions); err != nil { + if _, _, err := runtime.LibimageRuntime().LookupImage(name, nil); err != nil { utils.ImageNotFound(w, name, err) return } @@ -281,8 +286,8 @@ var ( output string ) - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Compress bool `schema:"compress"` Format string `schema:"format"` @@ -352,7 +357,6 @@ Format: query.Format, MultiImageArchive: len(query.References) > 1, Output: output, - RemoveSignatures: true, } imageEngine := abi.ImageEngine{Libpod: runtime} @@ -371,7 +375,7 @@ } func ImagesLoad(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) tmpfile, err := ioutil.TempFile("", "libpod-images-load.tar") if err != nil { @@ -400,8 +404,8 @@ } func ImagesImport(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Changes []string `schema:"changes"` Message string `schema:"message"` @@ -455,8 +459,8 @@ // PushImage is the handler for the compat http endpoint for pushing images. func PushImage(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Destination string `schema:"destination"` @@ -482,7 +486,7 @@ destination = source } - if _, err := utils.ParseDockerReference(destination); err != nil { + if err := utils.IsRegistryReference(destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } @@ -524,8 +528,8 @@ destImage string mimeType string ) - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { Author string `schema:"author"` @@ -599,7 +603,7 @@ } func UntagImage(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) tags := []string{} // Note: if empty, all tags will be removed from the image. repo := r.Form.Get("repo") @@ -643,8 +647,8 @@ // ImagesBatchRemove is the endpoint for batch image removal. func ImagesBatchRemove(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { All bool `schema:"all"` Force bool `schema:"force"` @@ -667,8 +671,8 @@ // ImagesRemove is the endpoint for removing one image. func ImagesRemove(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Force bool `schema:"force"` }{ diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/images_pull.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/images_pull.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/images_pull.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/images_pull.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/channel" "github.com/containers/podman/v3/pkg/domain/entities" @@ -23,17 +24,20 @@ // transport or be normalized to one). Other transports are rejected as they // do not make sense in a remote context. func ImagesPull(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { - Reference string `schema:"reference"` - OS string `schema:"OS"` - Arch string `schema:"Arch"` - Variant string `schema:"Variant"` - TLSVerify bool `schema:"tlsVerify"` - AllTags bool `schema:"allTags"` + Reference string `schema:"reference"` + OS string `schema:"OS"` + Arch string `schema:"Arch"` + Variant string `schema:"Variant"` + TLSVerify bool `schema:"tlsVerify"` + AllTags bool `schema:"allTags"` + PullPolicy string `schema:"policy"` + Quiet bool `schema:"quiet"` }{ - TLSVerify: true, + TLSVerify: true, + PullPolicy: "always", } if err := decoder.Decode(&query, r.URL.Query()); err != nil { @@ -48,7 +52,7 @@ } // Make sure that the reference has no transport or the docker one. - if _, err := utils.ParseDockerReference(query.Reference); err != nil { + if err := utils.IsRegistryReference(query.Reference); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } @@ -83,12 +87,18 @@ pullOptions.Writer = writer + pullPolicy, err := config.ParsePullPolicy(query.PullPolicy) + if err != nil { + utils.Error(w, "failed to parse pull policy", http.StatusBadRequest, err) + return + } + var pulledImages []*libimage.Image var pullError error runCtx, cancel := context.WithCancel(r.Context()) go func() { defer cancel() - pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, config.PullPolicyAlways, pullOptions) + pulledImages, pullError = runtime.LibimageRuntime().Pull(runCtx, query.Reference, pullPolicy, pullOptions) }() flush := func() { @@ -98,7 +108,7 @@ } w.WriteHeader(http.StatusOK) - w.Header().Add("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") flush() enc := json.NewEncoder(w) @@ -108,8 +118,10 @@ select { case s := <-writer.Chan(): report.Stream = string(s) - if err := enc.Encode(report); err != nil { - logrus.Warnf("Failed to encode json: %v", err) + if !query.Quiet { + if err := enc.Encode(report); err != nil { + logrus.Warnf("Failed to encode json: %v", err) + } } flush() case <-runCtx.Done(): diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/info.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/info.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/info.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/info.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,11 +5,12 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/infra/abi" ) func GetInfo(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) containerEngine := abi.ContainerEngine{Libpod: runtime} info, err := containerEngine.Info(r.Context()) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/manifests.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/manifests.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/manifests.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/manifests.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -20,8 +21,8 @@ ) func ManifestCreate(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Name []string `schema:"name"` Image []string `schema:"image"` @@ -57,7 +58,7 @@ // ExistsManifest check if a manifest list exists func ExistsManifest(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) imageEngine := abi.ImageEngine{Libpod: runtime} @@ -74,7 +75,7 @@ } func ManifestInspect(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) imageEngine := abi.ImageEngine{Libpod: runtime} @@ -94,7 +95,7 @@ } func ManifestAdd(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var addOptions entities.ManifestAddOptions if err := json.NewDecoder(r.Body).Decode(&addOptions); err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) @@ -124,8 +125,8 @@ } func ManifestRemove(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Digest string `schema:"digest"` }{ @@ -155,8 +156,8 @@ } func ManifestPush(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { All bool `schema:"all"` Destination string `schema:"destination"` @@ -169,7 +170,7 @@ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } - if _, err := utils.ParseDockerReference(query.Destination); err != nil { + if err := utils.IsRegistryReference(query.Destination); err != nil { utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest, err) return } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/networks.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/networks.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/networks.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/networks.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/network" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/util" @@ -16,8 +17,8 @@ ) func CreateNetwork(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) options := entities.NetworkCreateOptions{} if err := json.NewDecoder(r.Body).Decode(&options); err != nil { utils.Error(w, "unable to marshall input", http.StatusInternalServerError, errors.Wrap(err, "Decode()")) @@ -45,7 +46,7 @@ utils.WriteResponse(w, http.StatusOK, report) } func ListNetworks(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) if err != nil { utils.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError, @@ -66,8 +67,8 @@ } func RemoveNetwork(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Force bool `schema:"force"` }{ @@ -100,8 +101,8 @@ } func InspectNetwork(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { }{ // override any golang type defaults @@ -129,7 +130,7 @@ // Connect adds a container to a network func Connect(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) var netConnect entities.NetworkConnectOptions if err := json.NewDecoder(r.Body).Decode(&netConnect); err != nil { @@ -155,7 +156,7 @@ // ExistsNetwork check if a network exists func ExistsNetwork(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) ic := abi.ContainerEngine{Libpod: runtime} @@ -173,7 +174,7 @@ // Prune removes unused networks func Prune(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/play.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/play.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/play.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,16 +10,18 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func PlayKube(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { Network string `schema:"network"` TLSVerify bool `schema:"tlsVerify"` @@ -66,9 +68,15 @@ utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) return } - defer os.Remove(tmpfile.Name()) + defer func() { + if err := os.Remove(tmpfile.Name()); err != nil { + logrus.Warn(err) + } + }() if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF { - tmpfile.Close() + if err := tmpfile.Close(); err != nil { + logrus.Warn(err) + } utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file")) return } @@ -105,12 +113,43 @@ if _, found := r.URL.Query()["start"]; found { options.Start = types.NewOptionalBool(query.Start) } - report, err := containerEngine.PlayKube(r.Context(), tmpfile.Name(), options) if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error playing YAML file")) return } + utils.WriteResponse(w, http.StatusOK, report) +} +func PlayKubeDown(w http.ResponseWriter, r *http.Request) { + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml") + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile")) + return + } + defer func() { + if err := os.Remove(tmpfile.Name()); err != nil { + logrus.Warn(err) + } + }() + if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF { + if err := tmpfile.Close(); err != nil { + logrus.Warn(err) + } + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file")) + return + } + if err := tmpfile.Close(); err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file")) + return + } + containerEngine := abi.ContainerEngine{Libpod: runtime} + options := new(entities.PlayKubeDownOptions) + report, err := containerEngine.PlayKubeDown(r.Context(), tmpfile.Name(), *options) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error tearing down YAML file")) + return + } utils.WriteResponse(w, http.StatusOK, report) } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/pods.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/pods.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,19 +1,25 @@ package libpod import ( + "context" "encoding/json" "fmt" "net/http" "strings" + "github.com/containers/common/libimage" + "github.com/containers/common/pkg/config" + "github.com/containers/image/v5/transports/alltransports" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -22,28 +28,80 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) err error ) - var psg specgen.PodSpecGenerator + psg := specgen.PodSpecGenerator{InfraContainerSpec: &specgen.SpecGenerator{}} if err := json.NewDecoder(r.Body).Decode(&psg); err != nil { - utils.Error(w, "failed to decode specgen", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) return } - pod, err := generate.MakePod(&psg, runtime) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + return + } + if !psg.NoInfra { + infraOptions := &entities.ContainerCreateOptions{ImageVolume: "bind", IsInfra: true, Net: &entities.NetOptions{}} // options for pulling the image and FillOutSpec + err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error filling out specgen")) + return + } + out, err := json.Marshal(psg) // marshal our spec so the matching options can be unmarshaled into infra + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + return + } + tempSpec := &specgen.SpecGenerator{} // temporary spec since infra cannot be decoded into + err = json.Unmarshal(out, tempSpec) // unmarhal matching options + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "failed to decode specgen")) + return + } + psg.InfraContainerSpec = tempSpec // set infra spec equal to temp + // a few extra that do not have the same json tags + psg.InfraContainerSpec.Name = psg.InfraName + psg.InfraContainerSpec.ConmonPidFile = psg.InfraConmonPidFile + psg.InfraContainerSpec.ContainerCreateCommand = psg.InfraCommand + imageName := psg.InfraImage + rawImageName := psg.InfraImage + if imageName == "" { + imageName = config.DefaultInfraImage + rawImageName = config.DefaultInfraImage + } + curr := infraOptions.Quiet + infraOptions.Quiet = true + pullOptions := &libimage.PullOptions{} + pulledImages, err := runtime.LibimageRuntime().Pull(context.Background(), imageName, config.PullPolicyMissing, pullOptions) + if err != nil { + utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "could not pull image")) + return + } + if _, err := alltransports.ParseImageName(imageName); err == nil { + if len(pulledImages) != 0 { + imageName = pulledImages[0].ID() + } + } + infraOptions.Quiet = curr + psg.InfraImage = imageName + psg.InfraContainerSpec.Image = imageName + psg.InfraContainerSpec.RawImageName = rawImageName + } + podSpecComplete := entities.PodSpec{PodSpecGen: psg} + pod, err := generate.MakePod(&podSpecComplete, runtime) if err != nil { httpCode := http.StatusInternalServerError if errors.Cause(err) == define.ErrPodExists { httpCode = http.StatusConflict } - utils.Error(w, "Something went wrong.", httpCode, err) + utils.Error(w, "Something went wrong.", httpCode, errors.Wrap(err, "failed to make pod")) return } utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()}) } func Pods(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) filterMap, err := util.PrepareFilters(r) if err != nil { @@ -65,7 +123,7 @@ } func PodInspect(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) if err != nil { @@ -87,8 +145,8 @@ func PodStop(w http.ResponseWriter, r *http.Request) { var ( stopError error - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) responses map[string]error ) query := struct { @@ -149,7 +207,7 @@ } func PodStart(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) if err != nil { @@ -186,8 +244,8 @@ func PodDelete(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { Force bool `schema:"force"` @@ -215,7 +273,7 @@ } func PodRestart(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) if err != nil { @@ -251,7 +309,7 @@ func PodPruneHelper(r *http.Request) ([]*entities.PodPruneReport, error) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) responses, err := runtime.PrunePods(r.Context()) if err != nil { @@ -268,7 +326,7 @@ } func PodPause(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) if err != nil { @@ -294,7 +352,7 @@ } func PodUnpause(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) pod, err := runtime.LookupPod(name) if err != nil { @@ -320,8 +378,8 @@ } func PodTop(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { PsArgs string `schema:"ps_args"` @@ -363,8 +421,8 @@ func PodKill(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) signal = "SIGKILL" ) query := struct { @@ -432,7 +490,7 @@ } func PodExists(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) _, err := runtime.LookupPod(name) if err != nil { @@ -443,8 +501,8 @@ } func PodStats(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - decoder := r.Context().Value("decoder").(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := struct { NamesOrIDs []string `schema:"namesOrIDs"` diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/secrets.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/secrets.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,13 @@ package libpod import ( + "encoding/json" "net/http" + "reflect" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/gorilla/schema" @@ -13,12 +16,20 @@ func CreateSecret(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) + + decoder.RegisterConverter(map[string]string{}, func(str string) reflect.Value { + res := make(map[string]string) + json.Unmarshal([]byte(str), &res) + return reflect.ValueOf(res) + }) + query := struct { - Name string `schema:"name"` - Driver string `schema:"driver"` + Name string `schema:"name"` + Driver string `schema:"driver"` + DriverOpts map[string]string `schema:"driveropts"` }{ // override any golang type defaults } @@ -28,7 +39,9 @@ errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return } + opts.Driver = query.Driver + opts.DriverOpts = query.DriverOpts ic := abi.ContainerEngine{Libpod: runtime} report, err := ic.SecretCreate(r.Context(), query.Name, r.Body, opts) diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/swagger.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/swagger.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/swagger.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/swagger.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,6 +4,7 @@ "net/http" "os" + "github.com/containernetworking/cni/libcni" "github.com/containers/image/v5/manifest" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" @@ -102,7 +103,7 @@ // swagger:response NetworkInspectReport type swagNetworkInspectReport struct { // in:body - Body entities.NetworkInspectReport + Body libcni.NetworkConfigList } // Network list diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/system.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/system.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,6 +5,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" "github.com/containers/podman/v3/pkg/util" @@ -14,8 +15,8 @@ // SystemPrune removes unused data func SystemPrune(w http.ResponseWriter, r *http.Request) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { All bool `schema:"all"` @@ -53,7 +54,7 @@ func DiskUsage(w http.ResponseWriter, r *http.Request) { // Options are only used by the CLI options := entities.SystemDfOptions{} - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ic := abi.ContainerEngine{Libpod: runtime} response, err := ic.SystemDf(r.Context(), options) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/libpod/volumes.go libpod-3.4.4+ds1/pkg/api/handlers/libpod/volumes.go --- libpod-3.2.1+ds1/pkg/api/handlers/libpod/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/libpod/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/api/handlers/utils" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/domain/filters" @@ -21,8 +22,8 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { var ( volumeOptions []libpod.VolumeCreateOption - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { }{ @@ -75,7 +76,7 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) name := utils.GetName(r) vol, err := runtime.GetVolume(name) @@ -96,7 +97,7 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) filterMap, err := util.PrepareFilters(r) if err != nil { @@ -142,7 +143,7 @@ func pruneVolumesHelper(r *http.Request) ([]*reports.PruneReport, error) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) ) filterMap, err := util.PrepareFilters(r) if err != nil { @@ -164,8 +165,8 @@ func RemoveVolume(w http.ResponseWriter, r *http.Request) { var ( - runtime = r.Context().Value("runtime").(*libpod.Runtime) - decoder = r.Context().Value("decoder").(*schema.Decoder) + runtime = r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + decoder = r.Context().Value(api.DecoderKey).(*schema.Decoder) ) query := struct { Force bool `schema:"force"` @@ -197,7 +198,7 @@ // ExistsVolume check if a volume exists func ExistsVolume(w http.ResponseWriter, r *http.Request) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) name := utils.GetName(r) ic := abi.ContainerEngine{Libpod: runtime} diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/swagger/swagger.go libpod-3.4.4+ds1/pkg/api/handlers/swagger/swagger.go --- libpod-3.2.1+ds1/pkg/api/handlers/swagger/swagger.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/swagger/swagger.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,15 @@ "github.com/docker/docker/api/types" ) +// Tree response +// swagger:response TreeResponse +type swagTree struct { + // in:body + Body struct { + entities.ImageTreeReport + } +} + // History response // swagger:response DocsHistory type swagHistory struct { @@ -143,13 +152,6 @@ } } -// List processes in pod -// swagger:response DocsPodStatsResponse -type swagPodStatsResponse struct { - // in:body - Body []*entities.PodStatsReport -} - // Inspect container // swagger:response LibpodInspectContainerResponse type swagLibpodInspectContainerResponse struct { @@ -175,20 +177,11 @@ } } -// Inspect volume -// swagger:response InspectVolumeResponse -type swagInspectVolumeResponse struct { - // in:body - Body struct { - define.InspectVolumeData - } -} - -// Image tree response -// swagger:response LibpodImageTreeResponse -type swagImageTreeResponse struct { +// Get stats for one or more containers +// swagger:response ContainerStats +type swagContainerStatsResponse struct { // in:body Body struct { - handlers.ImageTreeResponse + define.ContainerStats } } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/types/types.go libpod-3.4.4+ds1/pkg/api/handlers/types/types.go --- libpod-3.2.1+ds1/pkg/api/handlers/types/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/types/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,8 +1,6 @@ package types -import ( - "github.com/containers/podman/v3/pkg/domain/entities" -) +import "github.com/containers/podman/v3/pkg/domain/entities" // LibpodImagesRemoveReport is the return type for image removal via the rest // api. diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/types.go libpod-3.4.4+ds1/pkg/api/handlers/types.go --- libpod-3.2.1+ds1/pkg/api/handlers/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -98,12 +98,13 @@ type ContainerWaitOKBody struct { StatusCode int - Error struct { + Error *struct { Message string } } // CreateContainerConfig used when compatible endpoint creates a container +// swagger:model CreateContainerConfig type CreateContainerConfig struct { Name string // container name dockerContainer.Config // desired container configuration @@ -133,6 +134,7 @@ Infra bool `json:"infra"` InfraCommand string `json:"infra-command"` InfraImage string `json:"infra-image"` + InfraName string `json:"infra-name"` Labels []string `json:"labels"` Publish []string `json:"publish"` Share string `json:"share"` @@ -148,15 +150,6 @@ Comment string } -type ImageLayer struct{} - -type ImageTreeResponse struct { - ID string `json:"id"` - Tags []string `json:"tags"` - Size string `json:"size"` - Layers []ImageLayer `json:"layers"` -} - type ExecCreateConfig struct { docker.ExecConfig } @@ -166,8 +159,10 @@ } type ExecStartConfig struct { - Detach bool `json:"Detach"` - Tty bool `json:"Tty"` + Detach bool `json:"Detach"` + Tty bool `json:"Tty"` + Height uint16 `json:"h"` + Width uint16 `json:"w"` } func ImageToImageSummary(l *libimage.Image) (*entities.ImageSummary, error) { @@ -182,8 +177,14 @@ } containerCount := len(containers) + isDangling, err := l.IsDangling(context.TODO()) + if err != nil { + return nil, errors.Wrapf(err, "failed to check if image %s is dangling", l.ID()) + } + is := entities.ImageSummary{ - ID: l.ID(), + // docker adds sha256: in front of the ID + ID: "sha256:" + l.ID(), ParentId: imageData.Parent, RepoTags: imageData.RepoTags, RepoDigests: imageData.RepoDigests, @@ -194,7 +195,7 @@ Labels: imageData.Labels, Containers: containerCount, ReadOnly: l.IsReadOnly(), - Dangling: l.IsDangling(), + Dangling: isDangling, Names: l.Names(), Digest: string(imageData.Digest), ConfigDigest: "", // TODO: libpod/image didn't set it but libimage should @@ -239,27 +240,32 @@ Name: info.GraphDriver.Name, Data: info.GraphDriver.Data, } + // Add in basic ContainerConfig to satisfy docker-compose + cc := new(dockerContainer.Config) + cc.Hostname = info.ID[0:11] // short ID is the hostname + cc.Volumes = info.Config.Volumes + dockerImageInspect := docker.ImageInspect{ - Architecture: info.Architecture, - Author: info.Author, - Comment: info.Comment, - Config: &config, - Created: l.Created().Format(time.RFC3339Nano), - DockerVersion: info.Version, - GraphDriver: graphDriver, - ID: "sha256:" + l.ID(), - Metadata: docker.ImageMetadata{}, - Os: info.Os, - OsVersion: info.Version, - Parent: info.Parent, - RepoDigests: info.RepoDigests, - RepoTags: info.RepoTags, - RootFS: rootfs, - Size: info.Size, - Variant: "", - VirtualSize: info.VirtualSize, + Architecture: info.Architecture, + Author: info.Author, + Comment: info.Comment, + Config: &config, + ContainerConfig: cc, + Created: l.Created().Format(time.RFC3339Nano), + DockerVersion: info.Version, + GraphDriver: graphDriver, + ID: "sha256:" + l.ID(), + Metadata: docker.ImageMetadata{}, + Os: info.Os, + OsVersion: info.Version, + Parent: info.Parent, + RepoDigests: info.RepoDigests, + RepoTags: info.RepoTags, + RootFS: rootfs, + Size: info.Size, + Variant: "", + VirtualSize: info.VirtualSize, } - // TODO: consider filling the container config. return &ImageInspect{dockerImageInspect}, nil } diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/utils/containers.go libpod-3.4.4+ds1/pkg/api/handlers/utils/containers.go --- libpod-3.2.1+ds1/pkg/api/handlers/utils/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/utils/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "time" "github.com/containers/podman/v3/libpod/events" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -36,7 +37,7 @@ query := waitQueryDocker{} - decoder := ctx.Value("decoder").(*schema.Decoder) + decoder := ctx.Value(api.DecoderKey).(*schema.Decoder) if err = decoder.Decode(&query, r.URL.Query()); err != nil { Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) return @@ -68,25 +69,26 @@ // In docker compatibility mode we have to send headers in advance, // otherwise docker client would freeze. - w.Header().Add("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) if flusher, ok := w.(http.Flusher); ok { flusher.Flush() } exitCode, err := waitDockerCondition(ctx, name, interval, condition) - msg := "" + var errStruct *struct{ Message string } if err != nil { logrus.Errorf("error while waiting on condition: %q", err) - msg = err.Error() + errStruct = &struct { + Message string + }{ + Message: err.Error(), + } } + responseData := handlers.ContainerWaitOKBody{ StatusCode: int(exitCode), - Error: struct { - Message string - }{ - Message: msg, - }, + Error: errStruct, } enc := json.NewEncoder(w) enc.SetEscapeHTML(true) @@ -102,7 +104,7 @@ interval = time.Millisecond * 250 conditions = []define.ContainerStatus{define.ContainerStateStopped, define.ContainerStateExited} ) - decoder := r.Context().Value("decoder").(*schema.Decoder) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) query := waitQueryLibpod{} if err := decoder.Decode(&query, r.URL.Query()); err != nil { Error(w, "Something went wrong.", http.StatusBadRequest, errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String())) @@ -142,7 +144,7 @@ type containerWaitFn func(conditions ...define.ContainerStatus) (int32, error) func createContainerWaitFn(ctx context.Context, containerName string, interval time.Duration) containerWaitFn { - runtime := ctx.Value("runtime").(*libpod.Runtime) + runtime := ctx.Value(api.RuntimeKey).(*libpod.Runtime) var containerEngine entities.ContainerEngine = &abi.ContainerEngine{Libpod: runtime} return func(conditions ...define.ContainerStatus) (int32, error) { @@ -204,7 +206,7 @@ } func waitNextExit(ctx context.Context, containerName string) (int32, error) { - runtime := ctx.Value("runtime").(*libpod.Runtime) + runtime := ctx.Value(api.RuntimeKey).(*libpod.Runtime) containerEngine := &abi.ContainerEngine{Libpod: runtime} eventChannel := make(chan *events.Event) errChannel := make(chan error) @@ -236,7 +238,7 @@ } func containerExists(ctx context.Context, name string) (bool, error) { - runtime := ctx.Value("runtime").(*libpod.Runtime) + runtime := ctx.Value(api.RuntimeKey).(*libpod.Runtime) var containerEngine entities.ContainerEngine = &abi.ContainerEngine{Libpod: runtime} var ctrExistsOpts entities.ContainerExistsOptions diff -Nru libpod-3.2.1+ds1/pkg/api/handlers/utils/images.go libpod-3.4.4+ds1/pkg/api/handlers/utils/images.go --- libpod-3.2.1+ds1/pkg/api/handlers/utils/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/handlers/utils/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,26 +11,24 @@ "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" + api "github.com/containers/podman/v3/pkg/api/types" "github.com/gorilla/schema" "github.com/pkg/errors" ) -// ParseDockerReference parses the specified image name to a -// `types.ImageReference` and enforces it to refer to a docker-transport -// reference. -func ParseDockerReference(name string) (types.ImageReference, error) { - dockerPrefix := fmt.Sprintf("%s://", docker.Transport.Name()) +// IsRegistryReference checks if the specified name points to the "docker://" +// transport. If it points to no supported transport, we'll assume a +// non-transport reference pointing to an image (e.g., "fedora:latest"). +func IsRegistryReference(name string) error { imageRef, err := alltransports.ParseImageName(name) - if err == nil && imageRef.Transport().Name() != docker.Transport.Name() { - return nil, errors.Errorf("reference %q must be a docker reference", name) - } else if err != nil { - origErr := err - imageRef, err = alltransports.ParseImageName(fmt.Sprintf("%s%s", dockerPrefix, name)) - if err != nil { - return nil, errors.Wrapf(origErr, "reference %q must be a docker reference", name) - } + if err != nil { + // No supported transport -> assume a docker-stype reference. + return nil } - return imageRef, nil + if imageRef.Transport().Name() == docker.Transport.Name() { + return nil + } + return errors.Errorf("unsupported transport %s in %q: only docker transport is supported", imageRef.Transport().Name(), name) } // ParseStorageReference parses the specified image name to a @@ -54,8 +52,8 @@ // GetImages is a common function used to get images for libpod and other compatibility // mechanisms func GetImages(w http.ResponseWriter, r *http.Request) ([]*libimage.Image, error) { - decoder := r.Context().Value("decoder").(*schema.Decoder) - runtime := r.Context().Value("runtime").(*libpod.Runtime) + decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) query := struct { All bool Digests bool @@ -90,9 +88,8 @@ } func GetImage(r *http.Request, name string) (*libimage.Image, error) { - runtime := r.Context().Value("runtime").(*libpod.Runtime) - lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} - image, _, err := runtime.LibimageRuntime().LookupImage(name, lookupOptions) + runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime) + image, _, err := runtime.LibimageRuntime().LookupImage(name, nil) if err != nil { return nil, err } diff -Nru libpod-3.2.1+ds1/pkg/api/server/docs.go libpod-3.4.4+ds1/pkg/api/server/docs.go --- libpod-3.2.1+ds1/pkg/api/server/docs.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/docs.go 2021-12-26 00:45:51.000000000 +0000 @@ -42,7 +42,7 @@ // // InfoExtensions: // x-logo: -// - url: https://raw.githubusercontent.com/containers/libpod/master/logo/podman-logo.png +// - url: https://raw.githubusercontent.com/containers/libpod/main/logo/podman-logo.png // - altText: "Podman logo" // // Produces: diff -Nru libpod-3.2.1+ds1/pkg/api/server/handler_api.go libpod-3.4.4+ds1/pkg/api/server/handler_api.go --- libpod-3.2.1+ds1/pkg/api/server/handler_api.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/handler_api.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,61 +1,25 @@ package server import ( - "context" "fmt" "net/http" "runtime" - "github.com/containers/podman/v3/pkg/api/handlers/utils" - "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/version" - "github.com/google/uuid" "github.com/sirupsen/logrus" ) // APIHandler is a wrapper to enhance HandlerFunc's and remove redundant code func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - // http.Server hides panics, we want to see them and fix the cause. - defer func() { - err := recover() - if err != nil { - buf := make([]byte, 1<<20) - n := runtime.Stack(buf, true) - logrus.Warnf("Recovering from API handler panic: %v, %s", err, buf[:n]) - // Try to inform client things went south... won't work if handler already started writing response body - utils.InternalServerError(w, fmt.Errorf("%v", err)) - } - }() - - // Wrapper to hide some boiler plate + // Wrapper to hide some boilerplate fn := func(w http.ResponseWriter, r *http.Request) { - rid := uuid.New().String() - logrus.Infof("APIHandler(%s) -- %s %s BEGIN", rid, r.Method, r.URL.String()) - if logrus.IsLevelEnabled(logrus.DebugLevel) { - for k, v := range r.Header { - switch auth.HeaderAuthName(k) { - case auth.XRegistryConfigHeader, auth.XRegistryAuthHeader: - logrus.Debugf("APIHandler(%s) -- Header: %s=", rid, k) - default: - logrus.Debugf("APIHandler(%s) -- Header: %s=%v", rid, k, v) - } - } - } - // Set in case handler wishes to correlate logging events - r.Header.Set("X-Reference-Id", rid) - if err := r.ParseForm(); err != nil { - logrus.Infof("Failed Request: unable to parse form: %q (%s)", err, rid) + logrus.WithFields(logrus.Fields{ + "X-Reference-Id": r.Header.Get("X-Reference-Id"), + }).Info("Failed Request: unable to parse form: " + err.Error()) } - // TODO: Use r.ConnContext when ported to go 1.13 - c := context.WithValue(r.Context(), "decoder", s.Decoder) // nolint - c = context.WithValue(c, "runtime", s.Runtime) // nolint - c = context.WithValue(c, "shutdownFunc", s.Shutdown) // nolint - c = context.WithValue(c, "idletracker", s.idleTracker) // nolint - r = r.WithContext(c) - cv := version.APIVersion[version.Compat][version.CurrentAPI] w.Header().Set("API-Version", fmt.Sprintf("%d.%d", cv.Major, cv.Minor)) @@ -63,8 +27,13 @@ w.Header().Set("Libpod-API-Version", lv) w.Header().Set("Server", "Libpod/"+lv+" ("+runtime.GOOS+")") + if s.CorsHeaders != "" { + w.Header().Set("Access-Control-Allow-Origin", s.CorsHeaders) + w.Header().Set("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth, Connection, Upgrade, X-Registry-Config") + w.Header().Set("Access-Control-Allow-Methods", "HEAD, GET, POST, DELETE, PUT, OPTIONS") + } + h(w, r) - logrus.Debugf("APIHandler(%s) -- %s %s END", rid, r.Method, r.URL.String()) } fn(w, r) } diff -Nru libpod-3.2.1+ds1/pkg/api/server/handler_logging.go libpod-3.4.4+ds1/pkg/api/server/handler_logging.go --- libpod-3.2.1+ds1/pkg/api/server/handler_logging.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/handler_logging.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,51 @@ +package server + +import ( + "io" + "io/ioutil" + "net/http" + "time" + + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" +) + +type responseWriter struct { + http.ResponseWriter +} + +var apiLogger = &logrus.Logger{ + Formatter: &logrus.TextFormatter{ + DisableColors: true, + DisableLevelTruncation: true, + FullTimestamp: true, + QuoteEmptyFields: true, + TimestampFormat: time.RFC3339, + }, + Level: logrus.TraceLevel, + Out: logrus.StandardLogger().Out, +} + +func (l responseWriter) Write(b []byte) (int, error) { + apiLogger.WithFields(logrus.Fields{ + "API": "response", + "X-Reference-Id": l.Header().Get("X-Reference-Id"), + }).Trace(string(b)) + return l.ResponseWriter.Write(b) +} + +func loggingHandler() mux.MiddlewareFunc { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + annotated := apiLogger.WithFields(logrus.Fields{ + "API": "request", + "X-Reference-Id": r.Header.Get("X-Reference-Id"), + }) + r.Body = ioutil.NopCloser( + io.TeeReader(r.Body, annotated.WriterLevel(logrus.TraceLevel))) + + w = responseWriter{ResponseWriter: w} + h.ServeHTTP(w, r) + }) + } +} diff -Nru libpod-3.2.1+ds1/pkg/api/server/handler_panic.go libpod-3.4.4+ds1/pkg/api/server/handler_panic.go --- libpod-3.2.1+ds1/pkg/api/server/handler_panic.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/handler_panic.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,32 @@ +package server + +import ( + "fmt" + "net/http" + "runtime" + + "github.com/containers/podman/v3/pkg/api/handlers/utils" + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" +) + +// panicHandler captures panics from endpoint handlers and logs stack trace +func panicHandler() mux.MiddlewareFunc { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // http.Server hides panics from handlers, we want to record them and fix the cause + defer func() { + err := recover() + if err != nil { + buf := make([]byte, 1<<20) + n := runtime.Stack(buf, true) + logrus.Warnf("Recovering from API service endpoint handler panic: %v, %s", err, buf[:n]) + // Try to inform client things went south... won't work if handler already started writing response body + utils.InternalServerError(w, fmt.Errorf("%v", err)) + } + }() + + h.ServeHTTP(w, r) + }) + } +} diff -Nru libpod-3.2.1+ds1/pkg/api/server/handler_rid.go libpod-3.4.4+ds1/pkg/api/server/handler_rid.go --- libpod-3.2.1+ds1/pkg/api/server/handler_rid.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/handler_rid.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,41 @@ +package server + +import ( + "fmt" + "io/ioutil" + "net/http" + + "github.com/containers/podman/v3/pkg/api/types" + "github.com/google/uuid" + "github.com/gorilla/handlers" + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" +) + +// referenceIDHandler adds X-Reference-Id Header allowing event correlation +// and Apache style request logging +func referenceIDHandler() mux.MiddlewareFunc { + return func(h http.Handler) http.Handler { + // Only log Apache access_log-like entries at Info level or below + out := ioutil.Discard + if logrus.IsLevelEnabled(logrus.InfoLevel) { + out = logrus.StandardLogger().Out + } + + return handlers.CombinedLoggingHandler(out, + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rid := r.Header.Get("X-Reference-Id") + if rid == "" { + if c := r.Context().Value(types.ConnKey); c == nil { + rid = uuid.New().String() + } else { + rid = fmt.Sprintf("%p", c) + } + } + + r.Header.Set("X-Reference-Id", rid) + w.Header().Set("X-Reference-Id", rid) + h.ServeHTTP(w, r) + })) + } +} diff -Nru libpod-3.2.1+ds1/pkg/api/server/idle/tracker.go libpod-3.4.4+ds1/pkg/api/server/idle/tracker.go --- libpod-3.2.1+ds1/pkg/api/server/idle/tracker.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/idle/tracker.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package idle import ( + "fmt" "net" "net/http" "sync" @@ -39,7 +40,10 @@ t.mux.Lock() defer t.mux.Unlock() - logrus.Debugf("IdleTracker %p:%v %dm+%dh/%dt connection(s)", conn, state, len(t.managed), t.hijacked, t.TotalConnections()) + logrus.WithFields(logrus.Fields{ + "X-Reference-Id": fmt.Sprintf("%p", conn), + }).Debugf("IdleTracker:%v %dm+%dh/%dt connection(s)", state, len(t.managed), t.hijacked, t.TotalConnections()) + switch state { case http.StateNew: t.total++ @@ -68,7 +72,9 @@ if _, found := t.managed[conn]; found { delete(t.managed, conn) } else { - logrus.Warnf("IdleTracker %p: StateClosed transition by un-managed connection", conn) + logrus.WithFields(logrus.Fields{ + "X-Reference-Id": fmt.Sprintf("%p", conn), + }).Warnf("IdleTracker: StateClosed transition by connection marked un-managed") } } diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_archive.go libpod-3.4.4+ds1/pkg/api/server/register_archive.go --- libpod-3.2.1+ds1/pkg/api/server/register_archive.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_archive.go 2021-12-26 00:45:51.000000000 +0000 @@ -151,6 +151,10 @@ // type: string // description: Path to a directory in the container to extract // required: true + // - in: query + // name: rename + // type: string + // description: JSON encoded map[string]string to translate paths // responses: // 200: // description: no error diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_containers.go libpod-3.4.4+ds1/pkg/api/server/register_containers.go --- libpod-3.2.1+ds1/pkg/api/server/register_containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -21,6 +21,12 @@ // name: name // type: string // description: container name + // - in: body + // name: body + // description: Container to create + // schema: + // $ref: "#/definitions/CreateContainerConfig" + // required: true // responses: // 201: // $ref: "#/responses/ContainerCreateResponse" @@ -375,6 +381,11 @@ // type: boolean // default: true // description: Stream the output + // - in: query + // name: one-shot + // type: boolean + // default: false + // description: Provide a one-shot response in which preCPU stats are blank, resulting in a single cycle return. // produces: // - application/json // responses: @@ -1017,7 +1028,8 @@ // - in: query // name: t // type: integer - // description: timeout before sending kill signal to container + // default: 10 + // description: number of seconds to wait before killing container // produces: // - application/json // responses: @@ -1080,6 +1092,8 @@ // description: no error // 404: // $ref: "#/responses/NoSuchContainer" + // 409: + // $ref: "#/responses/ConflictError" // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/containers/{name}/stats"), s.APIHandler(compat.StatsContainer)).Methods(http.MethodGet) @@ -1101,11 +1115,16 @@ // type: boolean // default: true // description: Stream the output + // - in: query + // name: interval + // type: integer + // default: 5 + // description: Time in seconds between stats reports // produces: // - application/json // responses: // 200: - // description: no error + // $ref: "#/responses/ContainerStats" // 404: // $ref: "#/responses/NoSuchContainer" // 500: @@ -1500,6 +1519,15 @@ // type: string // required: true // description: the name or id of the container + // - in: query + // name: parent + // type: string + // description: specify a second layer which is used to compare against it instead of the parent layer + // - in: query + // name: diffType + // type: string + // enum: [all, container, image] + // description: select what you want to match, default is all // responses: // 200: // description: Array of Changes diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_exec.go libpod-3.4.4+ds1/pkg/api/server/register_exec.go --- libpod-3.2.1+ds1/pkg/api/server/register_exec.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_exec.go 2021-12-26 00:45:51.000000000 +0000 @@ -269,10 +269,16 @@ // properties: // Detach: // type: boolean - // description: Detach from the command. Not presently supported. + // description: Detach from the command. // Tty: // type: boolean - // description: Allocate a pseudo-TTY. Presently ignored. + // description: Allocate a pseudo-TTY. + // h: + // type: integer + // description: Height of the TTY session in characters. Tty must be set to true to use it. + // w: + // type: integer + // description: Width of the TTY session in characters. Tty must be set to true to use it. // produces: // - application/json // responses: diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_images.go libpod-3.4.4+ds1/pkg/api/server/register_images.go --- libpod-3.2.1+ds1/pkg/api/server/register_images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_images.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,26 +25,39 @@ // produces: // - application/json // parameters: + // - in: header + // name: X-Registry-Auth + // type: string + // description: A base64-encoded auth configuration. // - in: query // name: fromImage // type: string - // description: needs description + // description: Name of the image to pull. The name may include a tag or digest. This parameter may only be used when pulling an image. The pull is cancelled if the HTTP connection is closed. // - in: query // name: fromSrc // type: string - // description: needs description + // description: Source to import. The value may be a URL from which the image can be retrieved or - to read the image from the request body. This parameter may only be used when importing an image + // - in: query + // name: repo + // type: string + // description: Repository name given to an image when it is imported. The repo may include a tag. This parameter may only be used when importing an image. // - in: query // name: tag // type: string - // description: needs description - // - in: header - // name: X-Registry-Auth + // description: Tag or digest. If empty when pulling an image, this causes all tags for the given image to be pulled. + // - in: query + // name: message // type: string - // description: A base64-encoded auth configuration. + // description: Set commit message for imported image. + // - in: query + // name: platform + // type: string + // description: Platform in the format os[/arch[/variant]] // - in: body - // name: request + // name: inputImage // schema: // type: string + // format: binary // description: Image content if fromSrc parameter was used // responses: // 200: @@ -90,7 +103,7 @@ // - application/json // responses: // 200: - // $ref: "#/responses/DockerImageSummary" + // $ref: "#/responses/DockerImageSummaryResponse" // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/images/json"), s.APIHandler(compat.GetImages)).Methods(http.MethodGet) @@ -458,6 +471,14 @@ // summary: Create image // description: Build an image from the given Dockerfile(s) // parameters: + // - in: header + // name: Content-Type + // type: string + // default: application/x-tar + // enum: ["application/x-tar"] + // - in: header + // name: X-Registry-Config + // type: string // - in: query // name: dockerfile // type: string @@ -639,6 +660,14 @@ // description: | // output configuration TBD // (As of version 1.xx) + // - in: body + // name: inputStream + // description: | + // A tar archive compressed with one of the following algorithms: + // identity (no compression), gzip, bzip2, xz. + // schema: + // type: string + // format: binary // produces: // - application/json // responses: @@ -747,7 +776,7 @@ // - application/json // responses: // 200: - // $ref: '#/responses/LibpodImageTreeResponse' + // $ref: "#/responses/TreeResponse" // 404: // $ref: '#/responses/NoSuchImage' // 500: @@ -802,7 +831,7 @@ // - application/json // responses: // 200: - // $ref: "#/responses/DockerImageSummary" + // $ref: "#/responses/LibpodImageSummaryResponse" // 500: // $ref: '#/responses/InternalError' r.Handle(VersionedPath("/libpod/images/json"), s.APIHandler(libpod.GetImages)).Methods(http.MethodGet) @@ -838,6 +867,11 @@ // summary: Import image // description: Import a previously exported tarball as an image. // parameters: + // - in: header + // name: Content-Type + // type: string + // default: application/x-tar + // enum: ["application/x-tar"] // - in: query // name: changes // description: "Apply the following possible instructions to the created image: CMD | ENTRYPOINT | ENV | EXPOSE | LABEL | STOPSIGNAL | USER | VOLUME | WORKDIR. JSON encoded string" @@ -861,7 +895,8 @@ // required: true // description: tarball for imported image // schema: - // type: "string" + // type: string + // format: binary // produces: // - application/json // consumes: @@ -948,6 +983,11 @@ // description: "Mandatory reference to the image (e.g., quay.io/image/name:tag)" // type: string // - in: query + // name: quiet + // description: "silences extra stream data on pull" + // type: boolean + // default: false + // - in: query // name: credentials // description: "username:password for the registry" // type: string @@ -964,6 +1004,10 @@ // description: Pull image for the specified variant. // type: string // - in: query + // name: policy + // description: Pull policy, "always" (default), "missing", "newer", "never". + // type: string + // - in: query // name: tlsVerify // description: Require TLS verification. // type: boolean @@ -972,6 +1016,10 @@ // name: allTags // description: Pull all tagged images in the repository. // type: boolean + // - in: header + // name: X-Registry-Auth + // description: "base-64 encoded auth config. Must include the following four values: username, password, email and server address OR simply just an identity token." + // type: string // produces: // - application/json // responses: @@ -990,6 +1038,12 @@ // description: Remove images that are not being used by a container // parameters: // - in: query + // name: all + // default: false + // type: boolean + // description: | + // Remove all images not in use by containers, not just dangling ones + // - in: query // name: filters // type: string // description: | @@ -1268,7 +1322,16 @@ // name: name // type: string // required: true - // description: the name or id of the container + // description: the name or id of the image + // - in: query + // name: parent + // type: string + // description: specify a second layer which is used to compare against it instead of the parent layer + // - in: query + // name: diffType + // type: string + // enum: [all, container, image] + // description: select what you want to match, default is all // responses: // 200: // description: Array of Changes @@ -1437,6 +1500,13 @@ // JSON map of key, value pairs to set as labels on the new image // (As of version 1.xx) // - in: query + // name: layers + // type: boolean + // default: true + // description: | + // Cache intermediate layers during build. + // (As of version 1.xx) + // - in: query // name: networkmode // type: string // default: bridge diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_networks.go libpod-3.4.4+ds1/pkg/api/server/register_networks.go --- libpod-3.2.1+ds1/pkg/api/server/register_networks.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_networks.go 2021-12-26 00:45:51.000000000 +0000 @@ -44,6 +44,16 @@ // type: string // required: true // description: the name of the network + // - in: query + // name: verbose + // type: boolean + // required: false + // description: Detailed inspect output for troubleshooting + // - in: query + // name: scope + // type: string + // required: false + // description: Filter the network by scope (swarm, global, or local) // produces: // - application/json // responses: @@ -66,11 +76,11 @@ // name: filters // type: string // description: | - // JSON encoded value of the filters (a map[string][]string) to process on the network list. Currently available filters: - // - name=[name] Matches network name (accepts regex). - // - id=[id] Matches for full or partial ID. - // - driver=[driver] Only bridge is supported. - // - label=[key] or label=[key=value] Matches networks based on the presence of a label alone or a label and a value. + // JSON encoded value of the filters (a `map[string][]string`) to process on the network list. Currently available filters: + // - `name=[name]` Matches network name (accepts regex). + // - `id=[id]` Matches for full or partial ID. + // - `driver=[driver]` Only bridge is supported. + // - `label=[key]` or `label=[key=value]` Matches networks based on the presence of a label alone or a label and a value. // produces: // - application/json // responses: @@ -174,8 +184,8 @@ // description: | // Filters to process on the prune list, encoded as JSON (a map[string][]string). // Available filters: - // - until= Prune networks created before this timestamp. The can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time. - // - label (label=, label==, label!=, or label!==) Prune networks with (or without, in case label!=... is used) the specified labels. + // - `until=` Prune networks created before this timestamp. The can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. // responses: // 200: // description: OK @@ -244,18 +254,20 @@ // tags: // - networks // summary: List networks - // description: Display summary of network configurations + // description: | + // Display summary of network configurations. + // - In a 200 response, all of the fields named Bytes are returned as a Base64 encoded string. // parameters: // - in: query // name: filters // type: string // description: | - // JSON encoded value of the filters (a map[string][]string) to process on the network list. Available filters: - // - name=[name] Matches network name (accepts regex). - // - id=[id] Matches for full or partial ID. - // - driver=[driver] Only bridge is supported. - // - label=[key] or label=[key=value] Matches networks based on the presence of a label alone or a label and a value. - // - plugin=[plugin] Matches CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) + // JSON encoded value of the filters (a `map[string][]string`) to process on the network list. Available filters: + // - `name=[name]` Matches network name (accepts regex). + // - `id=[id]` Matches for full or partial ID. + // - `driver=[driver]` Only bridge is supported. + // - `label=[key]` or `label=[key=value]` Matches networks based on the presence of a label alone or a label and a value. + // - `plugin=[plugin]` Matches CNI plugins included in a network (e.g `bridge`,`portmap`,`firewall`,`tuning`,`dnsname`,`macvlan`) // produces: // - application/json // responses: @@ -269,7 +281,9 @@ // tags: // - networks // summary: Inspect a network - // description: Display low level configuration for a CNI network + // description: | + // Display low level configuration for a CNI network. + // - In a 200 response, all of the fields named Bytes are returned as a Base64 encoded string. // parameters: // - in: path // name: name @@ -380,10 +394,10 @@ // name: filters // type: string // description: | - // Filters to process on the prune list, encoded as JSON (a map[string][]string). + // Filters to process on the prune list, encoded as JSON (a `map[string][]string`). // Available filters: - // - until= Prune networks created before this timestamp. The can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. 10m, 1h30m) computed relative to the daemon machine’s time. - // - label (label=, label==, label!=, or label!==) Prune networks with (or without, in case label!=... is used) the specified labels. + // - `until=` Prune networks created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune networks with (or without, in case `label!=...` is used) the specified labels. // responses: // 200: // $ref: "#/responses/NetworkPruneResponse" diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_play.go libpod-3.4.4+ds1/pkg/api/server/register_play.go --- libpod-3.2.1+ds1/pkg/api/server/register_play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_play.go 2021-12-26 00:45:51.000000000 +0000 @@ -59,5 +59,20 @@ // 500: // $ref: "#/responses/InternalError" r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKube)).Methods(http.MethodPost) + // swagger:operation DELETE /libpod/play/kube libpod PlayKubeDownLibpod + // --- + // tags: + // - containers + // - pods + // summary: Remove pods from play kube + // description: Tears down pods defined in a YAML file + // produces: + // - application/json + // responses: + // 200: + // $ref: "#/responses/DocsLibpodPlayKubeResponse" + // 500: + // $ref: "#/responses/InternalError" + r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKubeDown)).Methods(http.MethodDelete) return nil } diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_pods.go libpod-3.4.4+ds1/pkg/api/server/register_pods.go --- libpod-3.2.1+ds1/pkg/api/server/register_pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -17,7 +17,18 @@ // - in: query // name: filters // type: string - // description: needs description and plumbing for filters + // description: | + // JSON encoded value of the filters (a map[string][]string) to process on the pods list. Available filters: + // - `id=` Matches all of pod id. + // - `label=` or `label=:` Matches pods based on the presence of a label alone or a label and a value. + // - `name=` Matches all of pod name. + // - `until=` List pods created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `status=` Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded`. + // - `network=` Name or full ID of network. + // - `ctr-names=` Container name within the pod. + // - `ctr-ids=` Container ID within the pod. + // - `ctr-status=` Container status within the pod. + // - `ctr-number=` Number of containers in the pod. // responses: // 200: // $ref: "#/responses/ListPodsResponse" @@ -40,7 +51,7 @@ // responses: // 201: // schema: - // $ref: "#/definitions/IdResponse" + // $ref: "#/definitions/IDResponse" // 400: // $ref: "#/responses/BadParamError" // 409: diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_secrets.go libpod-3.4.4+ds1/pkg/api/server/register_secrets.go --- libpod-3.2.1+ds1/pkg/api/server/register_secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -44,6 +44,14 @@ // - secrets // summary: List secrets // description: Returns a list of secrets + // parameters: + // - in: query + // name: filters + // type: string + // description: | + // JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Currently available filters: + // - `name=[name]` Matches secrets name (accepts regex). + // - `id=[id]` Matches for full or partial ID. // produces: // - application/json // parameters: @@ -110,6 +118,14 @@ // - secrets (compat) // summary: List secrets // description: Returns a list of secrets + // parameters: + // - in: query + // name: filters + // type: string + // description: | + // JSON encoded value of the filters (a `map[string][]string`) to process on the secrets list. Currently available filters: + // - `name=[name]` Matches secrets name (accepts regex). + // - `id=[id]` Matches for full or partial ID. // produces: // - application/json // parameters: diff -Nru libpod-3.2.1+ds1/pkg/api/server/register_volumes.go libpod-3.4.4+ds1/pkg/api/server/register_volumes.go --- libpod-3.2.1+ds1/pkg/api/server/register_volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/register_volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -68,6 +68,7 @@ // - label= or label=: Matches volumes based on the presence of a label alone or a label and a value. // - name= Matches all of volume name. // - opt= Matches a storage driver options + // - `until=` List volumes created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. // responses: // '200': // "$ref": "#/responses/VolumeList" @@ -88,7 +89,8 @@ // description: | // JSON encoded value of filters (a map[string][]string) to match volumes against before pruning. // Available filters: - // - label (label=, label==, label!=, or label!==) Prune volumes with (or without, in case label!=... is used) the specified labels. + // - `until=` Prune volumes created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. // responses: // '200': // "$ref": "#/responses/VolumePruneResponse" @@ -165,6 +167,7 @@ // - driver= Matches volumes based on their driver. // - label= or label=: Matches volumes based on the presence of a label alone or a label and a value. // - name= Matches all of volume name. + // - `until=` List volumes created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. // // Note: // The boolean `dangling` filter is not yet implemented for this endpoint. @@ -268,7 +271,8 @@ // description: | // JSON encoded value of filters (a map[string][]string) to match volumes against before pruning. // Available filters: - // - label (label=, label==, label!=, or label!==) Prune volumes with (or without, in case label!=... is used) the specified labels. + // - `until=` Prune volumes created before this timestamp. The `` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time. + // - `label` (`label=`, `label==`, `label!=`, or `label!==`) Prune volumes with (or without, in case `label!=...` is used) the specified labels. // responses: // '200': // "$ref": "#/responses/DockerVolumePruneResponse" diff -Nru libpod-3.2.1+ds1/pkg/api/server/server.go libpod-3.4.4+ds1/pkg/api/server/server.go --- libpod-3.2.1+ds1/pkg/api/server/server.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/server.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,7 +7,7 @@ "net" "net/http" "os" - goRuntime "runtime" + "runtime" "strings" "sync" "syscall" @@ -17,11 +17,11 @@ "github.com/containers/podman/v3/libpod/shutdown" "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/api/server/idle" + "github.com/containers/podman/v3/pkg/api/types" "github.com/coreos/go-systemd/v22/activation" "github.com/coreos/go-systemd/v22/daemon" "github.com/gorilla/mux" "github.com/gorilla/schema" - "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -34,62 +34,87 @@ context.CancelFunc // Stop APIServer idleTracker *idle.Tracker // Track connections to support idle shutdown pprof *http.Server // Sidecar http server for providing performance data + CorsHeaders string // Inject CORS headers to each request } // Number of seconds to wait for next request, if exceeded shutdown server const ( + DefaultCorsHeaders = "" DefaultServiceDuration = 300 * time.Second UnlimitedServiceDuration = 0 * time.Second ) -// shutdownOnce ensures Shutdown() may safely be called from several go routines -var shutdownOnce sync.Once +var ( + // shutdownOnce ensures Shutdown() may safely be called from several go routines + shutdownOnce sync.Once +) + +type Options struct { + Timeout time.Duration + CorsHeaders string +} // NewServer will create and configure a new API server with all defaults func NewServer(runtime *libpod.Runtime) (*APIServer, error) { - return newServer(runtime, DefaultServiceDuration, nil) + return newServer(runtime, DefaultServiceDuration, nil, DefaultCorsHeaders) } // NewServerWithSettings will create and configure a new API server using provided settings -func NewServerWithSettings(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { - return newServer(runtime, duration, listener) +func NewServerWithSettings(runtime *libpod.Runtime, listener *net.Listener, opts Options) (*APIServer, error) { + return newServer(runtime, opts.Timeout, listener, opts.CorsHeaders) } -func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener) (*APIServer, error) { +func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Listener, corsHeaders string) (*APIServer, error) { // If listener not provided try socket activation protocol if listener == nil { if _, found := os.LookupEnv("LISTEN_PID"); !found { - return nil, errors.Errorf("Cannot create API Server, no listener provided and socket activation protocol is not active.") + return nil, fmt.Errorf("no service listener provided and socket activation protocol is not active") } listeners, err := activation.Listeners() if err != nil { - return nil, errors.Wrap(err, "Cannot retrieve file descriptors from systemd") + return nil, fmt.Errorf("cannot retrieve file descriptors from systemd: %w", err) } if len(listeners) != 1 { - return nil, errors.Errorf("Wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners)) + return nil, fmt.Errorf("wrong number of file descriptors for socket activation protocol (%d != 1)", len(listeners)) } listener = &listeners[0] } + if corsHeaders == "" { + logrus.Debug("CORS Headers were not set") + } else { + logrus.Debugf("CORS Headers were set to %s", corsHeaders) + } - logrus.Infof("API server listening on %q", (*listener).Addr()) + logrus.Infof("API service listening on %q", (*listener).Addr()) router := mux.NewRouter().UseEncodedPath() - idle := idle.NewTracker(duration) + tracker := idle.NewTracker(duration) server := APIServer{ Server: http.Server{ - Handler: router, - ReadHeaderTimeout: 20 * time.Second, - IdleTimeout: duration * 2, - ConnState: idle.ConnState, - ErrorLog: log.New(logrus.StandardLogger().Out, "", 0), + ConnContext: func(ctx context.Context, c net.Conn) context.Context { + return context.WithValue(ctx, types.ConnKey, c) + }, + ConnState: tracker.ConnState, + ErrorLog: log.New(logrus.StandardLogger().Out, "", 0), + Handler: router, + IdleTimeout: duration * 2, }, - Decoder: handlers.NewAPIDecoder(), - idleTracker: idle, + CorsHeaders: corsHeaders, Listener: *listener, - Runtime: runtime, + idleTracker: tracker, + } + + server.BaseContext = func(l net.Listener) context.Context { + ctx := context.WithValue(context.Background(), types.DecoderKey, handlers.NewAPIDecoder()) + ctx = context.WithValue(ctx, types.RuntimeKey, runtime) + ctx = context.WithValue(ctx, types.IdleTrackerKey, tracker) + return ctx } + // Capture panics and print stack traces for diagnostics, + // additionally process X-Reference-Id Header to support event correlation + router.Use(panicHandler(), referenceIDHandler()) router.NotFoundHandler = http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { // We can track user errors... @@ -137,6 +162,8 @@ } if logrus.IsLevelEnabled(logrus.TraceLevel) { + // If in trace mode log request and response bodies + router.Use(loggingHandler()) router.Walk(func(route *mux.Route, r *mux.Router, ancestors []*mux.Route) error { // nolint path, err := route.GetPathTemplate() if err != nil { @@ -165,13 +192,13 @@ payload := fmt.Sprintf("MAINPID=%d\n", os.Getpid()) payload += daemon.SdNotifyReady if sent, err := daemon.SdNotify(true, payload); err != nil { - logrus.Errorf("Error notifying systemd of Conmon PID: %s", err.Error()) + logrus.Error("API service error notifying systemd of Conmon PID: " + err.Error()) } else if !sent { - logrus.Warn("SDNotify not sent successfully") + logrus.Warn("API service unable to successfully send SDNotify") } if err := os.Unsetenv("INVOCATION_ID"); err != nil { - logrus.Errorf("Error unsetting INVOCATION_ID: %s", err.Error()) + logrus.Error("API service failed unsetting INVOCATION_ID: " + err.Error()) } } @@ -193,7 +220,7 @@ go func() { <-s.idleTracker.Done() - logrus.Debugf("API Server idle for %s", s.idleTracker.Duration.Round(time.Second).String()) + logrus.Debug("API service shutting down, idle for " + s.idleTracker.Duration.Round(time.Second).String()) _ = s.Shutdown() }() @@ -201,12 +228,12 @@ go func() { pprofMux := mux.NewRouter() pprofMux.PathPrefix("/debug/pprof").Handler(http.DefaultServeMux) - goRuntime.SetMutexProfileFraction(1) - goRuntime.SetBlockProfileRate(1) + runtime.SetMutexProfileFraction(1) + runtime.SetBlockProfileRate(1) s.pprof = &http.Server{Addr: "localhost:8888", Handler: pprofMux} err := s.pprof.ListenAndServe() if err != nil && err != http.ErrServerClosed { - logrus.Warn("Profiler Service failed: " + err.Error()) + logrus.Warn("API profiler service failed: " + err.Error()) } }() } @@ -218,7 +245,7 @@ go func() { err := s.Server.Serve(s.Listener) if err != nil && err != http.ErrServerClosed { - errChan <- errors.Wrap(err, "failed to start API server") + errChan <- fmt.Errorf("failed to start API service: %w", err) return } errChan <- nil @@ -230,14 +257,14 @@ // Shutdown is a clean shutdown waiting on existing clients func (s *APIServer) Shutdown() error { if s.idleTracker.Duration == UnlimitedServiceDuration { - logrus.Debug("APIServer.Shutdown ignored as Duration is UnlimitedService") + logrus.Debug("API service shutdown ignored as Duration is UnlimitedService") return nil } shutdownOnce.Do(func() { if logrus.IsLevelEnabled(logrus.DebugLevel) { - _, file, line, _ := goRuntime.Caller(1) - logrus.Debugf("APIServer.Shutdown by %s:%d, %d/%d connection(s)", + _, file, line, _ := runtime.Caller(1) + logrus.Debugf("API service shutdown by %s:%d, %d/%d connection(s)", file, line, s.idleTracker.ActiveConnections(), s.idleTracker.TotalConnections()) go func() { @@ -245,8 +272,7 @@ go func() { defer cancel() if err := s.pprof.Shutdown(ctx); err != nil { - logrus.Warn( - errors.Wrapf(err, "failed to cleanly shutdown pprof Server")) + logrus.Warn("Failed to cleanly shutdown API pprof service: " + err.Error()) } }() <-ctx.Done() @@ -260,8 +286,7 @@ err := s.Server.Shutdown(ctx) if err != nil && err != context.Canceled && err != http.ErrServerClosed { - logrus.Error( - errors.Wrapf(err, "failed to cleanly shutdown APIServer")) + logrus.Error("Failed to cleanly shutdown API service: " + err.Error()) } }() <-ctx.Done() diff -Nru libpod-3.2.1+ds1/pkg/api/server/swagger.go libpod-3.4.4+ds1/pkg/api/server/swagger.go --- libpod-3.2.1+ds1/pkg/api/server/swagger.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/server/swagger.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/errorhandling" + docker "github.com/docker/docker/api/types" ) // No such image @@ -134,18 +135,18 @@ } } -// Image summary -// swagger:response DockerImageSummary -type swagImageSummary struct { +// Image summary for compat API +// swagger:response DockerImageSummaryResponse +type swagDockerImageSummaryResponse struct { // in:body - Body []entities.ImageSummary + Body []docker.ImageSummary } -// Registries summary -// swagger:response DocsRegistriesList -type swagRegistriesList struct { +// Image summary for libpod API +// swagger:response LibpodImageSummaryResponse +type swagLibpodImageSummaryResponse struct { // in:body - Body entities.ListRegistriesReport + Body []entities.ImageSummary } // List Containers diff -Nru libpod-3.2.1+ds1/pkg/api/types/types.go libpod-3.4.4+ds1/pkg/api/types/types.go --- libpod-3.2.1+ds1/pkg/api/types/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/api/types/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,9 +1,18 @@ package types const ( - // DefaultAPIVersion is the version of the API the server defaults to. + // DefaultAPIVersion is the version of the compatible API the server defaults to DefaultAPIVersion = "1.40" // See https://docs.docker.com/engine/api/v1.40/ - // DefaultAPIVersion is the minimal required version of the API. + // MinimalAPIVersion is the minimal required version of the compatible API MinimalAPIVersion = "1.24" ) + +type APIContextKey int + +const ( + DecoderKey APIContextKey = iota + RuntimeKey + IdleTrackerKey + ConnKey +) diff -Nru libpod-3.2.1+ds1/pkg/auth/auth.go libpod-3.4.4+ds1/pkg/auth/auth.go --- libpod-3.2.1+ds1/pkg/auth/auth.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/auth/auth.go 2021-12-26 00:45:51.000000000 +0000 @@ -259,7 +259,9 @@ // tested, and we make sure to use the same code as the image backend. sys := types.SystemContext{AuthFilePath: authFilePath} for server, config := range authConfigs { - // Note that we do not validate the credentials here. Wassume + server = normalize(server) + + // Note that we do not validate the credentials here. We assume // that all credentials are valid. They'll be used on demand // later. if err := imageAuth.SetAuthentication(&sys, server, config.Username, config.Password); err != nil { @@ -270,6 +272,22 @@ return authFilePath, nil } +// normalize takes a server and removes the leading "http[s]://" prefix as well +// as removes path suffixes from docker registries. +func normalize(server string) string { + stripped := strings.TrimPrefix(server, "http://") + stripped = strings.TrimPrefix(stripped, "https://") + + /// Normalize docker registries + if strings.HasPrefix(stripped, "index.docker.io/") || + strings.HasPrefix(stripped, "registry-1.docker.io/") || + strings.HasPrefix(stripped, "docker.io/") { + stripped = strings.SplitN(stripped, "/", 2)[0] + } + + return stripped +} + // dockerAuthToImageAuth converts a docker auth config to one we're using // internally from c/image. Note that the Docker types look slightly // different, so we need to convert to be extra sure we're not running into diff -Nru libpod-3.2.1+ds1/pkg/auth/auth_test.go libpod-3.4.4+ds1/pkg/auth/auth_test.go --- libpod-3.2.1+ds1/pkg/auth/auth_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/auth/auth_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,66 @@ +package auth + +import ( + "io/ioutil" + "testing" + + "github.com/containers/image/v5/types" + "github.com/stretchr/testify/assert" +) + +func TestAuthConfigsToAuthFile(t *testing.T) { + for _, tc := range []struct { + name string + server string + shouldErr bool + expectedContains string + }{ + { + name: "empty auth configs", + server: "", + shouldErr: false, + expectedContains: "{}", + }, + { + name: "registry with prefix", + server: "my-registry.local/username", + shouldErr: false, + expectedContains: `"my-registry.local/username":`, + }, + { + name: "normalize https:// prefix", + server: "http://my-registry.local/username", + shouldErr: false, + expectedContains: `"my-registry.local/username":`, + }, + { + name: "normalize docker registry with https prefix", + server: "http://index.docker.io/v1/", + shouldErr: false, + expectedContains: `"index.docker.io":`, + }, + { + name: "normalize docker registry without https prefix", + server: "docker.io/v2/", + shouldErr: false, + expectedContains: `"docker.io":`, + }, + } { + configs := map[string]types.DockerAuthConfig{} + if tc.server != "" { + configs[tc.server] = types.DockerAuthConfig{} + } + + filePath, err := authConfigsToAuthFile(configs) + + if tc.shouldErr { + assert.NotNil(t, err) + assert.Empty(t, filePath) + } else { + assert.Nil(t, err) + content, err := ioutil.ReadFile(filePath) + assert.Nil(t, err) + assert.Contains(t, string(content), tc.expectedContains) + } + } +} diff -Nru libpod-3.2.1+ds1/pkg/autoupdate/autoupdate.go libpod-3.4.4+ds1/pkg/autoupdate/autoupdate.go --- libpod-3.2.1+ds1/pkg/autoupdate/autoupdate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/autoupdate/autoupdate.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,12 +9,13 @@ "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker" "github.com/containers/image/v5/docker/reference" - "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/systemd" systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" + "github.com/coreos/go-systemd/v22/dbus" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -74,12 +75,6 @@ return "", errors.Errorf("invalid auto-update policy %q: valid policies are %+q", s, keys) } -// Options include parameters for auto updates. -type Options struct { - // Authfile to use when contacting registries. - Authfile string -} - // ValidateImageReference checks if the specified imageName is a fully-qualified // image reference to the docker transport (without digest). Such a reference // includes a domain, name and tag (e.g., quay.io/podman/stable:latest). The @@ -93,7 +88,7 @@ } else if err != nil { repo, err := reference.Parse(imageName) if err != nil { - return errors.Wrap(err, "error enforcing fully-qualified docker transport reference for auto updates") + return errors.Wrap(err, "enforcing fully-qualified docker transport reference for auto updates") } if _, ok := repo.(reference.NamedTagged); !ok { return errors.Errorf("auto updates require fully-qualified image references (no tag): %q", imageName) @@ -119,7 +114,7 @@ // // It returns a slice of successfully restarted systemd units and a slice of // errors encountered during auto update. -func AutoUpdate(runtime *libpod.Runtime, options Options) ([]string, []error) { +func AutoUpdate(ctx context.Context, runtime *libpod.Runtime, options entities.AutoUpdateOptions) ([]*entities.AutoUpdateReport, []error) { // Create a map from `image ID -> []*Container`. containerMap, errs := imageContainersMap(runtime) if len(containerMap) == 0 { @@ -130,7 +125,7 @@ listOptions := &libimage.ListImagesOptions{ Filters: []string{"readonly=false"}, } - imagesSlice, err := runtime.LibimageRuntime().ListImages(context.Background(), nil, listOptions) + imagesSlice, err := runtime.LibimageRuntime().ListImages(ctx, nil, listOptions) if err != nil { return nil, []error{err} } @@ -147,8 +142,8 @@ } defer conn.Close() - // Update images. - containersToRestart := []*libpod.Container{} + // Update all images/container according to their auto-update policy. + var allReports []*entities.AutoUpdateReport updatedRawImages := make(map[string]bool) for imageID, policyMapper := range containerMap { image, exists := imageMap[imageID] @@ -156,76 +151,187 @@ errs = append(errs, errors.Errorf("container image ID %q not found in local storage", imageID)) return nil, errs } - // Now we have to check if the image of any containers must be updated. - // Note that the image ID is NOT enough for this check as a given image - // may have multiple tags. - for _, registryCtr := range policyMapper[PolicyRegistryImage] { - cid := registryCtr.ID() - rawImageName := registryCtr.RawImageName() - if rawImageName == "" { - errs = append(errs, errors.Errorf("error registry auto-updating container %q: raw-image name is empty", cid)) - } - readAuthenticationPath(registryCtr, options) - needsUpdate, err := newerRemoteImageAvailable(runtime, image, rawImageName, options) + + for _, ctr := range policyMapper[PolicyRegistryImage] { + report, err := autoUpdateRegistry(ctx, image, ctr, updatedRawImages, &options, conn, runtime) if err != nil { - errs = append(errs, errors.Wrapf(err, "error registry auto-updating container %q: image check for %q failed", cid, rawImageName)) - continue + errs = append(errs, err) } - - if needsUpdate { - logrus.Infof("Auto-updating container %q using registry image %q", cid, rawImageName) - if _, updated := updatedRawImages[rawImageName]; !updated { - _, err = updateImage(runtime, rawImageName, options) - if err != nil { - errs = append(errs, errors.Wrapf(err, "error registry auto-updating container %q: image update for %q failed", cid, rawImageName)) - continue - } - updatedRawImages[rawImageName] = true - } - containersToRestart = append(containersToRestart, registryCtr) + if report != nil { + allReports = append(allReports, report) } } - for _, localCtr := range policyMapper[PolicyLocalImage] { - cid := localCtr.ID() - rawImageName := localCtr.RawImageName() - if rawImageName == "" { - errs = append(errs, errors.Errorf("error locally auto-updating container %q: raw-image name is empty", cid)) - } - // This avoids restarting containers unnecessarily. - needsUpdate, err := newerLocalImageAvailable(runtime, image, rawImageName) + for _, ctr := range policyMapper[PolicyLocalImage] { + report, err := autoUpdateLocally(ctx, image, ctr, &options, conn, runtime) if err != nil { - errs = append(errs, errors.Wrapf(err, "error locally auto-updating container %q: image check for %q failed", cid, rawImageName)) - continue + errs = append(errs, err) } - - if needsUpdate { - logrus.Infof("Auto-updating container %q using local image %q", cid, rawImageName) - containersToRestart = append(containersToRestart, localCtr) + if report != nil { + allReports = append(allReports, report) } } } - // Restart containers. - updatedUnits := []string{} - for _, ctr := range containersToRestart { - labels := ctr.Labels() - unit, exists := labels[systemdDefine.EnvVariable] - if !exists { - // Shouldn't happen but let's be sure of it. - errs = append(errs, errors.Errorf("error auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable)) - continue - } - _, err := conn.RestartUnit(unit, "replace", nil) - if err != nil { - errs = append(errs, errors.Wrapf(err, "error auto-updating container %q: restarting systemd unit %q failed", ctr.ID(), unit)) - continue + return allReports, errs +} + +// autoUpdateRegistry updates the image/container according to the "registry" policy. +func autoUpdateRegistry(ctx context.Context, image *libimage.Image, ctr *libpod.Container, updatedRawImages map[string]bool, options *entities.AutoUpdateOptions, conn *dbus.Conn, runtime *libpod.Runtime) (*entities.AutoUpdateReport, error) { + cid := ctr.ID() + rawImageName := ctr.RawImageName() + if rawImageName == "" { + return nil, errors.Errorf("registry auto-updating container %q: raw-image name is empty", cid) + } + + labels := ctr.Labels() + unit, exists := labels[systemdDefine.EnvVariable] + if !exists { + return nil, errors.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable) + } + + report := &entities.AutoUpdateReport{ + ContainerID: cid, + ContainerName: ctr.Name(), + ImageName: rawImageName, + Policy: PolicyRegistryImage, + SystemdUnit: unit, + Updated: "failed", + } + + if _, updated := updatedRawImages[rawImageName]; updated { + logrus.Infof("Auto-updating container %q using registry image %q", cid, rawImageName) + if err := restartSystemdUnit(ctx, ctr, unit, conn); err != nil { + return report, err } - logrus.Infof("Successfully restarted systemd unit %q", unit) - updatedUnits = append(updatedUnits, unit) + report.Updated = "true" + return report, nil } - return updatedUnits, errs + authfile := getAuthfilePath(ctr, options) + needsUpdate, err := newerRemoteImageAvailable(ctx, runtime, image, rawImageName, authfile) + if err != nil { + return report, errors.Wrapf(err, "registry auto-updating container %q: image check for %q failed", cid, rawImageName) + } + + if !needsUpdate { + report.Updated = "false" + return report, nil + } + + if options.DryRun { + report.Updated = "pending" + return report, nil + } + + if _, err := updateImage(ctx, runtime, rawImageName, authfile); err != nil { + return report, errors.Wrapf(err, "registry auto-updating container %q: image update for %q failed", cid, rawImageName) + } + updatedRawImages[rawImageName] = true + + logrus.Infof("Auto-updating container %q using registry image %q", cid, rawImageName) + updateErr := restartSystemdUnit(ctx, ctr, unit, conn) + if updateErr == nil { + report.Updated = "true" + return report, nil + } + + if !options.Rollback { + return report, updateErr + } + + // To fallback, simply retag the old image and restart the service. + if err := image.Tag(rawImageName); err != nil { + return report, errors.Wrap(err, "falling back to previous image") + } + if err := restartSystemdUnit(ctx, ctr, unit, conn); err != nil { + return report, errors.Wrap(err, "restarting unit with old image during fallback") + } + + report.Updated = "rolled back" + return report, nil +} + +// autoUpdateRegistry updates the image/container according to the "local" policy. +func autoUpdateLocally(ctx context.Context, image *libimage.Image, ctr *libpod.Container, options *entities.AutoUpdateOptions, conn *dbus.Conn, runtime *libpod.Runtime) (*entities.AutoUpdateReport, error) { + cid := ctr.ID() + rawImageName := ctr.RawImageName() + if rawImageName == "" { + return nil, errors.Errorf("locally auto-updating container %q: raw-image name is empty", cid) + } + + labels := ctr.Labels() + unit, exists := labels[systemdDefine.EnvVariable] + if !exists { + return nil, errors.Errorf("auto-updating container %q: no %s label found", ctr.ID(), systemdDefine.EnvVariable) + } + + report := &entities.AutoUpdateReport{ + ContainerID: cid, + ContainerName: ctr.Name(), + ImageName: rawImageName, + Policy: PolicyLocalImage, + SystemdUnit: unit, + Updated: "failed", + } + + needsUpdate, err := newerLocalImageAvailable(runtime, image, rawImageName) + if err != nil { + return report, errors.Wrapf(err, "locally auto-updating container %q: image check for %q failed", cid, rawImageName) + } + + if !needsUpdate { + report.Updated = "false" + return report, nil + } + + if options.DryRun { + report.Updated = "pending" + return report, nil + } + + logrus.Infof("Auto-updating container %q using local image %q", cid, rawImageName) + updateErr := restartSystemdUnit(ctx, ctr, unit, conn) + if updateErr == nil { + report.Updated = "true" + return report, nil + } + + if !options.Rollback { + return report, updateErr + } + + // To fallback, simply retag the old image and restart the service. + if err := image.Tag(rawImageName); err != nil { + return report, errors.Wrap(err, "falling back to previous image") + } + if err := restartSystemdUnit(ctx, ctr, unit, conn); err != nil { + return report, errors.Wrap(err, "restarting unit with old image during fallback") + } + + report.Updated = "rolled back" + return report, nil +} + +// restartSystemdUnit restarts the systemd unit the container is running in. +func restartSystemdUnit(ctx context.Context, ctr *libpod.Container, unit string, conn *dbus.Conn) error { + restartChan := make(chan string) + if _, err := conn.RestartUnitContext(ctx, unit, "replace", restartChan); err != nil { + return errors.Wrapf(err, "auto-updating container %q: restarting systemd unit %q failed", ctr.ID(), unit) + } + + // Wait for the restart to finish and actually check if it was + // successful or not. + result := <-restartChan + + switch result { + case "done": + logrus.Infof("Successfully restarted systemd unit %q of container %q", unit, ctr.ID()) + return nil + + default: + return errors.Errorf("auto-updating container %q: restarting systemd unit %q failed: expected %q but received %q", ctr.ID(), unit, "done", result) + } } // imageContainersMap generates a map[image ID] -> [containers using the image] @@ -280,52 +386,26 @@ return containerMap, errors } -// readAuthenticationPath reads a container's labels and reads authentication path into options -func readAuthenticationPath(ctr *libpod.Container, options Options) { +// getAuthfilePath returns an authfile path, if set. The authfile label in the +// container, if set, as precedence over the one set in the options. +func getAuthfilePath(ctr *libpod.Container, options *entities.AutoUpdateOptions) string { labels := ctr.Labels() authFilePath, exists := labels[AuthfileLabel] if exists { - options.Authfile = authFilePath + return authFilePath } + return options.Authfile } // newerRemoteImageAvailable returns true if there corresponding image on the remote // registry is newer. -func newerRemoteImageAvailable(runtime *libpod.Runtime, img *libimage.Image, origName string, options Options) (bool, error) { +func newerRemoteImageAvailable(ctx context.Context, runtime *libpod.Runtime, img *libimage.Image, origName string, authfile string) (bool, error) { remoteRef, err := docker.ParseReference("//" + origName) if err != nil { return false, err } - - data, err := img.Inspect(context.Background(), false) - if err != nil { - return false, err - } - - sys := runtime.SystemContext() - sys.AuthFilePath = options.Authfile - - // We need to account for the arch that the image uses. It seems - // common on ARM to tweak this option to pull the correct image. See - // github.com/containers/podman/issues/6613. - sys.ArchitectureChoice = data.Architecture - - remoteImg, err := remoteRef.NewImage(context.Background(), sys) - if err != nil { - return false, err - } - - rawManifest, _, err := remoteImg.Manifest(context.Background()) - if err != nil { - return false, err - } - - remoteDigest, err := manifest.Digest(rawManifest) - if err != nil { - return false, err - } - - return img.Digest().String() != remoteDigest.String(), nil + options := &libimage.HasDifferentDigestOptions{AuthFilePath: authfile} + return img.HasDifferentDigest(ctx, remoteRef, options) } // newerLocalImageAvailable returns true if the container and local image have different digests @@ -334,21 +414,16 @@ if err != nil { return false, err } - - localDigest := localImg.Digest().String() - - ctrDigest := img.Digest().String() - - return localDigest != ctrDigest, nil + return localImg.Digest().String() != img.Digest().String(), nil } // updateImage pulls the specified image. -func updateImage(runtime *libpod.Runtime, name string, options Options) (*libimage.Image, error) { +func updateImage(ctx context.Context, runtime *libpod.Runtime, name, authfile string) (*libimage.Image, error) { pullOptions := &libimage.PullOptions{} - pullOptions.AuthFilePath = options.Authfile + pullOptions.AuthFilePath = authfile pullOptions.Writer = os.Stderr - pulledImages, err := runtime.LibimageRuntime().Pull(context.Background(), name, config.PullPolicyAlways, pullOptions) + pulledImages, err := runtime.LibimageRuntime().Pull(ctx, name, config.PullPolicyAlways, pullOptions) if err != nil { return nil, err } diff -Nru libpod-3.2.1+ds1/pkg/bindings/connection.go libpod-3.4.4+ds1/pkg/bindings/connection.go --- libpod-3.2.1+ds1/pkg/bindings/connection.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/connection.go 2021-12-26 00:45:51.000000000 +0000 @@ -56,7 +56,7 @@ return NewConnectionWithIdentity(ctx, uri, "") } -// NewConnection takes a URI as a string and returns a context with the +// NewConnectionWithIdentity takes a URI as a string and returns a context with the // Connection embedded as a value. This context needs to be passed to each // endpoint to work correctly. // @@ -112,12 +112,12 @@ return nil, errors.Errorf("unable to create connection. %q is not a supported schema", _url.Scheme) } if err != nil { - return nil, errors.Wrapf(err, "failed to create %sClient", _url.Scheme) + return nil, errors.Wrapf(err, "unable to connect to Podman. failed to create %sClient", _url.Scheme) } ctx = context.WithValue(ctx, clientKey, &connection) if err := pingNewConnection(ctx); err != nil { - return nil, errors.Wrap(err, "cannot connect to the Podman socket, please verify that Podman REST API service is running") + return nil, errors.Wrap(err, "unable to connect to Podman socket") } return ctx, nil } @@ -149,6 +149,7 @@ if err != nil { return err } + defer response.Body.Close() if response.StatusCode == http.StatusOK { versionHdr := response.Header.Get("Libpod-API-Version") @@ -327,7 +328,7 @@ uri := fmt.Sprintf("http://d/v%d.%d.%d/libpod"+endpoint, params...) logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri) - req, err := http.NewRequest(httpMethod, uri, httpBody) + req, err := http.NewRequestWithContext(context.WithValue(context.Background(), clientKey, c), httpMethod, uri, httpBody) if err != nil { return nil, err } @@ -337,9 +338,8 @@ for key, val := range header { req.Header.Set(key, val) } - req = req.WithContext(context.WithValue(context.Background(), clientKey, c)) // Give the Do three chances in the case of a comm/service hiccup - for i := 0; i < 3; i++ { + for i := 1; i <= 3; i++ { response, err = c.Client.Do(req) // nolint if err == nil { break @@ -359,7 +359,7 @@ return jsoniter.MarshalToString(lowerCaseKeys) } -// IsInformation returns true if the response code is 1xx +// IsInformational returns true if the response code is 1xx func (h *APIResponse) IsInformational() bool { return h.Response.StatusCode/100 == 1 } @@ -379,6 +379,11 @@ return h.Response.StatusCode/100 == 4 } +// IsConflictError returns true if the response code is 409 +func (h *APIResponse) IsConflictError() bool { + return h.Response.StatusCode == 409 +} + // IsServerError returns true if the response code is 5xx func (h *APIResponse) IsServerError() bool { return h.Response.StatusCode/100 == 5 diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/archive.go libpod-3.4.4+ds1/pkg/bindings/containers/archive.go --- libpod-3.2.1+ds1/pkg/bindings/containers/archive.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/archive.go 2021-12-26 00:45:51.000000000 +0000 @@ -27,6 +27,7 @@ if err != nil { return nil, err } + defer response.Body.Close() var finalErr error if response.StatusCode == http.StatusNotFound { @@ -50,11 +51,23 @@ } func CopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { + return CopyFromArchiveWithOptions(ctx, nameOrID, path, reader, nil) +} + +// CopyFromArchiveWithOptions copy files into container +// +// FIXME: remove this function and make CopyFromArchive accept the option as the last parameter in podman 4.0 +func CopyFromArchiveWithOptions(ctx context.Context, nameOrID string, path string, reader io.Reader, options *CopyOptions) (entities.ContainerCopyFunc, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - params := url.Values{} + + params, err := options.ToParams() + if err != nil { + return nil, err + } + params.Set("path", path) return func() error { @@ -62,6 +75,7 @@ if err != nil { return err } + if response.StatusCode != http.StatusOK { return errors.New(response.Status) } @@ -69,6 +83,7 @@ }, nil } +// CopyToArchive copy files from container func CopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { conn, err := bindings.GetClient(ctx) if err != nil { @@ -81,11 +96,14 @@ if err != nil { return nil, err } + if response.StatusCode != http.StatusOK { + defer response.Body.Close() return nil, response.Process(nil) } return func() error { + defer response.Body.Close() _, err := io.Copy(writer, response.Body) return err }, nil diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/attach.go libpod-3.4.4+ds1/pkg/bindings/containers/attach.go --- libpod-3.2.1+ds1/pkg/bindings/containers/attach.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/attach.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,6 +25,12 @@ "golang.org/x/crypto/ssh/terminal" ) +// The CloseWriter interface is used to determine whether we can do a one-sided +// close of a hijacked connection. +type CloseWriter interface { + CloseWrite() error +} + // Attach attaches to a running container func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool, options *AttachOptions) error { if options == nil { @@ -128,7 +134,9 @@ if err != nil { return err } + if !(response.IsSuccess() || response.IsInformational()) { + defer response.Body.Close() return response.Process(nil) } @@ -149,17 +157,23 @@ } stdoutChan := make(chan error) - stdinChan := make(chan error) + stdinChan := make(chan error, 1) //stdin channel should not block if isSet.stdin { go func() { logrus.Debugf("Copying STDIN to socket") _, err := utils.CopyDetachable(socket, stdin, detachKeysInBytes) - if err != nil && err != define.ErrDetach { logrus.Error("failed to write input to service: " + err.Error()) } + if err == nil { + if closeWrite, ok := socket.(CloseWriter); ok { + if err := closeWrite.CloseWrite(); err != nil { + logrus.Warnf("Failed to close STDIN for writing: %v", err) + } + } + } stdinChan <- err }() } @@ -195,12 +209,12 @@ } } } else { - logrus.Debugf("Copying standard streams of container in non-terminal mode") + logrus.Debugf("Copying standard streams of container %q in non-terminal mode", ctnr.ID) for { // Read multiplexed channels and write to appropriate stream fd, l, err := DemuxHeader(socket, buffer) if err != nil { - if errors.Is(err, io.EOF) { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { return nil } return err @@ -312,6 +326,8 @@ if err != nil { return err } + defer rsp.Body.Close() + return rsp.Process(nil) } @@ -343,7 +359,7 @@ resizeErr = ResizeContainerTTY(ctx, id, new(ResizeTTYOptions).WithHeight(h).WithWidth(w)) } if resizeErr != nil { - logrus.Warnf("failed to resize TTY: %v", resizeErr) + logrus.Infof("failed to resize TTY: %v", resizeErr) } } @@ -395,6 +411,7 @@ if err != nil { return err } + defer resp.Body.Close() respStruct := new(define.InspectExecSession) if err := resp.Process(respStruct); err != nil { @@ -408,6 +425,17 @@ // If we are in TTY mode, we need to set raw mode for the terminal. // TODO: Share all of this with Attach() for containers. needTTY := terminalFile != nil && terminal.IsTerminal(int(terminalFile.Fd())) && isTerm + + body := struct { + Detach bool `json:"Detach"` + TTY bool `json:"Tty"` + Height uint16 `json:"h"` + Width uint16 `json:"w"` + }{ + Detach: false, + TTY: needTTY, + } + if needTTY { state, err := setRawTerminal(terminalFile) if err != nil { @@ -419,13 +447,14 @@ } logrus.SetFormatter(&logrus.TextFormatter{}) }() + w, h, err := terminal.GetSize(int(terminalFile.Fd())) + if err != nil { + logrus.Warnf("failed to obtain TTY size: %v", err) + } + body.Width = uint16(w) + body.Height = uint16(h) } - body := struct { - Detach bool `json:"Detach"` - }{ - Detach: false, - } bodyJSON, err := json.Marshal(body) if err != nil { return err @@ -453,6 +482,8 @@ if err != nil { return err } + defer response.Body.Close() + if !(response.IsSuccess() || response.IsInformational()) { return response.Process(nil) } @@ -473,6 +504,13 @@ if err != nil { logrus.Error("failed to write input to service: " + err.Error()) } + + if closeWrite, ok := socket.(CloseWriter); ok { + logrus.Debugf("Closing STDIN") + if err := closeWrite.CloseWrite(); err != nil { + logrus.Warnf("Failed to close STDIN for writing: %v", err) + } + } }() } @@ -493,7 +531,7 @@ // Read multiplexed channels and write to appropriate stream fd, l, err := DemuxHeader(socket, buffer) if err != nil { - if errors.Is(err, io.EOF) { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { return nil } return err diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/checkpoint.go libpod-3.4.4+ds1/pkg/bindings/containers/checkpoint.go --- libpod-3.2.1+ds1/pkg/bindings/containers/checkpoint.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/checkpoint.go 2021-12-26 00:45:51.000000000 +0000 @@ -27,6 +27,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -54,5 +56,7 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/commit.go libpod-3.4.4+ds1/pkg/bindings/containers/commit.go --- libpod-3.2.1+ds1/pkg/bindings/containers/commit.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/commit.go 2021-12-26 00:45:51.000000000 +0000 @@ -28,5 +28,7 @@ if err != nil { return id, err } + defer response.Body.Close() + return id, response.Process(&id) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/containers.go libpod-3.4.4+ds1/pkg/bindings/containers/containers.go --- libpod-3.2.1+ds1/pkg/bindings/containers/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -42,6 +42,8 @@ if err != nil { return containers, err } + defer response.Body.Close() + return containers, response.Process(&containers) } @@ -66,6 +68,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return reports, response.Process(&reports) } @@ -90,6 +94,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -113,6 +119,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + inspect := define.InspectContainerData{} return &inspect, response.Process(&inspect) } @@ -136,6 +144,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -154,6 +164,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -176,6 +188,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -199,6 +213,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -223,11 +239,15 @@ if err != nil { return nil, err } + if !response.IsSuccess() { + return nil, response.Process(nil) + } statsChan := make(chan entities.ContainerStatsReport) go func() { defer close(statsChan) + defer response.Body.Close() dec := json.NewDecoder(response.Body) doStream := true @@ -242,6 +262,7 @@ default: // fall through and do some work } + var report entities.ContainerStatsReport if err := dec.Decode(&report); err != nil { report = entities.ContainerStatsReport{Error: err} @@ -276,6 +297,7 @@ if err != nil { return nil, err } + defer response.Body.Close() body := handlers.ContainerTopOKBody{} if err = response.Process(&body); err != nil { @@ -308,6 +330,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -331,6 +355,8 @@ if err != nil { return exitCode, err } + defer response.Body.Close() + return exitCode, response.Process(&exitCode) } @@ -350,6 +376,8 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } @@ -371,6 +399,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -390,6 +420,8 @@ if err != nil { return err } + defer response.Body.Close() + if response.StatusCode/100 == 2 { _, err = io.Copy(w, response.Body) return err @@ -413,6 +445,8 @@ if err != nil { return err } + defer response.Body.Close() + if response.StatusCode == http.StatusNotModified { return errors.Wrapf(define.ErrCtrStateInvalid, "container %s has already been created in runtime", nameOrID) } @@ -432,5 +466,7 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/create.go libpod-3.4.4+ds1/pkg/bindings/containers/create.go --- libpod-3.2.1+ds1/pkg/bindings/containers/create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/create.go 2021-12-26 00:45:51.000000000 +0000 @@ -30,5 +30,7 @@ if err != nil { return ccr, err } + defer response.Body.Close() + return ccr, response.Process(&ccr) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/diff.go libpod-3.4.4+ds1/pkg/bindings/containers/diff.go --- libpod-3.2.1+ds1/pkg/bindings/containers/diff.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,16 +13,21 @@ if options == nil { options = new(DiffOptions) } - _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nil, nameOrID) + params, err := options.ToParams() if err != nil { return nil, err } + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", params, nil, nameOrID) + if err != nil { + return nil, err + } + defer response.Body.Close() + var changes []archive.Change return changes, response.Process(&changes) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/exec.go libpod-3.4.4+ds1/pkg/bindings/containers/exec.go --- libpod-3.2.1+ds1/pkg/bindings/containers/exec.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/exec.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,6 +39,7 @@ if err != nil { return "", err } + defer resp.Body.Close() respStruct := new(handlers.ExecCreateResponse) if err := resp.Process(respStruct); err != nil { @@ -66,6 +67,7 @@ if err != nil { return nil, err } + defer resp.Body.Close() respStruct := new(define.InspectExecSession) if err := resp.Process(respStruct); err != nil { @@ -103,6 +105,7 @@ if err != nil { return err } + defer resp.Body.Close() return resp.Process(nil) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/healthcheck.go libpod-3.4.4+ds1/pkg/bindings/containers/healthcheck.go --- libpod-3.2.1+ds1/pkg/bindings/containers/healthcheck.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/healthcheck.go 2021-12-26 00:45:51.000000000 +0000 @@ -26,5 +26,7 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &status, response.Process(&status) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/logs.go libpod-3.4.4+ds1/pkg/bindings/containers/logs.go --- libpod-3.2.1+ds1/pkg/bindings/containers/logs.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/logs.go 2021-12-26 00:45:51.000000000 +0000 @@ -33,12 +33,13 @@ if err != nil { return err } + defer response.Body.Close() buffer := make([]byte, 1024) for { fd, l, err := DemuxHeader(response.Body, buffer) if err != nil { - if errors.Is(err, io.EOF) { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { return nil } return err diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/mount.go libpod-3.4.4+ds1/pkg/bindings/containers/mount.go --- libpod-3.2.1+ds1/pkg/bindings/containers/mount.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/mount.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,6 +25,8 @@ if err != nil { return path, err } + defer response.Body.Close() + return path, response.Process(&path) } @@ -43,6 +45,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -61,5 +65,7 @@ if err != nil { return mounts, err } + defer response.Body.Close() + return mounts, response.Process(&mounts) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/rename.go libpod-3.4.4+ds1/pkg/bindings/containers/rename.go --- libpod-3.2.1+ds1/pkg/bindings/containers/rename.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/rename.go 2021-12-26 00:45:51.000000000 +0000 @@ -24,5 +24,7 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_attach_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_attach_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_attach_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_attach_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,64 +7,57 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *AttachOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *AttachOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithDetachKeys +// WithDetachKeys set keys to detach from running container func (o *AttachOptions) WithDetachKeys(value string) *AttachOptions { - v := &value - o.DetachKeys = v + o.DetachKeys = &value return o } -// GetDetachKeys +// GetDetachKeys returns value of keys to detach from running container func (o *AttachOptions) GetDetachKeys() string { - var detachKeys string if o.DetachKeys == nil { - return detachKeys + var z string + return z } return *o.DetachKeys } -// WithLogs +// WithLogs set flag to return all logs from container when true func (o *AttachOptions) WithLogs(value bool) *AttachOptions { - v := &value - o.Logs = v + o.Logs = &value return o } -// GetLogs +// GetLogs returns value of flag to return all logs from container when true func (o *AttachOptions) GetLogs() bool { - var logs bool if o.Logs == nil { - return logs + var z bool + return z } return *o.Logs } -// WithStream +// WithStream set flag only return container logs when false and Logs is true func (o *AttachOptions) WithStream(value bool) *AttachOptions { - v := &value - o.Stream = v + o.Stream = &value return o } -// GetStream +// GetStream returns value of flag only return container logs when false and Logs is true func (o *AttachOptions) GetStream() bool { - var stream bool if o.Stream == nil { - return stream + var z bool + return z } return *o.Stream } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_checkpoint_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_checkpoint_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_checkpoint_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_checkpoint_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,96 +7,87 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CheckpointOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CheckpointOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithExport +// WithExport set field Export to given value func (o *CheckpointOptions) WithExport(value string) *CheckpointOptions { - v := &value - o.Export = v + o.Export = &value return o } -// GetExport +// GetExport returns value of field Export func (o *CheckpointOptions) GetExport() string { - var export string if o.Export == nil { - return export + var z string + return z } return *o.Export } -// WithIgnoreRootfs +// WithIgnoreRootfs set field IgnoreRootfs to given value func (o *CheckpointOptions) WithIgnoreRootfs(value bool) *CheckpointOptions { - v := &value - o.IgnoreRootfs = v + o.IgnoreRootfs = &value return o } -// GetIgnoreRootfs +// GetIgnoreRootfs returns value of field IgnoreRootfs func (o *CheckpointOptions) GetIgnoreRootfs() bool { - var ignoreRootfs bool if o.IgnoreRootfs == nil { - return ignoreRootfs + var z bool + return z } return *o.IgnoreRootfs } -// WithKeep +// WithKeep set field Keep to given value func (o *CheckpointOptions) WithKeep(value bool) *CheckpointOptions { - v := &value - o.Keep = v + o.Keep = &value return o } -// GetKeep +// GetKeep returns value of field Keep func (o *CheckpointOptions) GetKeep() bool { - var keep bool if o.Keep == nil { - return keep + var z bool + return z } return *o.Keep } -// WithLeaveRunning +// WithLeaveRunning set field LeaveRunning to given value func (o *CheckpointOptions) WithLeaveRunning(value bool) *CheckpointOptions { - v := &value - o.LeaveRunning = v + o.LeaveRunning = &value return o } -// GetLeaveRunning +// GetLeaveRunning returns value of field LeaveRunning func (o *CheckpointOptions) GetLeaveRunning() bool { - var leaveRunning bool if o.LeaveRunning == nil { - return leaveRunning + var z bool + return z } return *o.LeaveRunning } -// WithTCPEstablished +// WithTCPEstablished set field TCPEstablished to given value func (o *CheckpointOptions) WithTCPEstablished(value bool) *CheckpointOptions { - v := &value - o.TCPEstablished = v + o.TCPEstablished = &value return o } -// GetTCPEstablished +// GetTCPEstablished returns value of field TCPEstablished func (o *CheckpointOptions) GetTCPEstablished() bool { - var tCPEstablished bool if o.TCPEstablished == nil { - return tCPEstablished + var z bool + return z } return *o.TCPEstablished } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_commit_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_commit_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_commit_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_commit_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,128 +7,117 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CommitOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CommitOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAuthor +// WithAuthor set field Author to given value func (o *CommitOptions) WithAuthor(value string) *CommitOptions { - v := &value - o.Author = v + o.Author = &value return o } -// GetAuthor +// GetAuthor returns value of field Author func (o *CommitOptions) GetAuthor() string { - var author string if o.Author == nil { - return author + var z string + return z } return *o.Author } -// WithChanges +// WithChanges set field Changes to given value func (o *CommitOptions) WithChanges(value []string) *CommitOptions { - v := value - o.Changes = v + o.Changes = value return o } -// GetChanges +// GetChanges returns value of field Changes func (o *CommitOptions) GetChanges() []string { - var changes []string if o.Changes == nil { - return changes + var z []string + return z } return o.Changes } -// WithComment +// WithComment set field Comment to given value func (o *CommitOptions) WithComment(value string) *CommitOptions { - v := &value - o.Comment = v + o.Comment = &value return o } -// GetComment +// GetComment returns value of field Comment func (o *CommitOptions) GetComment() string { - var comment string if o.Comment == nil { - return comment + var z string + return z } return *o.Comment } -// WithFormat +// WithFormat set field Format to given value func (o *CommitOptions) WithFormat(value string) *CommitOptions { - v := &value - o.Format = v + o.Format = &value return o } -// GetFormat +// GetFormat returns value of field Format func (o *CommitOptions) GetFormat() string { - var format string if o.Format == nil { - return format + var z string + return z } return *o.Format } -// WithPause +// WithPause set field Pause to given value func (o *CommitOptions) WithPause(value bool) *CommitOptions { - v := &value - o.Pause = v + o.Pause = &value return o } -// GetPause +// GetPause returns value of field Pause func (o *CommitOptions) GetPause() bool { - var pause bool if o.Pause == nil { - return pause + var z bool + return z } return *o.Pause } -// WithRepo +// WithRepo set field Repo to given value func (o *CommitOptions) WithRepo(value string) *CommitOptions { - v := &value - o.Repo = v + o.Repo = &value return o } -// GetRepo +// GetRepo returns value of field Repo func (o *CommitOptions) GetRepo() string { - var repo string if o.Repo == nil { - return repo + var z string + return z } return *o.Repo } -// WithTag +// WithTag set field Tag to given value func (o *CommitOptions) WithTag(value string) *CommitOptions { - v := &value - o.Tag = v + o.Tag = &value return o } -// GetTag +// GetTag returns value of field Tag func (o *CommitOptions) GetTag() string { - var tag string if o.Tag == nil { - return tag + var z string + return z } return *o.Tag } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_copy_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_copy_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_copy_options.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_copy_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,48 @@ +// Code generated by go generate; DO NOT EDIT. +package containers + +import ( + "net/url" + + "github.com/containers/podman/v3/pkg/bindings/internal/util" +) + +// Changed returns true if named field has been set +func (o *CopyOptions) Changed(fieldName string) bool { + return util.Changed(o, fieldName) +} + +// ToParams formats struct fields to be passed to API service +func (o *CopyOptions) ToParams() (url.Values, error) { + return util.ToParams(o) +} + +// WithChown set field Chown to given value +func (o *CopyOptions) WithChown(value bool) *CopyOptions { + o.Chown = &value + return o +} + +// GetChown returns value of field Chown +func (o *CopyOptions) GetChown() bool { + if o.Chown == nil { + var z bool + return z + } + return *o.Chown +} + +// WithRename set field Rename to given value +func (o *CopyOptions) WithRename(value map[string]string) *CopyOptions { + o.Rename = value + return o +} + +// GetRename returns value of field Rename +func (o *CopyOptions) GetRename() map[string]string { + if o.Rename == nil { + var z map[string]string + return z + } + return o.Rename +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_create_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_create_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_create_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_create_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CreateOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CreateOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_diff_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_diff_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_diff_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_diff_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *DiffOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *DiffOptions) ToParams() (url.Values, error) { return util.ToParams(o) } + +// WithParent set field Parent to given value +func (o *DiffOptions) WithParent(value string) *DiffOptions { + o.Parent = &value + return o +} + +// GetParent returns value of field Parent +func (o *DiffOptions) GetParent() string { + if o.Parent == nil { + var z string + return z + } + return *o.Parent +} + +// WithDiffType set field DiffType to given value +func (o *DiffOptions) WithDiffType(value string) *DiffOptions { + o.DiffType = &value + return o +} + +// GetDiffType returns value of field DiffType +func (o *DiffOptions) GetDiffType() string { + if o.DiffType == nil { + var z string + return z + } + return *o.DiffType +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_execinspect_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_execinspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_execinspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_execinspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExecInspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExecInspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_execstartandattach_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_execstartandattach_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_execstartandattach_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_execstartandattach_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -8,112 +9,102 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExecStartAndAttachOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExecStartAndAttachOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithOutputStream +// WithOutputStream set field OutputStream to given value func (o *ExecStartAndAttachOptions) WithOutputStream(value io.WriteCloser) *ExecStartAndAttachOptions { - v := &value - o.OutputStream = v + o.OutputStream = &value return o } -// GetOutputStream +// GetOutputStream returns value of field OutputStream func (o *ExecStartAndAttachOptions) GetOutputStream() io.WriteCloser { - var outputStream io.WriteCloser if o.OutputStream == nil { - return outputStream + var z io.WriteCloser + return z } return *o.OutputStream } -// WithErrorStream +// WithErrorStream set field ErrorStream to given value func (o *ExecStartAndAttachOptions) WithErrorStream(value io.WriteCloser) *ExecStartAndAttachOptions { - v := &value - o.ErrorStream = v + o.ErrorStream = &value return o } -// GetErrorStream +// GetErrorStream returns value of field ErrorStream func (o *ExecStartAndAttachOptions) GetErrorStream() io.WriteCloser { - var errorStream io.WriteCloser if o.ErrorStream == nil { - return errorStream + var z io.WriteCloser + return z } return *o.ErrorStream } -// WithInputStream +// WithInputStream set field InputStream to given value func (o *ExecStartAndAttachOptions) WithInputStream(value bufio.Reader) *ExecStartAndAttachOptions { - v := &value - o.InputStream = v + o.InputStream = &value return o } -// GetInputStream +// GetInputStream returns value of field InputStream func (o *ExecStartAndAttachOptions) GetInputStream() bufio.Reader { - var inputStream bufio.Reader if o.InputStream == nil { - return inputStream + var z bufio.Reader + return z } return *o.InputStream } -// WithAttachOutput +// WithAttachOutput set field AttachOutput to given value func (o *ExecStartAndAttachOptions) WithAttachOutput(value bool) *ExecStartAndAttachOptions { - v := &value - o.AttachOutput = v + o.AttachOutput = &value return o } -// GetAttachOutput +// GetAttachOutput returns value of field AttachOutput func (o *ExecStartAndAttachOptions) GetAttachOutput() bool { - var attachOutput bool if o.AttachOutput == nil { - return attachOutput + var z bool + return z } return *o.AttachOutput } -// WithAttachError +// WithAttachError set field AttachError to given value func (o *ExecStartAndAttachOptions) WithAttachError(value bool) *ExecStartAndAttachOptions { - v := &value - o.AttachError = v + o.AttachError = &value return o } -// GetAttachError +// GetAttachError returns value of field AttachError func (o *ExecStartAndAttachOptions) GetAttachError() bool { - var attachError bool if o.AttachError == nil { - return attachError + var z bool + return z } return *o.AttachError } -// WithAttachInput +// WithAttachInput set field AttachInput to given value func (o *ExecStartAndAttachOptions) WithAttachInput(value bool) *ExecStartAndAttachOptions { - v := &value - o.AttachInput = v + o.AttachInput = &value return o } -// GetAttachInput +// GetAttachInput returns value of field AttachInput func (o *ExecStartAndAttachOptions) GetAttachInput() bool { - var attachInput bool if o.AttachInput == nil { - return attachInput + var z bool + return z } return *o.AttachInput } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_execstart_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_execstart_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_execstart_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_execstart_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExecStartOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExecStartOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_exists_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_exists_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_exists_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_exists_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExistsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExistsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithExternal +// WithExternal set field External to given value func (o *ExistsOptions) WithExternal(value bool) *ExistsOptions { - v := &value - o.External = v + o.External = &value return o } -// GetExternal +// GetExternal returns value of field External func (o *ExistsOptions) GetExternal() bool { - var external bool if o.External == nil { - return external + var z bool + return z } return *o.External } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_export_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_export_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_export_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_export_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExportOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExportOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types.go libpod-3.4.4+ds1/pkg/bindings/containers/types.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,9 +37,9 @@ //go:generate go run ../generator/generator.go AttachOptions // AttachOptions are optional options for attaching to containers type AttachOptions struct { - DetachKeys *string - Logs *bool - Stream *bool + DetachKeys *string // Keys to detach from running container + Logs *bool // Flag to return all logs from container when true + Stream *bool // Flag only return container logs when false and Logs is true } //go:generate go run ../generator/generator.go CheckpointOptions @@ -62,6 +62,7 @@ Keep *bool Name *string TCPEstablished *bool + Pod *string } //go:generate go run ../generator/generator.go CreateOptions @@ -70,7 +71,12 @@ //go:generate go run ../generator/generator.go DiffOptions // DiffOptions are optional options for creating containers -type DiffOptions struct{} +type DiffOptions struct { + // By the default diff will compare against the parent layer. Change the Parent if you want to compare against something else. + Parent *string + // Change the type the backend should match. This can be set to "all", "container" or "image". + DiffType *string +} //go:generate go run ../generator/generator.go ExecInspectOptions // ExecInspectOptions are optional options for inspecting @@ -160,7 +166,8 @@ //go:generate go run ../generator/generator.go StatsOptions // StatsOptions are optional options for getting stats on containers type StatsOptions struct { - Stream *bool + Stream *bool + Interval *int } //go:generate go run ../generator/generator.go TopOptions @@ -251,3 +258,13 @@ // External checks for containers created outside of Podman External *bool } + +//go:generate go run ../generator/generator.go CopyOptions +// CopyOptions are options for copying to containers. +type CopyOptions struct { + // If used with CopyFromArchive and set to true it will change ownership of files from the source tar archive + // to the primary uid/gid of the target container. + Chown *bool `schema:"copyUIDGID"` + // Map to translate path names. + Rename map[string]string +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_healthcheck_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_healthcheck_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_healthcheck_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_healthcheck_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *HealthCheckOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *HealthCheckOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_init_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_init_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_init_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_init_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InitOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InitOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_inspect_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_inspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_inspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_inspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithSize +// WithSize set field Size to given value func (o *InspectOptions) WithSize(value bool) *InspectOptions { - v := &value - o.Size = v + o.Size = &value return o } -// GetSize +// GetSize returns value of field Size func (o *InspectOptions) GetSize() bool { - var size bool if o.Size == nil { - return size + var z bool + return z } return *o.Size } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_kill_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_kill_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_kill_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_kill_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *KillOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *KillOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithSignal +// WithSignal set field Signal to given value func (o *KillOptions) WithSignal(value string) *KillOptions { - v := &value - o.Signal = v + o.Signal = &value return o } -// GetSignal +// GetSignal returns value of field Signal func (o *KillOptions) GetSignal() string { - var signal string if o.Signal == nil { - return signal + var z string + return z } return *o.Signal } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_list_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_list_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_list_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_list_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,128 +7,117 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ListOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ListOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *ListOptions) WithAll(value bool) *ListOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *ListOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithExternal +// WithExternal set field External to given value func (o *ListOptions) WithExternal(value bool) *ListOptions { - v := &value - o.External = v + o.External = &value return o } -// GetExternal +// GetExternal returns value of field External func (o *ListOptions) GetExternal() bool { - var external bool if o.External == nil { - return external + var z bool + return z } return *o.External } -// WithFilters +// WithFilters set field Filters to given value func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *ListOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } -// WithLast +// WithLast set field Last to given value func (o *ListOptions) WithLast(value int) *ListOptions { - v := &value - o.Last = v + o.Last = &value return o } -// GetLast +// GetLast returns value of field Last func (o *ListOptions) GetLast() int { - var last int if o.Last == nil { - return last + var z int + return z } return *o.Last } -// WithNamespace +// WithNamespace set field Namespace to given value func (o *ListOptions) WithNamespace(value bool) *ListOptions { - v := &value - o.Namespace = v + o.Namespace = &value return o } -// GetNamespace +// GetNamespace returns value of field Namespace func (o *ListOptions) GetNamespace() bool { - var namespace bool if o.Namespace == nil { - return namespace + var z bool + return z } return *o.Namespace } -// WithSize +// WithSize set field Size to given value func (o *ListOptions) WithSize(value bool) *ListOptions { - v := &value - o.Size = v + o.Size = &value return o } -// GetSize +// GetSize returns value of field Size func (o *ListOptions) GetSize() bool { - var size bool if o.Size == nil { - return size + var z bool + return z } return *o.Size } -// WithSync +// WithSync set field Sync to given value func (o *ListOptions) WithSync(value bool) *ListOptions { - v := &value - o.Sync = v + o.Sync = &value return o } -// GetSync +// GetSync returns value of field Sync func (o *ListOptions) GetSync() bool { - var sync bool if o.Sync == nil { - return sync + var z bool + return z } return *o.Sync } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_log_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_log_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_log_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_log_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,128 +7,117 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *LogOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *LogOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFollow +// WithFollow set field Follow to given value func (o *LogOptions) WithFollow(value bool) *LogOptions { - v := &value - o.Follow = v + o.Follow = &value return o } -// GetFollow +// GetFollow returns value of field Follow func (o *LogOptions) GetFollow() bool { - var follow bool if o.Follow == nil { - return follow + var z bool + return z } return *o.Follow } -// WithSince +// WithSince set field Since to given value func (o *LogOptions) WithSince(value string) *LogOptions { - v := &value - o.Since = v + o.Since = &value return o } -// GetSince +// GetSince returns value of field Since func (o *LogOptions) GetSince() string { - var since string if o.Since == nil { - return since + var z string + return z } return *o.Since } -// WithStderr +// WithStderr set field Stderr to given value func (o *LogOptions) WithStderr(value bool) *LogOptions { - v := &value - o.Stderr = v + o.Stderr = &value return o } -// GetStderr +// GetStderr returns value of field Stderr func (o *LogOptions) GetStderr() bool { - var stderr bool if o.Stderr == nil { - return stderr + var z bool + return z } return *o.Stderr } -// WithStdout +// WithStdout set field Stdout to given value func (o *LogOptions) WithStdout(value bool) *LogOptions { - v := &value - o.Stdout = v + o.Stdout = &value return o } -// GetStdout +// GetStdout returns value of field Stdout func (o *LogOptions) GetStdout() bool { - var stdout bool if o.Stdout == nil { - return stdout + var z bool + return z } return *o.Stdout } -// WithTail +// WithTail set field Tail to given value func (o *LogOptions) WithTail(value string) *LogOptions { - v := &value - o.Tail = v + o.Tail = &value return o } -// GetTail +// GetTail returns value of field Tail func (o *LogOptions) GetTail() string { - var tail string if o.Tail == nil { - return tail + var z string + return z } return *o.Tail } -// WithTimestamps +// WithTimestamps set field Timestamps to given value func (o *LogOptions) WithTimestamps(value bool) *LogOptions { - v := &value - o.Timestamps = v + o.Timestamps = &value return o } -// GetTimestamps +// GetTimestamps returns value of field Timestamps func (o *LogOptions) GetTimestamps() bool { - var timestamps bool if o.Timestamps == nil { - return timestamps + var z bool + return z } return *o.Timestamps } -// WithUntil +// WithUntil set field Until to given value func (o *LogOptions) WithUntil(value string) *LogOptions { - v := &value - o.Until = v + o.Until = &value return o } -// GetUntil +// GetUntil returns value of field Until func (o *LogOptions) GetUntil() string { - var until string if o.Until == nil { - return until + var z string + return z } return *o.Until } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_mountedcontainerpaths_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_mountedcontainerpaths_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_mountedcontainerpaths_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_mountedcontainerpaths_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *MountedContainerPathsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *MountedContainerPathsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_mount_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_mount_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_mount_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_mount_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *MountOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *MountOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_pause_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_pause_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_pause_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_pause_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PauseOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PauseOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_prune_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_prune_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_prune_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_prune_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PruneOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PruneOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *PruneOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,64 +7,57 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithIgnore +// WithIgnore set field Ignore to given value func (o *RemoveOptions) WithIgnore(value bool) *RemoveOptions { - v := &value - o.Ignore = v + o.Ignore = &value return o } -// GetIgnore +// GetIgnore returns value of field Ignore func (o *RemoveOptions) GetIgnore() bool { - var ignore bool if o.Ignore == nil { - return ignore + var z bool + return z } return *o.Ignore } -// WithForce +// WithForce set field Force to given value func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { - v := &value - o.Force = v + o.Force = &value return o } -// GetForce +// GetForce returns value of field Force func (o *RemoveOptions) GetForce() bool { - var force bool if o.Force == nil { - return force + var z bool + return z } return *o.Force } -// WithVolumes +// WithVolumes set field Volumes to given value func (o *RemoveOptions) WithVolumes(value bool) *RemoveOptions { - v := &value - o.Volumes = v + o.Volumes = &value return o } -// GetVolumes +// GetVolumes returns value of field Volumes func (o *RemoveOptions) GetVolumes() bool { - var volumes bool if o.Volumes == nil { - return volumes + var z bool + return z } return *o.Volumes } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_rename_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_rename_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_rename_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_rename_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RenameOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RenameOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithName +// WithName set field Name to given value func (o *RenameOptions) WithName(value string) *RenameOptions { - v := &value - o.Name = v + o.Name = &value return o } -// GetName +// GetName returns value of field Name func (o *RenameOptions) GetName() string { - var name string if o.Name == nil { - return name + var z string + return z } return *o.Name } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_resizeexectty_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_resizeexectty_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_resizeexectty_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_resizeexectty_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ResizeExecTTYOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ResizeExecTTYOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithHeight +// WithHeight set field Height to given value func (o *ResizeExecTTYOptions) WithHeight(value int) *ResizeExecTTYOptions { - v := &value - o.Height = v + o.Height = &value return o } -// GetHeight +// GetHeight returns value of field Height func (o *ResizeExecTTYOptions) GetHeight() int { - var height int if o.Height == nil { - return height + var z int + return z } return *o.Height } -// WithWidth +// WithWidth set field Width to given value func (o *ResizeExecTTYOptions) WithWidth(value int) *ResizeExecTTYOptions { - v := &value - o.Width = v + o.Width = &value return o } -// GetWidth +// GetWidth returns value of field Width func (o *ResizeExecTTYOptions) GetWidth() int { - var width int if o.Width == nil { - return width + var z int + return z } return *o.Width } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_resizetty_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_resizetty_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_resizetty_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_resizetty_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,64 +7,57 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ResizeTTYOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ResizeTTYOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithHeight +// WithHeight set field Height to given value func (o *ResizeTTYOptions) WithHeight(value int) *ResizeTTYOptions { - v := &value - o.Height = v + o.Height = &value return o } -// GetHeight +// GetHeight returns value of field Height func (o *ResizeTTYOptions) GetHeight() int { - var height int if o.Height == nil { - return height + var z int + return z } return *o.Height } -// WithWidth +// WithWidth set field Width to given value func (o *ResizeTTYOptions) WithWidth(value int) *ResizeTTYOptions { - v := &value - o.Width = v + o.Width = &value return o } -// GetWidth +// GetWidth returns value of field Width func (o *ResizeTTYOptions) GetWidth() int { - var width int if o.Width == nil { - return width + var z int + return z } return *o.Width } -// WithRunning +// WithRunning set field Running to given value func (o *ResizeTTYOptions) WithRunning(value bool) *ResizeTTYOptions { - v := &value - o.Running = v + o.Running = &value return o } -// GetRunning +// GetRunning returns value of field Running func (o *ResizeTTYOptions) GetRunning() bool { - var running bool if o.Running == nil { - return running + var z bool + return z } return *o.Running } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_restart_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_restart_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_restart_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_restart_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RestartOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RestartOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithTimeout +// WithTimeout set field Timeout to given value func (o *RestartOptions) WithTimeout(value int) *RestartOptions { - v := &value - o.Timeout = v + o.Timeout = &value return o } -// GetTimeout +// GetTimeout returns value of field Timeout func (o *RestartOptions) GetTimeout() int { - var timeout int if o.Timeout == nil { - return timeout + var z int + return z } return *o.Timeout } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_restore_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_restore_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_restore_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_restore_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,128 +7,132 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RestoreOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RestoreOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithIgnoreRootfs +// WithIgnoreRootfs set field IgnoreRootfs to given value func (o *RestoreOptions) WithIgnoreRootfs(value bool) *RestoreOptions { - v := &value - o.IgnoreRootfs = v + o.IgnoreRootfs = &value return o } -// GetIgnoreRootfs +// GetIgnoreRootfs returns value of field IgnoreRootfs func (o *RestoreOptions) GetIgnoreRootfs() bool { - var ignoreRootfs bool if o.IgnoreRootfs == nil { - return ignoreRootfs + var z bool + return z } return *o.IgnoreRootfs } -// WithIgnoreStaticIP +// WithIgnoreStaticIP set field IgnoreStaticIP to given value func (o *RestoreOptions) WithIgnoreStaticIP(value bool) *RestoreOptions { - v := &value - o.IgnoreStaticIP = v + o.IgnoreStaticIP = &value return o } -// GetIgnoreStaticIP +// GetIgnoreStaticIP returns value of field IgnoreStaticIP func (o *RestoreOptions) GetIgnoreStaticIP() bool { - var ignoreStaticIP bool if o.IgnoreStaticIP == nil { - return ignoreStaticIP + var z bool + return z } return *o.IgnoreStaticIP } -// WithIgnoreStaticMAC +// WithIgnoreStaticMAC set field IgnoreStaticMAC to given value func (o *RestoreOptions) WithIgnoreStaticMAC(value bool) *RestoreOptions { - v := &value - o.IgnoreStaticMAC = v + o.IgnoreStaticMAC = &value return o } -// GetIgnoreStaticMAC +// GetIgnoreStaticMAC returns value of field IgnoreStaticMAC func (o *RestoreOptions) GetIgnoreStaticMAC() bool { - var ignoreStaticMAC bool if o.IgnoreStaticMAC == nil { - return ignoreStaticMAC + var z bool + return z } return *o.IgnoreStaticMAC } -// WithImportAchive +// WithImportAchive set field ImportAchive to given value func (o *RestoreOptions) WithImportAchive(value string) *RestoreOptions { - v := &value - o.ImportAchive = v + o.ImportAchive = &value return o } -// GetImportAchive +// GetImportAchive returns value of field ImportAchive func (o *RestoreOptions) GetImportAchive() string { - var importAchive string if o.ImportAchive == nil { - return importAchive + var z string + return z } return *o.ImportAchive } -// WithKeep +// WithKeep set field Keep to given value func (o *RestoreOptions) WithKeep(value bool) *RestoreOptions { - v := &value - o.Keep = v + o.Keep = &value return o } -// GetKeep +// GetKeep returns value of field Keep func (o *RestoreOptions) GetKeep() bool { - var keep bool if o.Keep == nil { - return keep + var z bool + return z } return *o.Keep } -// WithName +// WithName set field Name to given value func (o *RestoreOptions) WithName(value string) *RestoreOptions { - v := &value - o.Name = v + o.Name = &value return o } -// GetName +// GetName returns value of field Name func (o *RestoreOptions) GetName() string { - var name string if o.Name == nil { - return name + var z string + return z } return *o.Name } -// WithTCPEstablished +// WithTCPEstablished set field TCPEstablished to given value func (o *RestoreOptions) WithTCPEstablished(value bool) *RestoreOptions { - v := &value - o.TCPEstablished = v + o.TCPEstablished = &value return o } -// GetTCPEstablished +// GetTCPEstablished returns value of field TCPEstablished func (o *RestoreOptions) GetTCPEstablished() bool { - var tCPEstablished bool if o.TCPEstablished == nil { - return tCPEstablished + var z bool + return z } return *o.TCPEstablished } + +// WithPod set field Pod to given value +func (o *RestoreOptions) WithPod(value string) *RestoreOptions { + o.Pod = &value + return o +} + +// GetPod returns value of field Pod +func (o *RestoreOptions) GetPod() string { + if o.Pod == nil { + var z string + return z + } + return *o.Pod +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_shouldrestart_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_shouldrestart_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_shouldrestart_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_shouldrestart_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ShouldRestartOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ShouldRestartOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_start_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_start_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_start_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_start_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *StartOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *StartOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithDetachKeys +// WithDetachKeys set field DetachKeys to given value func (o *StartOptions) WithDetachKeys(value string) *StartOptions { - v := &value - o.DetachKeys = v + o.DetachKeys = &value return o } -// GetDetachKeys +// GetDetachKeys returns value of field DetachKeys func (o *StartOptions) GetDetachKeys() string { - var detachKeys string if o.DetachKeys == nil { - return detachKeys + var z string + return z } return *o.DetachKeys } -// WithRecursive +// WithRecursive set field Recursive to given value func (o *StartOptions) WithRecursive(value bool) *StartOptions { - v := &value - o.Recursive = v + o.Recursive = &value return o } -// GetRecursive +// GetRecursive returns value of field Recursive func (o *StartOptions) GetRecursive() bool { - var recursive bool if o.Recursive == nil { - return recursive + var z bool + return z } return *o.Recursive } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_stats_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_stats_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_stats_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_stats_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *StatsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *StatsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithStream +// WithStream set field Stream to given value func (o *StatsOptions) WithStream(value bool) *StatsOptions { - v := &value - o.Stream = v + o.Stream = &value return o } -// GetStream +// GetStream returns value of field Stream func (o *StatsOptions) GetStream() bool { - var stream bool if o.Stream == nil { - return stream + var z bool + return z } return *o.Stream } + +// WithInterval set field Interval to given value +func (o *StatsOptions) WithInterval(value int) *StatsOptions { + o.Interval = &value + return o +} + +// GetInterval returns value of field Interval +func (o *StatsOptions) GetInterval() int { + if o.Interval == nil { + var z int + return z + } + return *o.Interval +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_stop_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_stop_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_stop_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_stop_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *StopOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *StopOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithIgnore +// WithIgnore set field Ignore to given value func (o *StopOptions) WithIgnore(value bool) *StopOptions { - v := &value - o.Ignore = v + o.Ignore = &value return o } -// GetIgnore +// GetIgnore returns value of field Ignore func (o *StopOptions) GetIgnore() bool { - var ignore bool if o.Ignore == nil { - return ignore + var z bool + return z } return *o.Ignore } -// WithTimeout +// WithTimeout set field Timeout to given value func (o *StopOptions) WithTimeout(value uint) *StopOptions { - v := &value - o.Timeout = v + o.Timeout = &value return o } -// GetTimeout +// GetTimeout returns value of field Timeout func (o *StopOptions) GetTimeout() uint { - var timeout uint if o.Timeout == nil { - return timeout + var z uint + return z } return *o.Timeout } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_top_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_top_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_top_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_top_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *TopOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *TopOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithDescriptors +// WithDescriptors set field Descriptors to given value func (o *TopOptions) WithDescriptors(value []string) *TopOptions { - v := &value - o.Descriptors = v + o.Descriptors = &value return o } -// GetDescriptors +// GetDescriptors returns value of field Descriptors func (o *TopOptions) GetDescriptors() []string { - var descriptors []string if o.Descriptors == nil { - return descriptors + var z []string + return z } return *o.Descriptors } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_unmount_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_unmount_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_unmount_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_unmount_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *UnmountOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *UnmountOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_unpause_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_unpause_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_unpause_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_unpause_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *UnpauseOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *UnpauseOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/containers/types_wait_options.go libpod-3.4.4+ds1/pkg/bindings/containers/types_wait_options.go --- libpod-3.2.1+ds1/pkg/bindings/containers/types_wait_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/containers/types_wait_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package containers import ( @@ -7,48 +8,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *WaitOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *WaitOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithCondition +// WithCondition set field Condition to given value func (o *WaitOptions) WithCondition(value []define.ContainerStatus) *WaitOptions { - v := value - o.Condition = v + o.Condition = value return o } -// GetCondition +// GetCondition returns value of field Condition func (o *WaitOptions) GetCondition() []define.ContainerStatus { - var condition []define.ContainerStatus if o.Condition == nil { - return condition + var z []define.ContainerStatus + return z } return o.Condition } -// WithInterval +// WithInterval set field Interval to given value func (o *WaitOptions) WithInterval(value string) *WaitOptions { - v := &value - o.Interval = v + o.Interval = &value return o } -// GetInterval +// GetInterval returns value of field Interval func (o *WaitOptions) GetInterval() string { - var interval string if o.Interval == nil { - return interval + var z string + return z } return *o.Interval } diff -Nru libpod-3.2.1+ds1/pkg/bindings/doc.go libpod-3.4.4+ds1/pkg/bindings/doc.go --- libpod-3.2.1+ds1/pkg/bindings/doc.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/doc.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,5 @@ +package bindings + +/* + See https://github.com/containers/podman/blob/main/pkg/bindings/README.md for details. +*/ diff -Nru libpod-3.2.1+ds1/pkg/bindings/errors.go libpod-3.4.4+ds1/pkg/bindings/errors.go --- libpod-3.2.1+ds1/pkg/bindings/errors.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/errors.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,15 +12,22 @@ ErrNotImplemented = errors.New("function not implemented") ) -func handleError(data []byte) error { - e := errorhandling.ErrorModel{} - if err := json.Unmarshal(data, &e); err != nil { +func handleError(data []byte, unmarshalErrorInto interface{}) error { + if err := json.Unmarshal(data, unmarshalErrorInto); err != nil { return err } - return e + return unmarshalErrorInto.(error) } +// Process drains the response body, and processes the HTTP status code +// Note: Closing the response.Body is left to the caller func (h APIResponse) Process(unmarshalInto interface{}) error { + return h.ProcessWithError(unmarshalInto, &errorhandling.ErrorModel{}) +} + +// Process drains the response body, and processes the HTTP status code +// Note: Closing the response.Body is left to the caller +func (h APIResponse) ProcessWithError(unmarshalInto interface{}, unmarshalErrorInto interface{}) error { data, err := ioutil.ReadAll(h.Response.Body) if err != nil { return errors.Wrap(err, "unable to process API response") @@ -31,14 +38,22 @@ } return nil } + + if h.IsConflictError() { + return handleError(data, unmarshalErrorInto) + } + // TODO should we add a debug here with the response code? - return handleError(data) + return handleError(data, &errorhandling.ErrorModel{}) } func CheckResponseCode(inError error) (int, error) { - e, ok := inError.(errorhandling.ErrorModel) - if !ok { + switch e := inError.(type) { + case *errorhandling.ErrorModel: + return e.Code(), nil + case *errorhandling.PodConflictErrorModel: + return e.Code(), nil + default: return -1, errors.New("error is not type ErrorModel") } - return e.Code(), nil } diff -Nru libpod-3.2.1+ds1/pkg/bindings/generate/generate.go libpod-3.4.4+ds1/pkg/bindings/generate/generate.go --- libpod-3.2.1+ds1/pkg/bindings/generate/generate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/generate/generate.go 2021-12-26 00:45:51.000000000 +0000 @@ -26,10 +26,15 @@ if err != nil { return nil, err } + defer response.Body.Close() + report := &entities.GenerateSystemdReport{} return report, response.Process(&report.Units) } +// Kube generate Kubernetes YAML (v1 specification) +// +// Note: Caller is responsible for closing returned reader func Kube(ctx context.Context, nameOrIDs []string, options *KubeOptions) (*entities.GenerateKubeReport, error) { if options == nil { options = new(KubeOptions) diff -Nru libpod-3.2.1+ds1/pkg/bindings/generate/types_kube_options.go libpod-3.4.4+ds1/pkg/bindings/generate/types_kube_options.go --- libpod-3.2.1+ds1/pkg/bindings/generate/types_kube_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/generate/types_kube_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package generate import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *KubeOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *KubeOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithService +// WithService set field Service to given value func (o *KubeOptions) WithService(value bool) *KubeOptions { - v := &value - o.Service = v + o.Service = &value return o } -// GetService +// GetService returns value of field Service func (o *KubeOptions) GetService() bool { - var service bool if o.Service == nil { - return service + var z bool + return z } return *o.Service } diff -Nru libpod-3.2.1+ds1/pkg/bindings/generate/types_systemd_options.go libpod-3.4.4+ds1/pkg/bindings/generate/types_systemd_options.go --- libpod-3.2.1+ds1/pkg/bindings/generate/types_systemd_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/generate/types_systemd_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package generate import ( @@ -6,144 +7,132 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *SystemdOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *SystemdOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithUseName +// WithUseName set field UseName to given value func (o *SystemdOptions) WithUseName(value bool) *SystemdOptions { - v := &value - o.UseName = v + o.UseName = &value return o } -// GetUseName +// GetUseName returns value of field UseName func (o *SystemdOptions) GetUseName() bool { - var useName bool if o.UseName == nil { - return useName + var z bool + return z } return *o.UseName } -// WithNew +// WithNew set field New to given value func (o *SystemdOptions) WithNew(value bool) *SystemdOptions { - v := &value - o.New = v + o.New = &value return o } -// GetNew +// GetNew returns value of field New func (o *SystemdOptions) GetNew() bool { - var new bool if o.New == nil { - return new + var z bool + return z } return *o.New } -// WithNoHeader +// WithNoHeader set field NoHeader to given value func (o *SystemdOptions) WithNoHeader(value bool) *SystemdOptions { - v := &value - o.NoHeader = v + o.NoHeader = &value return o } -// GetNoHeader +// GetNoHeader returns value of field NoHeader func (o *SystemdOptions) GetNoHeader() bool { - var noHeader bool if o.NoHeader == nil { - return noHeader + var z bool + return z } return *o.NoHeader } -// WithRestartPolicy +// WithRestartPolicy set field RestartPolicy to given value func (o *SystemdOptions) WithRestartPolicy(value string) *SystemdOptions { - v := &value - o.RestartPolicy = v + o.RestartPolicy = &value return o } -// GetRestartPolicy +// GetRestartPolicy returns value of field RestartPolicy func (o *SystemdOptions) GetRestartPolicy() string { - var restartPolicy string if o.RestartPolicy == nil { - return restartPolicy + var z string + return z } return *o.RestartPolicy } -// WithStopTimeout +// WithStopTimeout set field StopTimeout to given value func (o *SystemdOptions) WithStopTimeout(value uint) *SystemdOptions { - v := &value - o.StopTimeout = v + o.StopTimeout = &value return o } -// GetStopTimeout +// GetStopTimeout returns value of field StopTimeout func (o *SystemdOptions) GetStopTimeout() uint { - var stopTimeout uint if o.StopTimeout == nil { - return stopTimeout + var z uint + return z } return *o.StopTimeout } -// WithContainerPrefix +// WithContainerPrefix set field ContainerPrefix to given value func (o *SystemdOptions) WithContainerPrefix(value string) *SystemdOptions { - v := &value - o.ContainerPrefix = v + o.ContainerPrefix = &value return o } -// GetContainerPrefix +// GetContainerPrefix returns value of field ContainerPrefix func (o *SystemdOptions) GetContainerPrefix() string { - var containerPrefix string if o.ContainerPrefix == nil { - return containerPrefix + var z string + return z } return *o.ContainerPrefix } -// WithPodPrefix +// WithPodPrefix set field PodPrefix to given value func (o *SystemdOptions) WithPodPrefix(value string) *SystemdOptions { - v := &value - o.PodPrefix = v + o.PodPrefix = &value return o } -// GetPodPrefix +// GetPodPrefix returns value of field PodPrefix func (o *SystemdOptions) GetPodPrefix() string { - var podPrefix string if o.PodPrefix == nil { - return podPrefix + var z string + return z } return *o.PodPrefix } -// WithSeparator +// WithSeparator set field Separator to given value func (o *SystemdOptions) WithSeparator(value string) *SystemdOptions { - v := &value - o.Separator = v + o.Separator = &value return o } -// GetSeparator +// GetSeparator returns value of field Separator func (o *SystemdOptions) GetSeparator() string { - var separator string if o.Separator == nil { - return separator + var z string + return z } return *o.Separator } diff -Nru libpod-3.2.1+ds1/pkg/bindings/generator/generator.go libpod-3.4.4+ds1/pkg/bindings/generator/generator.go --- libpod-3.2.1+ds1/pkg/bindings/generator/generator.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/generator/generator.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,5 +1,10 @@ +// +build ignore + package main +// This program generates *_options_.go files to be used by the bindings calls to API service. +// It can be invoked by running go generate + import ( "errors" "fmt" @@ -11,56 +16,52 @@ "os/exec" "strings" "text/template" - "time" + "unicode" + "unicode/utf8" ) -var bodyTmpl = `package {{.PackageName}} +var bodyTmpl = `// Code generated by go generate; DO NOT EDIT. +package {{.PackageName}} import ( {{range $import := .Imports}} {{$import}} {{end}} ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *{{.StructName}}) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *{{.StructName}}) ToParams() (url.Values, error) { return util.ToParams(o) } {{range $field := .Fields}} -// With{{.Name}} -func(o *{{$field.StructName}}) With{{$field.Name}}(value {{$field.Type}}) *{{$field.StructName}} { - v := {{$field.TypedValue}} - o.{{$field.Name}} = v +// With{{.Name}} set {{if .Comment}}{{.Comment}}{{else}}field {{.Name}} to given value{{end}} +func(o *{{.StructName}}) With{{.Name}}(value {{.Type}}) *{{.StructName}} { + o.{{.Name}} = {{if not .Composite}}&{{end}}value return o } -// Get{{.Name}} -func(o *{{$field.StructName}}) Get{{$field.Name}}() {{$field.Type}} { - var {{$field.ZeroName}} {{$field.Type}} - if o.{{$field.Name}} == nil { - return {{$field.ZeroName}} +// Get{{.Name}} returns value of {{if .Comment}}{{.Comment}}{{else}}field {{.Name}}{{end}} +func(o *{{.StructName}}) Get{{.Name}}() {{.Type}} { + if o.{{.Name}} == nil { + var z {{.Type}} + return z } - return {{$field.TypedName}} + return {{if not .Composite}}*{{end}}o.{{.Name}} } {{end}} ` type fieldStruct struct { + Comment string + Composite bool Name string StructName string Type string - TypedName string - TypedValue string - ZeroName string } func main() { @@ -69,7 +70,6 @@ fieldStructs []fieldStruct ) srcFile := os.Getenv("GOFILE") - pkg := os.Getenv("GOPACKAGE") inputStructName := os.Args[1] b, err := ioutil.ReadFile(srcFile) if err != nil { @@ -80,6 +80,7 @@ if err != nil { panic(err) } + // always add reflect imports := []string{"\"reflect\"", "\"github.com/containers/podman/v3/pkg/bindings/internal/util\""} for _, imp := range f.Imports { @@ -96,95 +97,94 @@ } }() + body := template.Must(template.New("body").Parse(bodyTmpl)) + ast.Inspect(f, func(n ast.Node) bool { ref, refOK := n.(*ast.TypeSpec) - if refOK { - if ref.Name.Name == inputStructName { - x := ref.Type.(*ast.StructType) - for _, field := range x.Fields.List { - var ( - name, zeroName, typedName, typedValue string - ) - if len(field.Names) > 0 { - name = field.Names[0].Name - if len(name) < 1 { - panic(errors.New("bad name")) - } - } - for k, v := range name { - zeroName = strings.ToLower(string(v)) + name[k+1:] - break - } - //sub := "*" - typeExpr := field.Type - switch field.Type.(type) { - case *ast.MapType, *ast.StructType, *ast.ArrayType: - typedName = "o." + name - typedValue = "value" - default: - typedName = "*o." + name - typedValue = "&value" - } - start := typeExpr.Pos() - 1 - end := typeExpr.End() - 1 - fieldType := strings.Replace(string(b[start:end]), "*", "", 1) - fStruct := fieldStruct{ - Name: name, - StructName: inputStructName, - Type: fieldType, - TypedName: typedName, - TypedValue: typedValue, - ZeroName: zeroName, - } - fieldStructs = append(fieldStructs, fStruct) - } // for - - bodyStruct := struct { - PackageName string - Imports []string - Date string - StructName string - Fields []fieldStruct - }{ - PackageName: pkg, - Imports: imports, - Date: time.Now().String(), - StructName: inputStructName, - Fields: fieldStructs, + if !(refOK && ref.Name.Name == inputStructName) { + return true + } + + x := ref.Type.(*ast.StructType) + for _, field := range x.Fields.List { + var name string + if len(field.Names) > 0 { + name = field.Names[0].Name + if len(name) < 1 { + panic(errors.New("bad name")) } + } - body := template.Must(template.New("body").Parse(bodyTmpl)) + var composite bool + switch field.Type.(type) { + case *ast.MapType, *ast.StructType, *ast.ArrayType: + composite = true + } - // create the body - if err := body.Execute(out, bodyStruct); err != nil { - fmt.Println(err) - os.Exit(1) - } + //sub := "*" + typeExpr := field.Type + start := typeExpr.Pos() - 1 + end := typeExpr.End() - 1 + fieldType := strings.Replace(string(b[start:end]), "*", "", 1) + + fieldStructs = append(fieldStructs, fieldStruct{ + Comment: fmtComment(field.Comment.Text()), + Composite: composite, + Name: name, + StructName: inputStructName, + Type: fieldType, + }) + } // for + + bodyStruct := struct { + PackageName string + Imports []string + StructName string + Fields []fieldStruct + }{ + PackageName: os.Getenv("GOPACKAGE"), + Imports: imports, + StructName: inputStructName, + Fields: fieldStructs, + } - // close out file - if err := out.Close(); err != nil { - fmt.Println(err) - os.Exit(1) - } - closed = true + // create the body + if err := body.Execute(out, bodyStruct); err != nil { + fmt.Println(err) + os.Exit(1) + } - // go fmt file - gofmt := exec.Command("go", "fmt", out.Name()) - gofmt.Stderr = os.Stdout - if err := gofmt.Run(); err != nil { - fmt.Println(err) - os.Exit(1) - } + // close out file + if err := out.Close(); err != nil { + fmt.Println(err) + os.Exit(1) + } + closed = true - // go import file - goimport := exec.Command("goimports", "-w", out.Name()) - goimport.Stderr = os.Stdout - if err := goimport.Run(); err != nil { - fmt.Println(err) - os.Exit(1) - } - } + // go fmt file + gofmt := exec.Command("go", "fmt", out.Name()) + gofmt.Stderr = os.Stdout + if err := gofmt.Run(); err != nil { + fmt.Println(err) + os.Exit(1) + } + + // go import file + goimport := exec.Command("goimports", "-w", out.Name()) + goimport.Stderr = os.Stdout + if err := goimport.Run(); err != nil { + fmt.Println(err) + os.Exit(1) } return true }) } + +func fmtComment(comment string) string { + r, n := utf8.DecodeRuneInString(comment) + if r != utf8.RuneError { + comment = string(unicode.ToLower(r)) + comment[n:] + } + comment = strings.TrimSpace(comment) + return comment +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/build.go libpod-3.4.4+ds1/pkg/bindings/images/build.go --- libpod-3.2.1+ds1/pkg/bindings/images/build.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/build.go 2021-12-26 00:45:51.000000000 +0000 @@ -16,6 +16,7 @@ "strconv" "strings" + "github.com/containers/buildah/define" "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" @@ -39,6 +40,10 @@ // Build creates an image using a containerfile reference func Build(ctx context.Context, containerFiles []string, options entities.BuildOptions) (*entities.BuildReport, error) { + if options.CommonBuildOpts == nil { + options.CommonBuildOpts = new(define.CommonBuildOptions) + } + params := url.Values{} if caps := options.AddCapabilities; len(caps) > 0 { @@ -74,18 +79,25 @@ } params.Set("excludes", bArgs) } - if cpuShares := options.CommonBuildOpts.CPUShares; cpuShares > 0 { - params.Set("cpushares", strconv.Itoa(int(cpuShares))) - } - if cpuSetCpus := options.CommonBuildOpts.CPUSetCPUs; len(cpuSetCpus) > 0 { - params.Set("cpusetcpus", cpuSetCpus) - } if cpuPeriod := options.CommonBuildOpts.CPUPeriod; cpuPeriod > 0 { params.Set("cpuperiod", strconv.Itoa(int(cpuPeriod))) } if cpuQuota := options.CommonBuildOpts.CPUQuota; cpuQuota > 0 { params.Set("cpuquota", strconv.Itoa(int(cpuQuota))) } + if cpuSetCpus := options.CommonBuildOpts.CPUSetCPUs; len(cpuSetCpus) > 0 { + params.Set("cpusetcpus", cpuSetCpus) + } + if cpuSetMems := options.CommonBuildOpts.CPUSetMems; len(cpuSetMems) > 0 { + params.Set("cpusetmems", cpuSetMems) + } + if cpuShares := options.CommonBuildOpts.CPUShares; cpuShares > 0 { + params.Set("cpushares", strconv.Itoa(int(cpuShares))) + } + if len(options.CommonBuildOpts.CgroupParent) > 0 { + params.Set("cgroupparent", options.CommonBuildOpts.CgroupParent) + } + params.Set("networkmode", strconv.Itoa(int(options.ConfigureNetwork))) params.Set("outputformat", options.OutputFormat) @@ -178,6 +190,9 @@ if options.LogRusage { params.Set("rusage", "1") } + if len(options.RusageLogFile) > 0 { + params.Set("rusagelogfile", options.RusageLogFile) + } if len(options.Manifest) > 0 { params.Set("manifest", options.Manifest) } @@ -210,6 +225,19 @@ if len(platform) > 0 { params.Set("platform", platform) } + if len(options.Platforms) > 0 { + params.Del("platform") + for _, platformSpec := range options.Platforms { + platform = platformSpec.OS + "/" + platformSpec.Arch + if platformSpec.Variant != "" { + platform += "/" + platformSpec.Variant + } + params.Add("platform", platform) + } + } + if contextDir, err := filepath.EvalSymlinks(options.ContextDirectory); err == nil { + options.ContextDirectory = contextDir + } params.Set("pullpolicy", options.PullPolicy.String()) @@ -298,7 +326,30 @@ tarContent := []string{options.ContextDirectory} newContainerFiles := []string{} + + dontexcludes := []string{"!Dockerfile", "!Containerfile", "!.dockerignore", "!.containerignore"} for _, c := range containerFiles { + if c == "/dev/stdin" { + content, err := ioutil.ReadAll(os.Stdin) + if err != nil { + return nil, err + } + tmpFile, err := ioutil.TempFile("", "build") + if err != nil { + return nil, err + } + defer os.Remove(tmpFile.Name()) // clean up + defer tmpFile.Close() + if _, err := tmpFile.Write(content); err != nil { + return nil, err + } + c = tmpFile.Name() + } + cfDir := filepath.Dir(c) + if absDir, err := filepath.EvalSymlinks(cfDir); err == nil { + name := filepath.ToSlash(strings.TrimPrefix(c, cfDir+string(filepath.Separator))) + c = filepath.Join(absDir, name) + } containerfile, err := filepath.Abs(c) if err != nil { logrus.Errorf("cannot find absolute path of %v: %v", c, err) @@ -309,6 +360,7 @@ // Do NOT add to tarfile if strings.HasPrefix(containerfile, contextDir+string(filepath.Separator)) { containerfile = strings.TrimPrefix(containerfile, contextDir+string(filepath.Separator)) + dontexcludes = append(dontexcludes, "!"+containerfile) } else { // If Containerfile does not exists assume it is in context directory, do Not add to tarfile if _, err := os.Lstat(containerfile); err != nil { @@ -330,8 +382,7 @@ } params.Set("dockerfile", string(cFileJSON)) } - - tarfile, err := nTar(excludes, tarContent...) + tarfile, err := nTar(append(excludes, dontexcludes...), tarContent...) if err != nil { logrus.Errorf("cannot tar container entries %v error: %v", tarContent, err) return nil, err @@ -370,42 +421,50 @@ dec := json.NewDecoder(body) var id string - var mErr error for { var s struct { Stream string `json:"stream,omitempty"` Error string `json:"error,omitempty"` } - if err := dec.Decode(&s); err != nil { - if errors.Is(err, io.EOF) { - if mErr == nil && id == "" { - mErr = errors.New("stream dropped, unexpected failure") - } - break - } - s.Error = err.Error() + "\n" - } select { + // FIXME(vrothberg): it seems we always hit the EOF case below, + // even when the server quit but it seems desirable to + // distinguish a proper build from a transient EOF. case <-response.Request.Context().Done(): - return &entities.BuildReport{ID: id}, mErr + return &entities.BuildReport{ID: id}, nil default: // non-blocking select } + if err := dec.Decode(&s); err != nil { + if errors.Is(err, io.ErrUnexpectedEOF) { + return nil, errors.Wrap(err, "server probably quit") + } + // EOF means the stream is over in which case we need + // to have read the id. + if errors.Is(err, io.EOF) && id != "" { + break + } + return &entities.BuildReport{ID: id}, errors.Wrap(err, "decoding stream") + } + switch { case s.Stream != "": - stdout.Write([]byte(s.Stream)) - if iidRegex.Match([]byte(s.Stream)) { + raw := []byte(s.Stream) + stdout.Write(raw) + if iidRegex.Match(raw) { id = strings.TrimSuffix(s.Stream, "\n") } case s.Error != "": - mErr = errors.New(s.Error) + // If there's an error, return directly. The stream + // will be closed on return. + return &entities.BuildReport{ID: id}, errors.New(s.Error) default: return &entities.BuildReport{ID: id}, errors.New("failed to parse build results stream, unexpected input") } } - return &entities.BuildReport{ID: id}, mErr + return &entities.BuildReport{ID: id}, nil } func nTar(excludes []string, sources ...string) (io.ReadCloser, error) { @@ -445,9 +504,9 @@ return nil // skip root dir } - name := strings.TrimPrefix(path, s+string(filepath.Separator)) + name := filepath.ToSlash(strings.TrimPrefix(path, s+string(filepath.Separator))) - excluded, err := pm.Matches(filepath.ToSlash(name)) // nolint:staticcheck + excluded, err := pm.Matches(name) // nolint:staticcheck if err != nil { return errors.Wrapf(err, "error checking if %q is excluded", name) } @@ -465,6 +524,7 @@ if err != nil { return err } + hdr.Uid, hdr.Gid = 0, 0 orig, ok := seen[di] if ok { hdr.Typeflag = tar.TypeLink @@ -496,6 +556,7 @@ return lerr } hdr.Name = name + hdr.Uid, hdr.Gid = 0, 0 if lerr := tw.WriteHeader(hdr); lerr != nil { return lerr } @@ -509,6 +570,7 @@ return lerr } hdr.Name = name + hdr.Uid, hdr.Gid = 0, 0 if lerr := tw.WriteHeader(hdr); lerr != nil { return lerr } @@ -529,9 +591,13 @@ } func parseDockerignore(root string) ([]string, error) { - ignore, err := ioutil.ReadFile(filepath.Join(root, ".dockerignore")) - if err != nil && !os.IsNotExist(err) { - return nil, errors.Wrapf(err, "error reading .dockerignore: '%s'", root) + ignore, err := ioutil.ReadFile(filepath.Join(root, ".containerignore")) + if err != nil { + var dockerIgnoreErr error + ignore, dockerIgnoreErr = ioutil.ReadFile(filepath.Join(root, ".dockerignore")) + if dockerIgnoreErr != nil && !os.IsNotExist(dockerIgnoreErr) { + return nil, errors.Wrapf(err, "error reading .containerignore: '%s'", root) + } } rawexcludes := strings.Split(string(ignore), "\n") excludes := make([]string, 0, len(rawexcludes)) diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/diff.go libpod-3.4.4+ds1/pkg/bindings/images/diff.go --- libpod-3.2.1+ds1/pkg/bindings/images/diff.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/diff.go 2021-12-26 00:45:51.000000000 +0000 @@ -23,6 +23,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + var changes []archive.Change return changes, response.Process(&changes) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/images.go libpod-3.4.4+ds1/pkg/bindings/images/images.go --- libpod-3.2.1+ds1/pkg/bindings/images/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -27,6 +27,8 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } @@ -49,6 +51,8 @@ if err != nil { return imageSummary, err } + defer response.Body.Close() + return imageSummary, response.Process(&imageSummary) } @@ -71,6 +75,8 @@ if err != nil { return &inspectedData, err } + defer response.Body.Close() + return &inspectedData, response.Process(&inspectedData) } @@ -92,6 +98,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -110,6 +118,8 @@ if err != nil { return history, err } + defer response.Body.Close() + return history, response.Process(&history) } @@ -123,6 +133,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -147,6 +159,7 @@ if err != nil { return err } + defer response.Body.Close() if response.StatusCode/100 == 2 || response.StatusCode/100 == 3 { _, err = io.Copy(w, response.Body) @@ -176,8 +189,9 @@ if err != nil { return deleted, err } - err = response.Process(&deleted) - return deleted, err + defer response.Body.Close() + + return deleted, response.Process(&deleted) } // Tag adds an additional name to locally-stored image. Both the tag and repo parameters are required. @@ -197,6 +211,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -217,10 +233,12 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } -// Imports adds the given image to the local image store. This can be done by file and the given reader +// Import adds the given image to the local image store. This can be done by file and the given reader // or via the url parameter. Additional metadata can be associated with the image by using the changes and // message parameters. The image can also be tagged given a reference. One of url OR r must be provided. func Import(ctx context.Context, r io.Reader, options *ImportOptions) (*entities.ImageImportReport, error) { @@ -243,6 +261,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -269,8 +289,8 @@ if err != nil { return err } - //SkipTLSVerify is special. We need to delete the param added by - //toparams and change the key and flip the bool + // SkipTLSVerify is special. We need to delete the param added by + // toparams and change the key and flip the bool if options.SkipTLSVerify != nil { params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) @@ -282,6 +302,7 @@ if err != nil { return err } + defer response.Body.Close() return response.Process(err) } @@ -317,6 +338,7 @@ if err != nil { return nil, err } + defer response.Body.Close() results := []entities.ImageSearchReport{} if err := response.Process(&results); err != nil { diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/pull.go libpod-3.4.4+ds1/pkg/bindings/images/pull.go --- libpod-3.2.1+ds1/pkg/bindings/images/pull.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/pull.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,7 +13,7 @@ "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/hashicorp/go-multierror" + "github.com/containers/podman/v3/pkg/errorhandling" "github.com/pkg/errors" ) @@ -65,7 +65,7 @@ dec := json.NewDecoder(response.Body) var images []string - var mErr error + var pullErrors []error for { var report entities.ImagePullReport if err := dec.Decode(&report); err != nil { @@ -77,7 +77,7 @@ select { case <-response.Request.Context().Done(): - return images, mErr + break default: // non-blocking select } @@ -86,7 +86,7 @@ case report.Stream != "": fmt.Fprint(stderr, report.Stream) case report.Error != "": - mErr = multierror.Append(mErr, errors.New(report.Error)) + pullErrors = append(pullErrors, errors.New(report.Error)) case len(report.Images) > 0: images = report.Images case report.ID != "": @@ -94,5 +94,5 @@ return images, errors.Errorf("failed to parse pull results stream, unexpected input: %v", report) } } - return images, mErr + return images, errorhandling.JoinErrors(pullErrors) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/rm.go libpod-3.4.4+ds1/pkg/bindings/images/rm.go --- libpod-3.2.1+ds1/pkg/bindings/images/rm.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/rm.go 2021-12-26 00:45:51.000000000 +0000 @@ -36,6 +36,8 @@ if err != nil { return nil, []error{err} } + defer response.Body.Close() + if err := response.Process(&report); err != nil { return nil, []error{err} } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_diff_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_diff_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_diff_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_diff_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,16 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *DiffOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *DiffOptions) ToParams() (url.Values, error) { return util.ToParams(o) } + +// WithParent set field Parent to given value +func (o *DiffOptions) WithParent(value string) *DiffOptions { + o.Parent = &value + return o +} + +// GetParent returns value of field Parent +func (o *DiffOptions) GetParent() string { + if o.Parent == nil { + var z string + return z + } + return *o.Parent +} + +// WithDiffType set field DiffType to given value +func (o *DiffOptions) WithDiffType(value string) *DiffOptions { + o.DiffType = &value + return o +} + +// GetDiffType returns value of field DiffType +func (o *DiffOptions) GetDiffType() string { + if o.DiffType == nil { + var z string + return z + } + return *o.DiffType +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_exists_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_exists_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_exists_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_exists_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExistsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExistsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_export_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_export_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_export_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_export_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExportOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExportOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithCompress +// WithCompress set field Compress to given value func (o *ExportOptions) WithCompress(value bool) *ExportOptions { - v := &value - o.Compress = v + o.Compress = &value return o } -// GetCompress +// GetCompress returns value of field Compress func (o *ExportOptions) GetCompress() bool { - var compress bool if o.Compress == nil { - return compress + var z bool + return z } return *o.Compress } -// WithFormat +// WithFormat set field Format to given value func (o *ExportOptions) WithFormat(value string) *ExportOptions { - v := &value - o.Format = v + o.Format = &value return o } -// GetFormat +// GetFormat returns value of field Format func (o *ExportOptions) GetFormat() string { - var format string if o.Format == nil { - return format + var z string + return z } return *o.Format } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_get_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_get_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_get_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_get_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *GetOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *GetOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithSize +// WithSize set field Size to given value func (o *GetOptions) WithSize(value bool) *GetOptions { - v := &value - o.Size = v + o.Size = &value return o } -// GetSize +// GetSize returns value of field Size func (o *GetOptions) GetSize() bool { - var size bool if o.Size == nil { - return size + var z bool + return z } return *o.Size } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types.go libpod-3.4.4+ds1/pkg/bindings/images/types.go --- libpod-3.2.1+ds1/pkg/bindings/images/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -16,6 +16,10 @@ //go:generate go run ../generator/generator.go DiffOptions // DiffOptions are optional options image diffs type DiffOptions struct { + // By the default diff will compare against the parent layer. Change the Parent if you want to compare against something else. + Parent *string + // Change the type the backend should match. This can be set to "all", "container" or "image". + DiffType *string } //go:generate go run ../generator/generator.go ListOptions @@ -147,6 +151,9 @@ // OS will overwrite the local operating system (OS) for image // pulls. OS *string + // Policy is the pull policy. Supported values are "missing", "never", + // "newer", "always". An empty string defaults to "always". + Policy *string // Password for authenticating against the registry. Password *string // Quiet can be specified to suppress pull progress when pulling. Ignored diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_history_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_history_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_history_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_history_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *HistoryOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *HistoryOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_import_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_import_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_import_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_import_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,80 +7,72 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ImportOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ImportOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithChanges +// WithChanges set field Changes to given value func (o *ImportOptions) WithChanges(value []string) *ImportOptions { - v := &value - o.Changes = v + o.Changes = &value return o } -// GetChanges +// GetChanges returns value of field Changes func (o *ImportOptions) GetChanges() []string { - var changes []string if o.Changes == nil { - return changes + var z []string + return z } return *o.Changes } -// WithMessage +// WithMessage set field Message to given value func (o *ImportOptions) WithMessage(value string) *ImportOptions { - v := &value - o.Message = v + o.Message = &value return o } -// GetMessage +// GetMessage returns value of field Message func (o *ImportOptions) GetMessage() string { - var message string if o.Message == nil { - return message + var z string + return z } return *o.Message } -// WithReference +// WithReference set field Reference to given value func (o *ImportOptions) WithReference(value string) *ImportOptions { - v := &value - o.Reference = v + o.Reference = &value return o } -// GetReference +// GetReference returns value of field Reference func (o *ImportOptions) GetReference() string { - var reference string if o.Reference == nil { - return reference + var z string + return z } return *o.Reference } -// WithURL +// WithURL set field URL to given value func (o *ImportOptions) WithURL(value string) *ImportOptions { - v := &value - o.URL = v + o.URL = &value return o } -// GetURL +// GetURL returns value of field URL func (o *ImportOptions) GetURL() string { - var uRL string if o.URL == nil { - return uRL + var z string + return z } return *o.URL } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_list_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_list_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_list_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_list_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ListOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ListOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *ListOptions) WithAll(value bool) *ListOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *ListOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithFilters +// WithFilters set field Filters to given value func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *ListOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_load_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_load_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_load_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_load_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *LoadOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *LoadOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithReference +// WithReference set field Reference to given value func (o *LoadOptions) WithReference(value string) *LoadOptions { - v := &value - o.Reference = v + o.Reference = &value return o } -// GetReference +// GetReference returns value of field Reference func (o *LoadOptions) GetReference() string { - var reference string if o.Reference == nil { - return reference + var z string + return z } return *o.Reference } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_prune_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_prune_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_prune_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_prune_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PruneOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PruneOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *PruneOptions) WithAll(value bool) *PruneOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *PruneOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithFilters +// WithFilters set field Filters to given value func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *PruneOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_pull_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_pull_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_pull_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_pull_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,160 +7,162 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PullOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PullOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAllTags +// WithAllTags set field AllTags to given value func (o *PullOptions) WithAllTags(value bool) *PullOptions { - v := &value - o.AllTags = v + o.AllTags = &value return o } -// GetAllTags +// GetAllTags returns value of field AllTags func (o *PullOptions) GetAllTags() bool { - var allTags bool if o.AllTags == nil { - return allTags + var z bool + return z } return *o.AllTags } -// WithArch +// WithArch set field Arch to given value func (o *PullOptions) WithArch(value string) *PullOptions { - v := &value - o.Arch = v + o.Arch = &value return o } -// GetArch +// GetArch returns value of field Arch func (o *PullOptions) GetArch() string { - var arch string if o.Arch == nil { - return arch + var z string + return z } return *o.Arch } -// WithAuthfile +// WithAuthfile set field Authfile to given value func (o *PullOptions) WithAuthfile(value string) *PullOptions { - v := &value - o.Authfile = v + o.Authfile = &value return o } -// GetAuthfile +// GetAuthfile returns value of field Authfile func (o *PullOptions) GetAuthfile() string { - var authfile string if o.Authfile == nil { - return authfile + var z string + return z } return *o.Authfile } -// WithOS +// WithOS set field OS to given value func (o *PullOptions) WithOS(value string) *PullOptions { - v := &value - o.OS = v + o.OS = &value return o } -// GetOS +// GetOS returns value of field OS func (o *PullOptions) GetOS() string { - var oS string if o.OS == nil { - return oS + var z string + return z } return *o.OS } -// WithPassword +// WithPolicy set field Policy to given value +func (o *PullOptions) WithPolicy(value string) *PullOptions { + o.Policy = &value + return o +} + +// GetPolicy returns value of field Policy +func (o *PullOptions) GetPolicy() string { + if o.Policy == nil { + var z string + return z + } + return *o.Policy +} + +// WithPassword set field Password to given value func (o *PullOptions) WithPassword(value string) *PullOptions { - v := &value - o.Password = v + o.Password = &value return o } -// GetPassword +// GetPassword returns value of field Password func (o *PullOptions) GetPassword() string { - var password string if o.Password == nil { - return password + var z string + return z } return *o.Password } -// WithQuiet +// WithQuiet set field Quiet to given value func (o *PullOptions) WithQuiet(value bool) *PullOptions { - v := &value - o.Quiet = v + o.Quiet = &value return o } -// GetQuiet +// GetQuiet returns value of field Quiet func (o *PullOptions) GetQuiet() bool { - var quiet bool if o.Quiet == nil { - return quiet + var z bool + return z } return *o.Quiet } -// WithSkipTLSVerify +// WithSkipTLSVerify set field SkipTLSVerify to given value func (o *PullOptions) WithSkipTLSVerify(value bool) *PullOptions { - v := &value - o.SkipTLSVerify = v + o.SkipTLSVerify = &value return o } -// GetSkipTLSVerify +// GetSkipTLSVerify returns value of field SkipTLSVerify func (o *PullOptions) GetSkipTLSVerify() bool { - var skipTLSVerify bool if o.SkipTLSVerify == nil { - return skipTLSVerify + var z bool + return z } return *o.SkipTLSVerify } -// WithUsername +// WithUsername set field Username to given value func (o *PullOptions) WithUsername(value string) *PullOptions { - v := &value - o.Username = v + o.Username = &value return o } -// GetUsername +// GetUsername returns value of field Username func (o *PullOptions) GetUsername() string { - var username string if o.Username == nil { - return username + var z string + return z } return *o.Username } -// WithVariant +// WithVariant set field Variant to given value func (o *PullOptions) WithVariant(value string) *PullOptions { - v := &value - o.Variant = v + o.Variant = &value return o } -// GetVariant +// GetVariant returns value of field Variant func (o *PullOptions) GetVariant() string { - var variant string if o.Variant == nil { - return variant + var z string + return z } return *o.Variant } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_push_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_push_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_push_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_push_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,128 +7,117 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PushOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PushOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *PushOptions) WithAll(value bool) *PushOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *PushOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithAuthfile +// WithAuthfile set field Authfile to given value func (o *PushOptions) WithAuthfile(value string) *PushOptions { - v := &value - o.Authfile = v + o.Authfile = &value return o } -// GetAuthfile +// GetAuthfile returns value of field Authfile func (o *PushOptions) GetAuthfile() string { - var authfile string if o.Authfile == nil { - return authfile + var z string + return z } return *o.Authfile } -// WithCompress +// WithCompress set field Compress to given value func (o *PushOptions) WithCompress(value bool) *PushOptions { - v := &value - o.Compress = v + o.Compress = &value return o } -// GetCompress +// GetCompress returns value of field Compress func (o *PushOptions) GetCompress() bool { - var compress bool if o.Compress == nil { - return compress + var z bool + return z } return *o.Compress } -// WithFormat +// WithFormat set field Format to given value func (o *PushOptions) WithFormat(value string) *PushOptions { - v := &value - o.Format = v + o.Format = &value return o } -// GetFormat +// GetFormat returns value of field Format func (o *PushOptions) GetFormat() string { - var format string if o.Format == nil { - return format + var z string + return z } return *o.Format } -// WithPassword +// WithPassword set field Password to given value func (o *PushOptions) WithPassword(value string) *PushOptions { - v := &value - o.Password = v + o.Password = &value return o } -// GetPassword +// GetPassword returns value of field Password func (o *PushOptions) GetPassword() string { - var password string if o.Password == nil { - return password + var z string + return z } return *o.Password } -// WithSkipTLSVerify +// WithSkipTLSVerify set field SkipTLSVerify to given value func (o *PushOptions) WithSkipTLSVerify(value bool) *PushOptions { - v := &value - o.SkipTLSVerify = v + o.SkipTLSVerify = &value return o } -// GetSkipTLSVerify +// GetSkipTLSVerify returns value of field SkipTLSVerify func (o *PushOptions) GetSkipTLSVerify() bool { - var skipTLSVerify bool if o.SkipTLSVerify == nil { - return skipTLSVerify + var z bool + return z } return *o.SkipTLSVerify } -// WithUsername +// WithUsername set field Username to given value func (o *PushOptions) WithUsername(value string) *PushOptions { - v := &value - o.Username = v + o.Username = &value return o } -// GetUsername +// GetUsername returns value of field Username func (o *PushOptions) GetUsername() string { - var username string if o.Username == nil { - return username + var z string + return z } return *o.Username } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,48 +7,42 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *RemoveOptions) WithAll(value bool) *RemoveOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *RemoveOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithForce +// WithForce set field Force to given value func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { - v := &value - o.Force = v + o.Force = &value return o } -// GetForce +// GetForce returns value of field Force func (o *RemoveOptions) GetForce() bool { - var force bool if o.Force == nil { - return force + var z bool + return z } return *o.Force } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_search_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_search_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_search_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_search_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,112 +7,102 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *SearchOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *SearchOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAuthfile +// WithAuthfile set field Authfile to given value func (o *SearchOptions) WithAuthfile(value string) *SearchOptions { - v := &value - o.Authfile = v + o.Authfile = &value return o } -// GetAuthfile +// GetAuthfile returns value of field Authfile func (o *SearchOptions) GetAuthfile() string { - var authfile string if o.Authfile == nil { - return authfile + var z string + return z } return *o.Authfile } -// WithFilters +// WithFilters set field Filters to given value func (o *SearchOptions) WithFilters(value map[string][]string) *SearchOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *SearchOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } -// WithLimit +// WithLimit set field Limit to given value func (o *SearchOptions) WithLimit(value int) *SearchOptions { - v := &value - o.Limit = v + o.Limit = &value return o } -// GetLimit +// GetLimit returns value of field Limit func (o *SearchOptions) GetLimit() int { - var limit int if o.Limit == nil { - return limit + var z int + return z } return *o.Limit } -// WithNoTrunc +// WithNoTrunc set field NoTrunc to given value func (o *SearchOptions) WithNoTrunc(value bool) *SearchOptions { - v := &value - o.NoTrunc = v + o.NoTrunc = &value return o } -// GetNoTrunc +// GetNoTrunc returns value of field NoTrunc func (o *SearchOptions) GetNoTrunc() bool { - var noTrunc bool if o.NoTrunc == nil { - return noTrunc + var z bool + return z } return *o.NoTrunc } -// WithSkipTLSVerify +// WithSkipTLSVerify set field SkipTLSVerify to given value func (o *SearchOptions) WithSkipTLSVerify(value bool) *SearchOptions { - v := &value - o.SkipTLSVerify = v + o.SkipTLSVerify = &value return o } -// GetSkipTLSVerify +// GetSkipTLSVerify returns value of field SkipTLSVerify func (o *SearchOptions) GetSkipTLSVerify() bool { - var skipTLSVerify bool if o.SkipTLSVerify == nil { - return skipTLSVerify + var z bool + return z } return *o.SkipTLSVerify } -// WithListTags +// WithListTags set field ListTags to given value func (o *SearchOptions) WithListTags(value bool) *SearchOptions { - v := &value - o.ListTags = v + o.ListTags = &value return o } -// GetListTags +// GetListTags returns value of field ListTags func (o *SearchOptions) GetListTags() bool { - var listTags bool if o.ListTags == nil { - return listTags + var z bool + return z } return *o.ListTags } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_tag_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_tag_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_tag_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_tag_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *TagOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *TagOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_tree_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_tree_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_tree_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_tree_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *TreeOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *TreeOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithWhatRequires +// WithWhatRequires set field WhatRequires to given value func (o *TreeOptions) WithWhatRequires(value bool) *TreeOptions { - v := &value - o.WhatRequires = v + o.WhatRequires = &value return o } -// GetWhatRequires +// GetWhatRequires returns value of field WhatRequires func (o *TreeOptions) GetWhatRequires() bool { - var whatRequires bool if o.WhatRequires == nil { - return whatRequires + var z bool + return z } return *o.WhatRequires } diff -Nru libpod-3.2.1+ds1/pkg/bindings/images/types_untag_options.go libpod-3.4.4+ds1/pkg/bindings/images/types_untag_options.go --- libpod-3.2.1+ds1/pkg/bindings/images/types_untag_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/images/types_untag_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package images import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *UntagOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *UntagOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/internal/util/util.go libpod-3.4.4+ds1/pkg/bindings/internal/util/util.go --- libpod-3.2.1+ds1/pkg/bindings/internal/util/util.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/internal/util/util.go 2021-12-26 00:45:51.000000000 +0000 @@ -72,30 +72,34 @@ if reflect.Ptr == f.Kind() { f = f.Elem() } + paramName := fieldName + if pn, ok := sType.Field(i).Tag.Lookup("schema"); ok { + paramName = pn + } switch { case IsSimpleType(f): - params.Set(fieldName, SimpleTypeToParam(f)) + params.Set(paramName, SimpleTypeToParam(f)) case f.Kind() == reflect.Slice: for i := 0; i < f.Len(); i++ { elem := f.Index(i) if IsSimpleType(elem) { - params.Add(fieldName, SimpleTypeToParam(elem)) + params.Add(paramName, SimpleTypeToParam(elem)) } else { return nil, errors.New("slices must contain only simple types") } } case f.Kind() == reflect.Map: - lowerCaseKeys := make(map[string][]string) + lowerCaseKeys := make(map[string]interface{}) iter := f.MapRange() for iter.Next() { - lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface().([]string) + lowerCaseKeys[iter.Key().Interface().(string)] = iter.Value().Interface() } s, err := json.MarshalToString(lowerCaseKeys) if err != nil { return nil, err } - params.Set(fieldName, s) + params.Set(paramName, s) } } return params, nil diff -Nru libpod-3.2.1+ds1/pkg/bindings/manifests/manifests.go libpod-3.4.4+ds1/pkg/bindings/manifests/manifests.go --- libpod-3.2.1+ds1/pkg/bindings/manifests/manifests.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/manifests/manifests.go 2021-12-26 00:45:51.000000000 +0000 @@ -46,10 +46,12 @@ if err != nil { return "", err } + defer response.Body.Close() + return idr.ID, response.Process(&idr) } -// Exists returns true if a given maifest list exists +// Exists returns true if a given manifest list exists func Exists(ctx context.Context, name string, options *ExistsOptions) (bool, error) { conn, err := bindings.GetClient(ctx) if err != nil { @@ -59,6 +61,8 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } @@ -77,6 +81,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &list, response.Process(&list) } @@ -100,6 +106,8 @@ if err != nil { return "", err } + defer response.Body.Close() + return idr.ID, response.Process(&idr) } @@ -121,6 +129,8 @@ if err != nil { return "", err } + defer response.Body.Close() + return idr.ID, response.Process(&idr) } @@ -145,18 +155,20 @@ if err != nil { return "", err } - //SkipTLSVerify is special. We need to delete the param added by - //toparams and change the key and flip the bool + // SkipTLSVerify is special. We need to delete the param added by + // toparams and change the key and flip the bool if options.SkipTLSVerify != nil { params.Del("SkipTLSVerify") params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify())) } params.Set("image", name) params.Set("destination", destination) - _, err = conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, nil, name) + response, err := conn.DoRequest(nil, http.MethodPost, "/manifests/%s/push", params, nil, name) if err != nil { return "", err } + defer response.Body.Close() + return idr.ID, err } @@ -179,5 +191,6 @@ // if err != nil { // return "", err // } +// defer response.Body.Close() // return idr.ID, response.Process(&idr) //} diff -Nru libpod-3.2.1+ds1/pkg/bindings/manifests/types_add_options.go libpod-3.4.4+ds1/pkg/bindings/manifests/types_add_options.go --- libpod-3.2.1+ds1/pkg/bindings/manifests/types_add_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/manifests/types_add_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package manifests import ( @@ -6,144 +7,132 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *AddOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *AddOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *AddOptions) WithAll(value bool) *AddOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *AddOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithAnnotation +// WithAnnotation set field Annotation to given value func (o *AddOptions) WithAnnotation(value map[string]string) *AddOptions { - v := value - o.Annotation = v + o.Annotation = value return o } -// GetAnnotation +// GetAnnotation returns value of field Annotation func (o *AddOptions) GetAnnotation() map[string]string { - var annotation map[string]string if o.Annotation == nil { - return annotation + var z map[string]string + return z } return o.Annotation } -// WithArch +// WithArch set field Arch to given value func (o *AddOptions) WithArch(value string) *AddOptions { - v := &value - o.Arch = v + o.Arch = &value return o } -// GetArch +// GetArch returns value of field Arch func (o *AddOptions) GetArch() string { - var arch string if o.Arch == nil { - return arch + var z string + return z } return *o.Arch } -// WithFeatures +// WithFeatures set field Features to given value func (o *AddOptions) WithFeatures(value []string) *AddOptions { - v := value - o.Features = v + o.Features = value return o } -// GetFeatures +// GetFeatures returns value of field Features func (o *AddOptions) GetFeatures() []string { - var features []string if o.Features == nil { - return features + var z []string + return z } return o.Features } -// WithImages +// WithImages set field Images to given value func (o *AddOptions) WithImages(value []string) *AddOptions { - v := value - o.Images = v + o.Images = value return o } -// GetImages +// GetImages returns value of field Images func (o *AddOptions) GetImages() []string { - var images []string if o.Images == nil { - return images + var z []string + return z } return o.Images } -// WithOS +// WithOS set field OS to given value func (o *AddOptions) WithOS(value string) *AddOptions { - v := &value - o.OS = v + o.OS = &value return o } -// GetOS +// GetOS returns value of field OS func (o *AddOptions) GetOS() string { - var oS string if o.OS == nil { - return oS + var z string + return z } return *o.OS } -// WithOSVersion +// WithOSVersion set field OSVersion to given value func (o *AddOptions) WithOSVersion(value string) *AddOptions { - v := &value - o.OSVersion = v + o.OSVersion = &value return o } -// GetOSVersion +// GetOSVersion returns value of field OSVersion func (o *AddOptions) GetOSVersion() string { - var oSVersion string if o.OSVersion == nil { - return oSVersion + var z string + return z } return *o.OSVersion } -// WithVariant +// WithVariant set field Variant to given value func (o *AddOptions) WithVariant(value string) *AddOptions { - v := &value - o.Variant = v + o.Variant = &value return o } -// GetVariant +// GetVariant returns value of field Variant func (o *AddOptions) GetVariant() string { - var variant string if o.Variant == nil { - return variant + var z string + return z } return *o.Variant } diff -Nru libpod-3.2.1+ds1/pkg/bindings/manifests/types_create_options.go libpod-3.4.4+ds1/pkg/bindings/manifests/types_create_options.go --- libpod-3.2.1+ds1/pkg/bindings/manifests/types_create_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/manifests/types_create_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package manifests import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CreateOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CreateOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *CreateOptions) WithAll(value bool) *CreateOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *CreateOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } diff -Nru libpod-3.2.1+ds1/pkg/bindings/manifests/types_exists_options.go libpod-3.4.4+ds1/pkg/bindings/manifests/types_exists_options.go --- libpod-3.2.1+ds1/pkg/bindings/manifests/types_exists_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/manifests/types_exists_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package manifests import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExistsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExistsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/manifests/types_inspect_options.go libpod-3.4.4+ds1/pkg/bindings/manifests/types_inspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/manifests/types_inspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/manifests/types_inspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package manifests import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/manifests/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/manifests/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/manifests/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/manifests/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package manifests import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/network.go libpod-3.4.4+ds1/pkg/bindings/network/network.go --- libpod-3.2.1+ds1/pkg/bindings/network/network.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -34,6 +34,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -53,6 +55,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return reports, response.Process(&reports[0]) } @@ -76,6 +80,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return reports, response.Process(&reports) } @@ -99,6 +105,8 @@ if err != nil { return netList, err } + defer response.Body.Close() + return netList, response.Process(&netList) } @@ -133,6 +141,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -166,6 +176,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -179,6 +191,8 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } @@ -203,5 +217,7 @@ if err != nil { return nil, err } + defer response.Body.Close() + return prunedNetworks, response.Process(&prunedNetworks) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_connect_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_connect_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_connect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_connect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ConnectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ConnectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAliases +// WithAliases set field Aliases to given value func (o *ConnectOptions) WithAliases(value []string) *ConnectOptions { - v := &value - o.Aliases = v + o.Aliases = &value return o } -// GetAliases +// GetAliases returns value of field Aliases func (o *ConnectOptions) GetAliases() []string { - var aliases []string if o.Aliases == nil { - return aliases + var z []string + return z } return *o.Aliases } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_create_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_create_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_create_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_create_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -7,192 +8,177 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CreateOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CreateOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithDisableDNS +// WithDisableDNS set field DisableDNS to given value func (o *CreateOptions) WithDisableDNS(value bool) *CreateOptions { - v := &value - o.DisableDNS = v + o.DisableDNS = &value return o } -// GetDisableDNS +// GetDisableDNS returns value of field DisableDNS func (o *CreateOptions) GetDisableDNS() bool { - var disableDNS bool if o.DisableDNS == nil { - return disableDNS + var z bool + return z } return *o.DisableDNS } -// WithDriver +// WithDriver set field Driver to given value func (o *CreateOptions) WithDriver(value string) *CreateOptions { - v := &value - o.Driver = v + o.Driver = &value return o } -// GetDriver +// GetDriver returns value of field Driver func (o *CreateOptions) GetDriver() string { - var driver string if o.Driver == nil { - return driver + var z string + return z } return *o.Driver } -// WithGateway +// WithGateway set field Gateway to given value func (o *CreateOptions) WithGateway(value net.IP) *CreateOptions { - v := &value - o.Gateway = v + o.Gateway = &value return o } -// GetGateway +// GetGateway returns value of field Gateway func (o *CreateOptions) GetGateway() net.IP { - var gateway net.IP if o.Gateway == nil { - return gateway + var z net.IP + return z } return *o.Gateway } -// WithInternal +// WithInternal set field Internal to given value func (o *CreateOptions) WithInternal(value bool) *CreateOptions { - v := &value - o.Internal = v + o.Internal = &value return o } -// GetInternal +// GetInternal returns value of field Internal func (o *CreateOptions) GetInternal() bool { - var internal bool if o.Internal == nil { - return internal + var z bool + return z } return *o.Internal } -// WithLabels +// WithLabels set field Labels to given value func (o *CreateOptions) WithLabels(value map[string]string) *CreateOptions { - v := value - o.Labels = v + o.Labels = value return o } -// GetLabels +// GetLabels returns value of field Labels func (o *CreateOptions) GetLabels() map[string]string { - var labels map[string]string if o.Labels == nil { - return labels + var z map[string]string + return z } return o.Labels } -// WithMacVLAN +// WithMacVLAN set field MacVLAN to given value func (o *CreateOptions) WithMacVLAN(value string) *CreateOptions { - v := &value - o.MacVLAN = v + o.MacVLAN = &value return o } -// GetMacVLAN +// GetMacVLAN returns value of field MacVLAN func (o *CreateOptions) GetMacVLAN() string { - var macVLAN string if o.MacVLAN == nil { - return macVLAN + var z string + return z } return *o.MacVLAN } -// WithIPRange +// WithIPRange set field IPRange to given value func (o *CreateOptions) WithIPRange(value net.IPNet) *CreateOptions { - v := &value - o.IPRange = v + o.IPRange = &value return o } -// GetIPRange +// GetIPRange returns value of field IPRange func (o *CreateOptions) GetIPRange() net.IPNet { - var iPRange net.IPNet if o.IPRange == nil { - return iPRange + var z net.IPNet + return z } return *o.IPRange } -// WithSubnet +// WithSubnet set field Subnet to given value func (o *CreateOptions) WithSubnet(value net.IPNet) *CreateOptions { - v := &value - o.Subnet = v + o.Subnet = &value return o } -// GetSubnet +// GetSubnet returns value of field Subnet func (o *CreateOptions) GetSubnet() net.IPNet { - var subnet net.IPNet if o.Subnet == nil { - return subnet + var z net.IPNet + return z } return *o.Subnet } -// WithIPv6 +// WithIPv6 set field IPv6 to given value func (o *CreateOptions) WithIPv6(value bool) *CreateOptions { - v := &value - o.IPv6 = v + o.IPv6 = &value return o } -// GetIPv6 +// GetIPv6 returns value of field IPv6 func (o *CreateOptions) GetIPv6() bool { - var iPv6 bool if o.IPv6 == nil { - return iPv6 + var z bool + return z } return *o.IPv6 } -// WithOptions +// WithOptions set field Options to given value func (o *CreateOptions) WithOptions(value map[string]string) *CreateOptions { - v := value - o.Options = v + o.Options = value return o } -// GetOptions +// GetOptions returns value of field Options func (o *CreateOptions) GetOptions() map[string]string { - var options map[string]string if o.Options == nil { - return options + var z map[string]string + return z } return o.Options } -// WithName +// WithName set field Name to given value func (o *CreateOptions) WithName(value string) *CreateOptions { - v := &value - o.Name = v + o.Name = &value return o } -// GetName +// GetName returns value of field Name func (o *CreateOptions) GetName() string { - var name string if o.Name == nil { - return name + var z string + return z } return *o.Name } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_disconnect_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_disconnect_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_disconnect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_disconnect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *DisconnectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *DisconnectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithForce +// WithForce set field Force to given value func (o *DisconnectOptions) WithForce(value bool) *DisconnectOptions { - v := &value - o.Force = v + o.Force = &value return o } -// GetForce +// GetForce returns value of field Force func (o *DisconnectOptions) GetForce() bool { - var force bool if o.Force == nil { - return force + var z bool + return z } return *o.Force } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_exists_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_exists_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_exists_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_exists_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExistsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExistsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_inspect_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_inspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_inspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_inspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_list_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_list_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_list_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_list_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ListOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ListOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *ListOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_prune_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_prune_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_prune_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_prune_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PruneOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PruneOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *PruneOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/network/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/network/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/network/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/network/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package network import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithForce +// WithForce set field Force to given value func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { - v := &value - o.Force = v + o.Force = &value return o } -// GetForce +// GetForce returns value of field Force func (o *RemoveOptions) GetForce() bool { - var force bool if o.Force == nil { - return force + var z bool + return z } return *o.Force } diff -Nru libpod-3.2.1+ds1/pkg/bindings/play/play.go libpod-3.4.4+ds1/pkg/bindings/play/play.go --- libpod-3.2.1+ds1/pkg/bindings/play/play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/play/play.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,8 @@ "os" "strconv" + "github.com/sirupsen/logrus" + "github.com/containers/podman/v3/pkg/auth" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" @@ -48,6 +50,35 @@ if err != nil { return nil, err } + defer response.Body.Close() + + if err := response.Process(&report); err != nil { + return nil, err + } + + return &report, nil +} + +func KubeDown(ctx context.Context, path string) (*entities.PlayKubeReport, error) { + var report entities.PlayKubeReport + conn, err := bindings.GetClient(ctx) + if err != nil { + return nil, err + } + + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer func() { + if err := f.Close(); err != nil { + logrus.Warn(err) + } + }() + response, err := conn.DoRequest(f, http.MethodDelete, "/play/kube", nil, nil) + if err != nil { + return nil, err + } if err := response.Process(&report); err != nil { return nil, err } diff -Nru libpod-3.2.1+ds1/pkg/bindings/play/types.go libpod-3.4.4+ds1/pkg/bindings/play/types.go --- libpod-3.2.1+ds1/pkg/bindings/play/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/play/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,8 @@ package play -import "net" +import ( + "net" +) //go:generate go run ../generator/generator.go KubeOptions // KubeOptions are optional options for replaying kube YAML files diff -Nru libpod-3.2.1+ds1/pkg/bindings/play/types_kube_options.go libpod-3.4.4+ds1/pkg/bindings/play/types_kube_options.go --- libpod-3.2.1+ds1/pkg/bindings/play/types_kube_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/play/types_kube_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package play import ( @@ -7,240 +8,222 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *KubeOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *KubeOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAuthfile +// WithAuthfile set field Authfile to given value func (o *KubeOptions) WithAuthfile(value string) *KubeOptions { - v := &value - o.Authfile = v + o.Authfile = &value return o } -// GetAuthfile +// GetAuthfile returns value of field Authfile func (o *KubeOptions) GetAuthfile() string { - var authfile string if o.Authfile == nil { - return authfile + var z string + return z } return *o.Authfile } -// WithCertDir +// WithCertDir set field CertDir to given value func (o *KubeOptions) WithCertDir(value string) *KubeOptions { - v := &value - o.CertDir = v + o.CertDir = &value return o } -// GetCertDir +// GetCertDir returns value of field CertDir func (o *KubeOptions) GetCertDir() string { - var certDir string if o.CertDir == nil { - return certDir + var z string + return z } return *o.CertDir } -// WithUsername +// WithUsername set field Username to given value func (o *KubeOptions) WithUsername(value string) *KubeOptions { - v := &value - o.Username = v + o.Username = &value return o } -// GetUsername +// GetUsername returns value of field Username func (o *KubeOptions) GetUsername() string { - var username string if o.Username == nil { - return username + var z string + return z } return *o.Username } -// WithPassword +// WithPassword set field Password to given value func (o *KubeOptions) WithPassword(value string) *KubeOptions { - v := &value - o.Password = v + o.Password = &value return o } -// GetPassword +// GetPassword returns value of field Password func (o *KubeOptions) GetPassword() string { - var password string if o.Password == nil { - return password + var z string + return z } return *o.Password } -// WithNetwork +// WithNetwork set field Network to given value func (o *KubeOptions) WithNetwork(value string) *KubeOptions { - v := &value - o.Network = v + o.Network = &value return o } -// GetNetwork +// GetNetwork returns value of field Network func (o *KubeOptions) GetNetwork() string { - var network string if o.Network == nil { - return network + var z string + return z } return *o.Network } -// WithQuiet +// WithQuiet set field Quiet to given value func (o *KubeOptions) WithQuiet(value bool) *KubeOptions { - v := &value - o.Quiet = v + o.Quiet = &value return o } -// GetQuiet +// GetQuiet returns value of field Quiet func (o *KubeOptions) GetQuiet() bool { - var quiet bool if o.Quiet == nil { - return quiet + var z bool + return z } return *o.Quiet } -// WithSignaturePolicy +// WithSignaturePolicy set field SignaturePolicy to given value func (o *KubeOptions) WithSignaturePolicy(value string) *KubeOptions { - v := &value - o.SignaturePolicy = v + o.SignaturePolicy = &value return o } -// GetSignaturePolicy +// GetSignaturePolicy returns value of field SignaturePolicy func (o *KubeOptions) GetSignaturePolicy() string { - var signaturePolicy string if o.SignaturePolicy == nil { - return signaturePolicy + var z string + return z } return *o.SignaturePolicy } -// WithSkipTLSVerify +// WithSkipTLSVerify set field SkipTLSVerify to given value func (o *KubeOptions) WithSkipTLSVerify(value bool) *KubeOptions { - v := &value - o.SkipTLSVerify = v + o.SkipTLSVerify = &value return o } -// GetSkipTLSVerify +// GetSkipTLSVerify returns value of field SkipTLSVerify func (o *KubeOptions) GetSkipTLSVerify() bool { - var skipTLSVerify bool if o.SkipTLSVerify == nil { - return skipTLSVerify + var z bool + return z } return *o.SkipTLSVerify } -// WithSeccompProfileRoot +// WithSeccompProfileRoot set field SeccompProfileRoot to given value func (o *KubeOptions) WithSeccompProfileRoot(value string) *KubeOptions { - v := &value - o.SeccompProfileRoot = v + o.SeccompProfileRoot = &value return o } -// GetSeccompProfileRoot +// GetSeccompProfileRoot returns value of field SeccompProfileRoot func (o *KubeOptions) GetSeccompProfileRoot() string { - var seccompProfileRoot string if o.SeccompProfileRoot == nil { - return seccompProfileRoot + var z string + return z } return *o.SeccompProfileRoot } -// WithStaticIPs +// WithStaticIPs set field StaticIPs to given value func (o *KubeOptions) WithStaticIPs(value []net.IP) *KubeOptions { - v := &value - o.StaticIPs = v + o.StaticIPs = &value return o } -// GetStaticIPs +// GetStaticIPs returns value of field StaticIPs func (o *KubeOptions) GetStaticIPs() []net.IP { - var staticIPs []net.IP if o.StaticIPs == nil { - return staticIPs + var z []net.IP + return z } return *o.StaticIPs } -// WithStaticMACs +// WithStaticMACs set field StaticMACs to given value func (o *KubeOptions) WithStaticMACs(value []net.HardwareAddr) *KubeOptions { - v := &value - o.StaticMACs = v + o.StaticMACs = &value return o } -// GetStaticMACs +// GetStaticMACs returns value of field StaticMACs func (o *KubeOptions) GetStaticMACs() []net.HardwareAddr { - var staticMACs []net.HardwareAddr if o.StaticMACs == nil { - return staticMACs + var z []net.HardwareAddr + return z } return *o.StaticMACs } -// WithConfigMaps +// WithConfigMaps set field ConfigMaps to given value func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions { - v := &value - o.ConfigMaps = v + o.ConfigMaps = &value return o } -// GetConfigMaps +// GetConfigMaps returns value of field ConfigMaps func (o *KubeOptions) GetConfigMaps() []string { - var configMaps []string if o.ConfigMaps == nil { - return configMaps + var z []string + return z } return *o.ConfigMaps } -// WithLogDriver +// WithLogDriver set field LogDriver to given value func (o *KubeOptions) WithLogDriver(value string) *KubeOptions { - v := &value - o.LogDriver = v + o.LogDriver = &value return o } -// GetLogDriver +// GetLogDriver returns value of field LogDriver func (o *KubeOptions) GetLogDriver() string { - var logDriver string if o.LogDriver == nil { - return logDriver + var z string + return z } return *o.LogDriver } -// WithStart +// WithStart set field Start to given value func (o *KubeOptions) WithStart(value bool) *KubeOptions { - v := &value - o.Start = v + o.Start = &value return o } -// GetStart +// GetStart returns value of field Start func (o *KubeOptions) GetStart() bool { - var start bool if o.Start == nil { - return start + var z bool + return z } return *o.Start } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/pods.go libpod-3.4.4+ds1/pkg/bindings/pods/pods.go --- libpod-3.2.1+ds1/pkg/bindings/pods/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,31 +9,32 @@ "github.com/containers/podman/v3/pkg/api/handlers" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/errorhandling" jsoniter "github.com/json-iterator/go" ) -func CreatePodFromSpec(ctx context.Context, s *specgen.PodSpecGenerator, options *CreateOptions) (*entities.PodCreateReport, error) { +func CreatePodFromSpec(ctx context.Context, spec *entities.PodSpec) (*entities.PodCreateReport, error) { var ( pcr entities.PodCreateReport ) - if options == nil { - options = new(CreateOptions) + if spec == nil { + spec = new(entities.PodSpec) } - _ = options conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - specgenString, err := jsoniter.MarshalToString(s) + specString, err := jsoniter.MarshalToString(spec.PodSpecGen) if err != nil { return nil, err } - stringReader := strings.NewReader(specgenString) + stringReader := strings.NewReader(specString) response, err := conn.DoRequest(stringReader, http.MethodPost, "/pods/create", nil, nil) if err != nil { return nil, err } + defer response.Body.Close() + return &pcr, response.Process(&pcr) } @@ -47,6 +48,8 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } @@ -67,6 +70,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -91,7 +96,9 @@ if err != nil { return nil, err } - return &report, response.Process(&report) + defer response.Body.Close() + + return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } // Pause pauses all running containers in a given pod. @@ -109,7 +116,9 @@ if err != nil { return nil, err } - return &report, response.Process(&report) + defer response.Body.Close() + + return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } // Prune by default removes all non-running pods in local storage. @@ -128,6 +137,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return reports, response.Process(&reports) } @@ -152,6 +163,8 @@ if err != nil { return podsReports, err } + defer response.Body.Close() + return podsReports, response.Process(&podsReports) } @@ -170,7 +183,9 @@ if err != nil { return nil, err } - return &report, response.Process(&report) + defer response.Body.Close() + + return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } // Remove deletes a Pod from from local storage. The optional force parameter denotes @@ -192,6 +207,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -210,11 +227,14 @@ if err != nil { return nil, err } + defer response.Body.Close() + if response.StatusCode == http.StatusNotModified { report.Id = nameOrID return &report, nil } - return &report, response.Process(&report) + + return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } // Stop stops all containers in a Pod. The optional timeout parameter can be @@ -236,11 +256,13 @@ if err != nil { return nil, err } + defer response.Body.Close() + if response.StatusCode == http.StatusNotModified { report.Id = nameOrID return &report, nil } - return &report, response.Process(&report) + return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } // Top gathers statistics about the running processes in a pod. The nameOrID can be a pod name @@ -261,6 +283,7 @@ if err != nil { return nil, err } + defer response.Body.Close() body := handlers.PodTopOKBody{} if err = response.Process(&body); err != nil { @@ -293,7 +316,9 @@ if err != nil { return nil, err } - return &report, response.Process(&report) + defer response.Body.Close() + + return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{}) } // Stats display resource-usage statistics of one or more pods. @@ -318,5 +343,7 @@ if err != nil { return nil, err } + defer response.Body.Close() + return reports, response.Process(&reports) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_create_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_create_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_create_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_create_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CreateOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CreateOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_exists_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_exists_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_exists_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_exists_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExistsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExistsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_inspect_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_inspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_inspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_inspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_kill_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_kill_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_kill_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_kill_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *KillOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *KillOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithSignal +// WithSignal set field Signal to given value func (o *KillOptions) WithSignal(value string) *KillOptions { - v := &value - o.Signal = v + o.Signal = &value return o } -// GetSignal +// GetSignal returns value of field Signal func (o *KillOptions) GetSignal() string { - var signal string if o.Signal == nil { - return signal + var z string + return z } return *o.Signal } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_list_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_list_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_list_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_list_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ListOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ListOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *ListOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_pause_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_pause_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_pause_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_pause_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PauseOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PauseOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_prune_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_prune_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_prune_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_prune_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PruneOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PruneOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithForce +// WithForce set field Force to given value func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { - v := &value - o.Force = v + o.Force = &value return o } -// GetForce +// GetForce returns value of field Force func (o *RemoveOptions) GetForce() bool { - var force bool if o.Force == nil { - return force + var z bool + return z } return *o.Force } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_restart_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_restart_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_restart_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_restart_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RestartOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RestartOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_start_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_start_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_start_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_start_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *StartOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *StartOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_stats_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_stats_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_stats_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_stats_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *StatsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *StatsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *StatsOptions) WithAll(value bool) *StatsOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *StatsOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_stop_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_stop_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_stop_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_stop_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *StopOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *StopOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithTimeout +// WithTimeout set field Timeout to given value func (o *StopOptions) WithTimeout(value int) *StopOptions { - v := &value - o.Timeout = v + o.Timeout = &value return o } -// GetTimeout +// GetTimeout returns value of field Timeout func (o *StopOptions) GetTimeout() int { - var timeout int if o.Timeout == nil { - return timeout + var z int + return z } return *o.Timeout } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_top_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_top_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_top_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_top_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *TopOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *TopOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithDescriptors +// WithDescriptors set field Descriptors to given value func (o *TopOptions) WithDescriptors(value []string) *TopOptions { - v := value - o.Descriptors = v + o.Descriptors = value return o } -// GetDescriptors +// GetDescriptors returns value of field Descriptors func (o *TopOptions) GetDescriptors() []string { - var descriptors []string if o.Descriptors == nil { - return descriptors + var z []string + return z } return o.Descriptors } diff -Nru libpod-3.2.1+ds1/pkg/bindings/pods/types_unpause_options.go libpod-3.4.4+ds1/pkg/bindings/pods/types_unpause_options.go --- libpod-3.2.1+ds1/pkg/bindings/pods/types_unpause_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/pods/types_unpause_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package pods import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *UnpauseOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *UnpauseOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/README.md libpod-3.4.4+ds1/pkg/bindings/README.md --- libpod-3.2.1+ds1/pkg/bindings/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -57,6 +57,10 @@ ### Examples The following examples build upon the connection example from above. They are all rootful connections as well. +Note: Optional arguments to the bindings methods are set using With*() methods on *Option structures. +Composite types are not duplicated rather the address is used. As such, you should not change an underlying +field between initializing the *Option structure and calling the bindings method. + #### Inspect a container The following example obtains the inspect information for a container named `foorbar` and then prints the container's ID. Note the use of optional inspect options for size. @@ -154,3 +158,80 @@ fmt.Println("Container started.") } ``` + +## Debugging tips + +To debug in a development setup, you can start the Podman system service +in debug mode like: + +```bash +$ podman --log-level=debug system service -t 0 +``` + +The `--log-level=debug` echoes all the logged requests and is useful to +trace the execution path at a finer granularity. A snippet of a sample run looks like: + +```bash +INFO[0000] podman filtering at log level debug +DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) +DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. +DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" +DEBU[0000] Merged system config "/usr/share/containers/containers.conf": {Editors note: the remainder of this line was removed due to Jekyll formatting errors.} +DEBU[0000] Using conmon: "/usr/bin/conmon" +DEBU[0000] Initializing boltdb state at /home/lsm5/.local/share/containers/storage/libpod/bolt_state.db +DEBU[0000] Overriding run root "/run/user/1000/containers" with "/run/user/1000" from database +DEBU[0000] Using graph driver overlay +DEBU[0000] Using graph root /home/lsm5/.local/share/containers/storage +DEBU[0000] Using run root /run/user/1000 +DEBU[0000] Using static dir /home/lsm5/.local/share/containers/storage/libpod +DEBU[0000] Using tmp dir /run/user/1000/libpod/tmp +DEBU[0000] Using volume path /home/lsm5/.local/share/containers/storage/volumes +DEBU[0000] Set libpod namespace to "" +DEBU[0000] Not configuring container store +DEBU[0000] Initializing event backend file +DEBU[0000] using runtime "/usr/bin/runc" +DEBU[0000] using runtime "/usr/bin/crun" +WARN[0000] Error initializing configured OCI runtime kata: no valid executable found for OCI runtime kata: invalid argument +DEBU[0000] using runtime "/usr/bin/crun" +INFO[0000] Setting parallel job count to 25 +INFO[0000] podman filtering at log level debug +DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) +DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. +DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" +``` + +If the Podman system service has been started via systemd socket activation, +you can view the logs using journalctl. The logs after a sample run look like: + +```bash +$ journalctl --user --no-pager -u podman.socket +-- Reboot -- +Jul 22 13:50:40 nagato.nanadai.me systemd[1048]: Listening on Podman API Socket. +$ +``` + +```bash +$ journalctl --user --no-pager -u podman.service +Jul 22 13:50:53 nagato.nanadai.me systemd[1048]: Starting Podman API Service... +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: error acquiring lock 0 for volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: error acquiring lock 0 for volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: error acquiring lock 0 for volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: error acquiring lock 0 for volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: error acquiring lock 0 for volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: error acquiring lock 0 for volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: Trying to pull registry.fedoraproject.org/fedora:latest... +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Getting image source signatures +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying blob sha256:dd9f43919ba05f05d4f783c31e83e5e776c4f5d29dd72b9ec5056b9576c10053 +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying config sha256:00ff39a8bf19f810a7e641f7eb3ddc47635913a19c4996debd91fafb6b379069 +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Writing manifest to image destination +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Storing signatures +Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: podman.service: unit configures an IP firewall, but not running as root. +Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: (This warning is only shown for the first unit using IP firewalling.) +Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Succeeded. +Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: Finished Podman API Service. +Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Consumed 1.339s CPU time. +$ +``` + +You can also verify that the information being passed back and forth is correct by putting +with a tool like `socat`, which can dump what the socket is seeing. diff -Nru libpod-3.2.1+ds1/pkg/bindings/secrets/secrets.go libpod-3.4.4+ds1/pkg/bindings/secrets/secrets.go --- libpod-3.2.1+ds1/pkg/bindings/secrets/secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/secrets/secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -18,10 +18,16 @@ if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/secrets/json", nil, nil) + params, err := options.ToParams() + if err != nil { + return nil, err + } + response, err := conn.DoRequest(nil, http.MethodGet, "/secrets/json", params, nil) if err != nil { return secrs, err } + defer response.Body.Close() + return secrs, response.Process(&secrs) } @@ -38,6 +44,8 @@ if err != nil { return inspect, err } + defer response.Body.Close() + return inspect, response.Process(&inspect) } @@ -52,6 +60,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -74,5 +84,7 @@ if err != nil { return nil, err } + defer response.Body.Close() + return create, response.Process(&create) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/secrets/types_create_options.go libpod-3.4.4+ds1/pkg/bindings/secrets/types_create_options.go --- libpod-3.2.1+ds1/pkg/bindings/secrets/types_create_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/secrets/types_create_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package secrets import ( @@ -6,48 +7,57 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CreateOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CreateOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithDriver +// WithName set field Name to given value +func (o *CreateOptions) WithName(value string) *CreateOptions { + o.Name = &value + return o +} + +// GetName returns value of field Name +func (o *CreateOptions) GetName() string { + if o.Name == nil { + var z string + return z + } + return *o.Name +} + +// WithDriver set field Driver to given value func (o *CreateOptions) WithDriver(value string) *CreateOptions { - v := &value - o.Driver = v + o.Driver = &value return o } -// GetDriver +// GetDriver returns value of field Driver func (o *CreateOptions) GetDriver() string { - var driver string if o.Driver == nil { - return driver + var z string + return z } return *o.Driver } -// WithName -func (o *CreateOptions) WithName(value string) *CreateOptions { - v := &value - o.Name = v +// WithDriverOpts set field DriverOpts to given value +func (o *CreateOptions) WithDriverOpts(value map[string]string) *CreateOptions { + o.DriverOpts = value return o } -// GetName -func (o *CreateOptions) GetName() string { - var name string - if o.Name == nil { - return name +// GetDriverOpts returns value of field DriverOpts +func (o *CreateOptions) GetDriverOpts() map[string]string { + if o.DriverOpts == nil { + var z map[string]string + return z } - return *o.Name + return o.DriverOpts } diff -Nru libpod-3.2.1+ds1/pkg/bindings/secrets/types.go libpod-3.4.4+ds1/pkg/bindings/secrets/types.go --- libpod-3.2.1+ds1/pkg/bindings/secrets/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/secrets/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ //go:generate go run ../generator/generator.go ListOptions // ListOptions are optional options for inspecting secrets type ListOptions struct { + Filters map[string][]string } //go:generate go run ../generator/generator.go InspectOptions @@ -18,6 +19,7 @@ //go:generate go run ../generator/generator.go CreateOptions // CreateOptions are optional options for Creating secrets type CreateOptions struct { - Driver *string - Name *string + Name *string + Driver *string + DriverOpts map[string]string } diff -Nru libpod-3.2.1+ds1/pkg/bindings/secrets/types_inspect_options.go libpod-3.4.4+ds1/pkg/bindings/secrets/types_inspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/secrets/types_inspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/secrets/types_inspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package secrets import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/secrets/types_list_options.go libpod-3.4.4+ds1/pkg/bindings/secrets/types_list_options.go --- libpod-3.2.1+ds1/pkg/bindings/secrets/types_list_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/secrets/types_list_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package secrets import ( @@ -6,16 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ListOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ListOptions) ToParams() (url.Values, error) { return util.ToParams(o) } + +// WithFilters set field Filters to given value +func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { + o.Filters = value + return o +} + +// GetFilters returns value of field Filters +func (o *ListOptions) GetFilters() map[string][]string { + if o.Filters == nil { + var z map[string][]string + return z + } + return o.Filters +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/secrets/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/secrets/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/secrets/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/secrets/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package secrets import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/info.go libpod-3.4.4+ds1/pkg/bindings/system/info.go --- libpod-3.2.1+ds1/pkg/bindings/system/info.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/info.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,12 +9,7 @@ ) // Info returns information about the libpod environment and its stores -func Info(ctx context.Context, options *InfoOptions) (*define.Info, error) { - if options == nil { - options = new(InfoOptions) - } - _ = options - info := define.Info{} +func Info(ctx context.Context, _ *InfoOptions) (*define.Info, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err @@ -23,5 +18,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + + info := define.Info{} return &info, response.Process(&info) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/system.go libpod-3.4.4+ds1/pkg/bindings/system/system.go --- libpod-3.2.1+ds1/pkg/bindings/system/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -31,6 +31,8 @@ if err != nil { return err } + defer response.Body.Close() + if cancelChan != nil { go func() { <-cancelChan @@ -75,6 +77,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } @@ -101,6 +105,7 @@ if err != nil { return nil, err } + defer response.Body.Close() if err = response.Process(&component); err != nil { return nil, err @@ -141,5 +146,7 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &report, response.Process(&report) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/types_disk_options.go libpod-3.4.4+ds1/pkg/bindings/system/types_disk_options.go --- libpod-3.2.1+ds1/pkg/bindings/system/types_disk_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/types_disk_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package system import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *DiskOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *DiskOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/types_events_options.go libpod-3.4.4+ds1/pkg/bindings/system/types_events_options.go --- libpod-3.2.1+ds1/pkg/bindings/system/types_events_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/types_events_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package system import ( @@ -6,80 +7,72 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *EventsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *EventsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *EventsOptions) WithFilters(value map[string][]string) *EventsOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *EventsOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } -// WithSince +// WithSince set field Since to given value func (o *EventsOptions) WithSince(value string) *EventsOptions { - v := &value - o.Since = v + o.Since = &value return o } -// GetSince +// GetSince returns value of field Since func (o *EventsOptions) GetSince() string { - var since string if o.Since == nil { - return since + var z string + return z } return *o.Since } -// WithStream +// WithStream set field Stream to given value func (o *EventsOptions) WithStream(value bool) *EventsOptions { - v := &value - o.Stream = v + o.Stream = &value return o } -// GetStream +// GetStream returns value of field Stream func (o *EventsOptions) GetStream() bool { - var stream bool if o.Stream == nil { - return stream + var z bool + return z } return *o.Stream } -// WithUntil +// WithUntil set field Until to given value func (o *EventsOptions) WithUntil(value string) *EventsOptions { - v := &value - o.Until = v + o.Until = &value return o } -// GetUntil +// GetUntil returns value of field Until func (o *EventsOptions) GetUntil() string { - var until string if o.Until == nil { - return until + var z string + return z } return *o.Until } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/types_info_options.go libpod-3.4.4+ds1/pkg/bindings/system/types_info_options.go --- libpod-3.2.1+ds1/pkg/bindings/system/types_info_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/types_info_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package system import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InfoOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InfoOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/types_prune_options.go libpod-3.4.4+ds1/pkg/bindings/system/types_prune_options.go --- libpod-3.2.1+ds1/pkg/bindings/system/types_prune_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/types_prune_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package system import ( @@ -6,64 +7,57 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PruneOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PruneOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithAll +// WithAll set field All to given value func (o *PruneOptions) WithAll(value bool) *PruneOptions { - v := &value - o.All = v + o.All = &value return o } -// GetAll +// GetAll returns value of field All func (o *PruneOptions) GetAll() bool { - var all bool if o.All == nil { - return all + var z bool + return z } return *o.All } -// WithFilters +// WithFilters set field Filters to given value func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *PruneOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } -// WithVolumes +// WithVolumes set field Volumes to given value func (o *PruneOptions) WithVolumes(value bool) *PruneOptions { - v := &value - o.Volumes = v + o.Volumes = &value return o } -// GetVolumes +// GetVolumes returns value of field Volumes func (o *PruneOptions) GetVolumes() bool { - var volumes bool if o.Volumes == nil { - return volumes + var z bool + return z } return *o.Volumes } diff -Nru libpod-3.2.1+ds1/pkg/bindings/system/types_version_options.go libpod-3.4.4+ds1/pkg/bindings/system/types_version_options.go --- libpod-3.2.1+ds1/pkg/bindings/system/types_version_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/system/types_version_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package system import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *VersionOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *VersionOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/attach_test.go libpod-3.4.4+ds1/pkg/bindings/test/attach_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/attach_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/attach_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -81,10 +81,9 @@ tickTock := time.NewTimer(2 * time.Second) go func() { <-tickTock.C - timeout := uint(5) - err := containers.Stop(bt.conn, ctnr.ID, new(containers.StopOptions).WithTimeout(timeout)) + err := containers.Stop(bt.conn, ctnr.ID, new(containers.StopOptions).WithTimeout(uint(5))) if err != nil { - GinkgoWriter.Write([]byte(err.Error())) + fmt.Fprint(GinkgoWriter, err.Error()) } }() diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/common_test.go libpod-3.4.4+ds1/pkg/bindings/test/common_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/common_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/common_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "os/exec" "path/filepath" "strings" + "time" "github.com/containers/podman/v3/libpod/define" . "github.com/containers/podman/v3/pkg/bindings" @@ -150,11 +151,21 @@ } func (b *bindingTest) startAPIService() *gexec.Session { - var ( - cmd []string - ) - cmd = append(cmd, "--log-level=debug", "--events-backend=file", "system", "service", "--timeout=0", b.sock) - return b.runPodman(cmd) + cmd := []string{"--log-level=debug", "--events-backend=file", "system", "service", "--timeout=0", b.sock} + session := b.runPodman(cmd) + + sock := strings.TrimPrefix(b.sock, "unix://") + for i := 0; i < 10; i++ { + if _, err := os.Stat(sock); err != nil { + if !os.IsNotExist(err) { + break + } + time.Sleep(time.Second) + continue + } + break + } + return session } func (b *bindingTest) cleanup() { @@ -214,12 +225,23 @@ // This method creates a pod with the given pod name. // Podname is an optional parameter func (b *bindingTest) Podcreate(name *string) { + b.PodcreateAndExpose(name, nil) +} + +// This method creates a pod with the given pod name and publish port. +// Podname is an optional parameter +// port is an optional parameter +func (b *bindingTest) PodcreateAndExpose(name *string, port *string) { + command := []string{"pod", "create"} if name != nil { podname := *name - b.runPodman([]string{"pod", "create", "--name", podname}).Wait(45) - } else { - b.runPodman([]string{"pod", "create"}).Wait(45) + command = append(command, "--name", podname) + } + if port != nil { + podport := *port + command = append(command, "--publish", podport) } + b.runPodman(command).Wait(45) } // StringInSlice returns a boolean based on whether a given diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/containers_test.go libpod-3.4.4+ds1/pkg/bindings/test/containers_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/containers_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/containers_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -259,6 +259,7 @@ _, err = bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) go func() { + defer GinkgoRecover() exitCode, err = containers.Wait(bt.conn, name, nil) errChan <- err close(errChan) @@ -281,6 +282,7 @@ _, err := bt.RunTopContainer(&name, nil) Expect(err).To(BeNil()) go func() { + defer GinkgoRecover() exitCode, err = containers.Wait(bt.conn, name, new(containers.WaitOptions).WithCondition([]define.ContainerStatus{pause})) errChan <- err close(errChan) @@ -326,11 +328,11 @@ // TODO for the life of me, i cannot get this to work. maybe another set // of eyes will // successful healthcheck - //status := "healthy" + //status := define.HealthCheckHealthy //for i:=0; i < 10; i++ { // result, err := containers.RunHealthCheck(connText, "hc") // Expect(err).To(BeNil()) - // if result.Status != "healthy" { + // if result.Status != define.HealthCheckHealthy { // fmt.Println("Healthcheck container still starting, retrying in 1 second") // time.Sleep(1 * time.Second) // continue @@ -338,7 +340,7 @@ // status = result.Status // break //} - //Expect(status).To(Equal("healthy")) + //Expect(status).To(Equal(define.HealthCheckHealthy)) // TODO enable this when wait is working // healthcheck on a stopped container should be a 409 @@ -366,7 +368,10 @@ opts := new(containers.LogOptions).WithStdout(true).WithFollow(true) go func() { - containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil) + defer GinkgoRecover() + err := containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil) + close(stdoutChan) + Expect(err).ShouldNot(HaveOccurred()) }() o := <-stdoutChan o = strings.TrimSpace(o) diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/fixture/Containerfile libpod-3.4.4+ds1/pkg/bindings/test/fixture/Containerfile --- libpod-3.2.1+ds1/pkg/bindings/test/fixture/Containerfile 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/fixture/Containerfile 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1 @@ +From quay.io/libpod/alpine_nginx diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/generator_test.go libpod-3.4.4+ds1/pkg/bindings/test/generator_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/generator_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/generator_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,51 @@ +package test_bindings + +import ( + "github.com/containers/podman/v3/pkg/bindings/containers" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gstruct" +) + +var _ = Describe("Podman API Bindings", func() { + boxedTrue, boxedFalse := new(bool), new(bool) + *boxedTrue = true + *boxedFalse = false + + It("verify simple setters", func() { + boxedString := new(string) + *boxedString = "Test" + + actual := new(containers.AttachOptions). + WithDetachKeys("Test").WithLogs(true).WithStream(false) + + Expect(*actual).To(MatchAllFields(Fields{ + "DetachKeys": Equal(boxedString), + "Logs": Equal(boxedTrue), + "Stream": Equal(boxedFalse), + })) + + Expect(actual.GetDetachKeys()).To(Equal("Test")) + Expect(actual.GetLogs()).To(Equal(true)) + Expect(actual.GetStream()).To(Equal(false)) + }) + + It("verify composite setters", func() { + boxedInt := new(int) + *boxedInt = 50 + + actual := new(containers.ListOptions). + WithFilters(map[string][]string{"Test": {"Test Filter"}}). + WithLast(50) + + Expect(*actual).To(MatchAllFields(Fields{ + "All": BeNil(), + "External": BeNil(), + "Filters": HaveKeyWithValue("Test", []string{"Test Filter"}), + "Last": Equal(boxedInt), + "Namespace": BeNil(), + "Size": BeNil(), + "Sync": BeNil(), + })) + }) +}) diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/images_test.go libpod-3.4.4+ds1/pkg/bindings/test/images_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/images_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/images_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,9 +9,11 @@ "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/containers" "github.com/containers/podman/v3/pkg/bindings/images" + "github.com/containers/podman/v3/pkg/domain/entities" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" + . "github.com/onsi/gomega/gexec" + . "github.com/onsi/gomega/gstruct" ) var _ = Describe("Podman images", func() { @@ -20,7 +22,7 @@ // err error // podmanTest *PodmanTestIntegration bt *bindingTest - s *gexec.Session + s *Session err error ) @@ -37,7 +39,7 @@ s = bt.startAPIService() time.Sleep(1 * time.Second) err := bt.NewConnection() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) }) AfterEach(func() { @@ -57,19 +59,19 @@ // Inspect by short name data, err := images.GetImage(bt.conn, alpine.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Inspect with full ID _, err = images.GetImage(bt.conn, data.ID, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Inspect with partial ID _, err = images.GetImage(bt.conn, data.ID[0:12], nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Inspect by long name _, err = images.GetImage(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // TODO it looks like the images API always returns size regardless // of bool or not. What should we do ? // Expect(data.Size).To(BeZero()) @@ -77,7 +79,7 @@ options := new(images.GetOptions).WithSize(true) // Enabling the size parameter should result in size being populated data, err = images.GetImage(bt.conn, alpine.name, options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(data.Size).To(BeNumerically(">", 0)) }) @@ -90,7 +92,7 @@ // Remove an image by name, validate image is removed and error is nil inspectData, err := images.GetImage(bt.conn, busybox.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) response, errs = images.Remove(bt.conn, []string{busybox.shortName}, nil) Expect(len(errs)).To(BeZero()) @@ -101,10 +103,10 @@ // Start a container with alpine image var top string = "top" _, err = bt.RunTopContainer(&top, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // we should now have a container called "top" running containerResponse, err := containers.Inspect(bt.conn, "top", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(containerResponse.Name).To(Equal("top")) // try to remove the image "alpine". This should fail since we are not force @@ -115,7 +117,7 @@ // Removing the image "alpine" where force = true options := new(images.RemoveOptions).WithForce(true) response, errs = images.Remove(bt.conn, []string{alpine.shortName}, options) - Expect(errs).To(BeNil()) + Expect(errs).To(Or(HaveLen(0), BeNil())) // To be extra sure, check if the previously created container // is gone as well. _, err = containers.Inspect(bt.conn, "top", nil) @@ -141,11 +143,11 @@ // Validates if the image is tagged successfully. err = images.Tag(bt.conn, alpine.shortName, "demo", alpine.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Validates if name updates when the image is retagged. _, err := images.GetImage(bt.conn, "alpine:demo", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) }) @@ -154,7 +156,7 @@ // Array to hold the list of images returned imageSummary, err := images.List(bt.conn, nil) // There Should be no errors in the response. - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // Since in the begin context two images are created the // list context should have only 2 images Expect(len(imageSummary)).To(Equal(2)) @@ -163,23 +165,23 @@ // And the count should be three now. bt.Pull("testimage:20200929") imageSummary, err = images.List(bt.conn, nil) - Expect(err).To(BeNil()) - Expect(len(imageSummary)).To(Equal(3)) + Expect(err).ToNot(HaveOccurred()) + Expect(len(imageSummary)).To(BeNumerically(">=", 2)) // Validate the image names. var names []string for _, i := range imageSummary { names = append(names, i.RepoTags...) } - Expect(StringInSlice(alpine.name, names)).To(BeTrue()) - Expect(StringInSlice(busybox.name, names)).To(BeTrue()) + Expect(names).To(ContainElement(alpine.name)) + Expect(names).To(ContainElement(busybox.name)) // List images with a filter filters := make(map[string][]string) filters["reference"] = []string{alpine.name} options := new(images.ListOptions).WithFilters(filters).WithAll(false) filteredImages, err := images.List(bt.conn, options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(len(filteredImages)).To(BeNumerically("==", 1)) // List images with a bad filter @@ -194,17 +196,17 @@ It("Image Exists", func() { // exists on bogus image should be false, with no error exists, err := images.Exists(bt.conn, "foobar", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) // exists with shortname should be true exists, err = images.Exists(bt.conn, alpine.shortName, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) // exists with fqname should be true exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) }) @@ -213,37 +215,37 @@ _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) defer f.Close() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) names, err := images.Load(bt.conn, f) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(names.Names[0]).To(Equal(alpine.name)) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) // load with a repo name f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) names, err = images.Load(bt.conn, f) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(names.Names[0]).To(Equal(alpine.name)) // load with a bad repo name should trigger a 500 f, err = os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) _, errs = images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) }) @@ -252,11 +254,11 @@ exportPath := filepath.Join(bt.tempDirPath, alpine.tarballName) w, err := os.Create(filepath.Join(bt.tempDirPath, alpine.tarballName)) defer w.Close() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) err = images.Export(bt.conn, []string{alpine.name}, w, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) _, err = os.Stat(exportPath) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) // TODO how do we verify that a format change worked? }) @@ -266,21 +268,21 @@ _, errs := images.Remove(bt.conn, []string{alpine.name}, nil) Expect(len(errs)).To(BeZero()) exists, err := images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) f, err := os.Open(filepath.Join(ImageCacheDir, alpine.tarballName)) defer f.Close() - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) changes := []string{"CMD /bin/foobar"} testMessage := "test_import" options := new(images.ImportOptions).WithMessage(testMessage).WithChanges(changes).WithReference(alpine.name) _, err = images.Import(bt.conn, f, options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) exists, err = images.Exists(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeTrue()) data, err := images.GetImage(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(data.Comment).To(Equal(testMessage)) }) @@ -294,9 +296,9 @@ var foundID bool data, err := images.GetImage(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) history, err := images.History(bt.conn, alpine.name, nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) for _, i := range history { if i.ID == data.ID { foundID = true @@ -308,7 +310,7 @@ It("Search for an image", func() { reports, err := images.Search(bt.conn, "alpine", nil) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(len(reports)).To(BeNumerically(">", 1)) var foundAlpine bool for _, i := range reports { @@ -322,7 +324,7 @@ // Search for alpine with a limit of 10 options := new(images.SearchOptions).WithLimit(10) reports, err = images.Search(bt.conn, "docker.io/alpine", options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) Expect(len(reports)).To(BeNumerically("<=", 10)) filters := make(map[string][]string) @@ -330,7 +332,7 @@ // Search for alpine with stars greater than 100 options = new(images.SearchOptions).WithFilters(filters) reports, err = images.Search(bt.conn, "docker.io/alpine", options) - Expect(err).To(BeNil()) + Expect(err).ToNot(HaveOccurred()) for _, i := range reports { Expect(i.Stars).To(BeNumerically(">=", 100)) } @@ -367,4 +369,12 @@ _, err = images.Pull(bt.conn, "bogus-transport:bogus.com/image:reference", nil) Expect(err).To(HaveOccurred()) }) + + It("Build no options", func() { + results, err := images.Build(bt.conn, []string{"fixture/Containerfile"}, entities.BuildOptions{}) + Expect(err).ToNot(HaveOccurred()) + Expect(*results).To(MatchFields(IgnoreMissing, Fields{ + "ID": Not(BeEmpty()), + })) + }) }) diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/pods_test.go libpod-3.4.4+ds1/pkg/bindings/test/pods_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/pods_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/pods_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package test_bindings import ( + "fmt" "net/http" "strings" "time" @@ -8,7 +9,10 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/bindings" "github.com/containers/podman/v3/pkg/bindings/pods" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" @@ -207,6 +211,29 @@ } }) + It("start pod with port conflict", func() { + randomport, err := utils.GetRandomPort() + Expect(err).To(BeNil()) + + portPublish := fmt.Sprintf("%d:%d", randomport, randomport) + var podwithport string = "newpodwithport" + bt.PodcreateAndExpose(&podwithport, &portPublish) + + // Start pod and expose port 12345 + _, err = pods.Start(bt.conn, podwithport, nil) + Expect(err).To(BeNil()) + + // Start another pod and expose same port 12345 + var podwithport2 string = "newpodwithport2" + bt.PodcreateAndExpose(&podwithport2, &portPublish) + + _, err = pods.Start(bt.conn, podwithport2, nil) + Expect(err).ToNot(BeNil()) + code, _ := bindings.CheckResponseCode(err) + Expect(code).To(BeNumerically("==", http.StatusConflict)) + Expect(err).To(BeAssignableToTypeOf(&errorhandling.PodConflictErrorModel{})) + }) + It("start stop restart pod", func() { // Start an invalid pod _, err = pods.Start(bt.conn, "dummyName", nil) @@ -333,9 +360,9 @@ }) It("simple create pod", func() { - ps := specgen.PodSpecGenerator{} - ps.Name = "foobar" - _, err := pods.CreatePodFromSpec(bt.conn, &ps, nil) + ps := entities.PodSpec{PodSpecGen: specgen.PodSpecGenerator{InfraContainerSpec: &specgen.SpecGenerator{}}} + ps.PodSpecGen.Name = "foobar" + _, err := pods.CreatePodFromSpec(bt.conn, &ps) Expect(err).To(BeNil()) exists, err := pods.Exists(bt.conn, "foobar", nil) diff -Nru libpod-3.2.1+ds1/pkg/bindings/test/resource_test.go libpod-3.4.4+ds1/pkg/bindings/test/resource_test.go --- libpod-3.2.1+ds1/pkg/bindings/test/resource_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/test/resource_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,116 @@ +package test_bindings + +import ( + "context" + "fmt" + "io/fs" + "os" + "os/exec" + "path/filepath" + "reflect" + "strconv" + "syscall" + + "github.com/containers/podman/v3/pkg/bindings" + "github.com/containers/podman/v3/pkg/bindings/containers" + "github.com/containers/podman/v3/pkg/bindings/images" + "github.com/containers/podman/v3/pkg/bindings/pods" + "github.com/containers/podman/v3/pkg/bindings/system" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Verify Podman resources", func() { + var ( + bt *bindingTest + s *Session + ) + + BeforeEach(func() { + bt = newBindingTest() + s = bt.startAPIService() + err := bt.NewConnection() + Expect(err).ShouldNot(HaveOccurred()) + }) + + AfterEach(func() { + s.Kill() + bt.cleanup() + }) + + It("no leaked connections", func() { + conn, err := bindings.NewConnection(context.Background(), bt.sock) + Expect(err).ShouldNot(HaveOccurred()) + + // Record details on open file descriptors before using API + buffer := lsof() + + // Record open fd from /proc + start, err := readProc() + Expect(err).ShouldNot(HaveOccurred()) + + // Run some operations + _, err = system.Info(conn, nil) + Expect(err).ShouldNot(HaveOccurred()) + _, err = images.List(conn, nil) + Expect(err).ShouldNot(HaveOccurred()) + _, err = containers.List(conn, nil) + Expect(err).ShouldNot(HaveOccurred()) + _, err = pods.List(conn, nil) + Expect(err).ShouldNot(HaveOccurred()) + + podman, _ := bindings.GetClient(conn) + podman.Client.CloseIdleConnections() + + // Record open fd from /proc + finished, err := readProc() + Expect(err).ShouldNot(HaveOccurred()) + if !reflect.DeepEqual(finished, start) { + fmt.Fprintf(GinkgoWriter, "Open FDs:\nlsof Before:\n%s\n", buffer) + + // Record details on open file descriptors after using API + buffer := lsof() + fmt.Fprintf(GinkgoWriter, "lsof After:\n%s\n", buffer) + + // We know test has failed. Easier to let ginkgo format output. + Expect(finished).Should(Equal(start)) + } + }) +}) + +func lsof() string { + lsof := exec.Command("lsof", "+E", "-p", strconv.Itoa(os.Getpid())) + buffer, err := lsof.Output() + Expect(err).ShouldNot(HaveOccurred()) + return string(buffer) +} + +func readProc() ([]string, error) { + syscall.Sync() + + names := make([]string, 0) + err := filepath.WalkDir(fmt.Sprintf("/proc/%d/fd", os.Getpid()), + func(path string, d fs.DirEntry, err error) error { + name := path + " -> " + + switch { + case d.IsDir(): + return nil + case err != nil: + name += err.Error() + case d.Type()&fs.ModeSymlink != 0: + n, err := os.Readlink(path) + if err != nil && !os.IsNotExist(err) { + return err + } + if n == "" { + n = d.Type().String() + } + name += n + } + names = append(names, name) + return nil + }) + return names, err +} diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/types_create_options.go libpod-3.4.4+ds1/pkg/bindings/volumes/types_create_options.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/types_create_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/types_create_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package volumes import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *CreateOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *CreateOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/types_exists_options.go libpod-3.4.4+ds1/pkg/bindings/volumes/types_exists_options.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/types_exists_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/types_exists_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package volumes import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ExistsOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ExistsOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/types_inspect_options.go libpod-3.4.4+ds1/pkg/bindings/volumes/types_inspect_options.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/types_inspect_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/types_inspect_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package volumes import ( @@ -6,16 +7,12 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *InspectOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *InspectOptions) ToParams() (url.Values, error) { return util.ToParams(o) } diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/types_list_options.go libpod-3.4.4+ds1/pkg/bindings/volumes/types_list_options.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/types_list_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/types_list_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package volumes import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *ListOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *ListOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *ListOptions) WithFilters(value map[string][]string) *ListOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *ListOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/types_prune_options.go libpod-3.4.4+ds1/pkg/bindings/volumes/types_prune_options.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/types_prune_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/types_prune_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package volumes import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *PruneOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *PruneOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithFilters +// WithFilters set field Filters to given value func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions { - v := value - o.Filters = v + o.Filters = value return o } -// GetFilters +// GetFilters returns value of field Filters func (o *PruneOptions) GetFilters() map[string][]string { - var filters map[string][]string if o.Filters == nil { - return filters + var z map[string][]string + return z } return o.Filters } diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/types_remove_options.go libpod-3.4.4+ds1/pkg/bindings/volumes/types_remove_options.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/types_remove_options.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/types_remove_options.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,4 @@ +// Code generated by go generate; DO NOT EDIT. package volumes import ( @@ -6,32 +7,27 @@ "github.com/containers/podman/v3/pkg/bindings/internal/util" ) -/* -This file is generated automatically by go generate. Do not edit. -*/ - -// Changed +// Changed returns true if named field has been set func (o *RemoveOptions) Changed(fieldName string) bool { return util.Changed(o, fieldName) } -// ToParams +// ToParams formats struct fields to be passed to API service func (o *RemoveOptions) ToParams() (url.Values, error) { return util.ToParams(o) } -// WithForce +// WithForce set field Force to given value func (o *RemoveOptions) WithForce(value bool) *RemoveOptions { - v := &value - o.Force = v + o.Force = &value return o } -// GetForce +// GetForce returns value of field Force func (o *RemoveOptions) GetForce() bool { - var force bool if o.Force == nil { - return force + var z bool + return z } return *o.Force } diff -Nru libpod-3.2.1+ds1/pkg/bindings/volumes/volumes.go libpod-3.4.4+ds1/pkg/bindings/volumes/volumes.go --- libpod-3.2.1+ds1/pkg/bindings/volumes/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/bindings/volumes/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -33,6 +33,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return &v, response.Process(&v) } @@ -53,6 +55,8 @@ if err != nil { return &inspect, err } + defer response.Body.Close() + return &inspect, response.Process(&inspect) } @@ -74,6 +78,8 @@ if err != nil { return vols, err } + defer response.Body.Close() + return vols, response.Process(&vols) } @@ -94,6 +100,8 @@ if err != nil { return nil, err } + defer response.Body.Close() + return pruned, response.Process(&pruned) } @@ -112,6 +120,8 @@ if err != nil { return err } + defer response.Body.Close() + return response.Process(nil) } @@ -125,5 +135,7 @@ if err != nil { return false, err } + defer response.Body.Close() + return response.IsSuccess(), nil } diff -Nru libpod-3.2.1+ds1/pkg/cgroups/cgroups.go libpod-3.4.4+ds1/pkg/cgroups/cgroups.go --- libpod-3.2.1+ds1/pkg/cgroups/cgroups.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/cgroups/cgroups.go 2021-12-26 00:45:51.000000000 +0000 @@ -129,8 +129,8 @@ func getAvailableControllers(exclude map[string]controllerHandler, cgroup2 bool) ([]controller, error) { if cgroup2 { controllers := []controller{} - subtreeControl := cgroupRoot + "/cgroup.subtree_control" - // rootless cgroupv2: check available controllers for current user ,systemd or servicescope will inherit + controllersFile := cgroupRoot + "/cgroup.controllers" + // rootless cgroupv2: check available controllers for current user, systemd or servicescope will inherit if rootless.IsRootless() { userSlice, err := getCgroupPathForCurrentProcess() if err != nil { @@ -138,13 +138,13 @@ } //userSlice already contains '/' so not adding here basePath := cgroupRoot + userSlice - subtreeControl = fmt.Sprintf("%s/cgroup.subtree_control", basePath) + controllersFile = fmt.Sprintf("%s/cgroup.controllers", basePath) } - subtreeControlBytes, err := ioutil.ReadFile(subtreeControl) + controllersFileBytes, err := ioutil.ReadFile(controllersFile) if err != nil { - return nil, errors.Wrapf(err, "failed while reading controllers for cgroup v2 from %q", subtreeControl) + return nil, errors.Wrapf(err, "failed while reading controllers for cgroup v2 from %q", controllersFile) } - for _, controllerName := range strings.Fields(string(subtreeControlBytes)) { + for _, controllerName := range strings.Fields(string(controllersFileBytes)) { c := controller{ name: controllerName, symlink: false, @@ -165,14 +165,13 @@ if _, found := exclude[name]; found { continue } - isSymLink := false fileInfo, err := os.Stat(cgroupRoot + "/" + name) if err != nil { - isSymLink = !fileInfo.IsDir() + continue } c := controller{ name: name, - symlink: isSymLink, + symlink: !fileInfo.IsDir(), } controllers = append(controllers, c) } @@ -232,7 +231,10 @@ for s.Scan() { text := s.Text() procEntries := strings.SplitN(text, "::", 2) - cgroupPath = procEntries[1] + // set process cgroupPath only if entry is valid + if len(procEntries) > 1 { + cgroupPath = procEntries[1] + } } if err := s.Err(); err != nil { return cgroupPath, err @@ -459,10 +461,10 @@ return systemdCreate(path, conn) } -// GetUserConnection returns an user connection to D-BUS +// GetUserConnection returns a user connection to D-BUS func GetUserConnection(uid int) (*systemdDbus.Conn, error) { return systemdDbus.NewConnection(func() (*dbus.Conn, error) { - return dbusAuthConnection(uid, dbus.SessionBusPrivate) + return dbusAuthConnection(uid, dbus.SessionBusPrivateNoAutoStartup) }) } diff -Nru libpod-3.2.1+ds1/pkg/cgroups/systemd.go libpod-3.4.4+ds1/pkg/cgroups/systemd.go --- libpod-3.2.1+ds1/pkg/cgroups/systemd.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/cgroups/systemd.go 2021-12-26 00:45:51.000000000 +0000 @@ -58,7 +58,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff -Nru libpod-3.2.1+ds1/pkg/checkpoint/checkpoint_restore.go libpod-3.4.4+ds1/pkg/checkpoint/checkpoint_restore.go --- libpod-3.2.1+ds1/pkg/checkpoint/checkpoint_restore.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/checkpoint/checkpoint_restore.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,8 +9,12 @@ "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" + ann "github.com/containers/podman/v3/pkg/annotations" + "github.com/containers/podman/v3/pkg/checkpoint/crutils" + "github.com/containers/podman/v3/pkg/criu" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/errorhandling" + "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/storage/pkg/archive" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -67,6 +71,14 @@ return nil, err } + if ctrConfig.Pod != "" && restoreOptions.Pod == "" { + return nil, errors.New("cannot restore pod container without --pod") + } + + if ctrConfig.Pod == "" && restoreOptions.Pod != "" { + return nil, errors.New("cannot restore non pod container into pod") + } + // This should not happen as checkpoints with these options are not exported. if len(ctrConfig.Dependencies) > 0 { return nil, errors.Errorf("Cannot import checkpoints of containers with dependencies") @@ -95,6 +107,99 @@ newName = true } + if restoreOptions.Pod != "" { + // Restoring into a Pod requires much newer versions of CRIU + if !criu.CheckForCriu(criu.PodCriuVersion) { + return nil, errors.Errorf("restoring containers into pods requires at least CRIU %d", criu.PodCriuVersion) + } + // The runtime also has to support it + if !crutils.CRRuntimeSupportsPodCheckpointRestore(runtime.GetOCIRuntimePath()) { + return nil, errors.Errorf("runtime %s does not support pod restore", runtime.GetOCIRuntimePath()) + } + // Restoring into an existing Pod + ctrConfig.Pod = restoreOptions.Pod + + // According to podman pod create a pod can share the following namespaces: + // cgroup, ipc, net, pid, uts + // Let's make sure we a restoring into a pod with the same shared namespaces. + pod, err := runtime.LookupPod(ctrConfig.Pod) + if err != nil { + return nil, errors.Wrapf(err, "pod %q cannot be retrieved", ctrConfig.Pod) + } + + infraContainer, err := pod.InfraContainer() + if err != nil { + return nil, errors.Wrapf(err, "cannot retrieve infra container from pod %q", ctrConfig.Pod) + } + + // If a namespaces was shared (!= "") it needs to be set to the new infrastructure container + // If the infrastructure container does not share the same namespaces as the to be restored + // container we abort. + if ctrConfig.IPCNsCtr != "" { + if !pod.SharesIPC() { + return nil, errors.Errorf("pod %s does not share the IPC namespace", ctrConfig.Pod) + } + ctrConfig.IPCNsCtr = infraContainer.ID() + } + + if ctrConfig.NetNsCtr != "" { + if !pod.SharesNet() { + return nil, errors.Errorf("pod %s does not share the network namespace", ctrConfig.Pod) + } + ctrConfig.NetNsCtr = infraContainer.ID() + } + + if ctrConfig.PIDNsCtr != "" { + if !pod.SharesPID() { + return nil, errors.Errorf("pod %s does not share the PID namespace", ctrConfig.Pod) + } + ctrConfig.PIDNsCtr = infraContainer.ID() + } + + if ctrConfig.UTSNsCtr != "" { + if !pod.SharesUTS() { + return nil, errors.Errorf("pod %s does not share the UTS namespace", ctrConfig.Pod) + } + ctrConfig.UTSNsCtr = infraContainer.ID() + } + + if ctrConfig.CgroupNsCtr != "" { + if !pod.SharesCgroup() { + return nil, errors.Errorf("pod %s does not share the cgroup namespace", ctrConfig.Pod) + } + ctrConfig.CgroupNsCtr = infraContainer.ID() + } + + // Change SELinux labels to infrastructure container labels + ctrConfig.MountLabel = infraContainer.MountLabel() + ctrConfig.ProcessLabel = infraContainer.ProcessLabel() + + // Fix parent cgroup + cgroupPath, err := pod.CgroupPath() + if err != nil { + return nil, errors.Wrapf(err, "cannot retrieve cgroup path from pod %q", ctrConfig.Pod) + } + ctrConfig.CgroupParent = cgroupPath + + oldPodID := dumpSpec.Annotations[ann.SandboxID] + // Fix up SandboxID in the annotations + dumpSpec.Annotations[ann.SandboxID] = ctrConfig.Pod + // Fix up CreateCommand + for i, c := range ctrConfig.CreateCommand { + if c == oldPodID { + ctrConfig.CreateCommand[i] = ctrConfig.Pod + } + } + } + + if len(restoreOptions.PublishPorts) > 0 { + ports, _, _, err := generate.ParsePortMapping(restoreOptions.PublishPorts) + if err != nil { + return nil, err + } + ctrConfig.PortMappings = ports + } + pullOptions := &libimage.PullOptions{} pullOptions.Writer = os.Stderr if _, err := runtime.LibimageRuntime().Pull(ctx, ctrConfig.RootfsImageName, config.PullPolicyMissing, pullOptions); err != nil { diff -Nru libpod-3.2.1+ds1/pkg/checkpoint/crutils/checkpoint_restore_utils.go libpod-3.4.4+ds1/pkg/checkpoint/crutils/checkpoint_restore_utils.go --- libpod-3.2.1+ds1/pkg/checkpoint/crutils/checkpoint_restore_utils.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/checkpoint/crutils/checkpoint_restore_utils.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package crutils import ( + "bytes" "io" "os" "os/exec" @@ -189,3 +190,13 @@ } return false } + +// CRRuntimeSupportsCheckpointRestore tests if the runtime at 'runtimePath' +// supports restoring into existing Pods. The runtime needs to support +// the CRIU option --lsm-mount-context and the existence of this is checked +// by this function. In addition it is necessary to at least have CRIU 3.16. +func CRRuntimeSupportsPodCheckpointRestore(runtimePath string) bool { + cmd := exec.Command(runtimePath, "restore", "--lsm-mount-context") + out, _ := cmd.CombinedOutput() + return bytes.Contains(out, []byte("flag needs an argument")) +} diff -Nru libpod-3.2.1+ds1/pkg/copy/parse.go libpod-3.4.4+ds1/pkg/copy/parse.go --- libpod-3.2.1+ds1/pkg/copy/parse.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/copy/parse.go 2021-12-26 00:45:51.000000000 +0000 @@ -18,18 +18,6 @@ sourceContainer, sourcePath := parseUserInput(source) destContainer, destPath := parseUserInput(destination) - numContainers := 0 - if len(sourceContainer) > 0 { - numContainers++ - } - if len(destContainer) > 0 { - numContainers++ - } - - if numContainers != 1 { - return "", "", "", "", errors.Errorf("invalid arguments %q, %q: exactly 1 container expected but %d specified", source, destination, numContainers) - } - if len(sourcePath) == 0 || len(destPath) == 0 { return "", "", "", "", errors.Errorf("invalid arguments %q, %q: you must specify paths", source, destination) } diff -Nru libpod-3.2.1+ds1/pkg/criu/criu.go libpod-3.4.4+ds1/pkg/criu/criu.go --- libpod-3.2.1+ds1/pkg/criu/criu.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/criu/criu.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,17 +1,21 @@ package criu import ( - "github.com/checkpoint-restore/go-criu" + "github.com/checkpoint-restore/go-criu/v5" ) // MinCriuVersion for Podman at least CRIU 3.11 is required const MinCriuVersion = 31100 +// PodCriuVersion is the version of CRIU needed for +// checkpointing and restoring containers out of and into Pods. +const PodCriuVersion = 31600 + // CheckForCriu uses CRIU's go bindings to check if the CRIU // binary exists and if it at least the version Podman needs. -func CheckForCriu() bool { +func CheckForCriu(version int) bool { c := criu.MakeCriu() - result, err := c.IsCriuAtLeast(MinCriuVersion) + result, err := c.IsCriuAtLeast(version) if err != nil { return false } diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/auto-update.go libpod-3.4.4+ds1/pkg/domain/entities/auto-update.go --- libpod-3.2.1+ds1/pkg/domain/entities/auto-update.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/auto-update.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,10 +4,28 @@ type AutoUpdateOptions struct { // Authfile to use when contacting registries. Authfile string + // Only check for but do not perform any update. If an update is + // pending, it will be indicated in the Updated field of + // AutoUpdateReport. + DryRun bool + // If restarting the service with the new image failed, restart it + // another time with the previous image. + Rollback bool } // AutoUpdateReport contains the results from running auto-update. type AutoUpdateReport struct { - // Units - the restarted systemd units during auto-update. - Units []string + // ID of the container *before* an update. + ContainerID string + // Name of the container *before* an update. + ContainerName string + // Name of the image. + ImageName string + // The configured auto-update policy. + Policy string + // SystemdUnit running a container configured for auto updates. + SystemdUnit string + // Indicates the update status: true, false, failed, pending (see + // DryRun). + Updated string } diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/containers.go libpod-3.4.4+ds1/pkg/domain/entities/containers.go --- libpod-3.2.1+ds1/pkg/domain/entities/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,7 +8,9 @@ "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod/define" + nettypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/storage/pkg/archive" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -159,6 +161,15 @@ Writer io.Writer } +type CopyOptions struct { + // If used with ContainerCopyFromArchive and set to true + // it will change ownership of files from the source tar archive + // to the primary uid/gid of the destination container. + Chown bool + // Map to translate path names. + Rename map[string]string +} + type CommitReport struct { Id string //nolint } @@ -178,6 +189,7 @@ TCPEstablished bool PreCheckPoint bool WithPrevious bool + Compression archive.Compression } type CheckpointReport struct { @@ -197,6 +209,8 @@ Name string TCPEstablished bool ImportPrevious string + PublishPorts []nettypes.PortMapping + Pod string } type RestoreReport struct { @@ -232,6 +246,8 @@ Names bool // Show logs since this timestamp. Since time.Time + // Show logs until this timestamp. + Until time.Time // Number of lines to display at the end of the output. Tail int64 // Show timestamps in the logs. @@ -425,6 +441,8 @@ Latest bool // Stream stats. Stream bool + // Interval in seconds + Interval int } // ContainerStatsReport is used for streaming container stats. diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/engine_container.go libpod-3.4.4+ds1/pkg/domain/entities/engine_container.go --- libpod-3.2.1+ds1/pkg/domain/entities/engine_container.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/engine_container.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,22 +8,20 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities/reports" "github.com/containers/podman/v3/pkg/specgen" - "github.com/spf13/cobra" ) type ContainerCopyFunc func() error type ContainerEngine interface { - AutoUpdate(ctx context.Context, options AutoUpdateOptions) (*AutoUpdateReport, []error) + AutoUpdate(ctx context.Context, options AutoUpdateOptions) ([]*AutoUpdateReport, []error) Config(ctx context.Context) (*config.Config, error) ContainerAttach(ctx context.Context, nameOrID string, options AttachOptions) error ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) ContainerCommit(ctx context.Context, nameOrID string, options CommitOptions) (*CommitReport, error) - ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (ContainerCopyFunc, error) + ContainerCopyFromArchive(ctx context.Context, nameOrID, path string, reader io.Reader, options CopyOptions) (ContainerCopyFunc, error) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (ContainerCopyFunc, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) - ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error) ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error) ContainerExists(ctx context.Context, nameOrID string, options ContainerExistsOptions) (*BoolReport, error) @@ -32,6 +30,7 @@ ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, []error, error) ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error) ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error) + ContainerListExternal(ctx context.Context) ([]ListContainer, error) ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error ContainerMount(ctx context.Context, nameOrIDs []string, options ContainerMountOptions) ([]*ContainerMountReport, error) ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) @@ -51,6 +50,7 @@ ContainerUnmount(ctx context.Context, nameOrIDs []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) + Diff(ctx context.Context, namesOrIds []string, options DiffOptions) (*DiffReport, error) Events(ctx context.Context, opts EventsOptions) error GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error) GenerateKube(ctx context.Context, nameOrIDs []string, opts GenerateKubeOptions) (*GenerateKubeReport, error) @@ -67,10 +67,12 @@ NetworkReload(ctx context.Context, names []string, options NetworkReloadOptions) ([]*NetworkReloadReport, error) NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) - PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) + PlayKubeDown(ctx context.Context, path string, opts PlayKubeDownOptions) (*PlayKubeReport, error) + PodCreate(ctx context.Context, specg PodSpec) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrID string) (*BoolReport, error) PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) + PodLogs(ctx context.Context, pod string, options PodLogsOptions) error PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) PodPrune(ctx context.Context, options PodPruneOptions) ([]*PodPruneReport, error) PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error) @@ -81,10 +83,10 @@ PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) - SetupRootless(ctx context.Context, cmd *cobra.Command) error + SetupRootless(ctx context.Context, noMoveProcess bool) error SecretCreate(ctx context.Context, name string, reader io.Reader, options SecretCreateOptions) (*SecretCreateReport, error) SecretInspect(ctx context.Context, nameOrIDs []string) ([]*SecretInfoReport, []error, error) - SecretList(ctx context.Context) ([]*SecretInfoReport, error) + SecretList(ctx context.Context, opts SecretListRequest) ([]*SecretInfoReport, error) SecretRm(ctx context.Context, nameOrID []string, opts SecretRmOptions) ([]*SecretRmReport, error) Shutdown(ctx context.Context) SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error) @@ -92,6 +94,7 @@ Version(ctx context.Context) (*SystemVersionReport, error) VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) VolumeExists(ctx context.Context, namesOrID string) (*BoolReport, error) + VolumeMounted(ctx context.Context, namesOrID string) (*BoolReport, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts InspectOptions) ([]*VolumeInspectReport, []error, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumePrune(ctx context.Context, options VolumePruneOptions) ([]*reports.PruneReport, error) diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/engine.go libpod-3.4.4+ds1/pkg/domain/entities/engine.go --- libpod-3.2.1+ds1/pkg/domain/entities/engine.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/engine.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,6 +39,7 @@ EngineMode EngineMode // ABI or Tunneling mode Identity string // ssh identity for connecting to server MaxWorks int // maximum number of parallel threads + MemoryProfile string // Hidden: Should memory profile be taken RegistriesConf string // allows for specifying a custom registries.conf Remote bool // Connection to Podman API Service will use RESTful API RuntimePath string // --runtime flag will set Engine.RuntimePath diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/engine_image.go libpod-3.4.4+ds1/pkg/domain/entities/engine_image.go --- libpod-3.2.1+ds1/pkg/domain/entities/engine_image.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/engine_image.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,7 +10,6 @@ type ImageEngine interface { Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error) Config(ctx context.Context) (*config.Config, error) - Diff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) Exists(ctx context.Context, nameOrID string) (*BoolReport, error) History(ctx context.Context, nameOrID string, opts ImageHistoryOptions) (*ImageHistoryReport, error) Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) @@ -37,6 +36,7 @@ ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) ManifestRemove(ctx context.Context, names []string) (string, error) + ManifestRm(ctx context.Context, names []string) (*ImageRemoveReport, []error) ManifestPush(ctx context.Context, name, destination string, imagePushOpts ImagePushOptions) (string, error) Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error) } diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/events.go libpod-3.4.4+ds1/pkg/domain/entities/events.go --- libpod-3.2.1+ds1/pkg/domain/entities/events.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/events.go 2021-12-26 00:45:51.000000000 +0000 @@ -60,6 +60,10 @@ attributes["name"] = e.Name attributes["containerExitCode"] = strconv.Itoa(e.ContainerExitCode) return &Event{dockerEvents.Message{ + // Compatibility with clients that still look for deprecated API elements + Status: e.Status.String(), + ID: e.ID, + From: e.Image, Type: e.Type.String(), Action: e.Status.String(), Actor: dockerEvents.Actor{ diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/generate.go libpod-3.4.4+ds1/pkg/domain/entities/generate.go --- libpod-3.2.1+ds1/pkg/domain/entities/generate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/generate.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,7 +9,7 @@ // New - create a new container instead of starting a new one. New bool // RestartPolicy - systemd restart policy. - RestartPolicy string + RestartPolicy *string // StopTimeout - time when stopping the container. StopTimeout *uint // ContainerPrefix - systemd unit name prefix for containers @@ -35,6 +35,8 @@ } // GenerateKubeReport +// +// FIXME: Podman4.0 should change io.Reader to io.ReaderCloser type GenerateKubeReport struct { // Reader - the io.Reader to reader the generated YAML file. Reader io.Reader diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/images.go libpod-3.4.4+ds1/pkg/domain/entities/images.go --- libpod-3.2.1+ds1/pkg/domain/entities/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package entities import ( + "net/url" "time" "github.com/containers/common/pkg/config" @@ -49,6 +50,7 @@ return i.ID } +// swagger:model LibpodImageSummary type ImageSummary struct { ID string `json:"Id"` ParentId string // nolint @@ -88,6 +90,8 @@ All bool // Foce will force image removal including containers using the images. Force bool + // Confirms if given name is a manifest list and removes it, otherwise returns error. + LookupManifest bool } // ImageRemoveResponse is the response for removing one or more image(s) from storage @@ -184,7 +188,7 @@ // image to the file. Ignored for remote calls. DigestFile string // Format is the Manifest type (oci, v2s1, or v2s2) to use when pushing an - // image using the 'dir' transport. Default is manifest type of source. + // image. Default is manifest type of source, with fallbacks. // Ignored for remote calls. Format string // Quiet can be specified to suppress pull progress when pulling. Ignored @@ -271,8 +275,10 @@ } type ImageImportOptions struct { + Architecture string Changes []string Message string + OS string Quiet bool Reference string SignaturePolicy string @@ -298,12 +304,32 @@ MultiImageArchive bool // Output - write image to the specified path. Output string - // Do not save the signature from the source image - RemoveSignatures bool // Quiet - suppress output when copying images Quiet bool } +// ImageScpOptions provide options for securely copying images to podman remote +type ImageScpOptions struct { + // SoureImageName is the image the user is providing to load on a remote machine + SourceImageName string + // Tag allows for a new image to be created under the given name + Tag string + // ToRemote specifies that we are loading to the remote host + ToRemote bool + // FromRemote specifies that we are loading from the remote host + FromRemote bool + // Connections holds the raw string values for connections (ssh or unix) + Connections []string + // URI contains the ssh connection URLs to be used by the client + URI []*url.URL + // Iden contains ssh identity keys to be used by the client + Iden []string + // Save Options used for first half of the scp operation + Save ImageSaveOptions + // Load options used for the second half of the scp operation + Load ImageLoadOptions +} + // ImageTreeOptions provides options for ImageEngine.Tree() type ImageTreeOptions struct { WhatRequires bool // Show all child images and layers of the specified image diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/play.go libpod-3.4.4+ds1/pkg/domain/entities/play.go --- libpod-3.2.1+ds1/pkg/domain/entities/play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/play.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,8 +10,13 @@ type PlayKubeOptions struct { // Authfile - path to an authentication file. Authfile string + // Indicator to build all images with Containerfile or Dockerfile + Build bool // CertDir - to a directory containing TLS certifications and keys. CertDir string + // Down indicates whether to bring contents of a yaml file "down" + // as in stop + Down bool // Username for authenticating against the registry. Username string // Password for authenticating against the registry. @@ -46,6 +51,8 @@ ID string // Containers - the IDs of the containers running in the created pod. Containers []string + // InitContainers - the IDs of the init containers to be run in the created pod. + InitContainers []string // Logs - non-fatal errors and log messages while processing. Logs []string // ContainerErrors - any errors that occurred while starting containers @@ -65,4 +72,14 @@ Pods []PlayKubePod // Volumes - volumes created by play kube. Volumes []PlayKubeVolume + PlayKubeTeardown +} + +// PlayKubeDownOptions are options for tearing down pods +type PlayKubeDownOptions struct{} + +// PlayKubeDownReport contains the results of tearing down play kube +type PlayKubeTeardown struct { + StopReport []*PodStopReport + RmReport []*PodRmReport } diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/pods.go libpod-3.4.4+ds1/pkg/domain/entities/pods.go --- libpod-3.2.1+ds1/pkg/domain/entities/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,8 +5,11 @@ "strings" "time" + commonFlag "github.com/containers/common/pkg/flag" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/util" + "github.com/opencontainers/runtime-spec/specs-go" ) type PodKillOptions struct { @@ -104,27 +107,191 @@ Id string //nolint } +// PddSpec is an abstracted version of PodSpecGen designed to eventually accept options +// not meant to be in a specgen +type PodSpec struct { + PodSpecGen specgen.PodSpecGenerator +} + +// PodCreateOptions provides all possible options for creating a pod and its infra container +// swagger:model PodCreateOptions type PodCreateOptions struct { CGroupParent string CreateCommand []string Hostname string Infra bool InfraImage string + InfraName string InfraCommand string InfraConmonPidFile string Labels map[string]string Name string Net *NetOptions Share []string + Pid string + Cpus float64 + CpusetCpus string + Userns specgen.Namespace +} + +// PodLogsOptions describes the options to extract pod logs. +type PodLogsOptions struct { + // Other fields are exactly same as ContainerLogOpts + ContainerLogsOptions + // If specified will only fetch the logs of specified container + ContainerName string +} + +type ContainerCreateOptions struct { + Annotation []string + Attach []string + Authfile string + BlkIOWeight string + BlkIOWeightDevice []string + CapAdd []string + CapDrop []string + CgroupNS string + CGroupsMode string + CGroupParent string + CIDFile string + ConmonPIDFile string + CPUPeriod uint64 + CPUQuota int64 + CPURTPeriod uint64 + CPURTRuntime int64 + CPUShares uint64 + CPUS float64 + CPUSetCPUs string + CPUSetMems string + Devices []string + DeviceCGroupRule []string + DeviceReadBPs []string + DeviceReadIOPs []string + DeviceWriteBPs []string + DeviceWriteIOPs []string + Entrypoint *string + Env []string + EnvHost bool + EnvFile []string + Expose []string + GIDMap []string + GroupAdd []string + HealthCmd string + HealthInterval string + HealthRetries uint + HealthStartPeriod string + HealthTimeout string + Hostname string + HTTPProxy bool + ImageVolume string + Init bool + InitContainerType string + InitPath string + Interactive bool + IPC string + KernelMemory string + Label []string + LabelFile []string + LogDriver string + LogOptions []string + Memory string + MemoryReservation string + MemorySwap string + MemorySwappiness int64 + Name string + NoHealthCheck bool + OOMKillDisable bool + OOMScoreAdj int + Arch string + OS string + Variant string + PID string + PIDsLimit *int64 + Platform string + Pod string + PodIDFile string + Personality string + PreserveFDs uint + Privileged bool + PublishAll bool + Pull string + Quiet bool + ReadOnly bool + ReadOnlyTmpFS bool + Restart string + Replace bool + Requires []string + Rm bool + RootFS bool + Secrets []string + SecurityOpt []string + SdNotifyMode string + ShmSize string + SignaturePolicy string + StopSignal string + StopTimeout uint + StorageOpt []string + SubUIDName string + SubGIDName string + Sysctl []string + Systemd string + Timeout uint + TLSVerify commonFlag.OptionalBool + TmpFS []string + TTY bool + Timezone string + Umask string + UIDMap []string + Ulimit []string + User string + UserNS string + UTS string + Mount []string + Volume []string + VolumesFrom []string + Workdir string + SeccompPolicy string + PidFile string + IsInfra bool + + Net *NetOptions + + CgroupConf []string } type PodCreateReport struct { Id string //nolint } -func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { +func (p *PodCreateOptions) CPULimits() *specs.LinuxCPU { + cpu := &specs.LinuxCPU{} + hasLimits := false + + if p.Cpus != 0 { + period, quota := util.CoresToPeriodAndQuota(p.Cpus) + cpu.Period = &period + cpu.Quota = "a + hasLimits = true + } + if p.CpusetCpus != "" { + cpu.Cpus = p.CpusetCpus + hasLimits = true + } + if !hasLimits { + return cpu + } + return cpu +} + +func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.PodSpecGenerator, error) { // Basic Config s.Name = p.Name + s.InfraName = p.InfraName + out, err := specgen.ParseNamespace(p.Pid) + if err != nil { + return nil, err + } + s.Pid = out s.Hostname = p.Hostname s.Labels = p.Labels s.NoInfra = !p.Infra @@ -139,23 +306,42 @@ s.PodCreateCommand = p.CreateCommand // Networking config - s.NetNS = p.Net.Network - s.StaticIP = p.Net.StaticIP - s.StaticMAC = p.Net.StaticMAC - s.PortMappings = p.Net.PublishPorts - s.CNINetworks = p.Net.CNINetworks - s.NetworkOptions = p.Net.NetworkOptions - if p.Net.UseImageResolvConf { - s.NoManageResolvConf = true - } - s.DNSServer = p.Net.DNSServers - s.DNSSearch = p.Net.DNSSearch - s.DNSOption = p.Net.DNSOptions - s.NoManageHosts = p.Net.NoHosts - s.HostAdd = p.Net.AddHosts + + if p.Net != nil { + s.NetNS = p.Net.Network + s.StaticIP = p.Net.StaticIP + s.StaticMAC = p.Net.StaticMAC + s.PortMappings = p.Net.PublishPorts + s.CNINetworks = p.Net.CNINetworks + s.NetworkOptions = p.Net.NetworkOptions + if p.Net.UseImageResolvConf { + s.NoManageResolvConf = true + } + s.DNSServer = p.Net.DNSServers + s.DNSSearch = p.Net.DNSSearch + s.DNSOption = p.Net.DNSOptions + s.NoManageHosts = p.Net.NoHosts + s.HostAdd = p.Net.AddHosts + } // Cgroup s.CgroupParent = p.CGroupParent + + // Resource config + cpuDat := p.CPULimits() + if s.ResourceLimits == nil { + s.ResourceLimits = &specs.LinuxResources{} + s.ResourceLimits.CPU = &specs.LinuxCPU{} + } + if cpuDat != nil { + s.ResourceLimits.CPU = cpuDat + if p.Cpus != 0 { + s.CPUPeriod = *cpuDat.Period + s.CPUQuota = *cpuDat.Quota + } + } + s.Userns = p.Userns + return &s, nil } type PodPruneOptions struct { @@ -249,3 +435,22 @@ return errors.New("--all, --latest and arguments cannot be used together") } } + +// Converts PodLogOptions to ContainerLogOptions +func PodLogsOptionsToContainerLogsOptions(options PodLogsOptions) ContainerLogsOptions { + // PodLogsOptions are similar but contains few extra fields like ctrName + // So cast other values as is so we can re-use the code + containerLogsOpts := ContainerLogsOptions{ + Details: options.Details, + Latest: options.Latest, + Follow: options.Follow, + Names: options.Names, + Since: options.Since, + Until: options.Until, + Tail: options.Tail, + Timestamps: options.Timestamps, + StdoutWriter: options.StdoutWriter, + StderrWriter: options.StderrWriter, + } + return containerLogsOpts +} diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/secrets.go libpod-3.4.4+ds1/pkg/domain/entities/secrets.go --- libpod-3.2.1+ds1/pkg/domain/entities/secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,11 +11,12 @@ } type SecretCreateOptions struct { - Driver string + Driver string + DriverOpts map[string]string } type SecretListRequest struct { - Filters map[string]string + Filters map[string][]string } type SecretListReport struct { diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/system.go libpod-3.4.4+ds1/pkg/domain/entities/system.go --- libpod-3.2.1+ds1/pkg/domain/entities/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,9 +11,10 @@ // ServiceOptions provides the input for starting an API Service type ServiceOptions struct { - URI string // Path to unix domain socket service should listen on - Timeout time.Duration // duration of inactivity the service should wait before shutting down - Command *cobra.Command // CLI command provided. Used in V1 code + URI string // Path to unix domain socket service should listen on + Timeout time.Duration // duration of inactivity the service should wait before shutting down + Command *cobra.Command // CLI command provided. Used in V1 code + CorsHeaders string // CORS headers } // SystemPruneOptions provides options to prune system. diff -Nru libpod-3.2.1+ds1/pkg/domain/entities/types.go libpod-3.4.4+ds1/pkg/domain/entities/types.go --- libpod-3.2.1+ds1/pkg/domain/entities/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/entities/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,7 +4,9 @@ "net" buildahDefine "github.com/containers/buildah/define" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/libpod/events" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/storage/pkg/archive" ) @@ -29,21 +31,33 @@ // NetOptions reflect the shared network options between // pods and containers +type NetFlags struct { + AddHosts []string `json:"add-host,omitempty"` + DNS []string `json:"dns,omitempty"` + DNSOpt []string `json:"dns-opt,omitempty"` + DNDSearch []string `json:"dns-search,omitempty"` + MacAddr string `json:"mac-address,omitempty"` + Publish []string `json:"publish,omitempty"` + IP string `json:"ip,omitempty"` + NoHosts bool `json:"no-hosts,omitempty"` + Network string `json:"network,omitempty"` + NetworkAlias []string `json:"network-alias,omitempty"` +} type NetOptions struct { - AddHosts []string - Aliases []string - CNINetworks []string - UseImageResolvConf bool - DNSOptions []string - DNSSearch []string - DNSServers []net.IP - Network specgen.Namespace - NoHosts bool - PublishPorts []specgen.PortMapping - StaticIP *net.IP - StaticMAC *net.HardwareAddr + AddHosts []string `json:"hostadd,omitempty"` + Aliases []string `json:"network_alias,omitempty"` + CNINetworks []string `json:"cni_networks,omitempty"` + UseImageResolvConf bool `json:"no_manage_resolv_conf,omitempty"` + DNSOptions []string `json:"dns_option,omitempty"` + DNSSearch []string `json:"dns_search,omitempty"` + DNSServers []net.IP `json:"dns_server,omitempty"` + Network specgen.Namespace `json:"netns,omitempty"` + NoHosts bool `json:"no_manage_hosts,omitempty"` + PublishPorts []types.PortMapping `json:"portmappings,omitempty"` + StaticIP *net.IP `json:"static_ip,omitempty"` + StaticMAC *net.HardwareAddr `json:"static_mac,omitempty"` // NetworkOptions are additional options for each network - NetworkOptions map[string][]string + NetworkOptions map[string][]string `json:"network_options,omitempty"` } // All CLI inspect commands and inspect sub-commands use the same options @@ -62,9 +76,10 @@ // All API and CLI diff commands and diff sub-commands use the same options type DiffOptions struct { - Format string `json:",omitempty"` // CLI only - Latest bool `json:",omitempty"` // API and CLI, only supported by containers - Archive bool `json:",omitempty"` // CLI only + Format string `json:",omitempty"` // CLI only + Latest bool `json:",omitempty"` // API and CLI, only supported by containers + Archive bool `json:",omitempty"` // CLI only + Type define.DiffType // Type which should be compared } // DiffReport provides changes for object diff -Nru libpod-3.2.1+ds1/pkg/domain/filters/containers.go libpod-3.4.4+ds1/pkg/domain/filters/containers.go --- libpod-3.2.1+ds1/pkg/domain/filters/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/filters/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -211,6 +211,36 @@ }, nil case "network": return func(c *libpod.Container) bool { + networkMode := c.NetworkMode() + // support docker like `--filter network=container:` + // check if networkMode is configured as `container:` + // perform a match against filter `container:` + // networks is already going to be empty if `container:` is configured as Mode + if strings.HasPrefix(networkMode, "container:") { + networkModeContainerPart := strings.SplitN(networkMode, ":", 2) + if len(networkModeContainerPart) < 2 { + return false + } + networkModeContainerID := networkModeContainerPart[1] + for _, val := range filterValues { + if strings.HasPrefix(val, "container:") { + filterNetworkModePart := strings.SplitN(val, ":", 2) + if len(filterNetworkModePart) < 2 { + return false + } + filterNetworkModeIDorName := filterNetworkModePart[1] + filterID, err := r.LookupContainerID(filterNetworkModeIDorName) + if err != nil { + return false + } + if filterID == networkModeContainerID { + return true + } + } + } + return false + } + networks, _, err := c.Networks() // if err or no networks, quick out if err != nil || len(networks) == 0 { diff -Nru libpod-3.2.1+ds1/pkg/domain/filters/pods.go libpod-3.4.4+ds1/pkg/domain/filters/pods.go --- libpod-3.2.1+ds1/pkg/domain/filters/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/filters/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -116,6 +116,17 @@ labels := p.Labels() return util.MatchLabelFilters(filterValues, labels) }, nil + case "until": + return func(p *libpod.Pod) bool { + until, err := util.ComputeUntilTimestamp(filterValues) + if err != nil { + return false + } + if p.CreatedTime().Before(until) { + return true + } + return false + }, nil case "network": return func(p *libpod.Pod) bool { infra, err := p.InfraContainer() diff -Nru libpod-3.2.1+ds1/pkg/domain/filters/volumes.go libpod-3.4.4+ds1/pkg/domain/filters/volumes.go --- libpod-3.2.1+ds1/pkg/domain/filters/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/filters/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -51,6 +51,12 @@ } return false }) + case "until": + f, err := createUntilFilterVolumeFunction(val) + if err != nil { + return nil, err + } + vf = append(vf, f) case "dangling": danglingVal := val invert := false @@ -86,12 +92,18 @@ var vf []libpod.VolumeFilter for filter, v := range filters { for _, val := range v { + filterVal := val switch filter { case "label": - filter := val vf = append(vf, func(v *libpod.Volume) bool { - return util.MatchLabelFilters([]string{filter}, v.Labels()) + return util.MatchLabelFilters([]string{filterVal}, v.Labels()) }) + case "until": + f, err := createUntilFilterVolumeFunction(filterVal) + if err != nil { + return nil, err + } + vf = append(vf, f) default: return nil, errors.Errorf("%q is an invalid volume filter", filter) } @@ -99,3 +111,16 @@ } return vf, nil } + +func createUntilFilterVolumeFunction(filter string) (libpod.VolumeFilter, error) { + until, err := util.ComputeUntilTimestamp([]string{filter}) + if err != nil { + return nil, err + } + return func(v *libpod.Volume) bool { + if !until.IsZero() && v.CreatedTime().Before(until) { + return true + } + return false + }, nil +} diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/archive.go libpod-3.4.4+ds1/pkg/domain/infra/abi/archive.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/archive.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/archive.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,12 +7,12 @@ "github.com/containers/podman/v3/pkg/domain/entities" ) -func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, containerPath string, reader io.Reader) (entities.ContainerCopyFunc, error) { +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID, containerPath string, reader io.Reader, options entities.CopyOptions) (entities.ContainerCopyFunc, error) { container, err := ic.Libpod.LookupContainer(nameOrID) if err != nil { return nil, err } - return container.CopyFromArchive(ctx, containerPath, reader) + return container.CopyFromArchive(ctx, containerPath, options.Chown, options.Rename, reader) } func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, containerPath string, writer io.Writer) (entities.ContainerCopyFunc, error) { diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/auto-update.go libpod-3.4.4+ds1/pkg/domain/infra/abi/auto-update.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/auto-update.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/auto-update.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,11 +7,6 @@ "github.com/containers/podman/v3/pkg/domain/entities" ) -func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) (*entities.AutoUpdateReport, []error) { - // Convert the entities options to the autoupdate ones. We can't use - // them in the entities package as low-level packages must not leak - // into the remote client. - autoOpts := autoupdate.Options{Authfile: options.Authfile} - units, failures := autoupdate.AutoUpdate(ic.Libpod, autoOpts) - return &entities.AutoUpdateReport{Units: units}, failures +func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) ([]*entities.AutoUpdateReport, []error) { + return autoupdate.AutoUpdate(ctx, ic.Libpod, options) } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/containers.go libpod-3.4.4+ds1/pkg/domain/infra/abi/containers.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -119,6 +119,10 @@ report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := c.Pause() + if err != nil && options.All && errors.Cause(err) == define.ErrCtrStateInvalid { + logrus.Debugf("Container %s is not running", c.ID()) + continue + } report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) } return report, nil @@ -132,6 +136,10 @@ report := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := c.Unpause() + if err != nil && options.All && errors.Cause(err) == define.ErrCtrStateInvalid { + logrus.Debugf("Container %s is not paused", c.ID()) + continue + } report = append(report, &entities.PauseUnpauseReport{Id: c.ID(), Err: err}) } return report, nil @@ -161,17 +169,25 @@ logrus.Debugf("Container %s is already stopped", c.ID()) case options.All && errors.Cause(err) == define.ErrCtrStateInvalid: logrus.Debugf("Container %s is not running, could not stop", c.ID()) + // container never created in OCI runtime + // docker parity: do nothing just return container id + case errors.Cause(err) == define.ErrCtrStateInvalid: + logrus.Debugf("Container %s is either not created on runtime or is in a invalid state", c.ID()) default: return err } } - if c.AutoRemove() { - // Issue #7384: if the container is configured for - // auto-removal, it might already have been removed at - // this point. - return nil + err = c.Cleanup(ctx) + if err != nil { + // Issue #7384 and #11384: If the container is configured for + // auto-removal, it might already have been removed at this point. + // We still need to to cleanup since we do not know if the other cleanup process is successful + if c.AutoRemove() && (errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved)) { + return nil + } + return err } - return c.Cleanup(ctx) + return nil }) if err != nil { return nil, err @@ -220,9 +236,14 @@ } reports := make([]*entities.KillReport, 0, len(ctrs)) for _, con := range ctrs { + err := con.Kill(uint(sig)) + if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { + logrus.Debugf("Container %s is not running", con.ID()) + continue + } reports = append(reports, &entities.KillReport{ Id: con.ID(), - Err: con.Kill(uint(sig)), + Err: err, RawInput: ctrMap[con.ID()], }) } @@ -261,6 +282,24 @@ return reports, nil } +func (ic *ContainerEngine) removeContainer(ctx context.Context, ctr *libpod.Container, options entities.RmOptions) error { + err := ic.Libpod.RemoveContainer(ctx, ctr, options.Force, options.Volumes) + if err == nil { + return nil + } + logrus.Debugf("Failed to remove container %s: %s", ctr.ID(), err.Error()) + switch errors.Cause(err) { + case define.ErrNoSuchCtr: + if options.Ignore { + logrus.Debugf("Ignoring error (--allow-missing): %v", err) + return nil + } + case define.ErrCtrRemoved: + return nil + } + return err +} + func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, options entities.RmOptions) ([]*entities.RmReport, error) { reports := []*entities.RmReport{} @@ -318,21 +357,7 @@ } errMap, err := parallelctr.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { - err := ic.Libpod.RemoveContainer(ctx, c, options.Force, options.Volumes) - if err == nil { - return nil - } - logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error()) - switch errors.Cause(err) { - case define.ErrNoSuchCtr: - if options.Ignore { - logrus.Debugf("Ignoring error (--allow-missing): %v", err) - return nil - } - case define.ErrCtrRemoved: - return nil - } - return err + return ic.removeContainer(ctx, c, options) }) if err != nil { return nil, err @@ -350,7 +375,7 @@ if options.Latest { ctr, err := ic.Libpod.GetLatestContainer() if err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { + if errors.Is(err, define.ErrNoSuchCtr) { return nil, []error{errors.Wrapf(err, "no containers to inspect")}, nil } return nil, nil, err @@ -376,7 +401,7 @@ if err != nil { // ErrNoSuchCtr is non-fatal, other errors will be // treated as fatal. - if errors.Cause(err) == define.ErrNoSuchCtr { + if errors.Is(err, define.ErrNoSuchCtr) { errs = append(errs, errors.Errorf("no such container %s", name)) continue } @@ -385,6 +410,12 @@ inspect, err := ctr.Inspect(options.Size) if err != nil { + // ErrNoSuchCtr is non-fatal, other errors will be + // treated as fatal. + if errors.Is(err, define.ErrNoSuchCtr) { + errs = append(errs, errors.Errorf("no such container %s", name)) + continue + } return nil, nil, err } @@ -483,6 +514,7 @@ KeepRunning: options.LeaveRunning, PreCheckPoint: options.PreCheckPoint, WithPrevious: options.WithPrevious, + Compression: options.Compression, } if options.All { @@ -524,6 +556,7 @@ IgnoreStaticIP: options.IgnoreStaticIP, IgnoreStaticMAC: options.IgnoreStaticMAC, ImportPrevious: options.ImportPrevious, + Pod: options.Pod, } filterFuncs := []libpod.ContainerFilter{ @@ -564,7 +597,11 @@ for _, w := range warn { fmt.Fprintf(os.Stderr, "%s\n", w) } - ctr, err := generate.MakeContainer(ctx, ic.Libpod, s) + rtSpec, spec, opts, err := generate.MakeContainer(context.Background(), ic.Libpod, s) + if err != nil { + return nil, err + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) if err != nil { return nil, err } @@ -594,7 +631,7 @@ return nil } -func makeExecConfig(options entities.ExecOptions) *libpod.ExecConfig { +func makeExecConfig(options entities.ExecOptions, rt *libpod.Runtime) (*libpod.ExecConfig, error) { execConfig := new(libpod.ExecConfig) execConfig.Command = options.Cmd execConfig.Terminal = options.Tty @@ -606,7 +643,20 @@ execConfig.PreserveFDs = options.PreserveFDs execConfig.AttachStdin = options.Interactive - return execConfig + // Make an exit command + storageConfig := rt.StorageConfig() + runtimeConfig, err := rt.GetConfig() + if err != nil { + return nil, errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command") + } + // TODO: Add some ability to toggle syslog + exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, false, false, true) + if err != nil { + return nil, errors.Wrapf(err, "error constructing exit command for exec session") + } + execConfig.ExitCommand = exitCommandArgs + + return execConfig, nil } func checkExecPreserveFDs(options entities.ExecOptions) error { @@ -646,7 +696,10 @@ } ctr := ctrs[0] - execConfig := makeExecConfig(options) + execConfig, err := makeExecConfig(options, ic.Libpod) + if err != nil { + return ec, err + } ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams) return define.TranslateExecErrorToExitCode(ec, err), err @@ -663,20 +716,10 @@ } ctr := ctrs[0] - execConfig := makeExecConfig(options) - - // Make an exit command - storageConfig := ic.Libpod.StorageConfig() - runtimeConfig, err := ic.Libpod.GetConfig() - if err != nil { - return "", errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command") - } - // TODO: Add some ability to toggle syslog - exitCommandArgs, err := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, false, true, true) + execConfig, err := makeExecConfig(options, ic.Libpod) if err != nil { - return "", errors.Wrapf(err, "error constructing exit command for exec session") + return "", err } - execConfig.ExitCommand = exitCommandArgs // Create and start the exec session id, err := ctr.ExecCreate(execConfig) @@ -695,7 +738,9 @@ reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric containersNamesOrIds := namesOrIds + all := options.All if len(options.Filters) > 0 { + all = false filterFuncs := make([]libpod.ContainerFilter, 0, len(options.Filters)) if len(options.Filters) > 0 { for k, v := range options.Filters { @@ -712,6 +757,10 @@ } containersNamesOrIds = []string{} for _, candidate := range candidates { + if options.All { + containersNamesOrIds = append(containersNamesOrIds, candidate.ID()) + continue + } for _, nameOrID := range namesOrIds { if nameOrID == candidate.ID() || nameOrID == candidate.Name() { containersNamesOrIds = append(containersNamesOrIds, nameOrID) @@ -719,8 +768,7 @@ } } } - - ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, containersNamesOrIds, ic.Libpod) + ctrs, rawInputs, err := getContainersAndInputByContext(all, options.Latest, containersNamesOrIds, ic.Libpod) if err != nil { return nil, err } @@ -779,23 +827,14 @@ Err: err, ExitCode: exitCode, }) - return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) - } - - if ecode, err := ctr.Wait(ctx); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) - if err != nil { - logrus.Errorf("Cannot get exit code: %v", err) - exitCode = define.ExecErrorCodeNotFound - } else { - exitCode = event.ContainerExitCode + if ctr.AutoRemove() { + if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { + logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) } } - } else { - exitCode = int(ecode) + return reports, errors.Wrapf(err, "unable to start container %s", ctr.ID()) } + exitCode = ic.GetContainerExitCode(ctx, ctr) reports = append(reports, &entities.ContainerStartReport{ Id: ctr.ID(), RawInput: rawInput, @@ -815,9 +854,6 @@ ExitCode: 125, } if err := ctr.Start(ctx, true); err != nil { - // if lastError != nil { - // fmt.Fprintln(os.Stderr, lastError) - // } report.Err = err if errors.Cause(err) == define.ErrWillDeadlock { report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") @@ -826,6 +862,11 @@ } report.Err = errors.Wrapf(err, "unable to start container %q", ctr.ID()) reports = append(reports, report) + if ctr.AutoRemove() { + if err := ic.removeContainer(ctx, ctr, entities.RmOptions{}); err != nil { + logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) + } + } continue } report.ExitCode = 0 @@ -842,16 +883,30 @@ return ps.GetContainerLists(ic.Libpod, options) } -// ContainerDiff provides changes to given container -func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, opts entities.DiffOptions) (*entities.DiffReport, error) { +func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entities.ListContainer, error) { + return ps.GetExternalContainerLists(ic.Libpod) +} + +// Diff provides changes to given container +func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts entities.DiffOptions) (*entities.DiffReport, error) { + var ( + base string + parent string + ) if opts.Latest { ctnr, err := ic.Libpod.GetLatestContainer() if err != nil { return nil, errors.Wrap(err, "unable to get latest container") } - nameOrID = ctnr.ID() + base = ctnr.ID() + } + if len(namesOrIDs) > 0 { + base = namesOrIDs[0] + if len(namesOrIDs) > 1 { + parent = namesOrIDs[1] + } } - changes, err := ic.Libpod.GetDiff("", nameOrID) + changes, err := ic.Libpod.GetDiff(parent, base, opts.Type) return &entities.DiffReport{Changes: changes}, err } @@ -864,7 +919,11 @@ for _, w := range warn { fmt.Fprintf(os.Stderr, "%s\n", w) } - ctr, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec) + rtSpec, spec, optsN, err := generate.MakeContainer(ctx, ic.Libpod, opts.Spec) + if err != nil { + return nil, err + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, optsN...) if err != nil { return nil, err } @@ -916,21 +975,7 @@ report.ExitCode = define.ExitCode(err) return &report, err } - - if ecode, err := ctr.Wait(ctx); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { - // Check events - event, err := ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) - if err != nil { - logrus.Errorf("Cannot get exit code: %v", err) - report.ExitCode = define.ExecErrorCodeNotFound - } else { - report.ExitCode = event.ContainerExitCode - } - } - } else { - report.ExitCode = int(ecode) - } + report.ExitCode = ic.GetContainerExitCode(ctx, ctr) if opts.Rm && !ctr.ShouldRestart(ctx) { if err := ic.Libpod.RemoveContainer(ctx, ctr, false, true); err != nil { if errors.Cause(err) == define.ErrNoSuchCtr || @@ -944,6 +989,29 @@ return &report, nil } +func (ic *ContainerEngine) GetContainerExitCode(ctx context.Context, ctr *libpod.Container) int { + exitCode, err := ctr.Wait(ctx) + if err == nil { + return int(exitCode) + } + if errors.Cause(err) != define.ErrNoSuchCtr { + logrus.Errorf("Could not retrieve exit code: %v", err) + return define.ExecErrorCodeNotFound + } + // Make 4 attempt with 0.25s backoff between each for 1 second total + var event *events.Event + for i := 0; i < 4; i++ { + event, err = ic.Libpod.GetLastContainerEvent(ctx, ctr.ID(), events.Exited) + if err != nil { + time.Sleep(250 * time.Millisecond) + continue + } + return int(event.ContainerExitCode) + } + logrus.Errorf("Could not retrieve exit code from event: %v", err) + return define.ExecErrorCodeNotFound +} + func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { if options.StdoutWriter == nil && options.StderrWriter == nil { return errors.New("no io.Writer set for container logs") @@ -961,6 +1029,7 @@ Details: options.Details, Follow: options.Follow, Since: options.Since, + Until: options.Until, Tail: options.Tail, Timestamps: options.Timestamps, UseName: options.Names, @@ -1244,6 +1313,18 @@ } func (ic *ContainerEngine) ContainerStats(ctx context.Context, namesOrIds []string, options entities.ContainerStatsOptions) (statsChan chan entities.ContainerStatsReport, err error) { + if options.Interval < 1 { + return nil, errors.New("Invalid interval, must be a positive number greater zero") + } + if rootless.IsRootless() { + unified, err := cgroups.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if !unified { + return nil, errors.New("stats is not supported in rootless mode without cgroups v2") + } + } statsChan = make(chan entities.ContainerStatsReport, 1) containerFunc := ic.Libpod.GetRunningContainers @@ -1324,7 +1405,7 @@ return } - time.Sleep(time.Second) + time.Sleep(time.Second * time.Duration(options.Interval)) goto stream }() diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/containers_runlabel.go libpod-3.4.4+ds1/pkg/domain/infra/abi/containers_runlabel.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/containers_runlabel.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/containers_runlabel.go 2021-12-26 00:45:51.000000000 +0000 @@ -133,6 +133,9 @@ } splitImageName := strings.Split(normalize, "/") name = splitImageName[len(splitImageName)-1] + // make sure to remove the tag from the image name, otherwise the name cannot + // be used as container name because a colon is an illegal character + name = strings.SplitN(name, ":", 2)[0] } // Append the user-specified arguments to the runlabel (command). diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/generate.go libpod-3.4.4+ds1/pkg/domain/infra/abi/generate.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/generate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/generate.go 2021-12-26 00:45:51.000000000 +0000 @@ -60,9 +60,7 @@ return nil, err } } else { - if len(ctr.Dependencies()) > 0 { - return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies") - } + // now that infra holds NS data, we need to support dependencies. // we cannot deal with ctrs already in a pod. if len(ctr.PodID()) > 0 { return nil, errors.Errorf("container %s is associated with pod %s: use generate on the pod itself", ctr.ID(), ctr.PodID()) @@ -109,7 +107,7 @@ // Generate kube pods and services from pods. if len(pods) >= 1 { - pos, svcs, err := getKubePods(pods, options.Service) + pos, svcs, err := getKubePods(ctx, pods, options.Service) if err != nil { return nil, err } @@ -122,19 +120,30 @@ // Generate the kube pods from containers. if len(ctrs) >= 1 { - po, err := libpod.GenerateForKube(ctrs) + po, err := libpod.GenerateForKube(ctx, ctrs) if err != nil { return nil, err } - - b, err := generateKubeYAML(po) + if len(po.Spec.Volumes) != 0 { + warning := ` +# NOTE: If you generated this yaml from an unprivileged and rootless podman container on an SELinux +# enabled system, check the podman generate kube man page for steps to follow to ensure that your pod/container +# has the right permissions to access the volumes added. +` + content = append(content, []byte(warning)) + } + b, err := generateKubeYAML(libpod.ConvertV1PodToYAMLPod(po)) if err != nil { return nil, err } podContent = append(podContent, b) if options.Service { - b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{})) + svc, err := libpod.GenerateKubeServiceFromV1Pod(po, []k8sAPI.ServicePort{}) + if err != nil { + return nil, err + } + b, err := generateKubeYAML(svc) if err != nil { return nil, err } @@ -155,12 +164,12 @@ } // getKubePods returns kube pod and service YAML files from podman pods. -func getKubePods(pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) { +func getKubePods(ctx context.Context, pods []*libpod.Pod, getService bool) ([][]byte, [][]byte, error) { pos := [][]byte{} svcs := [][]byte{} for _, p := range pods { - po, sp, err := p.GenerateForKube() + po, sp, err := p.GenerateForKube(ctx) if err != nil { return nil, nil, err } @@ -172,7 +181,11 @@ pos = append(pos, b) if getService { - b, err := generateKubeYAML(libpod.GenerateKubeServiceFromV1Pod(po, sp)) + svc, err := libpod.GenerateKubeServiceFromV1Pod(po, sp) + if err != nil { + return nil, nil, err + } + b, err := generateKubeYAML(svc) if err != nil { return nil, nil, err } @@ -212,9 +225,7 @@ func generateKubeOutput(content [][]byte) ([]byte, error) { output := make([]byte, 0) - header := `# Generation of Kubernetes YAML is still under development! -# -# Save the output of this file and use kubectl create -f to import + header := `# Save the output of this file and use kubectl create -f to import # it into Kubernetes. # # Created with podman-%s diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/images.go libpod-3.4.4+ds1/pkg/domain/infra/abi/images.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -89,7 +89,7 @@ } func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { - image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true}) + image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, nil) if err != nil { return nil, err } @@ -245,7 +245,7 @@ reports := []*entities.ImageInspectReport{} errs := []error{} for _, i := range namesOrIDs { - img, _, err := ir.Libpod.LibimageRuntime().LookupImage(i, &libimage.LookupImageOptions{IgnorePlatform: true}) + img, _, err := ir.Libpod.LibimageRuntime().LookupImage(i, nil) if err != nil { // This is probably a no such image, treat as nonfatal. errs = append(errs, err) @@ -321,7 +321,9 @@ } func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { - image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true}) + // Allow tagging manifest list instead of resolving instances from manifest + lookupOptions := &libimage.LookupImageOptions{ManifestList: true} + image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, lookupOptions) if err != nil { return err } @@ -334,7 +336,7 @@ } func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error { - image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true}) + image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, nil) if err != nil { return err } @@ -367,7 +369,10 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { saveOptions := &libimage.SaveOptions{} saveOptions.DirForceCompress = options.Compress - saveOptions.RemoveSignatures = options.RemoveSignatures + + // Force signature removal to preserve backwards compat. + // See https://github.com/containers/podman/pull/11669#issuecomment-925250264 + saveOptions.RemoveSignatures = true if !options.Quiet { saveOptions.Writer = os.Stderr @@ -388,6 +393,8 @@ importOptions.CommitMessage = options.Message importOptions.Tag = options.Reference importOptions.SignaturePolicyPath = options.SignaturePolicy + importOptions.OS = options.OS + importOptions.Architecture = options.Architecture if !options.Quiet { importOptions.Writer = os.Stderr @@ -401,14 +408,6 @@ return &entities.ImageImportReport{Id: imageID}, nil } -func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := ir.Libpod.GetDiff("", nameOrID) - if err != nil { - return nil, err - } - return &entities.DiffReport{Changes: changes}, nil -} - func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { filter, err := libimage.ParseSearchFilter(opts.Filters) if err != nil { @@ -460,7 +459,7 @@ } func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { - image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, &libimage.LookupImageOptions{IgnorePlatform: true}) + image, _, err := ir.Libpod.LibimageRuntime().LookupImage(nameOrID, nil) if err != nil { return nil, err } @@ -527,6 +526,7 @@ libimageOptions := &libimage.RemoveImagesOptions{} libimageOptions.Filters = []string{"readonly=false"} libimageOptions.Force = opts.Force + libimageOptions.LookupManifest = opts.LookupManifest if !opts.All { libimageOptions.Filters = append(libimageOptions.Filters, "intermediate=false") } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/images_list.go libpod-3.4.4+ds1/pkg/domain/infra/abi/images_list.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/images_list.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/images_list.go 2021-12-26 00:45:51.000000000 +0000 @@ -30,12 +30,16 @@ for j, d := range img.Digests() { digests[j] = string(d) } + isDangling, err := img.IsDangling(ctx) + if err != nil { + return nil, errors.Wrapf(err, "error checking if image %q is dangling", img.ID()) + } e := entities.ImageSummary{ ID: img.ID(), // ConfigDigest: string(img.ConfigDigest), Created: img.Created().Unix(), - Dangling: img.IsDangling(), + Dangling: isDangling, Digest: string(img.Digest()), RepoDigests: digests, History: img.NamesHistory(), diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/manifest.go libpod-3.4.4+ds1/pkg/domain/infra/abi/manifest.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/manifest.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/manifest.go 2021-12-26 00:45:51.000000000 +0000 @@ -47,7 +47,7 @@ // ManifestExists checks if a manifest list with the given name exists in local storage func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entities.BoolReport, error) { - image, _, err := ir.Libpod.LibimageRuntime().LookupImage(name, &libimage.LookupImageOptions{IgnorePlatform: true}) + _, err := ir.Libpod.LibimageRuntime().LookupManifestList(name) if err != nil { if errors.Cause(err) == storage.ErrImageUnknown { return &entities.BoolReport{Value: false}, nil @@ -55,11 +55,7 @@ return nil, err } - isManifestList, err := image.IsManifestList(ctx) - if err != nil { - return nil, err - } - return &entities.BoolReport{Value: isManifestList}, nil + return &entities.BoolReport{Value: true}, nil } // ManifestInspect returns the content of a manifest list or image @@ -308,6 +304,11 @@ return manifestList.ID(), nil } +// ManifestRm removes the specified manifest list from storage +func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (report *entities.ImageRemoveReport, rmErrors []error) { + return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true}) +} + // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { manifestList, err := ir.Libpod.LibimageRuntime().LookupManifestList(name) @@ -336,6 +337,7 @@ pushOptions.ManifestMIMEType = manifestType pushOptions.RemoveSignatures = opts.RemoveSignatures pushOptions.SignBy = opts.SignBy + pushOptions.InsecureSkipTLSVerify = opts.SkipTLSVerify if opts.All { pushOptions.ImageListSelection = cp.CopyAllImages @@ -350,8 +352,8 @@ } if opts.Rm { - if _, err := ir.Libpod.GetStore().DeleteImage(manifestList.ID(), true); err != nil { - return "", errors.Wrap(err, "error removing manifest after push") + if _, rmErrors := ir.Libpod.LibimageRuntime().RemoveImages(ctx, []string{manifestList.ID()}, nil); len(rmErrors) > 0 { + return "", errors.Wrap(rmErrors[0], "error removing manifest after push") } } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/network.go libpod-3.4.4+ds1/pkg/domain/infra/abi/network.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/network.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,7 +11,7 @@ ) func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { - var reports []*entities.NetworkListReport + reports := make([]*entities.NetworkListReport, 0) config, err := ic.Libpod.GetConfig() if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/parse/parse.go libpod-3.4.4+ds1/pkg/domain/infra/abi/parse/parse.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/parse/parse.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/parse/parse.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,12 +6,13 @@ "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" + units "github.com/docker/go-units" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) // Handle volume options from CLI. -// Parse "o" option to find UID, GID. +// Parse "o" option to find UID, GID, Size. func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) { libpodOptions := []libpod.VolumeCreateOption{} volumeOptions := make(map[string]string) @@ -28,6 +29,24 @@ // "opt=value" splitO := strings.SplitN(o, "=", 2) switch strings.ToLower(splitO[0]) { + case "size": + size, err := units.FromHumanSize(splitO[1]) + if err != nil { + return nil, errors.Wrapf(err, "cannot convert size %s to integer", splitO[1]) + } + libpodOptions = append(libpodOptions, libpod.WithVolumeSize(uint64(size))) + finalVal = append(finalVal, o) + // set option "SIZE": "$size" + volumeOptions["SIZE"] = splitO[1] + case "inodes": + inodes, err := strconv.ParseUint(splitO[1], 10, 64) + if err != nil { + return nil, errors.Wrapf(err, "cannot convert inodes %s to integer", splitO[1]) + } + libpodOptions = append(libpodOptions, libpod.WithVolumeInodes(uint64(inodes))) + finalVal = append(finalVal, o) + // set option "INODES": "$size" + volumeOptions["INODES"] = splitO[1] case "uid": if len(splitO) != 2 { return nil, errors.Wrapf(define.ErrInvalidArg, "uid option must provide a UID") @@ -37,7 +56,7 @@ return nil, errors.Wrapf(err, "cannot convert UID %s to integer", splitO[1]) } logrus.Debugf("Removing uid= from options and adding WithVolumeUID for UID %d", intUID) - libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID)) + libpodOptions = append(libpodOptions, libpod.WithVolumeUID(intUID), libpod.WithVolumeNoChown()) finalVal = append(finalVal, o) // set option "UID": "$uid" volumeOptions["UID"] = splitO[1] @@ -50,7 +69,7 @@ return nil, errors.Wrapf(err, "cannot convert GID %s to integer", splitO[1]) } logrus.Debugf("Removing gid= from options and adding WithVolumeGID for GID %d", intGID) - libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID)) + libpodOptions = append(libpodOptions, libpod.WithVolumeGID(intGID), libpod.WithVolumeNoChown()) finalVal = append(finalVal, o) // set option "GID": "$gid" volumeOptions["GID"] = splitO[1] diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/play.go libpod-3.4.4+ds1/pkg/domain/infra/abi/play.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/play.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,13 +6,15 @@ "fmt" "io" "io/ioutil" + "net" "os" + "path/filepath" "strconv" "strings" + buildahDefine "github.com/containers/buildah/define" "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" - "github.com/containers/common/pkg/secrets" "github.com/containers/image/v5/types" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" @@ -21,6 +23,7 @@ "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/podman/v3/pkg/specgen/generate/kube" + "github.com/containers/podman/v3/pkg/specgenutil" "github.com/containers/podman/v3/pkg/util" "github.com/ghodss/yaml" "github.com/pkg/errors" @@ -54,6 +57,8 @@ ipIndex := 0 + var configMaps []v1.ConfigMap + // create pod on each document if it is a pod or deployment // any other kube kind will be skipped for _, document := range documentList { @@ -74,7 +79,7 @@ podTemplateSpec.ObjectMeta = podYAML.ObjectMeta podTemplateSpec.Spec = podYAML.Spec - r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex, podYAML.Annotations) + r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex, podYAML.Annotations, configMaps) if err != nil { return nil, err } @@ -88,7 +93,7 @@ return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path) } - r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex) + r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex, configMaps) if err != nil { return nil, err } @@ -109,6 +114,13 @@ report.Volumes = append(report.Volumes, r.Volumes...) validKinds++ + case "ConfigMap": + var configMap v1.ConfigMap + + if err := yaml.Unmarshal(document, &configMap); err != nil { + return nil, errors.Wrapf(err, "unable to read YAML %q as Kube ConfigMap", path) + } + configMaps = append(configMaps, configMap) default: logrus.Infof("kube kind %s not supported", kind) continue @@ -122,7 +134,7 @@ return report, nil } -func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int, configMaps []v1.ConfigMap) (*entities.PlayKubeReport, error) { var ( deploymentName string podSpec v1.PodTemplateSpec @@ -144,7 +156,7 @@ // create "replicas" number of pods for i = 0; i < numReplicas; i++ { podName := fmt.Sprintf("%s-pod-%d", deploymentName, i) - podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations) + podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex, deploymentYAML.Annotations, configMaps) if err != nil { return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName) } @@ -153,7 +165,7 @@ return &report, nil } -func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int, annotations map[string]string) (*entities.PlayKubeReport, error) { +func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int, annotations map[string]string, configMaps []v1.ConfigMap) (*entities.PlayKubeReport, error) { var ( writer io.Writer playKubePod entities.PlayKubePod @@ -161,7 +173,7 @@ ) // Create the secret manager before hand - secretsManager, err := secrets.NewManager(ic.Libpod.GetSecretsStorageDir()) + secretsManager, err := ic.Libpod.SecretsManager() if err != nil { return nil, err } @@ -178,54 +190,55 @@ } } - p, err := kube.ToPodGen(ctx, podName, podYAML) + podOpt := entities.PodCreateOptions{Infra: true, Net: &entities.NetOptions{StaticIP: &net.IP{}, StaticMAC: &net.HardwareAddr{}}} + podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, podYAML) if err != nil { return nil, err } + if options.Network != "" { - switch strings.ToLower(options.Network) { - case "bridge", "host": + ns, cniNets, netOpts, err := specgen.ParseNetworkString(options.Network) + if err != nil { + return nil, err + } + + if (ns.IsBridge() && len(cniNets) == 0) || ns.IsHost() { return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML") - case "": - return nil, errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks") - default: - // We'll assume this is a comma-separated list of CNI - // networks. - networks := strings.Split(options.Network, ",") - logrus.Debugf("Pod joining CNI networks: %v", networks) - p.NetNS.NSMode = specgen.Bridge - p.CNINetworks = append(p.CNINetworks, networks...) + } + + podOpt.Net.Network = ns + if len(cniNets) > 0 { + podOpt.Net.CNINetworks = append(podOpt.Net.CNINetworks, cniNets...) + } + if len(netOpts) > 0 { + podOpt.Net.NetworkOptions = netOpts } } + if len(options.StaticIPs) > *ipIndex { - p.StaticIP = &options.StaticIPs[*ipIndex] + podOpt.Net.StaticIP = &options.StaticIPs[*ipIndex] } else if len(options.StaticIPs) > 0 { // only warn if the user has set at least one ip logrus.Warn("No more static ips left using a random one") } if len(options.StaticMACs) > *ipIndex { - p.StaticMAC = &options.StaticMACs[*ipIndex] + podOpt.Net.StaticMAC = &options.StaticMACs[*ipIndex] } else if len(options.StaticIPs) > 0 { // only warn if the user has set at least one mac logrus.Warn("No more static macs left using a random one") } *ipIndex++ - // Create the Pod - pod, err := generate.MakePod(p, ic.Libpod) + p := specgen.NewPodSpecGenerator() if err != nil { return nil, err } - podInfraID, err := pod.InfraContainerID() + p, err = entities.ToPodSpecGen(*p, &podOpt) if err != nil { return nil, err } - - if !options.Quiet { - writer = os.Stderr - } - + podSpec := entities.PodSpec{PodSpecGen: *p} volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes) if err != nil { return nil, err @@ -248,7 +261,10 @@ ctrRestartPolicy = define.RestartPolicyAlways } - configMaps := []v1.ConfigMap{} + configMapIndex := make(map[string]struct{}) + for _, configMap := range configMaps { + configMapIndex[configMap.Name] = struct{}{} + } for _, p := range options.ConfigMaps { f, err := os.Open(p) if err != nil { @@ -261,82 +277,132 @@ return nil, errors.Wrapf(err, "%q", p) } + if _, present := configMapIndex[cm.Name]; present { + return nil, errors.Errorf("ambiguous configuration: the same config map %s is present in YAML and in --configmaps %s file", cm.Name, p) + } + configMaps = append(configMaps, cm) } - containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers)) - for _, container := range podYAML.Spec.Containers { - // Contains all labels obtained from kube - labels := make(map[string]string) + if podOpt.Infra { + containerConfig := util.DefaultContainerConfig() - // NOTE: set the pull policy to "newer". This will cover cases - // where the "latest" tag requires a pull and will also - // transparently handle "localhost/" prefixed files which *may* - // refer to a locally built image OR an image running a - // registry on localhost. - pullPolicy := config.PullPolicyNewer - if len(container.ImagePullPolicy) > 0 { - pullPolicy, err = config.ParsePullPolicy(string(container.ImagePullPolicy)) - if err != nil { - return nil, err - } + pulledImages, err := pullImage(ic, writer, containerConfig.Engine.InfraImage, options, config.PullPolicyNewer) + if err != nil { + return nil, err } - // This ensures the image is the image store - pullOptions := &libimage.PullOptions{} - pullOptions.AuthFilePath = options.Authfile - pullOptions.CertDirPath = options.CertDir - pullOptions.SignaturePolicyPath = options.SignaturePolicy - pullOptions.Writer = writer - pullOptions.Username = options.Username - pullOptions.Password = options.Password - pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify + infraOptions := entities.ContainerCreateOptions{ImageVolume: "bind"} - pulledImages, err := ic.Libpod.LibimageRuntime().Pull(ctx, container.Image, pullPolicy, pullOptions) + podSpec.PodSpecGen.InfraImage = pulledImages[0].Names()[0] + podSpec.PodSpecGen.NoInfra = false + podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(pulledImages[0].Names()[0], false) + podSpec.PodSpecGen.InfraContainerSpec.NetworkOptions = p.NetworkOptions + + err = specgenutil.FillOutSpecGen(podSpec.PodSpecGen.InfraContainerSpec, &infraOptions, []string{}) if err != nil { return nil, err } + } - // Handle kube annotations - for k, v := range annotations { - switch k { - // Auto update annotation without container name will apply to - // all containers within the pod - case autoupdate.Label, autoupdate.AuthfileLabel: - labels[k] = v - // Auto update annotation with container name will apply only - // to the specified container - case fmt.Sprintf("%s/%s", autoupdate.Label, container.Name), - fmt.Sprintf("%s/%s", autoupdate.AuthfileLabel, container.Name): - prefixAndCtr := strings.Split(k, "/") - labels[prefixAndCtr[0]] = v - } - } + // Create the Pod + pod, err := generate.MakePod(&podSpec, ic.Libpod) + if err != nil { + return nil, err + } + + podInfraID, err := pod.InfraContainerID() + if err != nil { + return nil, err + } + + if !options.Quiet { + writer = os.Stderr + } + + containers := make([]*libpod.Container, 0, len(podYAML.Spec.Containers)) + initContainers := make([]*libpod.Container, 0, len(podYAML.Spec.InitContainers)) + cwd, err := os.Getwd() + if err != nil { + return nil, err + } + for _, initCtr := range podYAML.Spec.InitContainers { + // 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") + } + pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, initCtr, options) + if err != nil { + return nil, err + } specgenOpts := kube.CtrSpecGenOptions{ - Container: container, - Image: pulledImages[0], - Volumes: volumes, - PodID: pod.ID(), - PodName: podName, - PodInfraID: podInfraID, - ConfigMaps: configMaps, - SeccompPaths: seccompPaths, - RestartPolicy: ctrRestartPolicy, - NetNSIsHost: p.NetNS.IsHost(), - SecretsManager: secretsManager, - LogDriver: options.LogDriver, - Labels: labels, + Annotations: annotations, + Container: initCtr, + Image: pulledImage, + Volumes: volumes, + PodID: pod.ID(), + PodName: podName, + PodInfraID: podInfraID, + ConfigMaps: configMaps, + SeccompPaths: seccompPaths, + RestartPolicy: ctrRestartPolicy, + NetNSIsHost: p.NetNS.IsHost(), + SecretsManager: secretsManager, + LogDriver: options.LogDriver, + Labels: labels, + InitContainerType: define.AlwaysInitContainer, } specGen, err := kube.ToSpecGen(ctx, &specgenOpts) if err != nil { return nil, err } - - ctr, err := generate.MakeContainer(ctx, ic.Libpod, specGen) + rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen) + if err != nil { + return nil, err + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) if err != nil { return nil, err } - containers = append(containers, ctr) + + initContainers = append(initContainers, ctr) + } + for _, container := range podYAML.Spec.Containers { + if !strings.Contains("infra", container.Name) { + pulledImage, labels, err := ic.getImageAndLabelInfo(ctx, cwd, annotations, writer, container, options) + if err != nil { + return nil, err + } + + specgenOpts := kube.CtrSpecGenOptions{ + Container: container, + Image: pulledImage, + Volumes: volumes, + PodID: pod.ID(), + PodName: podName, + PodInfraID: podInfraID, + ConfigMaps: configMaps, + SeccompPaths: seccompPaths, + RestartPolicy: ctrRestartPolicy, + NetNSIsHost: p.NetNS.IsHost(), + SecretsManager: secretsManager, + LogDriver: options.LogDriver, + Labels: labels, + } + specGen, err := kube.ToSpecGen(ctx, &specgenOpts) + if err != nil { + return nil, err + } + rtSpec, spec, opts, err := generate.MakeContainer(ctx, ic.Libpod, specGen) + if err != nil { + return nil, err + } + ctr, err := generate.ExecuteCreate(ctx, ic.Libpod, rtSpec, spec, false, opts...) + if err != nil { + return nil, err + } + containers = append(containers, ctr) + } } if options.Start != types.OptionalBoolFalse { @@ -347,6 +413,7 @@ } for id, err := range podStartErrors { playKubePod.ContainerErrors = append(playKubePod.ContainerErrors, errors.Wrapf(err, "error starting container %s", id).Error()) + fmt.Println(playKubePod.ContainerErrors) } } @@ -354,12 +421,97 @@ for _, ctr := range containers { playKubePod.Containers = append(playKubePod.Containers, ctr.ID()) } + for _, initCtr := range initContainers { + playKubePod.InitContainers = append(playKubePod.InitContainers, initCtr.ID()) + } report.Pods = append(report.Pods, playKubePod) return &report, nil } +// getImageAndLabelInfo returns the image information and how the image should be pulled plus as well as labels to be used for the container in the pod. +// Moved this to a separate function so that it can be used for both init and regular containers when playing a kube yaml. +func (ic *ContainerEngine) getImageAndLabelInfo(ctx context.Context, cwd string, annotations map[string]string, writer io.Writer, container v1.Container, options entities.PlayKubeOptions) (*libimage.Image, map[string]string, error) { + // Contains all labels obtained from kube + labels := make(map[string]string) + var pulledImage *libimage.Image + buildFile, err := getBuildFile(container.Image, cwd) + if err != nil { + return nil, nil, err + } + existsLocally, err := ic.Libpod.LibimageRuntime().Exists(container.Image) + if err != nil { + return nil, nil, err + } + if (len(buildFile) > 0 && !existsLocally) || (len(buildFile) > 0 && options.Build) { + buildOpts := new(buildahDefine.BuildOptions) + commonOpts := new(buildahDefine.CommonBuildOptions) + buildOpts.ConfigureNetwork = buildahDefine.NetworkDefault + buildOpts.Isolation = buildahDefine.IsolationChroot + buildOpts.CommonBuildOpts = commonOpts + buildOpts.Output = container.Image + buildOpts.ContextDirectory = filepath.Dir(buildFile) + if _, _, err := ic.Libpod.Build(ctx, *buildOpts, []string{buildFile}...); err != nil { + return nil, nil, err + } + i, _, err := ic.Libpod.LibimageRuntime().LookupImage(container.Image, new(libimage.LookupImageOptions)) + if err != nil { + return nil, nil, err + } + pulledImage = i + } else { + // NOTE: set the pull policy to "newer". This will cover cases + // where the "latest" tag requires a pull and will also + // transparently handle "localhost/" prefixed files which *may* + // refer to a locally built image OR an image running a + // registry on localhost. + pullPolicy := config.PullPolicyNewer + if len(container.ImagePullPolicy) > 0 { + // Make sure to lower the strings since K8s pull policy + // may be capitalized (see bugzilla.redhat.com/show_bug.cgi?id=1985905). + rawPolicy := string(container.ImagePullPolicy) + pullPolicy, err = config.ParsePullPolicy(strings.ToLower(rawPolicy)) + if err != nil { + return nil, nil, err + } + } + // This ensures the image is the image store + pullOptions := &libimage.PullOptions{} + pullOptions.AuthFilePath = options.Authfile + pullOptions.CertDirPath = options.CertDir + pullOptions.SignaturePolicyPath = options.SignaturePolicy + pullOptions.Writer = writer + pullOptions.Username = options.Username + pullOptions.Password = options.Password + pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify + + pulledImages, err := ic.Libpod.LibimageRuntime().Pull(ctx, container.Image, pullPolicy, pullOptions) + if err != nil { + return nil, nil, err + } + pulledImage = pulledImages[0] + } + + // Handle kube annotations + for k, v := range annotations { + switch k { + // Auto update annotation without container name will apply to + // all containers within the pod + case autoupdate.Label, autoupdate.AuthfileLabel: + labels[k] = v + // Auto update annotation with container name will apply only + // to the specified container + case fmt.Sprintf("%s/%s", autoupdate.Label, container.Name), + fmt.Sprintf("%s/%s", autoupdate.AuthfileLabel, container.Name): + prefixAndCtr := strings.Split(k, "/") + labels[prefixAndCtr[0]] = v + } + } + + return pulledImage, labels, nil +} + // playKubePVC creates a podman volume from a kube persistent volume claim. func (ic *ContainerEngine) playKubePVC(ctx context.Context, pvcYAML *v1.PersistentVolumeClaim, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { var report entities.PlayKubeReport @@ -505,3 +657,136 @@ return sortedDocumentList, nil } +func imageNamePrefix(imageName string) string { + prefix := imageName + s := strings.Split(prefix, ":") + if len(s) > 0 { + prefix = s[0] + } + s = strings.Split(prefix, "/") + if len(s) > 0 { + prefix = s[len(s)-1] + } + s = strings.Split(prefix, "@") + if len(s) > 0 { + prefix = s[0] + } + return prefix +} + +func getBuildFile(imageName string, cwd string) (string, error) { + buildDirName := imageNamePrefix(imageName) + containerfilePath := filepath.Join(cwd, buildDirName, "Containerfile") + dockerfilePath := filepath.Join(cwd, buildDirName, "Dockerfile") + + _, err := os.Stat(filepath.Join(containerfilePath)) + if err == nil { + logrus.Debugf("building %s with %s", imageName, containerfilePath) + return containerfilePath, nil + } + // If the error is not because the file does not exist, take + // a mulligan and try Dockerfile. If that also fails, return that + // error + if err != nil && !os.IsNotExist(err) { + logrus.Errorf("%v: unable to check for %s", err, containerfilePath) + } + + _, err = os.Stat(filepath.Join(dockerfilePath)) + if err == nil { + logrus.Debugf("building %s with %s", imageName, dockerfilePath) + return dockerfilePath, nil + } + // Strike two + if os.IsNotExist(err) { + return "", nil + } + return "", err +} + +func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { + var ( + podNames []string + ) + reports := new(entities.PlayKubeReport) + + // read yaml document + content, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + // split yaml document + documentList, err := splitMultiDocYAML(content) + if err != nil { + return nil, err + } + + // sort kube kinds + documentList, err = sortKubeKinds(documentList) + if err != nil { + return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path) + } + + for _, document := range documentList { + kind, err := getKubeKind(document) + if err != nil { + return nil, errors.Wrapf(err, "unable to read %q as kube YAML", path) + } + + switch kind { + case "Pod": + var podYAML v1.Pod + if err := yaml.Unmarshal(document, &podYAML); err != nil { + return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Pod", path) + } + podNames = append(podNames, podYAML.ObjectMeta.Name) + case "Deployment": + var deploymentYAML v1apps.Deployment + + if err := yaml.Unmarshal(document, &deploymentYAML); err != nil { + return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path) + } + var numReplicas int32 = 1 + deploymentName := deploymentYAML.ObjectMeta.Name + if deploymentYAML.Spec.Replicas != nil { + numReplicas = *deploymentYAML.Spec.Replicas + } + for i := 0; i < int(numReplicas); i++ { + podName := fmt.Sprintf("%s-pod-%d", deploymentName, i) + podNames = append(podNames, podName) + } + default: + continue + } + } + + // Add the reports + reports.StopReport, err = ic.PodStop(ctx, podNames, entities.PodStopOptions{}) + if err != nil { + return nil, err + } + + reports.RmReport, err = ic.PodRm(ctx, podNames, entities.PodRmOptions{}) + if err != nil { + return nil, err + } + return reports, nil +} + +// pullImage is a helper function to set up the proper pull options and pull the image for certain containers +func pullImage(ic *ContainerEngine, writer io.Writer, imagePull string, options entities.PlayKubeOptions, pullPolicy config.PullPolicy) ([]*libimage.Image, error) { + // This ensures the image is the image store + pullOptions := &libimage.PullOptions{} + pullOptions.AuthFilePath = options.Authfile + pullOptions.CertDirPath = options.CertDir + pullOptions.SignaturePolicyPath = options.SignaturePolicy + pullOptions.Writer = writer + pullOptions.Username = options.Username + pullOptions.Password = options.Password + pullOptions.InsecureSkipTLSVerify = options.SkipTLSVerify + pulledImages, err := ic.Libpod.LibimageRuntime().Pull(context.Background(), imagePull, pullPolicy, pullOptions) + if err != nil { + return nil, err + } + return pulledImages, nil +} diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/pods.go libpod-3.4.4+ds1/pkg/domain/infra/abi/pods.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,7 +8,6 @@ "github.com/containers/podman/v3/pkg/domain/entities" dfilters "github.com/containers/podman/v3/pkg/domain/filters" "github.com/containers/podman/v3/pkg/signal" - "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -84,6 +83,46 @@ return reports, nil } +func (ic *ContainerEngine) PodLogs(ctx context.Context, nameOrID string, options entities.PodLogsOptions) error { + // Implementation accepts slice + podName := []string{nameOrID} + pod, err := getPodsByContext(false, options.Latest, podName, ic.Libpod) + if err != nil { + return err + } + // Get pod containers + podCtrs, err := pod[0].AllContainers() + if err != nil { + return err + } + + ctrNames := []string{} + // Check if `kubectl pod logs -c ctrname ` alike command is used + if options.ContainerName != "" { + ctrFound := false + for _, ctr := range podCtrs { + if ctr.ID() == options.ContainerName || ctr.Name() == options.ContainerName { + ctrNames = append(ctrNames, options.ContainerName) + ctrFound = true + } + } + if !ctrFound { + return errors.Wrapf(define.ErrNoSuchCtr, "container %s is not in pod %s", options.ContainerName, nameOrID) + } + } else { + // No container name specified select all containers + for _, ctr := range podCtrs { + ctrNames = append(ctrNames, ctr.Name()) + } + } + + // PodLogsOptions are similar but contains few extra fields like ctrName + // So cast other values as is so we can re-use the code + containerLogsOpts := entities.PodLogsOptionsToContainerLogsOptions(options) + + return ic.ContainerLogs(ctx, ctrNames, containerLogsOpts) +} + func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { reports := []*entities.PodPauseReport{} pods, err := getPodsByContext(options.All, options.Latest, namesOrIds, ic.Libpod) @@ -248,10 +287,8 @@ return reports, nil } -func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { - podSpec := specgen.NewPodSpecGenerator() - opts.ToPodSpecGen(podSpec) - pod, err := generate.MakePod(podSpec, ic.Libpod) +func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec) (*entities.PodCreateReport, error) { + pod, err := generate.MakePod(&specg, ic.Libpod) if err != nil { return nil, err } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/secrets.go libpod-3.4.4+ds1/pkg/domain/infra/abi/secrets.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,27 +6,42 @@ "io/ioutil" "path/filepath" - "github.com/containers/common/pkg/secrets" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/containers/podman/v3/pkg/domain/utils" "github.com/pkg/errors" ) func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader io.Reader, options entities.SecretCreateOptions) (*entities.SecretCreateReport, error) { data, _ := ioutil.ReadAll(reader) secretsPath := ic.Libpod.GetSecretsStorageDir() - manager, err := secrets.NewManager(secretsPath) + manager, err := ic.Libpod.SecretsManager() if err != nil { return nil, err } - driverOptions := make(map[string]string) + // set defaults from config for the case they are not set by an upper layer + // (-> i.e. tests that talk directly to the api) + cfg, err := ic.Libpod.GetConfig() + if err != nil { + return nil, err + } if options.Driver == "" { - options.Driver = "file" + options.Driver = cfg.Secrets.Driver + } + if len(options.DriverOpts) == 0 { + options.DriverOpts = cfg.Secrets.Opts } + if options.DriverOpts == nil { + options.DriverOpts = make(map[string]string) + } + if options.Driver == "file" { - driverOptions["path"] = filepath.Join(secretsPath, "filedriver") + if _, ok := options.DriverOpts["path"]; !ok { + options.DriverOpts["path"] = filepath.Join(secretsPath, "filedriver") + } } - secretID, err := manager.Store(name, data, options.Driver, driverOptions) + + secretID, err := manager.Store(name, data, options.Driver, options.DriverOpts) if err != nil { return nil, err } @@ -36,8 +51,7 @@ } func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string) ([]*entities.SecretInfoReport, []error, error) { - secretsPath := ic.Libpod.GetSecretsStorageDir() - manager, err := secrets.NewManager(secretsPath) + manager, err := ic.Libpod.SecretsManager() if err != nil { return nil, nil, err } @@ -60,7 +74,8 @@ Spec: entities.SecretSpec{ Name: secret.Name, Driver: entities.SecretDriverSpec{ - Name: secret.Driver, + Name: secret.Driver, + Options: secret.DriverOptions, }, }, } @@ -70,9 +85,8 @@ return reports, errs, nil } -func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretInfoReport, error) { - secretsPath := ic.Libpod.GetSecretsStorageDir() - manager, err := secrets.NewManager(secretsPath) +func (ic *ContainerEngine) SecretList(ctx context.Context, opts entities.SecretListRequest) ([]*entities.SecretInfoReport, error) { + manager, err := ic.Libpod.SecretsManager() if err != nil { return nil, err } @@ -82,19 +96,25 @@ } report := make([]*entities.SecretInfoReport, 0, len(secretList)) for _, secret := range secretList { - reportItem := entities.SecretInfoReport{ - ID: secret.ID, - CreatedAt: secret.CreatedAt, - UpdatedAt: secret.CreatedAt, - Spec: entities.SecretSpec{ - Name: secret.Name, - Driver: entities.SecretDriverSpec{ - Name: secret.Driver, - Options: secret.DriverOptions, + result, err := utils.IfPassesSecretsFilter(secret, opts.Filters) + if err != nil { + return nil, err + } + if result { + reportItem := entities.SecretInfoReport{ + ID: secret.ID, + CreatedAt: secret.CreatedAt, + UpdatedAt: secret.CreatedAt, + Spec: entities.SecretSpec{ + Name: secret.Name, + Driver: entities.SecretDriverSpec{ + Name: secret.Driver, + Options: secret.DriverOptions, + }, }, - }, + } + report = append(report, &reportItem) } - report = append(report, &reportItem) } return report, nil } @@ -105,8 +125,7 @@ toRemove []string reports = []*entities.SecretRmReport{} ) - secretsPath := ic.Libpod.GetSecretsStorageDir() - manager, err := secrets.NewManager(secretsPath) + manager, err := ic.Libpod.SecretsManager() if err != nil { return nil, err } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/system.go libpod-3.4.4+ds1/pkg/domain/infra/abi/system.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,16 +3,12 @@ import ( "context" "fmt" - "io/ioutil" "net/url" "os" "os/exec" "path/filepath" - "strconv" - "strings" "github.com/containers/common/pkg/config" - "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/domain/entities" @@ -24,7 +20,6 @@ "github.com/containers/storage/pkg/unshare" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/spf13/cobra" "github.com/spf13/pflag" ) @@ -57,7 +52,7 @@ return info, err } -func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { +func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) error { // do it only after podman has already re-execed and running with uid==0. hasCapSysAdmin, err := unshare.HasCapSysAdmin() if err != nil { @@ -73,11 +68,7 @@ if err != nil { return err } - - initCommand, err := ioutil.ReadFile("/proc/1/comm") - // On errors, default to systemd - runsUnderSystemd := err != nil || strings.TrimRight(string(initCommand), "\n") == "systemd" - + runsUnderSystemd := utils.RunsOnSystemd() unitName := fmt.Sprintf("podman-%d.scope", os.Getpid()) if runsUnderSystemd || conf.Engine.CgroupManager == config.SystemdCgroupsManager { if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil { @@ -104,6 +95,9 @@ if became { os.Exit(ret) } + if noMoveProcess { + return nil + } // if there is no pid file, try to join existing containers, and create a pause process. ctrs, err := ic.Libpod.GetRunningContainers() @@ -118,17 +112,7 @@ } became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths) - if err := movePauseProcessToScope(ic.Libpod); err != nil { - conf, err := ic.Config(context.Background()) - if err != nil { - return err - } - if conf.Engine.CgroupManager == config.SystemdCgroupsManager { - logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err) - } else { - logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err) - } - } + utils.MovePauseProcessToScope(pausePidPath) if err != nil { logrus.Error(errors.Wrapf(err, "invalid internal status, try resetting the pause process with %q", os.Args[0]+" system migrate")) os.Exit(1) @@ -139,28 +123,6 @@ return nil } -func movePauseProcessToScope(r *libpod.Runtime) error { - tmpDir, err := r.TmpDir() - if err != nil { - return err - } - pausePidPath, err := util.GetRootlessPauseProcessPidPathGivenDir(tmpDir) - if err != nil { - return errors.Wrapf(err, "could not get pause process pid file path") - } - - data, err := ioutil.ReadFile(pausePidPath) - if err != nil { - return errors.Wrapf(err, "cannot read pause pid file") - } - pid, err := strconv.ParseUint(string(data), 10, 0) - if err != nil { - return errors.Wrapf(err, "cannot parse pid file %s", pausePidPath) - } - - return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope") -} - // SystemPrune removes unused data from the system. Pruning pods, containers, volumes and images. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { var systemPruneReport = new(entities.SystemPruneReport) @@ -403,6 +365,8 @@ if err != nil { return err } + // make sure to unlock, unshare can run for a long time + rootlesscni.Lock.Unlock() defer rootlesscni.Cleanup(ic.Libpod) return rootlesscni.Do(unshare) } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/terminal/sigproxy_linux.go libpod-3.4.4+ds1/pkg/domain/infra/abi/terminal/sigproxy_linux.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/terminal/sigproxy_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/terminal/sigproxy_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,13 +12,17 @@ "github.com/sirupsen/logrus" ) +// Make sure the signal buffer is sufficiently big. +// runc is using the same value. +const signalBufferSize = 2048 + // ProxySignals ... func ProxySignals(ctr *libpod.Container) { // Stop catching the shutdown signals (SIGINT, SIGTERM) - they're going // to the container now. shutdown.Stop() - sigBuffer := make(chan os.Signal, 128) + sigBuffer := make(chan os.Signal, signalBufferSize) signal.CatchAll(sigBuffer) logrus.Debugf("Enabling signal proxying") diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/terminal/terminal_linux.go libpod-3.4.4+ds1/pkg/domain/infra/abi/terminal/terminal_linux.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/terminal/terminal_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/terminal/terminal_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,12 +15,13 @@ // ExecAttachCtr execs and attaches to a container func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) { - resize := make(chan define.TerminalSize) + var resize chan define.TerminalSize haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) // Check if we are attached to a terminal. If we are, generate resize // events, and set the terminal to raw mode if haveTerminal && execConfig.Terminal { + resize = make(chan define.TerminalSize) cancel, oldTermState, err := handleTerminalAttach(ctx, resize) if err != nil { return -1, err @@ -32,7 +33,6 @@ } }() } - return ctr.Exec(execConfig, streams, resize) } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/abi/volumes.go libpod-3.4.4+ds1/pkg/domain/infra/abi/volumes.go --- libpod-3.2.1+ds1/pkg/domain/infra/abi/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/abi/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -162,3 +162,19 @@ } return &entities.BoolReport{Value: exists}, nil } + +// Volumemounted check if a given volume using plugin or filesystem is mounted or not. +func (ic *ContainerEngine) VolumeMounted(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + vol, err := ic.Libpod.LookupVolume(nameOrID) + if err != nil { + return nil, err + } + mountCount, err := vol.MountCount() + if err != nil { + return &entities.BoolReport{Value: false}, nil + } + if mountCount > 0 { + return &entities.BoolReport{Value: true}, nil + } + return &entities.BoolReport{Value: false}, nil +} diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/runtime_abi.go libpod-3.4.4+ds1/pkg/domain/infra/runtime_abi.go --- libpod-3.2.1+ds1/pkg/domain/infra/runtime_abi.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/runtime_abi.go 2021-12-26 00:45:51.000000000 +0000 @@ -33,6 +33,7 @@ r, err := NewLibpodImageRuntime(facts.FlagSet, facts) return r, err case entities.TunnelMode: + // TODO: look at me! ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity) return &tunnel.ImageEngine{ClientCtx: ctx}, err } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/runtime_libpod.go libpod-3.4.4+ds1/pkg/domain/infra/runtime_libpod.go --- libpod-3.2.1+ds1/pkg/domain/infra/runtime_libpod.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/runtime_libpod.go 2021-12-26 00:45:51.000000000 +0000 @@ -129,6 +129,7 @@ if fs.Changed("root") { storageSet = true storageOpts.GraphRoot = cfg.Engine.StaticDir + storageOpts.GraphDriverOptions = []string{} } if fs.Changed("runroot") { storageSet = true diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/auto-update.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/auto-update.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/auto-update.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/auto-update.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,6 @@ "github.com/pkg/errors" ) -func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) (*entities.AutoUpdateReport, []error) { +func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) ([]*entities.AutoUpdateReport, []error) { return nil, []error{errors.New("not implemented")} } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/containers.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/containers.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -63,19 +63,27 @@ reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := containers.Pause(ic.ClientCtx, c.ID, nil) + if err != nil && options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + logrus.Debugf("Container %s is not running", c.ID) + continue + } reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) } return reports, nil } func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []string, options entities.PauseUnPauseOptions) ([]*entities.PauseUnpauseReport, error) { + reports := []*entities.PauseUnpauseReport{} ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, namesOrIds) if err != nil { return nil, err } - reports := make([]*entities.PauseUnpauseReport, 0, len(ctrs)) for _, c := range ctrs { err := containers.Unpause(ic.ClientCtx, c.ID, nil) + if err != nil && options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + logrus.Debugf("Container %s is not paused", c.ID) + continue + } reports = append(reports, &entities.PauseUnpauseReport{Id: c.ID, Err: err}) } return reports, nil @@ -136,9 +144,14 @@ options := new(containers.KillOptions).WithSignal(opts.Signal) reports := make([]*entities.KillReport, 0, len(ctrs)) for _, c := range ctrs { + err := containers.Kill(ic.ClientCtx, c.ID, options) + if err != nil && opts.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + logrus.Debugf("Container %s is not running", c.ID) + continue + } reports = append(reports, &entities.KillReport{ Id: c.ID, - Err: containers.Kill(ic.ClientCtx, c.ID, options), + Err: err, RawInput: ctrMap[c.ID], }) } @@ -213,7 +226,7 @@ for _, name := range namesOrIds { inspect, err := containers.Inspect(ic.ClientCtx, name, options) if err != nil { - errModel, ok := err.(errorhandling.ErrorModel) + errModel, ok := err.(*errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -369,10 +382,11 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, opts entities.ContainerLogsOptions) error { since := opts.Since.Format(time.RFC3339) + until := opts.Until.Format(time.RFC3339) tail := strconv.FormatInt(opts.Tail, 10) stdout := opts.StdoutWriter != nil stderr := opts.StderrWriter != nil - options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithStderr(stderr) + options := new(containers.LogOptions).WithFollow(opts.Follow).WithSince(since).WithUntil(until).WithStderr(stderr) options.WithStdout(stdout).WithTail(tail) var err error @@ -508,7 +522,9 @@ reports := []*entities.ContainerStartReport{} var exitCode = define.ExecErrorCodeGeneric containersNamesOrIds := namesOrIds + all := options.All if len(options.Filters) > 0 { + all = false containersNamesOrIds = []string{} opts := new(containers.ListOptions).WithFilters(options.Filters).WithAll(true) candidates, listErr := containers.List(ic.ClientCtx, opts) @@ -516,6 +532,10 @@ return nil, listErr } for _, candidate := range candidates { + if options.All { + containersNamesOrIds = append(containersNamesOrIds, candidate.ID) + continue + } for _, nameOrID := range namesOrIds { if nameOrID == candidate.ID { containersNamesOrIds = append(containersNamesOrIds, nameOrID) @@ -530,11 +550,22 @@ } } } - ctrs, err := getContainersByContext(ic.ClientCtx, options.All, false, containersNamesOrIds) + ctrs, err := getContainersByContext(ic.ClientCtx, all, false, containersNamesOrIds) if err != nil { return nil, err } removeOptions := new(containers.RemoveOptions).WithVolumes(true).WithForce(false) + removeContainer := func(id string) { + if err := containers.Remove(ic.ClientCtx, id, removeOptions); err != nil { + if errorhandling.Contains(err, define.ErrNoSuchCtr) || + errorhandling.Contains(err, define.ErrCtrRemoved) { + logrus.Debugf("Container %s does not exist: %v", id, err) + } else { + logrus.Errorf("Error removing container %s: %v", id, err) + } + } + } + // There can only be one container if attach was used for i, ctr := range ctrs { name := ctr.ID @@ -562,6 +593,9 @@ } if err != nil { + if ctr.AutoRemove { + removeContainer(ctr.ID) + } report.ExitCode = define.ExitCode(report.Err) report.Err = err reports = append(reports, &report) @@ -576,16 +610,10 @@ logrus.Errorf("Failed to check if %s should restart: %v", ctr.ID, err) return } + logrus.Errorf("Should restart: %v", shouldRestart) - if !shouldRestart { - if err := containers.Remove(ic.ClientCtx, ctr.ID, removeOptions); err != nil { - if errorhandling.Contains(err, define.ErrNoSuchCtr) || - errorhandling.Contains(err, define.ErrCtrRemoved) { - logrus.Debugf("Container %s does not exist: %v", ctr.ID, err) - } else { - logrus.Errorf("Error removing container %s: %v", ctr.ID, err) - } - } + if !shouldRestart && ctr.AutoRemove { + removeContainer(ctr.ID) } }() } @@ -640,6 +668,12 @@ return containers.List(ic.ClientCtx, options) } +func (ic *ContainerEngine) ContainerListExternal(ctx context.Context) ([]entities.ListContainer, error) { + options := new(containers.ListOptions).WithAll(true) + options.WithNamespace(true).WithSize(true).WithSync(true).WithExternal(true) + return containers.List(ic.ClientCtx, options) +} + func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { con, err := containers.CreateWithSpec(ic.ClientCtx, opts.Spec, nil) if err != nil { @@ -753,8 +787,18 @@ return &report, err } -func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := containers.Diff(ic.ClientCtx, nameOrID, nil) +func (ic *ContainerEngine) Diff(ctx context.Context, namesOrIDs []string, opts entities.DiffOptions) (*entities.DiffReport, error) { + var base string + options := new(containers.DiffOptions).WithDiffType(opts.Type.String()) + if len(namesOrIDs) > 0 { + base = namesOrIDs[0] + if len(namesOrIDs) > 1 { + options.WithParent(namesOrIDs[1]) + } + } else { + return nil, errors.New("no arguments for diff") + } + changes, err := containers.Diff(ic.ClientCtx, base, options) return &entities.DiffReport{Changes: changes}, err } @@ -821,8 +865,9 @@ return reports, nil } -func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID string, path string, reader io.Reader) (entities.ContainerCopyFunc, error) { - return containers.CopyFromArchive(ic.ClientCtx, nameOrID, path, reader) +func (ic *ContainerEngine) ContainerCopyFromArchive(ctx context.Context, nameOrID, path string, reader io.Reader, options entities.CopyOptions) (entities.ContainerCopyFunc, error) { + copyOptions := new(containers.CopyOptions).WithChown(options.Chown).WithRename(options.Rename) + return containers.CopyFromArchiveWithOptions(ic.ClientCtx, nameOrID, path, reader, copyOptions) } func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID string, path string, writer io.Writer) (entities.ContainerCopyFunc, error) { @@ -841,7 +886,7 @@ if options.Latest { return nil, errors.New("latest is not supported for the remote client") } - return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream)) + return containers.Stats(ic.ClientCtx, namesOrIds, new(containers.StatsOptions).WithStream(options.Stream).WithInterval(options.Interval)) } // ShouldRestart reports back whether the container will restart diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/events.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/events.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/events.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/events.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/bindings/system" "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/pkg/errors" ) diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/generate.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/generate.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/generate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/generate.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,13 +9,19 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, opts entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) { options := new(generate.SystemdOptions).WithUseName(opts.Name).WithContainerPrefix(opts.ContainerPrefix).WithNew(opts.New).WithNoHeader(opts.NoHeader) - options.WithPodPrefix(opts.PodPrefix).WithRestartPolicy(opts.RestartPolicy).WithSeparator(opts.Separator) + options.WithPodPrefix(opts.PodPrefix).WithSeparator(opts.Separator) + if opts.RestartPolicy != nil { + options.WithRestartPolicy(*opts.RestartPolicy) + } if to := opts.StopTimeout; to != nil { options.WithStopTimeout(*opts.StopTimeout) } return generate.Systemd(ic.ClientCtx, nameOrID, options) } +// GenerateKube Kubernetes YAML (v1 specification) for nameOrIDs +// +// Note: Caller is responsible for closing returned Reader func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string, opts entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { options := new(generate.KubeOptions).WithService(opts.Service) return generate.Kube(ic.ClientCtx, nameOrIDs, options) diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/images.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/images.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/images.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/images.go 2021-12-26 00:45:51.000000000 +0000 @@ -107,7 +107,7 @@ options := new(images.PullOptions) options.WithAllTags(opts.AllTags).WithAuthfile(opts.Authfile).WithArch(opts.Arch).WithOS(opts.OS) options.WithVariant(opts.Variant).WithPassword(opts.Password) - options.WithQuiet(opts.Quiet).WithUsername(opts.Username) + options.WithQuiet(opts.Quiet).WithUsername(opts.Username).WithPolicy(opts.PullPolicy.String()) if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined { if s == types.OptionalBoolTrue { options.WithSkipTLSVerify(true) @@ -165,6 +165,9 @@ if t, ok := ref.(reference.Tagged); ok { tag = t.Tag() } + if t, ok := ref.(reference.Digested); ok { + tag += "@" + t.Digest().String() + } if r, ok := ref.(reference.Named); ok { repo = r.Name() } @@ -185,7 +188,7 @@ for _, i := range namesOrIDs { r, err := images.GetImage(ir.ClientCtx, i, options) if err != nil { - errModel, ok := err.(errorhandling.ErrorModel) + errModel, ok := err.(*errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -261,7 +264,10 @@ defer func() { _ = os.Remove(f.Name()) }() } default: - f, err = os.Create(opts.Output) + // This code was added to allow for opening stdout replacing + // os.Create(opts.Output) which was attempting to open the file + // for read/write which fails on Darwin platforms + f, err = os.OpenFile(opts.Output, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) } if err != nil { return err @@ -299,16 +305,6 @@ return utils2.UntarToFileSystem(opts.Output, f, nil) } -// Diff reports the changes to the given image -func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { - options := new(images.DiffOptions) - changes, err := images.Diff(ir.ClientCtx, nameOrID, options) - if err != nil { - return nil, err - } - return &entities.DiffReport{Changes: changes}, nil -} - func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { mappedFilters := make(map[string][]string) filters, err := libimage.ParseSearchFilter(opts.Filters) diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/manifest.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/manifest.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/manifest.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/manifest.go 2021-12-26 00:45:51.000000000 +0000 @@ -83,6 +83,11 @@ return fmt.Sprintf("%s :%s\n", updatedListID, names[1]), nil } +// ManifestRm removes the specified manifest list from storage +func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (*entities.ImageRemoveReport, []error) { + return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true}) +} + // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, name, destination string, opts entities.ImagePushOptions) (string, error) { options := new(images.PushOptions) diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/network.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/network.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/network.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/network.go 2021-12-26 00:45:51.000000000 +0000 @@ -23,7 +23,7 @@ for _, name := range namesOrIds { report, err := network.Inspect(ic.ClientCtx, name, options) if err != nil { - errModel, ok := err.(errorhandling.ErrorModel) + errModel, ok := err.(*errorhandling.ErrorModel) if !ok { return nil, nil, err } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/play.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/play.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/play.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/play.go 2021-12-26 00:45:51.000000000 +0000 @@ -22,3 +22,7 @@ } return play.Kube(ic.ClientCtx, path, options) } + +func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ entities.PlayKubeDownOptions) (*entities.PlayKubeReport, error) { + return play.KubeDown(ic.ClientCtx, path) +} diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/pods.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/pods.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,7 +6,6 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/bindings/pods" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/containers/podman/v3/pkg/specgen" "github.com/containers/podman/v3/pkg/util" "github.com/pkg/errors" ) @@ -43,6 +42,16 @@ return reports, nil } +func (ic *ContainerEngine) PodLogs(_ context.Context, nameOrIDs string, options entities.PodLogsOptions) error { + // PodLogsOptions are similar but contains few extra fields like ctrName + // So cast other values as is so we can re-use the code + containerLogsOpts := entities.PodLogsOptionsToContainerLogsOptions(options) + + // interface only accepts slice, keep everything consistent + name := []string{options.ContainerName} + return ic.ContainerLogs(nil, name, containerLogsOpts) +} + func (ic *ContainerEngine) PodPause(ctx context.Context, namesOrIds []string, options entities.PodPauseOptions) ([]*entities.PodPauseReport, error) { foundPods, err := getPodsByContext(ic.ClientCtx, options.All, namesOrIds) if err != nil { @@ -179,10 +188,8 @@ return pods.Prune(ic.ClientCtx, nil) } -func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { - podSpec := specgen.NewPodSpecGenerator() - opts.ToPodSpecGen(podSpec) - return pods.CreatePodFromSpec(ic.ClientCtx, podSpec, nil) +func (ic *ContainerEngine) PodCreate(ctx context.Context, specg entities.PodSpec) (*entities.PodCreateReport, error) { + return pods.CreatePodFromSpec(ic.ClientCtx, &specg) } func (ic *ContainerEngine) PodTop(ctx context.Context, opts entities.PodTopOptions) (*entities.StringSliceReport, error) { diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/secrets.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/secrets.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/secrets.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/secrets.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,8 +11,14 @@ ) func (ic *ContainerEngine) SecretCreate(ctx context.Context, name string, reader io.Reader, options entities.SecretCreateOptions) (*entities.SecretCreateReport, error) { - opts := new(secrets.CreateOptions).WithDriver(options.Driver).WithName(name) - created, _ := secrets.Create(ic.ClientCtx, reader, opts) + opts := new(secrets.CreateOptions). + WithDriver(options.Driver). + WithDriverOpts(options.DriverOpts). + WithName(name) + created, err := secrets.Create(ic.ClientCtx, reader, opts) + if err != nil { + return nil, err + } return created, nil } @@ -22,7 +28,7 @@ for _, name := range nameOrIDs { inspected, err := secrets.Inspect(ic.ClientCtx, name, nil) if err != nil { - errModel, ok := err.(errorhandling.ErrorModel) + errModel, ok := err.(*errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -37,8 +43,9 @@ return allInspect, errs, nil } -func (ic *ContainerEngine) SecretList(ctx context.Context) ([]*entities.SecretInfoReport, error) { - secrs, _ := secrets.List(ic.ClientCtx, nil) +func (ic *ContainerEngine) SecretList(ctx context.Context, opts entities.SecretListRequest) ([]*entities.SecretInfoReport, error) { + options := new(secrets.ListOptions).WithFilters(opts.Filters) + secrs, _ := secrets.List(ic.ClientCtx, options) return secrs, nil } @@ -60,7 +67,7 @@ for _, name := range nameOrIDs { secret, err := secrets.Inspect(ic.ClientCtx, name, nil) if err != nil { - errModel, ok := err.(errorhandling.ErrorModel) + errModel, ok := err.(*errorhandling.ErrorModel) if !ok { return nil, err } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/system.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/system.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/system.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/system.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,14 +7,13 @@ "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/bindings/system" "github.com/containers/podman/v3/pkg/domain/entities" - "github.com/spf13/cobra" ) func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { return system.Info(ic.ClientCtx, nil) } -func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { +func (ic *ContainerEngine) SetupRootless(_ context.Context, noMoveProcess bool) error { panic(errors.New("rootless engine mode is not supported when tunneling")) } diff -Nru libpod-3.2.1+ds1/pkg/domain/infra/tunnel/volumes.go libpod-3.4.4+ds1/pkg/domain/infra/tunnel/volumes.go --- libpod-3.2.1+ds1/pkg/domain/infra/tunnel/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/infra/tunnel/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -56,7 +56,7 @@ for _, id := range namesOrIds { data, err := volumes.Inspect(ic.ClientCtx, id, nil) if err != nil { - errModel, ok := err.(errorhandling.ErrorModel) + errModel, ok := err.(*errorhandling.ErrorModel) if !ok { return nil, nil, err } @@ -91,3 +91,9 @@ Value: exists, }, nil } + +// Volumemounted check if a given volume using plugin or filesystem is mounted or not. +// TODO: Not used and exposed to tunnel. Will be used by `export` command which is unavailable to `podman-remote` +func (ic *ContainerEngine) VolumeMounted(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + return nil, errors.New("not implemented") +} diff -Nru libpod-3.2.1+ds1/pkg/domain/utils/secrets_filters.go libpod-3.4.4+ds1/pkg/domain/utils/secrets_filters.go --- libpod-3.2.1+ds1/pkg/domain/utils/secrets_filters.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/domain/utils/secrets_filters.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,24 @@ +package utils + +import ( + "strings" + + "github.com/containers/common/pkg/secrets" + "github.com/containers/podman/v3/pkg/util" + "github.com/pkg/errors" +) + +func IfPassesSecretsFilter(s secrets.Secret, filters map[string][]string) (bool, error) { + result := true + for key, filterValues := range filters { + switch strings.ToLower(key) { + case "name": + result = util.StringMatchRegexSlice(s.Name, filterValues) + case "id": + result = util.StringMatchRegexSlice(s.ID, filterValues) + default: + return false, errors.Errorf("invalid filter %q", key) + } + } + return result, nil +} diff -Nru libpod-3.2.1+ds1/pkg/env/env.go libpod-3.4.4+ds1/pkg/env/env.go --- libpod-3.2.1+ds1/pkg/env/env.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/env/env.go 2021-12-26 00:45:51.000000000 +0000 @@ -17,8 +17,9 @@ // DefaultEnvVariables returns a default environment, with $PATH and $TERM set. func DefaultEnvVariables() map[string]string { return map[string]string{ - "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "TERM": "xterm", + "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM": "xterm", + "container": "podman", } } diff -Nru libpod-3.2.1+ds1/pkg/errorhandling/errorhandling.go libpod-3.4.4+ds1/pkg/errorhandling/errorhandling.go --- libpod-3.2.1+ds1/pkg/errorhandling/errorhandling.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/errorhandling/errorhandling.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,6 +15,12 @@ return nil } + // If there's just one error, return it. This prevents the "%d errors + // occurred:" header plus list from the multierror package. + if len(errs) == 1 { + return errs[0] + } + // `multierror` appends new lines which we need to remove to prevent // blank lines when printing the error. var multiE *multierror.Error @@ -24,9 +30,6 @@ if finalErr == nil { return finalErr } - if len(multiE.WrappedErrors()) == 1 && logrus.IsLevelEnabled(logrus.TraceLevel) { - return multiE.WrappedErrors()[0] - } return errors.New(strings.TrimSpace(finalErr.Error())) } @@ -80,6 +83,12 @@ return strings.Contains(err.Error(), sub.Error()) } +// PodConflictErrorModel is used in remote connections with podman +type PodConflictErrorModel struct { + Errs []string + Id string //nolint +} + // ErrorModel is used in remote connections with podman type ErrorModel struct { // API root cause formatted for automated parsing @@ -103,3 +112,11 @@ func (e ErrorModel) Code() int { return e.ResponseCode } + +func (e PodConflictErrorModel) Error() string { + return strings.Join(e.Errs, ",") +} + +func (e PodConflictErrorModel) Code() int { + return 409 +} diff -Nru libpod-3.2.1+ds1/pkg/hooks/docs/oci-hooks.5.md libpod-3.4.4+ds1/pkg/hooks/docs/oci-hooks.5.md --- libpod-3.2.1+ds1/pkg/hooks/docs/oci-hooks.5.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/hooks/docs/oci-hooks.5.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -% oci-hooks(5) OCI Hooks Configuration +% oci-hooks 5 OCI Hooks Configuration % W. Trevor King % MAY 2018 @@ -179,4 +179,4 @@ * [OCI Runtime Specification, 1.0.1, POSIX-platform hooks](https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks) * [OCI Runtime Specification, 1.0.1, process](https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#process) -* [POSIX extended regular expressions (EREs)](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04) +* [POSIX extended regular expressions (EREs)](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_04) diff -Nru libpod-3.2.1+ds1/pkg/kubeutils/LICENSE libpod-3.4.4+ds1/pkg/kubeutils/LICENSE --- libpod-3.2.1+ds1/pkg/kubeutils/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/kubeutils/LICENSE 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,6 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -192,7 +192,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff -Nru libpod-3.2.1+ds1/pkg/kubeutils/resize.go libpod-3.4.4+ds1/pkg/kubeutils/resize.go --- libpod-3.2.1+ds1/pkg/kubeutils/resize.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/kubeutils/resize.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,7 +5,7 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff -Nru libpod-3.2.1+ds1/pkg/machine/config.go libpod-3.4.4+ds1/pkg/machine/config.go --- libpod-3.2.1+ds1/pkg/machine/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine @@ -34,6 +34,7 @@ ErrVMAlreadyExists = errors.New("VM already exists") ErrVMAlreadyRunning = errors.New("VM already running") ErrMultipleActiveVM = errors.New("only one VM can be active at a time") + ForwarderBinaryName = "gvproxy" ) type Download struct { @@ -57,10 +58,14 @@ LastUp time.Time Running bool VMType string + CPUs uint64 + Memory uint64 + DiskSize uint64 } type SSHOptions struct { - Args []string + Username string + Args []string } type StartOptions struct{} diff -Nru libpod-3.2.1+ds1/pkg/machine/connection.go libpod-3.4.4+ds1/pkg/machine/connection.go --- libpod-3.2.1+ds1/pkg/machine/connection.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/connection.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff -Nru libpod-3.2.1+ds1/pkg/machine/fcos_amd64.go libpod-3.4.4+ds1/pkg/machine/fcos_amd64.go --- libpod-3.2.1+ds1/pkg/machine/fcos_amd64.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/fcos_amd64.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -package machine - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - - "github.com/coreos/stream-metadata-go/fedoracoreos" - "github.com/coreos/stream-metadata-go/stream" - "github.com/sirupsen/logrus" -) - -// This should get Exported and stay put as it will apply to all fcos downloads -// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version -func getFCOSDownload() (*fcosDownloadInfo, error) { - var ( - fcosstable stream.Stream - ) - streamurl := fedoracoreos.GetStreamURL(fedoracoreos.StreamNext) - resp, err := http.Get(streamurl.String()) - if err != nil { - return nil, err - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - defer func() { - if err := resp.Body.Close(); err != nil { - logrus.Error(err) - } - }() - - if err := json.Unmarshal(body, &fcosstable); err != nil { - return nil, err - } - arch, ok := fcosstable.Architectures[getFcosArch()] - if !ok { - return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") - } - artifacts := arch.Artifacts - if artifacts == nil { - return nil, fmt.Errorf("unable to pull VM image: no artifact in stream") - } - qemu, ok := artifacts[artifact] - if !ok { - return nil, fmt.Errorf("unable to pull VM image: no qemu artifact in stream") - } - formats := qemu.Formats - if formats == nil { - return nil, fmt.Errorf("unable to pull VM image: no formats in stream") - } - qcow, ok := formats[Format] - if !ok { - return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") - } - disk := qcow.Disk - if disk == nil { - return nil, fmt.Errorf("unable to pull VM image: no disk in stream") - } - return &fcosDownloadInfo{ - Location: disk.Location, - Release: qemu.Release, - Sha256Sum: disk.Sha256, - CompressionType: "xz", - }, nil -} diff -Nru libpod-3.2.1+ds1/pkg/machine/fcos_arm64.go libpod-3.4.4+ds1/pkg/machine/fcos_arm64.go --- libpod-3.2.1+ds1/pkg/machine/fcos_arm64.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/fcos_arm64.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,177 +0,0 @@ -package machine - -import ( - "encoding/json" - "io/ioutil" - "net/http" - url2 "net/url" - - "github.com/sirupsen/logrus" -) - -const aarchBaseURL = "https://fedorapeople.org/groups/fcos-images/builds/latest/aarch64/" - -// Total hack until automation is possible. -// We need a proper json file at least to automate -func getFCOSDownload() (*fcosDownloadInfo, error) { - meta := Build{} - resp, err := http.Get(aarchBaseURL + "meta.json") - if err != nil { - return nil, err - } - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - defer func() { - if err := resp.Body.Close(); err != nil { - logrus.Error(err) - } - }() - if err := json.Unmarshal(body, &meta); err != nil { - return nil, err - } - pathURL, err := url2.Parse(meta.BuildArtifacts.Qemu.Path) - if err != nil { - return nil, err - } - - baseURL, err := url2.Parse(aarchBaseURL) - if err != nil { - return nil, err - } - pullURL := baseURL.ResolveReference(pathURL) - return &fcosDownloadInfo{ - Location: pullURL.String(), - Release: "", - Sha256Sum: meta.BuildArtifacts.Qemu.Sha256, - }, nil -} - -/* - All of this can be nuked when fcos upstream generates a proper meta data file for aarch. -*/ -type AliyunImage struct { - ImageID string `json:"id"` - Region string `json:"name"` -} - -type Amis struct { - Hvm string `json:"hvm"` - Region string `json:"name"` - Snapshot string `json:"snapshot"` -} - -type Artifact struct { - Path string `json:"path"` - Sha256 string `json:"sha256"` - SizeInBytes float64 `json:"size,omitempty"` - UncompressedSha256 string `json:"uncompressed-sha256,omitempty"` - UncompressedSize int `json:"uncompressed-size,omitempty"` -} - -type Build struct { - AlibabaAliyunUploads []AliyunImage `json:"aliyun,omitempty"` - Amis []Amis `json:"amis,omitempty"` - Architecture string `json:"coreos-assembler.basearch,omitempty"` - Azure *Cloudartifact `json:"azure,omitempty"` - BuildArtifacts *BuildArtifacts `json:"images,omitempty"` - BuildID string `json:"buildid"` - BuildRef string `json:"ref,omitempty"` - BuildSummary string `json:"summary"` - BuildTimeStamp string `json:"coreos-assembler.build-timestamp,omitempty"` - BuildURL string `json:"build-url,omitempty"` - ConfigGitRev string `json:"coreos-assembler.config-gitrev,omitempty"` - ContainerConfigGit *Git `json:"coreos-assembler.container-config-git,omitempty"` - CoreOsSource string `json:"coreos-assembler.code-source,omitempty"` - CosaContainerImageGit *Git `json:"coreos-assembler.container-image-git,omitempty"` - CosaDelayedMetaMerge bool `json:"coreos-assembler.delayed-meta-merge,omitempty"` - CosaImageChecksum string `json:"coreos-assembler.image-config-checksum,omitempty"` - CosaImageVersion int `json:"coreos-assembler.image-genver,omitempty"` - Extensions *Extensions `json:"extensions,omitempty"` - FedoraCoreOsParentCommit string `json:"fedora-coreos.parent-commit,omitempty"` - FedoraCoreOsParentVersion string `json:"fedora-coreos.parent-version,omitempty"` - Gcp *Gcp `json:"gcp,omitempty"` - GitDirty string `json:"coreos-assembler.config-dirty,omitempty"` - ImageInputChecksum string `json:"coreos-assembler.image-input-checksum,omitempty"` - InputHasOfTheRpmOstree string `json:"rpm-ostree-inputhash"` - MetaStamp float64 `json:"coreos-assembler.meta-stamp,omitempty"` - Name string `json:"name"` - Oscontainer *Image `json:"oscontainer,omitempty"` - OstreeCommit string `json:"ostree-commit"` - OstreeContentBytesWritten int `json:"ostree-content-bytes-written,omitempty"` - OstreeContentChecksum string `json:"ostree-content-checksum"` - OstreeNCacheHits int `json:"ostree-n-cache-hits,omitempty"` - OstreeNContentTotal int `json:"ostree-n-content-total,omitempty"` - OstreeNContentWritten int `json:"ostree-n-content-written,omitempty"` - OstreeNMetadataTotal int `json:"ostree-n-metadata-total,omitempty"` - OstreeNMetadataWritten int `json:"ostree-n-metadata-written,omitempty"` - OstreeTimestamp string `json:"ostree-timestamp"` - OstreeVersion string `json:"ostree-version"` - OverridesActive bool `json:"coreos-assembler.overrides-active,omitempty"` - PkgdiffAgainstParent PackageSetDifferences `json:"parent-pkgdiff,omitempty"` - PkgdiffBetweenBuilds PackageSetDifferences `json:"pkgdiff,omitempty"` - ReleasePayload *Image `json:"release-payload,omitempty"` -} - -type BuildArtifacts struct { - Aliyun *Artifact `json:"aliyun,omitempty"` - Aws *Artifact `json:"aws,omitempty"` - Azure *Artifact `json:"azure,omitempty"` - AzureStack *Artifact `json:"azurestack,omitempty"` - Dasd *Artifact `json:"dasd,omitempty"` - DigitalOcean *Artifact `json:"digitalocean,omitempty"` - Exoscale *Artifact `json:"exoscale,omitempty"` - Gcp *Artifact `json:"gcp,omitempty"` - IbmCloud *Artifact `json:"ibmcloud,omitempty"` - Initramfs *Artifact `json:"initramfs,omitempty"` - Iso *Artifact `json:"iso,omitempty"` - Kernel *Artifact `json:"kernel,omitempty"` - LiveInitramfs *Artifact `json:"live-initramfs,omitempty"` - LiveIso *Artifact `json:"live-iso,omitempty"` - LiveKernel *Artifact `json:"live-kernel,omitempty"` - LiveRootfs *Artifact `json:"live-rootfs,omitempty"` - Metal *Artifact `json:"metal,omitempty"` - Metal4KNative *Artifact `json:"metal4k,omitempty"` - OpenStack *Artifact `json:"openstack,omitempty"` - Ostree Artifact `json:"ostree"` - Qemu *Artifact `json:"qemu,omitempty"` - Vmware *Artifact `json:"vmware,omitempty"` - Vultr *Artifact `json:"vultr,omitempty"` -} - -type Cloudartifact struct { - Image string `json:"image"` - URL string `json:"url"` -} - -type Extensions struct { - Manifest map[string]interface{} `json:"manifest"` - Path string `json:"path"` - RpmOstreeState string `json:"rpm-ostree-state"` - Sha256 string `json:"sha256"` -} - -type Gcp struct { - ImageFamily string `json:"family,omitempty"` - ImageName string `json:"image"` - ImageProject string `json:"project,omitempty"` - URL string `json:"url"` -} - -type Git struct { - Branch string `json:"branch,omitempty"` - Commit string `json:"commit"` - Dirty string `json:"dirty,omitempty"` - Origin string `json:"origin"` -} - -type Image struct { - Comment string `json:"comment,omitempty"` - Digest string `json:"digest"` - Image string `json:"image"` -} - -type Items interface{} - -type PackageSetDifferences []Items diff -Nru libpod-3.2.1+ds1/pkg/machine/fcos.go libpod-3.4.4+ds1/pkg/machine/fcos.go --- libpod-3.2.1+ds1/pkg/machine/fcos.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/fcos.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,16 +1,24 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine import ( - "crypto/sha256" + "encoding/json" + "fmt" "io/ioutil" + "net/http" url2 "net/url" + "os" "path/filepath" "runtime" "strings" + "github.com/coreos/stream-metadata-go/fedoracoreos" + "github.com/coreos/stream-metadata-go/stream" + "github.com/pkg/errors" + digest "github.com/opencontainers/go-digest" + "github.com/sirupsen/logrus" ) // These should eventually be moved into machine/qemu as @@ -24,8 +32,8 @@ Download } -func NewFcosDownloader(vmType, vmName string) (DistributionDownload, error) { - info, err := getFCOSDownload() +func NewFcosDownloader(vmType, vmName, imageStream string) (DistributionDownload, error) { + info, err := getFCOSDownload(imageStream) if err != nil { return nil, err } @@ -91,24 +99,23 @@ // check the sha of the local image if it exists // get the sha of the remote image // == dont bother to pull - files, err := ioutil.ReadDir(filepath.Dir(d.LocalPath)) + if _, err := os.Stat(d.LocalPath); os.IsNotExist(err) { + return false, nil + } + fd, err := os.Open(d.LocalPath) if err != nil { return false, err } - for _, file := range files { - if filepath.Base(d.LocalPath) == file.Name() { - b, err := ioutil.ReadFile(d.LocalPath) - if err != nil { - return false, err - } - s := sha256.Sum256(b) - sum := digest.NewDigestFromBytes(digest.SHA256, s[:]) - if sum.Encoded() == d.Sha256sum { - return true, nil - } + defer func() { + if err := fd.Close(); err != nil { + logrus.Error(err) } + }() + sum, err := digest.SHA256.FromReader(fd) + if err != nil { + return false, err } - return false, nil + return sum.Encoded() == d.Sha256sum, nil } func getFcosArch() string { @@ -122,3 +129,70 @@ } return arch } + +// This should get Exported and stay put as it will apply to all fcos downloads +// getFCOS parses fedoraCoreOS's stream and returns the image download URL and the release version +func getFCOSDownload(imageStream string) (*fcosDownloadInfo, error) { + var ( + fcosstable stream.Stream + streamType string + ) + switch imageStream { + case "testing", "": + streamType = fedoracoreos.StreamTesting + case "next": + streamType = fedoracoreos.StreamNext + case "stable": + streamType = fedoracoreos.StreamStable + default: + return nil, errors.Errorf("invalid stream %s: valid streams are `testing` and `stable`", imageStream) + } + streamurl := fedoracoreos.GetStreamURL(streamType) + resp, err := http.Get(streamurl.String()) + if err != nil { + return nil, err + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + defer func() { + if err := resp.Body.Close(); err != nil { + logrus.Error(err) + } + }() + + if err := json.Unmarshal(body, &fcosstable); err != nil { + return nil, err + } + arch, ok := fcosstable.Architectures[getFcosArch()] + if !ok { + return nil, fmt.Errorf("unable to pull VM image: no targetArch in stream") + } + artifacts := arch.Artifacts + if artifacts == nil { + return nil, fmt.Errorf("unable to pull VM image: no artifact in stream") + } + qemu, ok := artifacts[artifact] + if !ok { + return nil, fmt.Errorf("unable to pull VM image: no qemu artifact in stream") + } + formats := qemu.Formats + if formats == nil { + return nil, fmt.Errorf("unable to pull VM image: no formats in stream") + } + qcow, ok := formats[Format] + if !ok { + return nil, fmt.Errorf("unable to pull VM image: no qcow2.xz format in stream") + } + disk := qcow.Disk + if disk == nil { + return nil, fmt.Errorf("unable to pull VM image: no disk in stream") + } + return &fcosDownloadInfo{ + Location: disk.Location, + Release: qemu.Release, + Sha256Sum: disk.Sha256, + CompressionType: "xz", + }, nil +} diff -Nru libpod-3.2.1+ds1/pkg/machine/ignition.go libpod-3.4.4+ds1/pkg/machine/ignition.go --- libpod-3.2.1+ds1/pkg/machine/ignition.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/ignition.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine @@ -6,6 +6,7 @@ "encoding/json" "fmt" "io/ioutil" + "net/url" ) /* @@ -80,6 +81,7 @@ // so a listening host knows it can being interacting with it ready := `[Unit] Requires=dev-virtio\\x2dports-%s.device +After=remove-moby.service sshd.socket sshd.service OnFailure=emergency.target OnFailureJobMode=isolate [Service] @@ -87,8 +89,25 @@ RemainAfterExit=yes ExecStart=/bin/sh -c '/usr/bin/echo Ready >/dev/%s' [Install] -RequiredBy=multi-user.target +RequiredBy=default.target ` + deMoby := `[Unit] +Description=Remove moby-engine +# Run once for the machine +After=systemd-machine-id-commit.service +Before=zincati.service +ConditionPathExists=!/var/lib/%N.stamp + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/usr/bin/rpm-ostree override remove moby-engine +ExecStart=/usr/bin/rpm-ostree ex apply-live --allow-replacement +ExecStartPost=/bin/touch /var/lib/%N.stamp + +[Install] +WantedBy=default.target + ` _ = ready ignSystemd := Systemd{ Units: []Unit{ @@ -101,6 +120,21 @@ Name: "ready.service", Contents: strToPtr(fmt.Sprintf(ready, "vport1p1", "vport1p1")), }, + { + Enabled: boolToPtr(false), + Name: "docker.service", + Mask: boolToPtr(true), + }, + { + Enabled: boolToPtr(false), + Name: "docker.socket", + Mask: boolToPtr(true), + }, + { + Enabled: boolToPtr(true), + Name: "remove-moby.service", + Contents: &deMoby, + }, }} ignConfig := Config{ Ignition: ignVersion, @@ -120,6 +154,7 @@ // in one swoop, then the leading dirs are creates as root. newDirs := []string{ "/home/" + usrName + "/.config", + "/home/" + usrName + "/.config/containers", "/home/" + usrName + "/.config/systemd", "/home/" + usrName + "/.config/systemd/user", "/home/" + usrName + "/.config/systemd/user/default.target.wants", @@ -134,10 +169,25 @@ Path: d, User: getNodeUsr(usrName), }, - DirectoryEmbedded1: DirectoryEmbedded1{Mode: intToPtr(493)}, + DirectoryEmbedded1: DirectoryEmbedded1{Mode: intToPtr(0755)}, } dirs[i] = newDir } + + // Issue #11489: make sure that we can inject a custom registries.conf + // file on the system level to force a single search registry. + // The remote client does not yet support prompting for short-name + // resolution, so we enforce a single search registry (i.e., docker.io) + // as a workaround. + dirs = append(dirs, Directory{ + Node: Node{ + Group: getNodeGrp("root"), + Path: "/etc/containers/registries.conf.d", + User: getNodeUsr("root"), + }, + DirectoryEmbedded1: DirectoryEmbedded1{Mode: intToPtr(0755)}, + }) + return dirs } @@ -145,6 +195,22 @@ var ( files []File ) + + lingerExample := `[Unit] +Description=A systemd user unit demo +After=network-online.target +Wants=network-online.target podman.socket +[Service] +ExecStart=/usr/bin/sleep infinity +` + containers := `[containers] +netns="bridge" +rootless_networking="cni" +` + rootContainers := `[engine] +machine_enabled=true +` + // Add a fake systemd service to get the user socket rolling files = append(files, File{ Node: Node{ @@ -155,12 +221,28 @@ FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5BUnit%5D%0ADescription%3DA%20systemd%20user%20unit%20demo%0AAfter%3Dnetwork-online.target%0AWants%3Dnetwork-online.target%20podman.socket%0A%5BService%5D%0AExecStart%3D%2Fusr%2Fbin%2Fsleep%20infinity%0A"), + Source: encodeDataURLPtr(lingerExample), }, - Mode: intToPtr(484), + Mode: intToPtr(0744), }, }) + // Set containers.conf up for core user to use cni networks + // by default + files = append(files, File{ + Node: Node{ + Group: getNodeGrp(usrName), + Path: "/home/" + usrName + "/.config/containers/containers.conf", + User: getNodeUsr(usrName), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(containers), + }, + Mode: intToPtr(0744), + }, + }) // Add a file into linger files = append(files, File{ Node: Node{ @@ -168,7 +250,7 @@ Path: "/var/lib/systemd/linger/core", User: getNodeUsr(usrName), }, - FileEmbedded1: FileEmbedded1{Mode: intToPtr(420)}, + FileEmbedded1: FileEmbedded1{Mode: intToPtr(0644)}, }) // Set machine_enabled to true to indicate we're in a VM @@ -181,11 +263,65 @@ FileEmbedded1: FileEmbedded1{ Append: nil, Contents: Resource{ - Source: strToPtr("data:,%5Bengine%5D%0Amachine_enabled%3Dtrue%0A"), + Source: encodeDataURLPtr(rootContainers), }, - Mode: intToPtr(420), + Mode: intToPtr(0644), }, }) + + // Issue #11489: make sure that we can inject a custom registries.conf + // file on the system level to force a single search registry. + // The remote client does not yet support prompting for short-name + // resolution, so we enforce a single search registry (i.e., docker.io) + // as a workaround. + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: "/etc/containers/registries.conf.d/999-podman-machine.conf", + User: getNodeUsr("root"), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr("unqualified-search-registries=[\"docker.io\"]\n"), + }, + Mode: intToPtr(0644), + }, + }) + + files = append(files, File{ + Node: Node{ + Path: "/etc/tmpfiles.d/podman-docker.conf", + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + // Create a symlink from the docker socket to the podman socket. + // Taken from https://github.com/containers/podman/blob/main/contrib/systemd/system/podman-docker.conf + Contents: Resource{ + Source: encodeDataURLPtr("L+ /run/docker.sock - - - - /run/podman/podman.sock\n"), + }, + Mode: intToPtr(0644), + }, + }) + + setDockerHost := `export DOCKER_HOST="unix://$(podman info -f "{{.Host.RemoteSocket.Path}}")" +` + + files = append(files, File{ + Node: Node{ + Group: getNodeGrp("root"), + Path: "/etc/profile.d/docker-host.sh", + User: getNodeUsr("root"), + }, + FileEmbedded1: FileEmbedded1{ + Append: nil, + Contents: Resource{ + Source: encodeDataURLPtr(setDockerHost), + }, + Mode: intToPtr(0644), + }, + }) + return files } @@ -200,5 +336,20 @@ Hard: boolToPtr(false), Target: "/home/" + usrName + "/.config/systemd/user/linger-example.service", }, + }, { + Node: Node{ + Group: getNodeGrp("root"), + Path: "/usr/local/bin/docker", + Overwrite: boolToPtr(true), + User: getNodeUsr("root"), + }, + LinkEmbedded1: LinkEmbedded1{ + Hard: boolToPtr(false), + Target: "/usr/bin/podman", + }, }} } + +func encodeDataURLPtr(contents string) *string { + return strToPtr(fmt.Sprintf("data:,%s", url.PathEscape(contents))) +} diff -Nru libpod-3.2.1+ds1/pkg/machine/ignition_schema.go libpod-3.4.4+ds1/pkg/machine/ignition_schema.go --- libpod-3.2.1+ds1/pkg/machine/ignition_schema.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/ignition_schema.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff -Nru libpod-3.2.1+ds1/pkg/machine/keys.go libpod-3.4.4+ds1/pkg/machine/keys.go --- libpod-3.2.1+ds1/pkg/machine/keys.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/keys.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine diff -Nru libpod-3.2.1+ds1/pkg/machine/libvirt/config.go libpod-3.4.4+ds1/pkg/machine/libvirt/config.go --- libpod-3.2.1+ds1/pkg/machine/libvirt/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/libvirt/config.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin - -package libvirt - -type MachineVM struct { -} diff -Nru libpod-3.2.1+ds1/pkg/machine/libvirt/machine.go libpod-3.4.4+ds1/pkg/machine/libvirt/machine.go --- libpod-3.2.1+ds1/pkg/machine/libvirt/machine.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/libvirt/machine.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin - -package libvirt - -import "github.com/containers/podman/v3/pkg/machine" - -func (v *MachineVM) Init(name string, opts machine.InitOptions) error { - return nil -} - -func (v *MachineVM) Start(name string) error { - return nil -} - -func (v *MachineVM) Stop(name string) error { - return nil -} diff -Nru libpod-3.2.1+ds1/pkg/machine/libvirt/machine_unsupported.go libpod-3.4.4+ds1/pkg/machine/libvirt/machine_unsupported.go --- libpod-3.2.1+ds1/pkg/machine/libvirt/machine_unsupported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/libvirt/machine_unsupported.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -// +build !amd64 amd64,windows - -package libvirt diff -Nru libpod-3.2.1+ds1/pkg/machine/pull.go libpod-3.4.4+ds1/pkg/machine/pull.go --- libpod-3.2.1+ds1/pkg/machine/pull.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/pull.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package machine @@ -15,7 +15,7 @@ "time" "github.com/containers/image/v5/pkg/compression" - "github.com/docker/docker/pkg/archive" + "github.com/containers/storage/pkg/archive" "github.com/sirupsen/logrus" "github.com/vbauerster/mpb/v6" "github.com/vbauerster/mpb/v6/decor" diff -Nru libpod-3.2.1+ds1/pkg/machine/qemu/config.go libpod-3.4.4+ds1/pkg/machine/qemu/config.go --- libpod-3.2.1+ds1/pkg/machine/qemu/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/qemu/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package qemu @@ -17,6 +17,8 @@ ImagePath string // Memory in megabytes assigned to the vm Memory uint64 + // Disk size in gigabytes assigned to the vm + DiskSize uint64 // Name of the vm Name string // SSH port for user networking diff -Nru libpod-3.2.1+ds1/pkg/machine/qemu/machine.go libpod-3.4.4+ds1/pkg/machine/qemu/machine.go --- libpod-3.2.1+ds1/pkg/machine/qemu/machine.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/qemu/machine.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build amd64,linux arm64,linux amd64,darwin arm64,darwin +// +build amd64,!windows arm64,!windows package qemu @@ -15,7 +15,9 @@ "strings" "time" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/pkg/machine" + "github.com/containers/podman/v3/pkg/rootless" "github.com/containers/podman/v3/utils" "github.com/containers/storage/pkg/homedir" "github.com/digitalocean/go-qemu/qmp" @@ -62,6 +64,7 @@ vm.CPUs = opts.CPUS vm.Memory = opts.Memory + vm.DiskSize = opts.DiskSize // Look up the executable execPath, err := exec.LookPath(QemuCommand) @@ -84,9 +87,10 @@ cmd = append(cmd, []string{"-qmp", monitor.Network + ":/" + monitor.Address + ",server=on,wait=off"}...) // Add network - cmd = append(cmd, "-nic", "user,model=virtio,hostfwd=tcp::"+strconv.Itoa(vm.Port)+"-:22") - - socketPath, err := getSocketDir() + // Right now the mac address is hardcoded so that the host networking gives it a specific IP address. This is + // why we can only run one vm at a time right now + cmd = append(cmd, []string{"-netdev", "socket,id=vlan,fd=3", "-device", "virtio-net-pci,netdev=vlan,mac=5a:94:ef:e4:0c:ee"}...) + socketPath, err := getRuntimeDir() if err != nil { return nil, err } @@ -136,29 +140,29 @@ jsonFile := filepath.Join(vmConfigDir, v.Name) + ".json" v.IdentityPath = filepath.Join(sshDir, v.Name) - // The user has provided an alternate image which can be a file path - // or URL. - if len(opts.ImagePath) > 0 { - g, err := machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath) + switch opts.ImagePath { + case "testing", "next", "stable", "": + // Get image as usual + dd, err := machine.NewFcosDownloader(vmtype, v.Name, opts.ImagePath) if err != nil { return err } - v.ImagePath = g.Get().LocalUncompressedFile - if err := g.DownloadImage(); err != nil { + v.ImagePath = dd.Get().LocalUncompressedFile + if err := dd.DownloadImage(); err != nil { return err } - } else { - // Get the image as usual - dd, err := machine.NewFcosDownloader(vmtype, v.Name) + default: + // The user has provided an alternate image which can be a file path + // or URL. + g, err := machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath) if err != nil { return err } - v.ImagePath = dd.Get().LocalUncompressedFile - if err := dd.DownloadImage(); err != nil { + v.ImagePath = g.Get().LocalUncompressedFile + if err := g.DownloadImage(); err != nil { return err } } - // Add arch specific options including image location v.CmdLine = append(v.CmdLine, v.addArchOptions()...) @@ -237,12 +241,56 @@ // Start executes the qemu command line and forks it func (v *MachineVM) Start(name string, _ machine.StartOptions) error { var ( - conn net.Conn - err error - wait time.Duration = time.Millisecond * 500 + conn net.Conn + err error + qemuSocketConn net.Conn + wait time.Duration = time.Millisecond * 500 ) + + if err := v.startHostNetworking(); err != nil { + return errors.Errorf("unable to start host networking: %q", err) + } + + rtPath, err := getRuntimeDir() + if err != nil { + return err + } + + // If the temporary podman dir is not created, create it + podmanTempDir := filepath.Join(rtPath, "podman") + if _, err := os.Stat(podmanTempDir); os.IsNotExist(err) { + if mkdirErr := os.MkdirAll(podmanTempDir, 0755); mkdirErr != nil { + return err + } + } + qemuSocketPath, _, err := v.getSocketandPid() + if err != nil { + return err + } + // If the qemusocketpath exists and the vm is off/down, we should rm + // it before the dial as to avoid a segv + if err := os.Remove(qemuSocketPath); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Warn(err) + } + for i := 0; i < 6; i++ { + qemuSocketConn, err = net.Dial("unix", qemuSocketPath) + if err == nil { + break + } + time.Sleep(wait) + wait++ + } + if err != nil { + return err + } + + fd, err := qemuSocketConn.(*net.UnixConn).File() + if err != nil { + return err + } + attr := new(os.ProcAttr) - files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + files := []*os.File{os.Stdin, os.Stdout, os.Stderr, fd} attr.Files = files logrus.Debug(v.CmdLine) cmd := v.CmdLine @@ -258,7 +306,7 @@ return err } fmt.Println("Waiting for VM ...") - socketPath, err := getSocketDir() + socketPath, err := getRuntimeDir() if err != nil { return err } @@ -311,16 +359,51 @@ logrus.Error(err) } }() - _, err = qmpMonitor.Run(input) - return err + if _, err = qmpMonitor.Run(input); err != nil { + return err + } + qemuSocketFile, pidFile, err := v.getSocketandPid() + if err != nil { + return err + } + if _, err := os.Stat(pidFile); os.IsNotExist(err) { + logrus.Infof("pid file %s does not exist", pidFile) + return nil + } + pidString, err := ioutil.ReadFile(pidFile) + if err != nil { + return err + } + pidNum, err := strconv.Atoi(string(pidString)) + if err != nil { + return err + } + + p, err := os.FindProcess(pidNum) + if p == nil && err != nil { + return err + } + // Kill the process + if err := p.Kill(); err != nil { + return err + } + // Remove the pidfile + if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Warn(err) + } + // Remove socket + return os.Remove(qemuSocketFile) } // NewQMPMonitor creates the monitor subsection of our vm func NewQMPMonitor(network, name string, timeout time.Duration) (Monitor, error) { - rtDir, err := getSocketDir() + rtDir, err := getRuntimeDir() if err != nil { return Monitor{}, err } + if !rootless.IsRootless() { + rtDir = "/run" + } rtDir = filepath.Join(rtDir, "podman") if _, err := os.Stat(filepath.Join(rtDir)); os.IsNotExist(err) { // TODO 0644 is fine on linux but macos is weird @@ -377,6 +460,22 @@ for _, msg := range files { confirmationMessage += msg + "\n" } + + // Get path to socket and pidFile before we do any cleanups + qemuSocketFile, pidFile, errSocketFile := v.getSocketandPid() + //silently try to delete socket and pid file + //remove socket and pid file if any: warn at low priority if things fail + if errSocketFile == nil { + // Remove the pidfile + if err := os.Remove(pidFile); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Debugf("Error while removing pidfile: %v", err) + } + // Remove socket + if err := os.Remove(qemuSocketFile); err != nil && !errors.Is(err, os.ErrNotExist) { + logrus.Debugf("Error while removing podman-machine-socket: %v", err) + } + } + confirmationMessage += "\n" return confirmationMessage, func() error { for _, f := range files { @@ -407,10 +506,15 @@ return errors.Errorf("vm %q is not running.", v.Name) } - sshDestination := v.RemoteUsername + "@localhost" + username := opts.Username + if username == "" { + username = v.RemoteUsername + } + + sshDestination := username + "@localhost" port := strconv.Itoa(v.Port) - args := []string{"-i", v.IdentityPath, "-p", port, sshDestination} + args := []string{"-i", v.IdentityPath, "-p", port, sshDestination, "-o", "UserKnownHostsFile /dev/null", "-o", "StrictHostKeyChecking no"} if len(opts.Args) > 0 { args = append(args, opts.Args...) } else { @@ -418,6 +522,8 @@ } cmd := exec.Command("ssh", args...) + logrus.Debugf("Executing: ssh %v\n", args) + cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin @@ -485,6 +591,9 @@ listEntry.Name = vm.Name listEntry.VMType = "qemu" + listEntry.CPUs = vm.CPUs + listEntry.Memory = vm.Memory + listEntry.DiskSize = vm.DiskSize fi, err := os.Stat(fullPath) if err != nil { return err @@ -535,3 +644,52 @@ } return false, "", nil } + +// startHostNetworking runs a binary on the host system that allows users +// to setup port forwarding to the podman virtual machine +func (v *MachineVM) startHostNetworking() error { + cfg, err := config.Default() + if err != nil { + return err + } + binary, err := cfg.FindHelperBinary(machine.ForwarderBinaryName, false) + if err != nil { + return err + } + + // Listen on all at port 7777 for setting up and tearing + // down forwarding + listenSocket := "tcp://127.0.0.1:7777" + qemuSocket, pidFile, err := v.getSocketandPid() + if err != nil { + return err + } + attr := new(os.ProcAttr) + // Pass on stdin, stdout, stderr + files := []*os.File{os.Stdin, os.Stdout, os.Stderr} + attr.Files = files + cmd := []string{binary} + cmd = append(cmd, []string{"-listen", listenSocket, "-listen-qemu", fmt.Sprintf("unix://%s", qemuSocket), "-pid-file", pidFile}...) + // Add the ssh port + cmd = append(cmd, []string{"-ssh-port", fmt.Sprintf("%d", v.Port)}...) + if logrus.GetLevel() == logrus.DebugLevel { + cmd = append(cmd, "--debug") + fmt.Println(cmd) + } + _, err = os.StartProcess(cmd[0], cmd, attr) + return err +} + +func (v *MachineVM) getSocketandPid() (string, string, error) { + rtPath, err := getRuntimeDir() + if err != nil { + return "", "", err + } + if !rootless.IsRootless() { + rtPath = "/run" + } + socketDir := filepath.Join(rtPath, "podman") + pidFile := filepath.Join(socketDir, fmt.Sprintf("%s.pid", v.Name)) + qemuSocket := filepath.Join(socketDir, fmt.Sprintf("qemu_%s.sock", v.Name)) + return qemuSocket, pidFile, nil +} diff -Nru libpod-3.2.1+ds1/pkg/machine/qemu/options_darwin_amd64.go libpod-3.4.4+ds1/pkg/machine/qemu/options_darwin_amd64.go --- libpod-3.2.1+ds1/pkg/machine/qemu/options_darwin_amd64.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/qemu/options_darwin_amd64.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,7 +5,7 @@ ) func (v *MachineVM) addArchOptions() []string { - opts := []string{"-cpu", "host", "-accel", "hvf"} + opts := []string{"-machine", "q35,accel=hvf:tcg", "-cpu", "host"} return opts } diff -Nru libpod-3.2.1+ds1/pkg/machine/qemu/options_darwin_arm64.go libpod-3.4.4+ds1/pkg/machine/qemu/options_darwin_arm64.go --- libpod-3.2.1+ds1/pkg/machine/qemu/options_darwin_arm64.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/qemu/options_darwin_arm64.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,6 +1,7 @@ package qemu import ( + "os" "os/exec" "path/filepath" ) @@ -13,16 +14,17 @@ ovmfDir := getOvmfDir(v.ImagePath, v.Name) opts := []string{ "-accel", "hvf", + "-accel", "tcg", "-cpu", "cortex-a57", "-M", "virt,highmem=off", - "-drive", "file=/usr/local/share/qemu/edk2-aarch64-code.fd,if=pflash,format=raw,readonly=on", + "-drive", "file=" + getEdk2CodeFd("edk2-aarch64-code.fd") + ",if=pflash,format=raw,readonly=on", "-drive", "file=" + ovmfDir + ",if=pflash,format=raw"} return opts } func (v *MachineVM) prepare() error { ovmfDir := getOvmfDir(v.ImagePath, v.Name) - cmd := []string{"dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} + cmd := []string{"/bin/dd", "if=/dev/zero", "conv=sync", "bs=1m", "count=64", "of=" + ovmfDir} return exec.Command(cmd[0], cmd[1:]...).Run() } @@ -34,3 +36,23 @@ func getOvmfDir(imagePath, vmName string) string { return filepath.Join(filepath.Dir(imagePath), vmName+"_ovmf_vars.fd") } + +/* + * QEmu can be installed in multiple locations on MacOS, especially on + * Apple Silicon systems. A build from source will likely install it in + * /usr/local/bin, whereas Homebrew package management standard is to + * install in /opt/homebrew + */ +func getEdk2CodeFd(name string) string { + dirs := []string{ + "/usr/local/share/qemu", + "/opt/homebrew/share/qemu", + } + for _, dir := range dirs { + fullpath := filepath.Join(dir, name) + if _, err := os.Stat(fullpath); err == nil { + return fullpath + } + } + return name +} diff -Nru libpod-3.2.1+ds1/pkg/machine/qemu/options_darwin.go libpod-3.4.4+ds1/pkg/machine/qemu/options_darwin.go --- libpod-3.2.1+ds1/pkg/machine/qemu/options_darwin.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/qemu/options_darwin.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,14 +2,12 @@ import ( "os" - - "github.com/pkg/errors" ) -func getSocketDir() (string, error) { +func getRuntimeDir() (string, error) { tmpDir, ok := os.LookupEnv("TMPDIR") if !ok { - return "", errors.New("unable to resolve TMPDIR") + tmpDir = "/tmp" } return tmpDir, nil } diff -Nru libpod-3.2.1+ds1/pkg/machine/qemu/options_linux.go libpod-3.4.4+ds1/pkg/machine/qemu/options_linux.go --- libpod-3.2.1+ds1/pkg/machine/qemu/options_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/machine/qemu/options_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,13 @@ package qemu -import "github.com/containers/podman/v3/pkg/util" +import ( + "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/util" +) -func getSocketDir() (string, error) { +func getRuntimeDir() (string, error) { + if !rootless.IsRootless() { + return "/run", nil + } return util.GetRuntimeDir() } diff -Nru libpod-3.2.1+ds1/pkg/netns/netns_linux.go libpod-3.4.4+ds1/pkg/netns/netns_linux.go --- libpod-3.2.1+ds1/pkg/netns/netns_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/netns/netns_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, diff -Nru libpod-3.2.1+ds1/pkg/ps/ps.go libpod-3.4.4+ds1/pkg/ps/ps.go --- libpod-3.2.1+ds1/pkg/ps/ps.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/ps/ps.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,7 +9,6 @@ "strings" "time" - "github.com/containers/common/libimage" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" @@ -71,18 +70,11 @@ } if options.All && options.External { - externCons, err := runtime.StorageContainers() + listCon, err := GetExternalContainerLists(runtime) if err != nil { return nil, err } - - for _, con := range externCons { - listCon, err := ListStorageContainer(runtime, con, options) - if err != nil { - return nil, err - } - pss = append(pss, listCon) - } + pss = append(pss, listCon...) } // Sort the containers we got @@ -97,6 +89,27 @@ return pss, nil } +// GetExternalContainerLists returns list of external containers for e.g created by buildah +func GetExternalContainerLists(runtime *libpod.Runtime) ([]entities.ListContainer, error) { + var ( + pss = []entities.ListContainer{} + ) + + externCons, err := runtime.StorageContainers() + if err != nil { + return nil, err + } + + for _, con := range externCons { + listCon, err := ListStorageContainer(runtime, con) + if err != nil { + return nil, err + } + pss = append(pss, listCon) + } + return pss, nil +} + // BatchContainerOp is used in ps to reduce performance hits by "batching" // locks. func ListContainerBatch(rt *libpod.Runtime, ctr *libpod.Container, opts entities.ContainerListOptions) (entities.ListContainer, error) { @@ -114,6 +127,12 @@ ) batchErr := ctr.Batch(func(c *libpod.Container) error { + if opts.Sync { + if err := c.Sync(); err != nil { + return errors.Wrapf(err, "unable to update container state from OCI runtime") + } + } + conConfig = c.Config() conState, err = c.State() if err != nil { @@ -228,10 +247,17 @@ UTS: uts, } } + + if hc, err := ctr.HealthCheckStatus(); err == nil { + ps.Status = hc + } else { + logrus.Debug(err) + } + return ps, nil } -func ListStorageContainer(rt *libpod.Runtime, ctr storage.Container, opts entities.ContainerListOptions) (entities.ListContainer, error) { +func ListStorageContainer(rt *libpod.Runtime, ctr storage.Container) (entities.ListContainer, error) { name := "unknown" if len(ctr.Names) > 0 { name = ctr.Names[0] @@ -258,8 +284,7 @@ imageName := "" if ctr.ImageID != "" { - lookupOptions := &libimage.LookupImageOptions{IgnorePlatform: true} - image, _, err := rt.LibimageRuntime().LookupImage(ctr.ImageID, lookupOptions) + image, _, err := rt.LibimageRuntime().LookupImage(ctr.ImageID, nil) if err != nil { return ps, err } diff -Nru libpod-3.2.1+ds1/pkg/registries/registries.go libpod-3.4.4+ds1/pkg/registries/registries.go --- libpod-3.2.1+ds1/pkg/registries/registries.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/registries/registries.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -package registries - -// TODO: this package should not exist anymore. Users should either use -// c/image's `sysregistriesv2` package directly OR, even better, we cache a -// config in libpod's image runtime so we don't need to parse the -// registries.conf files redundantly. - -import ( - "os" - "path/filepath" - - "github.com/containers/image/v5/pkg/sysregistriesv2" - "github.com/containers/image/v5/types" - "github.com/containers/podman/v3/pkg/rootless" - "github.com/pkg/errors" -) - -// userRegistriesFile is the path to the per user registry configuration file. -var userRegistriesFile = filepath.Join(os.Getenv("HOME"), ".config/containers/registries.conf") - -// SystemRegistriesConfPath returns an appropriate value for types.SystemContext.SystemRegistriesConfPath -// (possibly "", which is not an error), taking into account rootless mode and environment variable overrides. -// -// FIXME: This should be centralized in a global SystemContext initializer inherited throughout the code, -// not haphazardly called throughout the way it is being called now. -func SystemRegistriesConfPath() string { - if envOverride, ok := os.LookupEnv("CONTAINERS_REGISTRIES_CONF"); ok { - return envOverride - } - if envOverride, ok := os.LookupEnv("REGISTRIES_CONFIG_PATH"); ok { - return envOverride - } - - if rootless.IsRootless() { - if _, err := os.Stat(userRegistriesFile); err == nil { - return userRegistriesFile - } - } - - return "" -} - -// GetRegistriesData obtains the list of registries -func GetRegistriesData() ([]sysregistriesv2.Registry, error) { - registries, err := sysregistriesv2.GetRegistries(&types.SystemContext{SystemRegistriesConfPath: SystemRegistriesConfPath()}) - if err != nil { - return nil, errors.Wrapf(err, "unable to parse the registries.conf file") - } - return registries, nil -} - -// GetRegistries obtains the list of search registries defined in the global registries file. -func GetRegistries() ([]string, error) { - return sysregistriesv2.UnqualifiedSearchRegistries(&types.SystemContext{SystemRegistriesConfPath: SystemRegistriesConfPath()}) -} - -// GetBlockedRegistries obtains the list of blocked registries defined in the global registries file. -func GetBlockedRegistries() ([]string, error) { - var blockedRegistries []string - registries, err := GetRegistriesData() - if err != nil { - return nil, err - } - for _, reg := range registries { - if reg.Blocked { - blockedRegistries = append(blockedRegistries, reg.Prefix) - } - } - return blockedRegistries, nil -} - -// GetInsecureRegistries obtains the list of insecure registries from the global registration file. -func GetInsecureRegistries() ([]string, error) { - var insecureRegistries []string - registries, err := GetRegistriesData() - if err != nil { - return nil, err - } - for _, reg := range registries { - if reg.Insecure { - insecureRegistries = append(insecureRegistries, reg.Prefix) - } - } - return insecureRegistries, nil -} diff -Nru libpod-3.2.1+ds1/pkg/rootless/rootless_linux.c libpod-3.4.4+ds1/pkg/rootless/rootless_linux.c --- libpod-3.2.1+ds1/pkg/rootless/rootless_linux.c 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/rootless/rootless_linux.c 2021-12-26 00:45:51.000000000 +0000 @@ -19,6 +19,33 @@ #include #include +#define cleanup_free __attribute__ ((cleanup (cleanup_freep))) +#define cleanup_close __attribute__ ((cleanup (cleanup_closep))) +#define cleanup_dir __attribute__ ((cleanup (cleanup_dirp))) + +static inline void +cleanup_freep (void *p) +{ + void **pp = (void **) p; + free (*pp); +} + +static inline void +cleanup_closep (void *p) +{ + int *pp = p; + if (*pp >= 0) + TEMP_FAILURE_RETRY (close (*pp)); +} + +static inline void +cleanup_dirp (DIR **p) +{ + DIR *dir = *p; + if (dir) + closedir (dir); +} + int rename_noreplace (int olddirfd, const char *oldpath, int newdirfd, const char *newpath) { int ret; @@ -114,8 +141,8 @@ static char ** get_cmd_line_args () { - int fd; - char *buffer; + cleanup_free char *buffer = NULL; + cleanup_close int fd = -1; size_t allocated; size_t used = 0; int ret; @@ -134,10 +161,7 @@ { ret = TEMP_FAILURE_RETRY (read (fd, buffer + used, allocated - used)); if (ret < 0) - { - free (buffer); - return NULL; - } + return NULL; if (ret == 0) break; @@ -148,30 +172,21 @@ allocated += 512; char *tmp = realloc (buffer, allocated); if (tmp == NULL) - { - free (buffer); - return NULL; - } + return NULL; buffer = tmp; } } - close (fd); for (i = 0; i < used; i++) if (buffer[i] == '\0') argc++; if (argc == 0) - { - free (buffer); - return NULL; - } + return NULL; argv = malloc (sizeof (char *) * (argc + 1)); if (argv == NULL) - { - free (buffer); - return NULL; - } + return NULL; + argc = 0; argv[argc++] = buffer; @@ -181,15 +196,19 @@ argv[argc] = NULL; + /* Move ownership. */ + buffer = NULL; + return argv; } static bool can_use_shortcut () { - int argc; - char **argv; + cleanup_free char **argv = NULL; + cleanup_free char *argv0 = NULL; bool ret = true; + int argc; #ifdef DISABLE_JOIN_SHORTCUT return false; @@ -199,12 +218,10 @@ if (argv == NULL) return false; + argv0 = argv[0]; + if (strstr (argv[0], "podman") == NULL) - { - free (argv[0]); - free (argv); - return false; - } + return false; for (argc = 0; argv[argc]; argc++) { @@ -212,6 +229,7 @@ continue; if (strcmp (argv[argc], "mount") == 0 + || strcmp (argv[argc], "machine") == 0 || strcmp (argv[argc], "search") == 0 || (strcmp (argv[argc], "system") == 0 && argv[argc+1] && strcmp (argv[argc+1], "service") != 0)) { @@ -228,11 +246,25 @@ } } - free (argv[0]); - free (argv); return ret; } +static int +open_namespace (int pid_to_join, const char *ns_file) +{ + char ns_path[PATH_MAX]; + int ret; + + ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file); + if (ret == PATH_MAX) + { + fprintf (stderr, "internal error: namespace path too long\n"); + return -1; + } + + return open (ns_path, O_CLOEXEC | O_RDONLY); +} + int is_fd_inherited(int fd) { @@ -249,8 +281,7 @@ const char *listen_pid; const char *listen_fds; const char *listen_fdnames; - - DIR *d; + cleanup_dir DIR *d = NULL; pause = getenv ("_PODMAN_PAUSE"); if (pause && pause[0]) @@ -298,7 +329,6 @@ FD_SET (fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE])); } - closedir (d); } listen_pid = getenv("LISTEN_PID"); @@ -311,12 +341,12 @@ do_socket_activation = true; saved_systemd_listen_pid = strdup(listen_pid); saved_systemd_listen_fds = strdup(listen_fds); - saved_systemd_listen_fdnames = strdup(listen_fdnames); + if (listen_fdnames != NULL) + saved_systemd_listen_fdnames = strdup(listen_fdnames); if (saved_systemd_listen_pid == NULL - || saved_systemd_listen_fds == NULL - || saved_systemd_listen_fdnames == NULL) + || saved_systemd_listen_fds == NULL) { - fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno)); + fprintf (stderr, "save socket listen environments error: %m\n"); _exit (EXIT_FAILURE); } } @@ -326,73 +356,70 @@ xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR"); if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ()) { - int r; - int fd; + cleanup_free char *cwd = NULL; + cleanup_close int userns_fd = -1; + cleanup_close int mntns_fd = -1; + cleanup_close int fd = -1; long pid; char buf[12]; uid_t uid; gid_t gid; char path[PATH_MAX]; const char *const suffix = "/libpod/tmp/pause.pid"; - char *cwd = getcwd (NULL, 0); char uid_fmt[16]; char gid_fmt[16]; size_t len; + int r; + cwd = getcwd (NULL, 0); if (cwd == NULL) { - fprintf (stderr, "error getting current working directory: %s\n", strerror (errno)); + fprintf (stderr, "error getting current working directory: %m\n"); _exit (EXIT_FAILURE); } len = snprintf (path, PATH_MAX, "%s%s", xdg_runtime_dir, suffix); if (len >= PATH_MAX) { - fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %s", strerror (ENAMETOOLONG)); + errno = ENAMETOOLONG; + fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %m"); exit (EXIT_FAILURE); } fd = open (path, O_RDONLY); if (fd < 0) - { - free (cwd); - return; - } + return; r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof (buf) - 1)); - close (fd); + if (r < 0) - { - free (cwd); - return; - } + return; buf[r] = '\0'; pid = strtol (buf, NULL, 10); if (pid == LONG_MAX) - { - free (cwd); - return; - } + return; uid = geteuid (); gid = getegid (); - sprintf (path, "/proc/%ld/ns/user", pid); - fd = open (path, O_RDONLY); - if (fd < 0 || setns (fd, 0) < 0) - { - free (cwd); - return; - } - close (fd); + userns_fd = open_namespace (pid, "user"); + if (userns_fd < 0) + return; - /* Errors here cannot be ignored as we already joined a ns. */ - sprintf (path, "/proc/%ld/ns/mnt", pid); - fd = open (path, O_RDONLY); - if (fd < 0) + mntns_fd = open_namespace (pid, "mnt"); + if (mntns_fd < 0) + return; + + if (setns (userns_fd, 0) < 0) + return; + + /* The user namespace was joined, after this point errors are + not recoverable anymore. */ + + if (setns (mntns_fd, 0) < 0) { - fprintf (stderr, "cannot open %s: %s", path, strerror (errno)); + fprintf (stderr, "cannot join mount namespace for %ld: %m", pid); exit (EXIT_FAILURE); } @@ -403,33 +430,24 @@ setenv ("_CONTAINERS_ROOTLESS_UID", uid_fmt, 1); setenv ("_CONTAINERS_ROOTLESS_GID", gid_fmt, 1); - r = setns (fd, 0); - if (r < 0) - { - fprintf (stderr, "cannot join mount namespace for %ld: %s", pid, strerror (errno)); - exit (EXIT_FAILURE); - } - close (fd); - if (syscall_setresgid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresgid: %m\n"); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresuid: %m\n"); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { - fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno)); + fprintf (stderr, "cannot chdir to %s: %m\n", cwd); _exit (EXIT_FAILURE); } - free (cwd); rootless_uid_init = uid; rootless_gid_init = gid; } @@ -465,38 +483,43 @@ static int create_pause_process (const char *pause_pid_file_path, char **argv) { - int r, p[2]; + pid_t pid; + int p[2]; if (pipe (p) < 0) - _exit (EXIT_FAILURE); + return -1; - r = fork (); - if (r < 0) - _exit (EXIT_FAILURE); + pid = fork (); + if (pid < 0) + { + close (p[0]); + close (p[1]); + return -1; + } - if (r) + if (pid) { char b; + int r; close (p[1]); /* Block until we write the pid file. */ r = TEMP_FAILURE_RETRY (read (p[0], &b, 1)); close (p[0]); - reexec_in_user_namespace_wait (r, 0); + reexec_in_user_namespace_wait (pid, 0); return r == 1 && b == '0' ? 0 : -1; } else { - int fd; - pid_t pid; + int r, fd; close (p[0]); setsid (); pid = fork (); - if (r < 0) + if (pid < 0) _exit (EXIT_FAILURE); if (pid) @@ -523,7 +546,7 @@ fd = mkstemp (tmp_file_path); if (fd < 0) { - fprintf (stderr, "error creating temporary file: %s\n", strerror (errno)); + fprintf (stderr, "error creating temporary file: %m\n"); kill (pid, SIGKILL); _exit (EXIT_FAILURE); } @@ -531,7 +554,7 @@ r = TEMP_FAILURE_RETRY (write (fd, pid_str, strlen (pid_str))); if (r < 0) { - fprintf (stderr, "cannot write to file descriptor: %s\n", strerror (errno)); + fprintf (stderr, "cannot write to file descriptor: %m\n"); kill (pid, SIGKILL); _exit (EXIT_FAILURE); } @@ -549,7 +572,7 @@ r = TEMP_FAILURE_RETRY (write (p[1], "0", 1)); if (r < 0) { - fprintf (stderr, "cannot write to pipe: %s\n", strerror (errno)); + fprintf (stderr, "cannot write to pipe: %m\n"); _exit (EXIT_FAILURE); } close (p[1]); @@ -584,22 +607,6 @@ } } -static int -open_namespace (int pid_to_join, const char *ns_file) -{ - char ns_path[PATH_MAX]; - int ret; - - ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file); - if (ret == PATH_MAX) - { - fprintf (stderr, "internal error: namespace path too long\n"); - return -1; - } - - return open (ns_path, O_CLOEXEC | O_RDONLY); -} - static void join_namespace_or_die (const char *name, int ns_fd) { @@ -613,18 +620,20 @@ int reexec_userns_join (int pid_to_join, char *pause_pid_file_path) { + cleanup_close int userns_fd = -1; + cleanup_close int mntns_fd = -1; + cleanup_free char *cwd = NULL; char uid[16]; char gid[16]; - char **argv; + cleanup_free char *argv0 = NULL; + cleanup_free char **argv = NULL; int pid; - int mnt_ns = -1; - int user_ns = -1; - char *cwd = getcwd (NULL, 0); sigset_t sigset, oldsigset; + cwd = getcwd (NULL, 0); if (cwd == NULL) { - fprintf (stderr, "error getting current working directory: %s\n", strerror (errno)); + fprintf (stderr, "error getting current working directory: %m\n"); _exit (EXIT_FAILURE); } @@ -634,32 +643,27 @@ argv = get_cmd_line_args (); if (argv == NULL) { - fprintf (stderr, "cannot read argv: %s\n", strerror (errno)); + fprintf (stderr, "cannot read argv: %m\n"); _exit (EXIT_FAILURE); } - user_ns = open_namespace (pid_to_join, "user"); - if (user_ns < 0) - return user_ns; - mnt_ns = open_namespace (pid_to_join, "mnt"); - if (mnt_ns < 0) - { - close (user_ns); - return mnt_ns; - } + argv0 = argv[0]; + + userns_fd = open_namespace (pid_to_join, "user"); + if (userns_fd < 0) + return userns_fd; + mntns_fd = open_namespace (pid_to_join, "mnt"); + if (mntns_fd < 0) + return mntns_fd; pid = fork (); if (pid < 0) - fprintf (stderr, "cannot fork: %s\n", strerror (errno)); + fprintf (stderr, "cannot fork: %m\n"); if (pid) { int f; - /* We passed down these fds, close them. */ - close (user_ns); - close (mnt_ns); - for (f = 3; f <= open_files_max_fd; f++) if (is_fd_inherited (f)) close (f); @@ -675,22 +679,22 @@ if (sigfillset (&sigset) < 0) { - fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno)); + fprintf (stderr, "cannot fill sigset: %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGCHLD) < 0) { - fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGTERM) < 0) { - fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n"); _exit (EXIT_FAILURE); } if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } @@ -700,7 +704,9 @@ sprintf (s, "%d", getpid()); setenv ("LISTEN_PID", s, true); setenv ("LISTEN_FDS", saved_systemd_listen_fds, true); - setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); + // Setting fdnames is optional for systemd_socket_activation + if (saved_systemd_listen_fdnames != NULL) + setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); } setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); @@ -709,33 +715,30 @@ if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) < 0) { - fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %s\n", strerror (errno)); + fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %m\n"); _exit (EXIT_FAILURE); } - join_namespace_or_die ("user", user_ns); - join_namespace_or_die ("mnt", mnt_ns); - close (user_ns); - close (mnt_ns); + join_namespace_or_die ("user", userns_fd); + join_namespace_or_die ("mnt", mntns_fd); if (syscall_setresgid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresgid: %m\n"); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresuid: %m\n"); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { - fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno)); + fprintf (stderr, "cannot chdir to %s: %m\n", cwd); _exit (EXIT_FAILURE); } - free (cwd); if (pause_pid_file_path && pause_pid_file_path[0] != '\0') { @@ -744,7 +747,7 @@ } if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } @@ -776,7 +779,7 @@ copy_file_to_fd (const char *file_to_read, int outfd) { char buf[512]; - int fd; + cleanup_close int fd = -1; fd = open (file_to_read, O_RDONLY); if (fd < 0) @@ -788,10 +791,7 @@ r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof buf)); if (r < 0) - { - close (fd); - return r; - } + return r; if (r == 0) break; @@ -800,43 +800,40 @@ { w = TEMP_FAILURE_RETRY (write (outfd, &buf[t], r - t)); if (w < 0) - { - close (fd); - return w; - } + return w; t += w; } } - close (fd); return 0; } int reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_read, int outputfd) { + cleanup_free char **argv = NULL; + cleanup_free char *argv0 = NULL; + cleanup_free char *cwd = NULL; + sigset_t sigset, oldsigset; int ret; pid_t pid; char b; - char **argv; char uid[16]; char gid[16]; - char *cwd = getcwd (NULL, 0); - sigset_t sigset, oldsigset; + cwd = getcwd (NULL, 0); if (cwd == NULL) { - fprintf (stderr, "error getting current working directory: %s\n", strerror (errno)); + fprintf (stderr, "error getting current working directory: %m\n"); _exit (EXIT_FAILURE); } - sprintf (uid, "%d", geteuid ()); sprintf (gid, "%d", getegid ()); pid = syscall_clone (CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL); if (pid < 0) { - fprintf (stderr, "cannot clone: %s\n", strerror (errno)); + fprintf (stderr, "cannot clone: %m\n"); check_proc_sys_userns_file (_max_user_namespaces); check_proc_sys_userns_file (_unprivileged_user_namespaces); } @@ -864,39 +861,43 @@ if (sigfillset (&sigset) < 0) { - fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno)); + fprintf (stderr, "cannot fill sigset: %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGCHLD) < 0) { - fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n"); _exit (EXIT_FAILURE); } if (sigdelset (&sigset, SIGTERM) < 0) { - fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno)); + fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n"); _exit (EXIT_FAILURE); } if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } argv = get_cmd_line_args (); if (argv == NULL) { - fprintf (stderr, "cannot read argv: %s\n", strerror (errno)); + fprintf (stderr, "cannot read argv: %m\n"); _exit (EXIT_FAILURE); } + argv0 = argv[0]; + if (do_socket_activation) { char s[32]; sprintf (s, "%d", getpid()); setenv ("LISTEN_PID", s, true); setenv ("LISTEN_FDS", saved_systemd_listen_fds, true); - setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); + // Setting fdnames is optional for systemd_socket_activation + if (saved_systemd_listen_fdnames != NULL) + setenv ("LISTEN_FDNAMES", saved_systemd_listen_fdnames, true); } setenv ("_CONTAINERS_USERNS_CONFIGURED", "init", 1); @@ -906,7 +907,7 @@ ret = TEMP_FAILURE_RETRY (read (ready, &b, 1)); if (ret < 0) { - fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno)); + fprintf (stderr, "cannot read from sync pipe: %m\n"); _exit (EXIT_FAILURE); } if (ret != 1 || b != '0') @@ -914,25 +915,24 @@ if (syscall_setresgid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresgid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresgid: %m\n"); TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } if (syscall_setresuid (0, 0, 0) < 0) { - fprintf (stderr, "cannot setresuid: %s\n", strerror (errno)); + fprintf (stderr, "cannot setresuid: %m\n"); TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } if (chdir (cwd) < 0) { - fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno)); + fprintf (stderr, "cannot chdir to %s: %m\n", cwd); TEMP_FAILURE_RETRY (write (ready, "1", 1)); _exit (EXIT_FAILURE); } - free (cwd); if (pause_pid_file_path && pause_pid_file_path[0] != '\0') { @@ -946,14 +946,14 @@ ret = TEMP_FAILURE_RETRY (write (ready, "0", 1)); if (ret < 0) { - fprintf (stderr, "cannot write to ready pipe: %s\n", strerror (errno)); - _exit (EXIT_FAILURE); + fprintf (stderr, "cannot write to ready pipe: %m\n"); + _exit (EXIT_FAILURE); } close (ready); if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0) { - fprintf (stderr, "cannot block signals: %s\n", strerror (errno)); + fprintf (stderr, "cannot block signals: %m\n"); _exit (EXIT_FAILURE); } diff -Nru libpod-3.2.1+ds1/pkg/rootless/rootless_linux.go libpod-3.4.4+ds1/pkg/rootless/rootless_linux.go --- libpod-3.2.1+ds1/pkg/rootless/rootless_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/rootless/rootless_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -14,11 +14,13 @@ "os/user" "runtime" "strconv" + "strings" "sync" "unsafe" "github.com/containers/podman/v3/pkg/errorhandling" "github.com/containers/storage/pkg/idtools" + pmount "github.com/containers/storage/pkg/mount" "github.com/containers/storage/pkg/unshare" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -235,6 +237,24 @@ return false, 0, nil } + if mounts, err := pmount.GetMounts(); err == nil { + for _, m := range mounts { + if m.Mountpoint == "/" { + isShared := false + for _, o := range strings.Split(m.Optional, ",") { + if strings.HasPrefix(o, "shared:") { + isShared = true + break + } + } + if !isShared { + logrus.Warningf("%q is not a shared mount, this could cause issues or missing mounts with rootless containers", m.Mountpoint) + } + break + } + } + } + cPausePid := C.CString(pausePid) defer C.free(unsafe.Pointer(cPausePid)) @@ -268,7 +288,9 @@ } if retErr != nil && pid > 0 { if err := unix.Kill(pid, unix.SIGKILL); err != nil { - logrus.Errorf("failed to kill %d", pid) + if err != unix.ESRCH { + logrus.Errorf("failed to cleanup process %d: %v", pid, err) + } } C.reexec_in_user_namespace_wait(C.int(pid), 0) } @@ -375,8 +397,6 @@ return false, -1, errors.Wrapf(err, "error setting up the process") } - c := make(chan os.Signal, 1) - signals := []os.Signal{} for sig := 0; sig < numSig; sig++ { if sig == int(unix.SIGTSTP) { @@ -385,6 +405,7 @@ signals = append(signals, unix.Signal(sig)) } + c := make(chan os.Signal, len(signals)) gosignal.Notify(c, signals...) defer gosignal.Reset() go func() { @@ -394,7 +415,9 @@ } if err := unix.Kill(int(pidC), s.(unix.Signal)); err != nil { - logrus.Errorf("failed to kill %d", int(pidC)) + if err != unix.ESRCH { + logrus.Errorf("failed to propagate signal to child process %d: %v", int(pidC), err) + } } } }() diff -Nru libpod-3.2.1+ds1/pkg/rootlessport/rootlessport_linux.go libpod-3.4.4+ds1/pkg/rootlessport/rootlessport_linux.go --- libpod-3.2.1+ds1/pkg/rootlessport/rootlessport_linux.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/rootlessport/rootlessport_linux.go 2021-12-26 00:45:51.000000000 +0000 @@ -17,9 +17,10 @@ "fmt" "io" "io/ioutil" + "net" "os" "os/exec" - "os/signal" + "path/filepath" "github.com/containernetworking/plugins/pkg/ns" "github.com/containers/storage/pkg/reexec" @@ -43,12 +44,14 @@ // Config needs to be provided to the process via stdin as a JSON string. // stdin needs to be closed after the message has been written. type Config struct { - Mappings []ocicni.PortMapping - NetNSPath string - ExitFD int - ReadyFD int - TmpDir string - ChildIP string + Mappings []ocicni.PortMapping + NetNSPath string + ExitFD int + ReadyFD int + TmpDir string + ChildIP string + ContainerID string + RootlessCNI bool } func init() { @@ -102,29 +105,11 @@ return err } - exitC := make(chan os.Signal, 1) - defer close(exitC) - - go func() { - sigC := make(chan os.Signal, 1) - signal.Notify(sigC, unix.SIGPIPE) - defer func() { - signal.Stop(sigC) - close(sigC) - }() - - select { - case s := <-sigC: - if s == unix.SIGPIPE { - if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil { - unix.Dup2(int(f.Fd()), 1) // nolint:errcheck - unix.Dup2(int(f.Fd()), 2) // nolint:errcheck - f.Close() - } - } - case <-exitC: - } - }() + socketDir := filepath.Join(cfg.TmpDir, "rp") + err = os.MkdirAll(socketDir, 0700) + if err != nil { + return err + } // create the parent driver stateDir, err := ioutil.TempDir(cfg.TmpDir, "rootlessport") @@ -231,8 +216,41 @@ return err } - // write and close ReadyFD (convention is same as slirp4netns --ready-fd) + // we only need to have a socket to reload ports when we run under rootless cni + if cfg.RootlessCNI { + socketfile := filepath.Join(socketDir, cfg.ContainerID) + // make sure to remove the file if it exists to prevent EADDRINUSE + _ = os.Remove(socketfile) + // workaround to bypass the 108 char socket path limit + // open the fd and use the path to the fd as bind argument + fd, err := unix.Open(socketDir, unix.O_PATH, 0) + if err != nil { + return err + } + socket, err := net.ListenUnix("unixpacket", &net.UnixAddr{Name: fmt.Sprintf("/proc/self/fd/%d/%s", fd, cfg.ContainerID), Net: "unixpacket"}) + if err != nil { + return err + } + err = unix.Close(fd) + // remove the socket file on exit + defer os.Remove(socketfile) + if err != nil { + logrus.Warnf("failed to close the socketDir fd: %v", err) + } + defer socket.Close() + go serve(socket, driver) + } + logrus.Info("ready") + + // https://github.com/containers/podman/issues/11248 + // Copy /dev/null to stdout and stderr to prevent SIGPIPE errors + if f, err := os.OpenFile("/dev/null", os.O_WRONLY, 0755); err == nil { + unix.Dup2(int(f.Fd()), 1) // nolint:errcheck + unix.Dup2(int(f.Fd()), 2) // nolint:errcheck + f.Close() + } + // write and close ReadyFD (convention is same as slirp4netns --ready-fd) if _, err := readyW.Write([]byte("1")); err != nil { return err } @@ -247,6 +265,53 @@ } return nil } + +func serve(listener net.Listener, pm rkport.Manager) { + for { + conn, err := listener.Accept() + if err != nil { + // we cannot log this error, stderr is already closed + continue + } + ctx := context.TODO() + err = handler(ctx, conn, pm) + if err != nil { + conn.Write([]byte(err.Error())) + } else { + conn.Write([]byte("OK")) + } + conn.Close() + } +} + +func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error { + var childIP string + dec := json.NewDecoder(conn) + err := dec.Decode(&childIP) + if err != nil { + return errors.Wrap(err, "rootless port failed to decode ports") + } + portStatus, err := pm.ListPorts(ctx) + if err != nil { + return errors.Wrap(err, "rootless port failed to list ports") + } + for _, status := range portStatus { + err = pm.RemovePort(ctx, status.ID) + if err != nil { + return errors.Wrap(err, "rootless port failed to remove port") + } + } + // add the ports with the new child IP + for _, status := range portStatus { + // set the new child IP + status.Spec.ChildIP = childIP + _, err = pm.AddPort(ctx, status.Spec) + if err != nil { + return errors.Wrap(err, "rootless port failed to add port") + } + } + return nil +} func exposePorts(pm rkport.Manager, portMappings []ocicni.PortMapping, childIP string) error { ctx := context.TODO() diff -Nru libpod-3.2.1+ds1/pkg/servicereaper/service.go libpod-3.4.4+ds1/pkg/servicereaper/service.go --- libpod-3.2.1+ds1/pkg/servicereaper/service.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/servicereaper/service.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,64 @@ +//+build linux + +package servicereaper + +import ( + "os" + "os/signal" + "sync" + "syscall" + + "github.com/sirupsen/logrus" +) + +type service struct { + pidMap map[int]bool + mutex *sync.Mutex +} + +var s = service{ + pidMap: map[int]bool{}, + mutex: &sync.Mutex{}, +} + +func AddPID(pid int) { + s.mutex.Lock() + s.pidMap[pid] = true + s.mutex.Unlock() +} + +func Start() { + // create signal channel and only wait for SIGCHLD + sigc := make(chan os.Signal, 1) + signal.Notify(sigc, syscall.SIGCHLD) + // wait and reap in an extra goroutine + go reaper(sigc) +} + +func reaper(sigc chan os.Signal) { + for { + // block until we receive SIGCHLD + <-sigc + s.mutex.Lock() + for pid := range s.pidMap { + var status syscall.WaitStatus + waitpid, err := syscall.Wait4(pid, &status, syscall.WNOHANG, nil) + if err != nil { + // do not log error for ECHILD + if err != syscall.ECHILD { + logrus.Warnf("wait for pid %d failed: %v ", pid, err) + } + delete(s.pidMap, pid) + continue + } + // if pid == 0 nothing happened + if waitpid == 0 { + continue + } + if status.Exited() { + delete(s.pidMap, pid) + } + } + s.mutex.Unlock() + } +} diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/container_create.go libpod-3.4.4+ds1/pkg/specgen/generate/container_create.go --- libpod-3.2.1+ds1/pkg/specgen/generate/container_create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/container_create.go 2021-12-26 00:45:51.000000000 +0000 @@ -22,10 +22,10 @@ // MakeContainer creates a container based on the SpecGenerator. // Returns the created, container and any warnings resulting from creating the // container, or an error. -func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*libpod.Container, error) { +func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator) (*spec.Spec, *specgen.SpecGenerator, []libpod.CtrCreateOption, error) { rtc, err := rt.GetConfig() if err != nil { - return nil, err + return nil, nil, nil, err } // If joining a pod, retrieve the pod for use. @@ -33,7 +33,7 @@ if s.Pod != "" { pod, err = rt.LookupPod(s.Pod) if err != nil { - return nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) + return nil, nil, nil, errors.Wrapf(err, "error retrieving pod %s", s.Pod) } } @@ -41,47 +41,48 @@ if s.PidNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("pid", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.PidNS = defaultNS } if s.IpcNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("ipc", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.IpcNS = defaultNS } if s.UtsNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("uts", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.UtsNS = defaultNS } if s.UserNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("user", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.UserNS = defaultNS } if s.NetNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("net", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.NetNS = defaultNS } if s.CgroupNS.IsDefault() { defaultNS, err := GetDefaultNamespaceMode("cgroup", rtc, pod) if err != nil { - return nil, err + return nil, nil, nil, err } s.CgroupNS = defaultNS } options := []libpod.CtrCreateOption{} + if s.ContainerCreateCommand != nil { options = append(options, libpod.WithCreateCommand(s.ContainerCreateCommand)) } @@ -94,12 +95,11 @@ var resolvedImageName string newImage, resolvedImageName, err = rt.LibimageRuntime().LookupImage(s.Image, nil) if err != nil { - return nil, err + return nil, nil, nil, err } - imageData, err = newImage.Inspect(ctx, false) if err != nil { - return nil, err + return nil, nil, nil, err } // If the input name changed, we could properly resolve the // image. Otherwise, it must have been an ID where we're @@ -115,45 +115,62 @@ options = append(options, libpod.WithRootFSFromImage(newImage.ID(), resolvedImageName, s.RawImageName)) } if err := s.Validate(); err != nil { - return nil, errors.Wrap(err, "invalid config provided") + return nil, nil, nil, errors.Wrap(err, "invalid config provided") } finalMounts, finalVolumes, finalOverlays, err := finalizeMounts(ctx, s, rt, rtc, newImage) if err != nil { - return nil, err + return nil, nil, nil, err } command, err := makeCommand(ctx, s, imageData, rtc) if err != nil { - return nil, err + return nil, nil, nil, err } opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command) if err != nil { - return nil, err + return nil, nil, nil, err } options = append(options, opts...) - exitCommandArgs, err := CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false) + var exitCommandArgs []string + + exitCommandArgs, err = CreateExitCommandArgs(rt.StorageConfig(), rtc, logrus.IsLevelEnabled(logrus.DebugLevel), s.Remove, false) if err != nil { - return nil, err + return nil, nil, nil, err } + options = append(options, libpod.WithExitCommand(exitCommandArgs)) if len(s.Aliases) > 0 { options = append(options, libpod.WithNetworkAliases(s.Aliases)) } + if containerType := s.InitContainerType; len(containerType) > 0 { + options = append(options, libpod.WithInitCtrType(containerType)) + } + if len(s.Name) > 0 { + logrus.Debugf("setting container name %s", s.Name) + options = append(options, libpod.WithName(s.Name)) + } if len(s.Devices) > 0 { opts = extractCDIDevices(s) options = append(options, opts...) } - runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod, command) if err != nil { - return nil, err + return nil, nil, nil, err + } + return runtimeSpec, s, options, err +} +func ExecuteCreate(ctx context.Context, rt *libpod.Runtime, runtimeSpec *spec.Spec, s *specgen.SpecGenerator, infra bool, options ...libpod.CtrCreateOption) (*libpod.Container, error) { + ctr, err := rt.NewContainer(ctx, runtimeSpec, s, infra, options...) + if err != nil { + return ctr, err } - return rt.NewContainer(ctx, runtimeSpec, options...) + + return ctr, rt.PrepareVolumeOnCreateContainer(ctx, ctr) } func extractCDIDevices(s *specgen.SpecGenerator) []libpod.CtrCreateOption { @@ -245,11 +262,6 @@ if len(s.SdNotifyMode) > 0 { options = append(options, libpod.WithSdNotifyMode(s.SdNotifyMode)) } - - if len(s.Name) > 0 { - logrus.Debugf("setting container name %s", s.Name) - options = append(options, libpod.WithName(s.Name)) - } if pod != nil { logrus.Debugf("adding container to pod %s", pod.Name()) options = append(options, rt.WithPod(pod)) @@ -321,6 +333,9 @@ if s.WorkDir == "" { s.WorkDir = "/" } + if s.CreateWorkingDir { + options = append(options, libpod.WithCreateWorkingDir()) + } if s.StopSignal != nil { options = append(options, libpod.WithStopSignal(*s.StopSignal)) } @@ -346,7 +361,6 @@ options = append(options, libpod.WithLogDriver(s.LogConfiguration.Driver)) } } - // Security options if len(s.SelinuxOpts) > 0 { options = append(options, libpod.WithSecLabels(s.SelinuxOpts)) @@ -369,11 +383,11 @@ options = append(options, libpod.WithPrivileged(s.Privileged)) // Get namespace related options - namespaceOptions, err := namespaceOptions(ctx, s, rt, pod, imageData) + namespaceOpts, err := namespaceOptions(ctx, s, rt, pod, imageData) if err != nil { return nil, err } - options = append(options, namespaceOptions...) + options = append(options, namespaceOpts...) if len(s.ConmonPidFile) > 0 { options = append(options, libpod.WithConmonPidFile(s.ConmonPidFile)) @@ -400,7 +414,25 @@ } if len(s.Secrets) != 0 { - options = append(options, libpod.WithSecrets(s.Secrets)) + manager, err := rt.SecretsManager() + if err != nil { + return nil, err + } + var secrs []*libpod.ContainerSecret + for _, s := range s.Secrets { + secr, err := manager.Lookup(s.Source) + if err != nil { + return nil, err + } + secrs = append(secrs, &libpod.ContainerSecret{ + Secret: secr, + UID: s.UID, + GID: s.GID, + Mode: s.Mode, + Target: s.Target, + }) + } + options = append(options, libpod.WithSecrets(secrs)) } if len(s.EnvSecrets) != 0 { diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/container.go libpod-3.4.4+ds1/pkg/specgen/generate/container.go --- libpod-3.2.1+ds1/pkg/specgen/generate/container.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/container.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,12 +7,14 @@ "github.com/containers/common/libimage" "github.com/containers/podman/v3/libpod" + "github.com/containers/podman/v3/libpod/define" ann "github.com/containers/podman/v3/pkg/annotations" envLib "github.com/containers/podman/v3/pkg/env" "github.com/containers/podman/v3/pkg/signal" "github.com/containers/podman/v3/pkg/specgen" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "github.com/sirupsen/logrus" "golang.org/x/sys/unix" ) @@ -138,10 +140,29 @@ // VM, which is the default behavior // - "container" denotes the container should join the VM of the SandboxID // (the infra container) - if len(s.Pod) > 0 { annotations[ann.SandboxID] = s.Pod annotations[ann.ContainerType] = ann.ContainerTypeContainer + // Check if this is an init-ctr and if so, check if + // the pod is running. we do not want to add init-ctrs to + // a running pod because it creates confusion for us. + if len(s.InitContainerType) > 0 { + p, err := r.LookupPod(s.Pod) + if err != nil { + return nil, err + } + containerStatuses, err := p.Status() + if err != nil { + return nil, err + } + // If any one of the containers is running, the pod is considered to be + // running + for _, con := range containerStatuses { + if con == define.ContainerStateRunning { + return nil, errors.New("cannot add init-ctr to a running pod") + } + } + } } for _, v := range rtc.Containers.Annotations { @@ -203,6 +224,17 @@ if len(s.LogConfiguration.Driver) < 1 { s.LogConfiguration.Driver = rtc.Containers.LogDriver } + if len(rtc.Containers.LogTag) > 0 { + if s.LogConfiguration.Driver != define.JSONLogging { + if s.LogConfiguration.Options == nil { + s.LogConfiguration.Options = make(map[string]string) + } + + s.LogConfiguration.Options["tag"] = rtc.Containers.LogTag + } else { + logrus.Warnf("log_tag %q is not allowed with %q log_driver", rtc.Containers.LogTag, define.JSONLogging) + } + } warnings, err := verifyContainerResources(s) if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/kube/kube.go libpod-3.4.4+ds1/pkg/specgen/generate/kube/kube.go --- libpod-3.2.1+ds1/pkg/specgen/generate/kube/kube.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/kube/kube.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,12 +6,18 @@ "fmt" "net" "strings" + "time" "github.com/containers/common/libimage" "github.com/containers/common/pkg/parse" "github.com/containers/common/pkg/secrets" + "github.com/containers/image/v5/manifest" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" ann "github.com/containers/podman/v3/pkg/annotations" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/specgen/generate" "github.com/containers/podman/v3/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -19,25 +25,26 @@ "k8s.io/apimachinery/pkg/api/resource" ) -func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec) (*specgen.PodSpecGenerator, error) { - p := specgen.NewPodSpecGenerator() +func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) { + // p := specgen.NewPodSpecGenerator() + p.Net = &entities.NetOptions{} p.Name = podName p.Labels = podYAML.ObjectMeta.Labels // Kube pods must share {ipc, net, uts} by default - p.SharedNamespaces = append(p.SharedNamespaces, "ipc") - p.SharedNamespaces = append(p.SharedNamespaces, "net") - p.SharedNamespaces = append(p.SharedNamespaces, "uts") + p.Share = append(p.Share, "ipc") + p.Share = append(p.Share, "net") + p.Share = append(p.Share, "uts") // TODO we only configure Process namespace. We also need to account for Host{IPC,Network,PID} // which is not currently possible with pod create if podYAML.Spec.ShareProcessNamespace != nil && *podYAML.Spec.ShareProcessNamespace { - p.SharedNamespaces = append(p.SharedNamespaces, "pid") + p.Share = append(p.Share, "pid") } p.Hostname = podYAML.Spec.Hostname if p.Hostname == "" { p.Hostname = podName } if podYAML.Spec.HostNetwork { - p.NetNS.NSMode = specgen.Host + p.Net.Network = specgen.Namespace{NSMode: "host"} } if podYAML.Spec.HostAliases != nil { hosts := make([]string, 0, len(podYAML.Spec.HostAliases)) @@ -46,10 +53,10 @@ hosts = append(hosts, host+":"+hostAlias.IP) } } - p.HostAdd = hosts + p.Net.AddHosts = hosts } podPorts := getPodPorts(podYAML.Spec.Containers) - p.PortMappings = podPorts + p.Net.PublishPorts = podPorts if dnsConfig := podYAML.Spec.DNSConfig; dnsConfig != nil { // name servers @@ -58,11 +65,11 @@ for _, server := range dnsServers { servers = append(servers, net.ParseIP(server)) } - p.DNSServer = servers + p.Net.DNSServers = servers } // search domains if domains := dnsConfig.Searches; len(domains) > 0 { - p.DNSSearch = domains + p.Net.DNSSearch = domains } // dns options if options := dnsConfig.Options; len(options) > 0 { @@ -80,6 +87,8 @@ } type CtrSpecGenOptions struct { + // Annotations from the Pod + Annotations map[string]string // Container as read from the pod yaml Container v1.Container // Image available to use (pulled or found local) @@ -106,6 +115,11 @@ LogDriver string // Labels define key-value pairs of metadata Labels map[string]string + // + IsInfra bool + // InitContainerType sets what type the init container is + // Note: When playing a kube yaml, the inti container type will be set to "always" only + InitContainerType string } func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGenerator, error) { @@ -127,7 +141,13 @@ Driver: opts.LogDriver, } + s.InitContainerType = opts.InitContainerType + setupSecurityContext(s, opts.Container) + err := setupLivenessProbe(s, opts.Container, opts.RestartPolicy) + if err != nil { + return nil, errors.Wrap(err, "Failed to configure livenessProbe") + } // Since we prefix the container name with pod name to work-around the uniqueness requirement, // the seccomp profile should reference the actual container name from the YAML @@ -182,6 +202,19 @@ if imageData.Config.WorkingDir != "" { s.WorkDir = imageData.Config.WorkingDir } + if s.User == "" { + s.User = imageData.Config.User + } + + exposed, err := generate.GenExposedPorts(imageData.Config.ExposedPorts) + if err != nil { + return nil, err + } + + for k, v := range s.Expose { + exposed[k] = v + } + s.Expose = exposed // Pull entrypoint and cmd from image s.Entrypoint = imageData.Config.Entrypoint s.Command = imageData.Config.Cmd @@ -195,19 +228,19 @@ } } // If only the yaml.Command is specified, set it as the entrypoint and drop the image Cmd - if len(opts.Container.Command) != 0 { + if !opts.IsInfra && len(opts.Container.Command) != 0 { s.Entrypoint = opts.Container.Command s.Command = []string{} } // Only override the cmd field if yaml.Args is specified // Keep the image entrypoint, or the yaml.command if specified - if len(opts.Container.Args) != 0 { + if !opts.IsInfra && len(opts.Container.Args) != 0 { s.Command = opts.Container.Args } // FIXME, // we are currently ignoring imageData.Config.ExposedPorts - if opts.Container.WorkingDir != "" { + if !opts.IsInfra && opts.Container.WorkingDir != "" { s.WorkDir = opts.Container.WorkingDir } @@ -221,7 +254,7 @@ // Environment Variables envs := map[string]string{} for _, env := range imageData.Config.Env { - keyval := strings.Split(env, "=") + keyval := strings.SplitN(env, "=", 2) envs[keyval[0]] = keyval[1] } @@ -250,27 +283,35 @@ if !exists { return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name) } + + dest, options, err := parseMountPath(volume.MountPath, volume.ReadOnly) + if err != nil { + return nil, err + } + + volume.MountPath = dest switch volumeSource.Type { case KubeVolumeTypeBindMount: - if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil { - return nil, errors.Wrapf(err, "error in parsing MountPath") + // If the container has bind mounts, we need to check if + // a selinux mount option exists for it + for k, v := range opts.Annotations { + // Make sure the z/Z option is not already there (from editing the YAML) + if strings.Replace(k, define.BindMountPrefix, "", 1) == volumeSource.Source && !util.StringInSlice("z", options) && !util.StringInSlice("Z", options) { + options = append(options, v) + } } mount := spec.Mount{ Destination: volume.MountPath, Source: volumeSource.Source, Type: "bind", - } - if volume.ReadOnly { - mount.Options = []string{"ro"} + Options: options, } s.Mounts = append(s.Mounts, mount) case KubeVolumeTypeNamed: namedVolume := specgen.NamedVolume{ - Dest: volume.MountPath, - Name: volumeSource.Source, - } - if volume.ReadOnly { - namedVolume.Options = []string{"ro"} + Dest: volume.MountPath, + Name: volumeSource.Source, + Options: options, } s.Volumes = append(s.Volumes, &namedVolume) default: @@ -283,6 +324,8 @@ if opts.NetNSIsHost { s.NetNS.NSMode = specgen.Host } + // Always set the userns to host since k8s doesn't have support for userns yet + s.UserNS.NSMode = specgen.Host // Add labels that come from kube if len(s.Labels) == 0 { @@ -300,6 +343,118 @@ return s, nil } +func parseMountPath(mountPath string, readOnly bool) (string, []string, error) { + options := []string{} + splitVol := strings.Split(mountPath, ":") + if len(splitVol) > 2 { + return "", options, errors.Errorf("%q incorrect volume format, should be ctr-dir[:option]", mountPath) + } + dest := splitVol[0] + if len(splitVol) > 1 { + options = strings.Split(splitVol[1], ",") + } + if err := parse.ValidateVolumeCtrDir(dest); err != nil { + return "", options, errors.Wrapf(err, "parsing MountPath") + } + if readOnly { + options = append(options, "ro") + } + opts, err := parse.ValidateVolumeOpts(options) + if err != nil { + return "", opts, errors.Wrapf(err, "parsing MountOptions") + } + return dest, opts, nil +} + +func setupLivenessProbe(s *specgen.SpecGenerator, containerYAML v1.Container, restartPolicy string) error { + var err error + if containerYAML.LivenessProbe == nil { + return nil + } + emptyHandler := v1.Handler{} + if containerYAML.LivenessProbe.Handler != emptyHandler { + var commandString string + failureCmd := "exit 1" + probe := containerYAML.LivenessProbe + probeHandler := probe.Handler + + // append `exit 1` to `cmd` so healthcheck can be marked as `unhealthy`. + // append `kill 1` to `cmd` if appropriate restart policy is configured. + if restartPolicy == "always" || restartPolicy == "onfailure" { + // container will be restarted so we can kill init. + failureCmd = "kill 1" + } + + // configure healthcheck on the basis of Handler Actions. + if probeHandler.Exec != nil { + execString := strings.Join(probeHandler.Exec.Command, " ") + commandString = fmt.Sprintf("%s || %s", execString, failureCmd) + } else if probeHandler.HTTPGet != nil { + commandString = fmt.Sprintf("curl %s://%s:%d/%s || %s", probeHandler.HTTPGet.Scheme, probeHandler.HTTPGet.Host, probeHandler.HTTPGet.Port.IntValue(), probeHandler.HTTPGet.Path, failureCmd) + } else if probeHandler.TCPSocket != nil { + commandString = fmt.Sprintf("nc -z -v %s %d || %s", probeHandler.TCPSocket.Host, probeHandler.TCPSocket.Port.IntValue(), failureCmd) + } + s.HealthConfig, err = makeHealthCheck(commandString, probe.PeriodSeconds, probe.FailureThreshold, probe.TimeoutSeconds, probe.InitialDelaySeconds) + if err != nil { + return err + } + return nil + } + return nil +} + +func makeHealthCheck(inCmd string, interval int32, retries int32, timeout int32, startPeriod int32) (*manifest.Schema2HealthConfig, error) { + // Every healthcheck requires a command + if len(inCmd) == 0 { + return nil, errors.New("Must define a healthcheck command for all healthchecks") + } + + // first try to parse option value as JSON array of strings... + cmd := []string{} + + if inCmd == "none" { + cmd = []string{"NONE"} + } else { + err := json.Unmarshal([]byte(inCmd), &cmd) + if err != nil { + // ...otherwise pass it to "/bin/sh -c" inside the container + cmd = []string{"CMD-SHELL"} + cmd = append(cmd, strings.Split(inCmd, " ")...) + } + } + hc := manifest.Schema2HealthConfig{ + Test: cmd, + } + + if interval < 1 { + //kubernetes interval defaults to 10 sec and cannot be less than 1 + interval = 10 + } + hc.Interval = (time.Duration(interval) * time.Second) + if retries < 1 { + //kubernetes retries defaults to 3 + retries = 3 + } + hc.Retries = int(retries) + if timeout < 1 { + //kubernetes timeout defaults to 1 + timeout = 1 + } + timeoutDuration := (time.Duration(timeout) * time.Second) + if timeoutDuration < time.Duration(1) { + return nil, errors.New("healthcheck-timeout must be at least 1 second") + } + hc.Timeout = timeoutDuration + + startPeriodDuration := (time.Duration(startPeriod) * time.Second) + if startPeriodDuration < time.Duration(0) { + return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") + } + hc.StartPeriod = startPeriodDuration + + return &hc, nil +} + func setupSecurityContext(s *specgen.SpecGenerator, containerYAML v1.Container) { if containerYAML.SecurityContext == nil { return @@ -454,8 +609,8 @@ // getPodPorts converts a slice of kube container descriptions to an // array of portmapping -func getPodPorts(containers []v1.Container) []specgen.PortMapping { - var infraPorts []specgen.PortMapping +func getPodPorts(containers []v1.Container) []types.PortMapping { + var infraPorts []types.PortMapping for _, container := range containers { for _, p := range container.Ports { if p.HostPort != 0 && p.ContainerPort == 0 { @@ -464,7 +619,7 @@ if p.Protocol == "" { p.Protocol = "tcp" } - portBinding := specgen.PortMapping{ + portBinding := types.PortMapping{ HostPort: uint16(p.HostPort), ContainerPort: uint16(p.ContainerPort), Protocol: strings.ToLower(string(p.Protocol)), diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/namespaces.go libpod-3.4.4+ds1/pkg/specgen/generate/namespaces.go --- libpod-3.2.1+ds1/pkg/specgen/generate/namespaces.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/namespaces.go 2021-12-26 00:45:51.000000000 +0000 @@ -66,7 +66,7 @@ case "cgroup": return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS) case "net": - ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS) + ns, _, err := specgen.ParseNetworkNamespace(cfg.Containers.NetNS, cfg.Containers.RootlessNetworking == "cni") return ns, err } @@ -175,6 +175,11 @@ if pod == nil || infraCtr == nil { return nil, errNoInfra } + // Inherit the user from the infra container if it is set and --user has not + // been set explicitly + if infraCtr.User() != "" && s.User == "" { + toReturn = append(toReturn, libpod.WithUser(infraCtr.User())) + } toReturn = append(toReturn, libpod.WithUserNSFrom(infraCtr)) case specgen.FromContainer: userCtr, err := rt.LookupContainer(s.UserNS.Value) @@ -184,7 +189,10 @@ toReturn = append(toReturn, libpod.WithUserNSFrom(userCtr)) } - if s.IDMappings != nil { + // This wipes the UserNS settings that get set from the infra container + // when we are inheritting from the pod. So only apply this if the container + // is not being created in a pod. + if s.IDMappings != nil && pod == nil { toReturn = append(toReturn, libpod.WithIDMappings(*s.IDMappings)) } if s.User != "" { @@ -234,7 +242,7 @@ } toReturn = append(toReturn, libpod.WithNetNSFrom(netCtr)) case specgen.Slirp: - portMappings, err := createPortMappings(ctx, s, imageData) + portMappings, expose, err := createPortMappings(ctx, s, imageData) if err != nil { return nil, err } @@ -242,15 +250,15 @@ if s.NetNS.Value != "" { val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value) } - toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, val, nil)) + toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, s.CNINetworks)) case specgen.Private: fallthrough case specgen.Bridge: - portMappings, err := createPortMappings(ctx, s, imageData) + portMappings, expose, err := createPortMappings(ctx, s, imageData) if err != nil { return nil, err } - toReturn = append(toReturn, libpod.WithNetNS(portMappings, postConfigureNetNS, "bridge", s.CNINetworks)) + toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, "bridge", s.CNINetworks)) } if s.UseImageHosts { @@ -379,46 +387,8 @@ } // User - switch s.UserNS.NSMode { - case specgen.Path: - if _, err := os.Stat(s.UserNS.Value); err != nil { - return errors.Wrap(err, "cannot find specified user namespace path") - } - if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), s.UserNS.Value); err != nil { - return err - } - // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping - g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) - g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) - case specgen.Host: - if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil { - return err - } - case specgen.KeepID: - var ( - err error - uid, gid int - ) - s.IDMappings, uid, gid, err = util.GetKeepIDMapping() - if err != nil { - return err - } - g.SetProcessUID(uint32(uid)) - g.SetProcessGID(uint32(gid)) - fallthrough - case specgen.Private: - if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { - return err - } - if s.IDMappings == nil || (len(s.IDMappings.UIDMap) == 0 && len(s.IDMappings.GIDMap) == 0) { - return errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace") - } - for _, uidmap := range s.IDMappings.UIDMap { - g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) - } - for _, gidmap := range s.IDMappings.GIDMap { - g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) - } + if _, err := specgen.SetupUserNS(s.IDMappings, s.UserNS, g); err != nil { + return err } // Cgroup @@ -474,7 +444,7 @@ // GetNamespaceOptions transforms a slice of kernel namespaces // into a slice of pod create options. Currently, not all // kernel namespaces are supported, and they will be returned in an error -func GetNamespaceOptions(ns []string) ([]libpod.PodCreateOption, error) { +func GetNamespaceOptions(ns []string, netnsIsHost bool) ([]libpod.PodCreateOption, error) { var options []libpod.PodCreateOption var erroredOptions []libpod.PodCreateOption if ns == nil { @@ -486,7 +456,10 @@ case "cgroup": options = append(options, libpod.WithPodCgroups()) case "net": - options = append(options, libpod.WithPodNet()) + // share the netns setting with other containers in the pod only when it is not set to host + if !netnsIsHost { + options = append(options, libpod.WithPodNet()) + } case "mnt": return erroredOptions, errors.Errorf("Mount sharing functionality not supported on pod level") case "pid": diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/oci.go libpod-3.4.4+ds1/pkg/specgen/generate/oci.go --- libpod-3.2.1+ds1/pkg/specgen/generate/oci.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/oci.go 2021-12-26 00:45:51.000000000 +0000 @@ -201,7 +201,8 @@ Options: []string{"rprivate", "nosuid", "noexec", "nodev", "rw"}, } g.AddMount(sysMnt) - } else if !canMountSys { + } + if !canMountSys { addCgroup = false g.RemoveMount("/sys") r := "ro" @@ -285,6 +286,9 @@ } g.AddMount(cgroupMnt) } + + g.Config.Linux.Personality = s.Personality + g.SetProcessCwd(s.WorkDir) g.SetProcessArgs(finalCmd) @@ -321,6 +325,10 @@ } } + for _, dev := range s.DeviceCGroupRule { + g.AddLinuxResourcesDevice(true, dev.Type, dev.Major, dev.Minor, dev.Access) + } + BlockAccessToKernelFilesystems(s.Privileged, s.PidNS.IsHost(), s.Mask, s.Unmask, &g) for name, val := range s.Env { diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/pod_create.go libpod-3.4.4+ds1/pkg/specgen/generate/pod_create.go --- libpod-3.2.1+ds1/pkg/specgen/generate/pod_create.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/pod_create.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,48 +2,81 @@ import ( "context" + "net" + "github.com/containers/common/pkg/config" "github.com/containers/podman/v3/libpod" - "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/specgen" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) -func MakePod(p *specgen.PodSpecGenerator, rt *libpod.Runtime) (*libpod.Pod, error) { - if err := p.Validate(); err != nil { +func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) { + if err := p.PodSpecGen.Validate(); err != nil { return nil, err } - options, err := createPodOptions(p, rt) + if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil { + var err error + p.PodSpecGen.InfraContainerSpec, err = MapSpec(&p.PodSpecGen) + if err != nil { + return nil, err + } + } + + options, err := createPodOptions(&p.PodSpecGen, rt, p.PodSpecGen.InfraContainerSpec) + if err != nil { + return nil, err + } + pod, err := rt.NewPod(context.Background(), p.PodSpecGen, options...) if err != nil { return nil, err } - return rt.NewPod(context.Background(), options...) + if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil { + p.PodSpecGen.InfraContainerSpec.ContainerCreateCommand = []string{} // we do NOT want os.Args as the command, will display the pod create cmd + if p.PodSpecGen.InfraContainerSpec.Name == "" { + p.PodSpecGen.InfraContainerSpec.Name = pod.ID()[:12] + "-infra" + } + _, err = CompleteSpec(context.Background(), rt, p.PodSpecGen.InfraContainerSpec) + if err != nil { + return nil, err + } + p.PodSpecGen.InfraContainerSpec.User = "" // infraSpec user will get incorrectly assigned via the container creation process, overwrite here + rtSpec, spec, opts, err := MakeContainer(context.Background(), rt, p.PodSpecGen.InfraContainerSpec) + if err != nil { + return nil, err + } + spec.Pod = pod.ID() + opts = append(opts, rt.WithPod(pod)) + spec.CgroupParent = pod.CgroupParent() + infraCtr, err := ExecuteCreate(context.Background(), rt, rtSpec, spec, true, opts...) + if err != nil { + return nil, err + } + pod, err = rt.AddInfra(context.Background(), pod, infraCtr) + if err != nil { + return nil, err + } + } + return pod, nil } -func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime) ([]libpod.PodCreateOption, error) { +func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec *specgen.SpecGenerator) ([]libpod.PodCreateOption, error) { var ( options []libpod.PodCreateOption ) - if !p.NoInfra { + if !p.NoInfra { //&& infraSpec != nil { options = append(options, libpod.WithInfraContainer()) - nsOptions, err := GetNamespaceOptions(p.SharedNamespaces) + nsOptions, err := GetNamespaceOptions(p.SharedNamespaces, p.InfraContainerSpec.NetNS.IsHost()) if err != nil { return nil, err } options = append(options, nsOptions...) - - // Make our exit command - storageConfig := rt.StorageConfig() - runtimeConfig, err := rt.GetConfig() - if err != nil { - return nil, err - } - exitCommand, err := CreateExitCommandArgs(storageConfig, runtimeConfig, logrus.IsLevelEnabled(logrus.DebugLevel), false, false) - if err != nil { - return nil, errors.Wrapf(err, "error creating infra container exit command") + // Use pod user and infra userns only when --userns is not set to host + if !p.InfraContainerSpec.UserNS.IsHost() && !p.InfraContainerSpec.UserNS.IsDefault() { + options = append(options, libpod.WithPodUser()) } - options = append(options, libpod.WithPodInfraExitCommand(exitCommand)) } if len(p.CgroupParent) > 0 { options = append(options, libpod.WithPodCgroupParent(p.CgroupParent)) @@ -54,89 +87,107 @@ if len(p.Name) > 0 { options = append(options, libpod.WithPodName(p.Name)) } + if p.PodCreateCommand != nil { + options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand)) + } + if len(p.Hostname) > 0 { options = append(options, libpod.WithPodHostname(p.Hostname)) } - if len(p.HostAdd) > 0 { - options = append(options, libpod.WithPodHosts(p.HostAdd)) - } - if len(p.DNSServer) > 0 { - var dnsServers []string - for _, d := range p.DNSServer { - dnsServers = append(dnsServers, d.String()) - } - options = append(options, libpod.WithPodDNS(dnsServers)) - } - if len(p.DNSOption) > 0 { - options = append(options, libpod.WithPodDNSOption(p.DNSOption)) - } - if len(p.DNSSearch) > 0 { - options = append(options, libpod.WithPodDNSSearch(p.DNSSearch)) - } - if p.StaticIP != nil { - options = append(options, libpod.WithPodStaticIP(*p.StaticIP)) - } - if p.StaticMAC != nil { - options = append(options, libpod.WithPodStaticMAC(*p.StaticMAC)) - } - if p.NoManageResolvConf { - options = append(options, libpod.WithPodUseImageResolvConf()) - } - if len(p.CNINetworks) > 0 { - options = append(options, libpod.WithPodNetworks(p.CNINetworks)) - } - if len(p.InfraImage) > 0 { - options = append(options, libpod.WithInfraImage(p.InfraImage)) - } + return options, nil +} - if len(p.InfraCommand) > 0 { - options = append(options, libpod.WithInfraCommand(p.InfraCommand)) +// MapSpec modifies the already filled Infra specgenerator, +// replacing necessary values with those specified in pod creation +func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) { + if len(p.PortMappings) > 0 { + ports, _, _, err := ParsePortMapping(p.PortMappings) + if err != nil { + return nil, err + } + p.InfraContainerSpec.PortMappings = libpod.WithInfraContainerPorts(ports, p.InfraContainerSpec) } - switch p.NetNS.NSMode { case specgen.Default, "": if p.NoInfra { logrus.Debugf("No networking because the infra container is missing") break } - if rootless.IsRootless() { - logrus.Debugf("Pod will use slirp4netns") - options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) - } else { - logrus.Debugf("Pod using bridge network mode") - } case specgen.Bridge: + p.InfraContainerSpec.NetNS.NSMode = specgen.Bridge logrus.Debugf("Pod using bridge network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") - options = append(options, libpod.WithPodHostNetwork()) + if len(p.InfraContainerSpec.PortMappings) > 0 || + p.InfraContainerSpec.StaticIP != nil || + p.InfraContainerSpec.StaticMAC != nil || + len(p.InfraContainerSpec.CNINetworks) > 0 || + p.InfraContainerSpec.NetNS.NSMode == specgen.NoNetwork { + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot set host network if network-related configuration is specified") + } + p.InfraContainerSpec.NetNS.NSMode = specgen.Host case specgen.Slirp: logrus.Debugf("Pod will use slirp4netns") - options = append(options, libpod.WithPodSlirp4netns(p.NetworkOptions)) + if p.InfraContainerSpec.NetNS.NSMode != "host" { + p.InfraContainerSpec.NetworkOptions = p.NetworkOptions + p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns") + } case specgen.NoNetwork: logrus.Debugf("Pod will not use networking") - options = append(options, libpod.WithPodNoNetwork()) + if len(p.InfraContainerSpec.PortMappings) > 0 || + p.InfraContainerSpec.StaticIP != nil || + p.InfraContainerSpec.StaticMAC != nil || + len(p.InfraContainerSpec.CNINetworks) > 0 || + p.InfraContainerSpec.NetNS.NSMode == "host" { + return nil, errors.Wrapf(define.ErrInvalidArg, "cannot disable pod network if network-related configuration is specified") + } + p.InfraContainerSpec.NetNS.NSMode = specgen.NoNetwork default: return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) } - if p.NoManageHosts { - options = append(options, libpod.WithPodUseImageHosts()) + if len(p.InfraCommand) > 0 { + p.InfraContainerSpec.Entrypoint = p.InfraCommand } - if len(p.PortMappings) > 0 { - ports, _, _, err := parsePortMapping(p.PortMappings) - if err != nil { - return nil, err - } - options = append(options, libpod.WithInfraContainerPorts(ports)) + + if len(p.HostAdd) > 0 { + p.InfraContainerSpec.HostAdd = p.HostAdd } - options = append(options, libpod.WithPodCgroups()) - if p.PodCreateCommand != nil { - options = append(options, libpod.WithPodCreateCommand(p.PodCreateCommand)) + if len(p.DNSServer) > 0 { + var dnsServers []net.IP + dnsServers = append(dnsServers, p.DNSServer...) + + p.InfraContainerSpec.DNSServers = dnsServers + } + if len(p.DNSOption) > 0 { + p.InfraContainerSpec.DNSOptions = p.DNSOption + } + if len(p.DNSSearch) > 0 { + p.InfraContainerSpec.DNSSearch = p.DNSSearch + } + if p.StaticIP != nil { + p.InfraContainerSpec.StaticIP = p.StaticIP + } + if p.StaticMAC != nil { + p.InfraContainerSpec.StaticMAC = p.StaticMAC + } + if p.NoManageResolvConf { + p.InfraContainerSpec.UseImageResolvConf = true } + if len(p.CNINetworks) > 0 { + p.InfraContainerSpec.CNINetworks = p.CNINetworks + } + if p.NoManageHosts { + p.InfraContainerSpec.UseImageHosts = p.NoManageHosts + } + if len(p.InfraConmonPidFile) > 0 { - options = append(options, libpod.WithInfraConmonPidFile(p.InfraConmonPidFile)) + p.InfraContainerSpec.ConmonPidFile = p.InfraConmonPidFile } - return options, nil + + if p.InfraImage != config.DefaultInfraImage { + p.InfraContainerSpec.Image = p.InfraImage + } + return p.InfraContainerSpec, nil } diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/ports.go libpod-3.4.4+ds1/pkg/specgen/generate/ports.go --- libpod-3.2.1+ds1/pkg/specgen/generate/ports.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/ports.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "strings" "github.com/containers/common/libimage" + "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/podman/v3/utils" "github.com/containers/podman/v3/pkg/specgen" @@ -24,7 +25,7 @@ // Parse port maps to OCICNI port mappings. // Returns a set of OCICNI port mappings, and maps of utilized container and // host ports. -func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { +func ParsePortMapping(portMappings []types.PortMapping) ([]ocicni.PortMapping, map[string]map[string]map[uint16]uint16, map[string]map[string]map[uint16]uint16, error) { // First, we need to validate the ports passed in the specgen, and then // convert them into CNI port mappings. type tempMapping struct { @@ -253,46 +254,31 @@ } // Make final port mappings for the container -func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]ocicni.PortMapping, error) { - finalMappings, containerPortValidate, hostPortValidate, err := parsePortMapping(s.PortMappings) +func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]ocicni.PortMapping, map[uint16][]string, error) { + finalMappings, containerPortValidate, hostPortValidate, err := ParsePortMapping(s.PortMappings) if err != nil { - return nil, err + return nil, nil, err } - // If not publishing exposed ports, or if we are publishing and there is - // nothing to publish - then just return the port mappings we've made so - // far. - if !s.PublishExposedPorts || (len(s.Expose) == 0 && imageData == nil) { - return finalMappings, nil + // No exposed ports so return the port mappings we've made so far. + if len(s.Expose) == 0 && imageData == nil { + return finalMappings, nil, nil } logrus.Debugf("Adding exposed ports") - // We need to merge s.Expose into image exposed ports expose := make(map[uint16]string) - for k, v := range s.Expose { - expose[k] = v - } if imageData != nil { - for imgExpose := range imageData.Config.ExposedPorts { - // Expose format is portNumber[/protocol] - splitExpose := strings.SplitN(imgExpose, "/", 2) - num, err := strconv.Atoi(splitExpose[0]) - if err != nil { - return nil, errors.Wrapf(err, "unable to convert image EXPOSE statement %q to port number", imgExpose) - } - if num > 65535 || num < 1 { - return nil, errors.Errorf("%d from image EXPOSE statement %q is not a valid port number", num, imgExpose) - } - // No need to validate protocol, we'll do it below. - if len(splitExpose) == 1 { - expose[uint16(num)] = "tcp" - } else { - expose[uint16(num)] = splitExpose[1] - } + expose, err = GenExposedPorts(imageData.Config.ExposedPorts) + if err != nil { + return nil, nil, err } } + // We need to merge s.Expose into image exposed ports + for k, v := range s.Expose { + expose[k] = v + } // There's been a request to expose some ports. Let's do that. // Start by figuring out what needs to be exposed. // This is a map of container port number to protocols to expose. @@ -301,11 +287,11 @@ // Validate protocol first protocols, err := checkProtocol(proto, false) if err != nil { - return nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port) + return nil, nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port) } if port == 0 { - return nil, errors.Errorf("cannot expose 0 as it is not a valid port number") + return nil, nil, errors.Errorf("cannot expose 0 as it is not a valid port number") } // Check to see if the port is already present in existing @@ -329,6 +315,11 @@ } } + // If not publishing exposed ports return mappings and exposed ports. + if !s.PublishExposedPorts { + return finalMappings, toExpose, nil + } + // We now have a final list of ports that we want exposed. // Let's find empty, unallocated host ports for them. for port, protocols := range toExpose { @@ -344,7 +335,7 @@ // unfortunate for the UDP case. candidate, err := utils.GetRandomPort() if err != nil { - return nil, err + return nil, nil, err } // Check if the host port is already bound @@ -375,12 +366,12 @@ } if tries == 0 && hostPort == 0 { // We failed to find an open port. - return nil, errors.Errorf("failed to find an open port to expose container port %d on the host", port) + return nil, nil, errors.Errorf("failed to find an open port to expose container port %d on the host", port) } } } - return finalMappings, nil + return finalMappings, nil, nil } // Check a string to ensure it is a comma-separated set of valid protocols @@ -417,3 +408,25 @@ return finalProto, nil } + +func GenExposedPorts(exposedPorts map[string]struct{}) (map[uint16]string, error) { + expose := make(map[uint16]string) + for imgExpose := range exposedPorts { + // Expose format is portNumber[/protocol] + splitExpose := strings.SplitN(imgExpose, "/", 2) + num, err := strconv.Atoi(splitExpose[0]) + if err != nil { + return nil, errors.Wrapf(err, "unable to convert image EXPOSE statement %q to port number", imgExpose) + } + if num > 65535 || num < 1 { + return nil, errors.Errorf("%d from image EXPOSE statement %q is not a valid port number", num, imgExpose) + } + // No need to validate protocol, we'll do it below. + if len(splitExpose) == 1 { + expose[uint16(num)] = "tcp" + } else { + expose[uint16(num)] = splitExpose[1] + } + } + return expose, nil +} diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/storage.go libpod-3.4.4+ds1/pkg/specgen/generate/storage.go --- libpod-3.2.1+ds1/pkg/specgen/generate/storage.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/storage.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "github.com/containers/common/libimage" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/parse" "github.com/containers/podman/v3/libpod" "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/specgen" @@ -59,6 +60,9 @@ for _, m := range s.Mounts { // Ensure that mount dest is clean, so that it can be // compared against named volumes and avoid duplicate mounts. + if err = parse.ValidateVolumeCtrDir(m.Destination); err != nil { + return nil, nil, nil, err + } cleanDestination := filepath.Clean(m.Destination) if _, ok := unifiedMounts[cleanDestination]; ok { return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination) @@ -67,34 +71,54 @@ } for _, m := range commonMounts { - if _, ok := unifiedMounts[m.Destination]; !ok { - unifiedMounts[m.Destination] = m + if err = parse.ValidateVolumeCtrDir(m.Destination); err != nil { + return nil, nil, nil, err + } + cleanDestination := filepath.Clean(m.Destination) + if _, ok := unifiedMounts[cleanDestination]; !ok { + unifiedMounts[cleanDestination] = m } } for _, v := range s.Volumes { - if _, ok := unifiedVolumes[v.Dest]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Dest) + if err = parse.ValidateVolumeCtrDir(v.Dest); err != nil { + return nil, nil, nil, err } - unifiedVolumes[v.Dest] = v + cleanDestination := filepath.Clean(v.Dest) + if _, ok := unifiedVolumes[cleanDestination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) + } + unifiedVolumes[cleanDestination] = v } for _, v := range commonVolumes { - if _, ok := unifiedVolumes[v.Dest]; !ok { - unifiedVolumes[v.Dest] = v + if err = parse.ValidateVolumeCtrDir(v.Dest); err != nil { + return nil, nil, nil, err + } + cleanDestination := filepath.Clean(v.Dest) + if _, ok := unifiedVolumes[cleanDestination]; !ok { + unifiedVolumes[cleanDestination] = v } } for _, v := range s.OverlayVolumes { - if _, ok := unifiedOverlays[v.Destination]; ok { - return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", v.Destination) + if err = parse.ValidateVolumeCtrDir(v.Destination); err != nil { + return nil, nil, nil, err } - unifiedOverlays[v.Destination] = v + cleanDestination := filepath.Clean(v.Destination) + if _, ok := unifiedOverlays[cleanDestination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) + } + unifiedOverlays[cleanDestination] = v } for _, v := range commonOverlayVolumes { - if _, ok := unifiedOverlays[v.Destination]; ok { - unifiedOverlays[v.Destination] = v + if err = parse.ValidateVolumeCtrDir(v.Destination); err != nil { + return nil, nil, nil, err + } + cleanDestination := filepath.Clean(v.Destination) + if _, ok := unifiedOverlays[cleanDestination]; !ok { + unifiedOverlays[cleanDestination] = v } } @@ -304,9 +328,13 @@ if _, ok := finalMounts[namedVol.Dest]; ok { logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID()) } + if err = parse.ValidateVolumeCtrDir(namedVol.Dest); err != nil { + return nil, nil, err + } + cleanDest := filepath.Clean(namedVol.Dest) newVol := new(specgen.NamedVolume) - newVol.Dest = namedVol.Dest + newVol.Dest = cleanDest newVol.Options = namedVol.Options newVol.Name = namedVol.Name diff -Nru libpod-3.2.1+ds1/pkg/specgen/generate/validate.go libpod-3.4.4+ds1/pkg/specgen/generate/validate.go --- libpod-3.2.1+ds1/pkg/specgen/generate/validate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/generate/validate.go 2021-12-26 00:45:51.000000000 +0000 @@ -72,10 +72,9 @@ // Pids checks if s.ResourceLimits.Pids != nil { - pids := s.ResourceLimits.Pids // TODO: Should this be 0, or checking that ResourceLimits.Pids // is set at all? - if pids.Limit > 0 && !sysInfo.PidsLimit { + if s.ResourceLimits.Pids.Limit >= 0 && !sysInfo.PidsLimit { warnings = append(warnings, "Your kernel does not support pids limit capabilities or the cgroup is not mounted. PIDs limit discarded.") s.ResourceLimits.Pids = nil } diff -Nru libpod-3.2.1+ds1/pkg/specgen/namespaces.go libpod-3.4.4+ds1/pkg/specgen/namespaces.go --- libpod-3.2.1+ds1/pkg/specgen/namespaces.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/namespaces.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,10 +1,16 @@ package specgen import ( + "fmt" + "os" "strings" "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/podman/v3/pkg/rootless" + "github.com/containers/podman/v3/pkg/util" + "github.com/containers/storage" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/runtime-tools/generate" "github.com/pkg/errors" ) @@ -68,6 +74,11 @@ return n.NSMode == Host } +// IsBridge returns a bool if the namespace is a Bridge +func (n *Namespace) IsBridge() bool { + return n.NSMode == Bridge +} + // IsPath indicates via bool if the namespace is based on a path func (n *Namespace) IsPath() bool { return n.NSMode == Path @@ -98,6 +109,13 @@ return n.NSMode == KeepID } +func (n *Namespace) String() string { + if n.Value != "" { + return fmt.Sprintf("%s:%s", n.NSMode, n.Value) + } + return string(n.NSMode) +} + func validateUserNS(n *Namespace) error { if n == nil { return nil @@ -253,7 +271,7 @@ // ParseNetworkNamespace parses a network namespace specification in string // form. // Returns a namespace and (optionally) a list of CNI networks to join. -func ParseNetworkNamespace(ns string) (Namespace, []string, error) { +func ParseNetworkNamespace(ns string, rootlessDefaultCNI bool) (Namespace, []string, error) { toReturn := Namespace{} var cniNetworks []string // Net defaults to Slirp on rootless @@ -264,7 +282,11 @@ toReturn.NSMode = FromPod case ns == "" || ns == string(Default) || ns == string(Private): if rootless.IsRootless() { - toReturn.NSMode = Slirp + if rootlessDefaultCNI { + toReturn.NSMode = Bridge + } else { + toReturn.NSMode = Slirp + } } else { toReturn.NSMode = Bridge } @@ -297,3 +319,65 @@ return toReturn, cniNetworks, nil } + +func ParseNetworkString(network string) (Namespace, []string, map[string][]string, error) { + var networkOptions map[string][]string + parts := strings.SplitN(network, ":", 2) + + ns, cniNets, err := ParseNetworkNamespace(network, containerConfig.Containers.RootlessNetworking == "cni") + if err != nil { + return Namespace{}, nil, nil, err + } + + if len(parts) > 1 { + networkOptions = make(map[string][]string) + networkOptions[parts[0]] = strings.Split(parts[1], ",") + cniNets = nil + } + return ns, cniNets, networkOptions, nil +} + +func SetupUserNS(idmappings *storage.IDMappingOptions, userns Namespace, g *generate.Generator) (string, error) { + // User + var user string + switch userns.NSMode { + case Path: + if _, err := os.Stat(userns.Value); err != nil { + return user, errors.Wrap(err, "cannot find specified user namespace path") + } + if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), userns.Value); err != nil { + return user, err + } + // runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping + g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1)) + g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1)) + case Host: + if err := g.RemoveLinuxNamespace(string(spec.UserNamespace)); err != nil { + return user, err + } + case KeepID: + mappings, uid, gid, err := util.GetKeepIDMapping() + if err != nil { + return user, err + } + idmappings = mappings + g.SetProcessUID(uint32(uid)) + g.SetProcessGID(uint32(gid)) + user = fmt.Sprintf("%d:%d", uid, gid) + fallthrough + case Private: + if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil { + return user, err + } + if idmappings == nil || (len(idmappings.UIDMap) == 0 && len(idmappings.GIDMap) == 0) { + return user, errors.Errorf("must provide at least one UID or GID mapping to configure a user namespace") + } + for _, uidmap := range idmappings.UIDMap { + g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size)) + } + for _, gidmap := range idmappings.GIDMap { + g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size)) + } + } + return user, nil +} diff -Nru libpod-3.2.1+ds1/pkg/specgen/podspecgen.go libpod-3.4.4+ds1/pkg/specgen/podspecgen.go --- libpod-3.2.1+ds1/pkg/specgen/podspecgen.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/podspecgen.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,6 +2,9 @@ import ( "net" + + "github.com/containers/podman/v3/libpod/network/types" + spec "github.com/opencontainers/runtime-spec/specs-go" ) // PodBasicConfig contains basic configuration options for pods. @@ -41,6 +44,12 @@ // Conflicts with NoInfra=true. // Optional. InfraImage string `json:"infra_image,omitempty"` + // InfraName is the name that will be used for the infra container. + // If not set, the default set in the Libpod configuration file will be + // used. + // Conflicts with NoInfra=true. + // Optional. + InfraName string `json:"infra_name,omitempty"` // SharedNamespaces instructs the pod to share a set of namespaces. // Shared namespaces will be joined (by default) by every container // which joins the pod. @@ -55,6 +64,14 @@ // (e.g. `podman generate systemd --new`). // Optional. PodCreateCommand []string `json:"pod_create_command,omitempty"` + // Pid sets the process id namespace of the pod + // Optional (defaults to private if unset). This sets the PID namespace of the infra container + // This configuration will then be shared with the entire pod if PID namespace sharing is enabled via --share + Pid Namespace `json:"pidns,omitempty"` + // Userns is used to indicate which kind of Usernamespace to enter. + // Any containers created within the pod will inherit the pod's userns settings. + // Optional + Userns Namespace `json:"userns,omitempty"` } // PodNetworkConfig contains networking configuration for a pod. @@ -86,7 +103,7 @@ // container, this will forward the ports to the entire pod. // Only available if NetNS is set to Bridge or Slirp. // Optional. - PortMappings []PortMapping `json:"portmappings,omitempty"` + PortMappings []types.PortMapping `json:"portmappings,omitempty"` // CNINetworks is a list of CNI networks that the infra container will // join. As, by default, containers share their network with the infra // container, these networks will effectively be joined by the @@ -155,6 +172,17 @@ PodBasicConfig PodNetworkConfig PodCgroupConfig + PodResourceConfig + InfraContainerSpec *SpecGenerator `json:"-"` +} + +type PodResourceConfig struct { + // ResourceLimits contains linux specific CPU data for the pod + ResourceLimits *spec.LinuxResources `json:"resource_limits,omitempty"` + // CPU period of the cpuset, determined by --cpus + CPUPeriod uint64 `json:"cpu_period,omitempty"` + // CPU quota of the cpuset, determined by --cpus + CPUQuota int64 `json:"cpu_quota,omitempty"` } // NewPodSpecGenerator creates a new pod spec diff -Nru libpod-3.2.1+ds1/pkg/specgen/pod_validate.go libpod-3.4.4+ds1/pkg/specgen/pod_validate.go --- libpod-3.2.1+ds1/pkg/specgen/pod_validate.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/pod_validate.go 2021-12-26 00:45:51.000000000 +0000 @@ -36,6 +36,9 @@ if len(p.InfraImage) > 0 { return exclusivePodOptions("NoInfra", "InfraImage") } + if len(p.InfraName) > 0 { + return exclusivePodOptions("NoInfra", "InfraName") + } if len(p.SharedNamespaces) > 0 { return exclusivePodOptions("NoInfra", "SharedNamespaces") } diff -Nru libpod-3.2.1+ds1/pkg/specgen/specgen.go libpod-3.4.4+ds1/pkg/specgen/specgen.go --- libpod-3.2.1+ds1/pkg/specgen/specgen.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/specgen.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,6 +5,7 @@ "syscall" "github.com/containers/image/v5/manifest" + nettypes "github.com/containers/podman/v3/libpod/network/types" "github.com/containers/storage/types" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -183,6 +184,14 @@ // EnvSecrets are secrets that will be set as environment variables // Optional. EnvSecrets map[string]string `json:"secret_env,omitempty"` + // InitContainerType describes if this container is an init container + // and if so, what type: always or once + InitContainerType string `json:"init_container_type"` + // Personality allows users to configure different execution domains. + // Execution domains tell Linux how to map signal numbers into signal actions. + // The execution domain system allows Linux to provide limited support + // for binaries compiled under other UNIX-like operating systems. + Personality *spec.LinuxPersonality `json:"personality,omitempty"` } // ContainerStorageConfig contains information on the storage configuration of a @@ -239,6 +248,9 @@ // Devices are devices that will be added to the container. // Optional. Devices []spec.LinuxDevice `json:"devices,omitempty"` + // DeviceCGroupRule are device cgroup rules that allow containers + // to use additional types of devices. + DeviceCGroupRule []spec.LinuxDeviceCgroup `json:"device_cgroup_rule,omitempty"` // IpcNS is the container's IPC namespace. // Default is private. // Conflicts with ShmSize if not set to private. @@ -252,13 +264,17 @@ // If unset, the default, /, will be used. // Optional. WorkDir string `json:"work_dir,omitempty"` + // Create the working directory if it doesn't exist. + // If unset, it doesn't create it. + // Optional. + CreateWorkingDir bool `json:"create_working_dir,omitempty"` // RootfsPropagation is the rootfs propagation mode for the container. // If not set, the default of rslave will be used. // Optional. RootfsPropagation string `json:"rootfs_propagation,omitempty"` // Secrets are the secrets that will be added to the container // Optional. - Secrets []string `json:"secrets,omitempty"` + Secrets []Secret `json:"secrets,omitempty"` // Volatile specifies whether the container storage can be optimized // at the cost of not syncing all the dirty files in memory. Volatile bool `json:"volatile,omitempty"` @@ -382,7 +398,7 @@ // PortBindings is a set of ports to map into the container. // Only available if NetNS is set to bridge or slirp. // Optional. - PortMappings []PortMapping `json:"portmappings,omitempty"` + PortMappings []nettypes.PortMapping `json:"portmappings,omitempty"` // PublishExposedPorts will publish ports specified in the image to // random unused ports (guaranteed to be above 1024) on the host. // This is based on ports set in Expose below, and any ports specified @@ -470,6 +486,10 @@ // that are used to configure cgroup v2. // Optional. CgroupConf map[string]string `json:"unified,omitempty"` + // CPU period of the cpuset, determined by --cpus + CPUPeriod uint64 `json:"cpu_period,omitempty"` + // CPU quota of the cpuset, determined by --cpus + CPUQuota int64 `json:"cpu_quota,omitempty"` } // ContainerHealthCheckConfig describes a container healthcheck with attributes @@ -491,34 +511,12 @@ ContainerHealthCheckConfig } -// PortMapping is one or more ports that will be mapped into the container. -type PortMapping struct { - // HostIP is the IP that we will bind to on the host. - // If unset, assumed to be 0.0.0.0 (all interfaces). - HostIP string `json:"host_ip,omitempty"` - // ContainerPort is the port number that will be exposed from the - // container. - // Mandatory. - ContainerPort uint16 `json:"container_port"` - // HostPort is the port number that will be forwarded from the host into - // the container. - // If omitted, a random port on the host (guaranteed to be over 1024) - // will be assigned. - HostPort uint16 `json:"host_port,omitempty"` - // Range is the number of ports that will be forwarded, starting at - // HostPort and ContainerPort and counting up. - // This is 1-indexed, so 1 is assumed to be a single port (only the - // Hostport:Containerport mapping will be added), 2 is two ports (both - // Hostport:Containerport and Hostport+1:Containerport+1), etc. - // If unset, assumed to be 1 (a single port). - // Both hostport + range and containerport + range must be less than - // 65536. - Range uint16 `json:"range,omitempty"` - // Protocol is the protocol forward. - // Must be either "tcp", "udp", and "sctp", or some combination of these - // separated by commas. - // If unset, assumed to be TCP. - Protocol string `json:"protocol,omitempty"` +type Secret struct { + Source string + Target string + UID uint32 + GID uint32 + Mode uint32 } var ( diff -Nru libpod-3.2.1+ds1/pkg/specgen/volumes.go libpod-3.4.4+ds1/pkg/specgen/volumes.go --- libpod-3.2.1+ds1/pkg/specgen/volumes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgen/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,6 @@ package specgen import ( - "path/filepath" "strings" "github.com/containers/common/pkg/parse" @@ -93,11 +92,6 @@ return nil, nil, nil, errors.New("host directory cannot be empty") } } - if err := parse.ValidateVolumeCtrDir(dest); err != nil { - return nil, nil, nil, err - } - - cleanDest := filepath.Clean(dest) if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { // This is not a named volume @@ -120,7 +114,7 @@ if overlayFlag { // This is a overlay volume newOverlayVol := new(OverlayVolume) - newOverlayVol.Destination = cleanDest + newOverlayVol.Destination = dest newOverlayVol.Source = src newOverlayVol.Options = options @@ -130,7 +124,7 @@ overlayVolumes[newOverlayVol.Destination] = newOverlayVol } else { newMount := spec.Mount{ - Destination: cleanDest, + Destination: dest, Type: "bind", Source: src, Options: options, @@ -144,7 +138,7 @@ // This is a named volume newNamedVol := new(NamedVolume) newNamedVol.Name = src - newNamedVol.Dest = cleanDest + newNamedVol.Dest = dest newNamedVol.Options = options if _, ok := volumes[newNamedVol.Dest]; ok { diff -Nru libpod-3.2.1+ds1/pkg/specgenutil/createparse.go libpod-3.4.4+ds1/pkg/specgenutil/createparse.go --- libpod-3.2.1+ds1/pkg/specgenutil/createparse.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgenutil/createparse.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,34 @@ +package specgenutil + +import ( + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v3/pkg/domain/entities" + "github.com/pkg/errors" +) + +// validate determines if the flags and values given by the user are valid. things checked +// by validate must not need any state information on the flag (i.e. changed) +func validate(c *entities.ContainerCreateOptions) error { + var () + if c.Rm && (c.Restart != "" && c.Restart != "no" && c.Restart != "on-failure") { + return errors.Errorf(`the --rm option conflicts with --restart, when the restartPolicy is not "" and "no"`) + } + + if _, err := config.ParsePullPolicy(c.Pull); err != nil { + return err + } + + var imageVolType = map[string]string{ + "bind": "", + "tmpfs": "", + "ignore": "", + } + if _, ok := imageVolType[c.ImageVolume]; !ok { + if c.IsInfra { + c.ImageVolume = "bind" + } else { + return errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.ImageVolume) + } + } + return nil +} diff -Nru libpod-3.2.1+ds1/pkg/specgenutil/ports.go libpod-3.4.4+ds1/pkg/specgenutil/ports.go --- libpod-3.2.1+ds1/pkg/specgenutil/ports.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgenutil/ports.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,22 @@ +package specgenutil + +import ( + "github.com/docker/go-connections/nat" + "github.com/pkg/errors" +) + +func verifyExpose(expose []string) error { + // add the expose ports from the user (--expose) + // can be single or a range + for _, expose := range expose { + // support two formats for expose, original format /[] or /[] + _, port := nat.SplitProtoPort(expose) + // parse the start and end port and create a sequence of ports to expose + // if expose a port, the start and end port are the same + _, _, err := nat.ParsePortRange(port) + if err != nil { + return errors.Wrapf(err, "invalid range format for --expose: %s", expose) + } + } + return nil +} diff -Nru libpod-3.2.1+ds1/pkg/specgenutil/specgen.go libpod-3.4.4+ds1/pkg/specgenutil/specgen.go --- libpod-3.2.1+ds1/pkg/specgenutil/specgen.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgenutil/specgen.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,1003 @@ +package specgenutil + +import ( + "encoding/json" + "fmt" + "os" + "strconv" + "strings" + "time" + + "github.com/containers/image/v5/manifest" + "github.com/containers/podman/v3/cmd/podman/parse" + "github.com/containers/podman/v3/libpod/define" + ann "github.com/containers/podman/v3/pkg/annotations" + "github.com/containers/podman/v3/pkg/domain/entities" + envLib "github.com/containers/podman/v3/pkg/env" + "github.com/containers/podman/v3/pkg/namespaces" + "github.com/containers/podman/v3/pkg/specgen" + systemdDefine "github.com/containers/podman/v3/pkg/systemd/define" + "github.com/containers/podman/v3/pkg/util" + "github.com/docker/go-units" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +func getCPULimits(c *entities.ContainerCreateOptions) *specs.LinuxCPU { + cpu := &specs.LinuxCPU{} + hasLimits := false + + if c.CPUS > 0 { + period, quota := util.CoresToPeriodAndQuota(c.CPUS) + + cpu.Period = &period + cpu.Quota = "a + hasLimits = true + } + if c.CPUShares > 0 { + cpu.Shares = &c.CPUShares + hasLimits = true + } + if c.CPUPeriod > 0 { + cpu.Period = &c.CPUPeriod + hasLimits = true + } + if c.CPUSetCPUs != "" { + cpu.Cpus = c.CPUSetCPUs + hasLimits = true + } + if c.CPUSetMems != "" { + cpu.Mems = c.CPUSetMems + hasLimits = true + } + if c.CPUQuota > 0 { + cpu.Quota = &c.CPUQuota + hasLimits = true + } + if c.CPURTPeriod > 0 { + cpu.RealtimePeriod = &c.CPURTPeriod + hasLimits = true + } + if c.CPURTRuntime > 0 { + cpu.RealtimeRuntime = &c.CPURTRuntime + hasLimits = true + } + + if !hasLimits { + return nil + } + return cpu +} + +func getIOLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) (*specs.LinuxBlockIO, error) { + var err error + io := &specs.LinuxBlockIO{} + hasLimits := false + if b := c.BlkIOWeight; len(b) > 0 { + u, err := strconv.ParseUint(b, 10, 16) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for blkio-weight") + } + nu := uint16(u) + io.Weight = &nu + hasLimits = true + } + + if len(c.BlkIOWeightDevice) > 0 { + if err := parseWeightDevices(s, c.BlkIOWeightDevice); err != nil { + return nil, err + } + hasLimits = true + } + + if bps := c.DeviceReadBPs; len(bps) > 0 { + if s.ThrottleReadBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { + return nil, err + } + hasLimits = true + } + + if bps := c.DeviceWriteBPs; len(bps) > 0 { + if s.ThrottleWriteBpsDevice, err = parseThrottleBPSDevices(bps); err != nil { + return nil, err + } + hasLimits = true + } + + if iops := c.DeviceReadIOPs; len(iops) > 0 { + if s.ThrottleReadIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { + return nil, err + } + hasLimits = true + } + + if iops := c.DeviceWriteIOPs; len(iops) > 0 { + if s.ThrottleWriteIOPSDevice, err = parseThrottleIOPsDevices(iops); err != nil { + return nil, err + } + hasLimits = true + } + + if !hasLimits { + return nil, nil + } + return io, nil +} + +func getMemoryLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) (*specs.LinuxMemory, error) { + var err error + memory := &specs.LinuxMemory{} + hasLimits := false + if m := c.Memory; len(m) > 0 { + ml, err := units.RAMInBytes(m) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for memory") + } + if ml > 0 { + memory.Limit = &ml + if c.MemorySwap == "" { + limit := 2 * ml + memory.Swap = &(limit) + } + hasLimits = true + } + } + if m := c.MemoryReservation; len(m) > 0 { + mr, err := units.RAMInBytes(m) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for memory") + } + memory.Reservation = &mr + hasLimits = true + } + if m := c.MemorySwap; len(m) > 0 { + var ms int64 + // only set memory swap if it was set + // -1 indicates unlimited + if m != "-1" { + ms, err = units.RAMInBytes(m) + memory.Swap = &ms + if err != nil { + return nil, errors.Wrapf(err, "invalid value for memory") + } + hasLimits = true + } + } + if m := c.KernelMemory; len(m) > 0 { + mk, err := units.RAMInBytes(m) + if err != nil { + return nil, errors.Wrapf(err, "invalid value for kernel-memory") + } + memory.Kernel = &mk + hasLimits = true + } + if c.MemorySwappiness > 0 { + swappiness := uint64(c.MemorySwappiness) + memory.Swappiness = &swappiness + hasLimits = true + } + if c.OOMKillDisable { + memory.DisableOOMKiller = &c.OOMKillDisable + hasLimits = true + } + if !hasLimits { + return nil, nil + } + return memory, nil +} + +func setNamespaces(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions) error { + var err error + + if c.PID != "" { + s.PidNS, err = specgen.ParseNamespace(c.PID) + if err != nil { + return err + } + } + if c.IPC != "" { + s.IpcNS, err = specgen.ParseNamespace(c.IPC) + if err != nil { + return err + } + } + if c.UTS != "" { + s.UtsNS, err = specgen.ParseNamespace(c.UTS) + if err != nil { + return err + } + } + if c.CgroupNS != "" { + s.CgroupNS, err = specgen.ParseNamespace(c.CgroupNS) + if err != nil { + return err + } + } + // userns must be treated differently + if c.UserNS != "" { + s.UserNS, err = specgen.ParseUserNamespace(c.UserNS) + if err != nil { + return err + } + } + if c.Net != nil { + s.NetNS = c.Net.Network + } + return nil +} + +func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions, args []string) error { + var ( + err error + ) + // validate flags as needed + if err := validate(c); err != nil { + return err + } + s.User = c.User + var inputCommand []string + if !c.IsInfra { + if len(args) > 1 { + inputCommand = args[1:] + } + } + + if len(c.HealthCmd) > 0 { + if c.NoHealthCheck { + return errors.New("Cannot specify both --no-healthcheck and --health-cmd") + } + s.HealthConfig, err = makeHealthCheckFromCli(c.HealthCmd, c.HealthInterval, c.HealthRetries, c.HealthTimeout, c.HealthStartPeriod) + if err != nil { + return err + } + } else if c.NoHealthCheck { + s.HealthConfig = &manifest.Schema2HealthConfig{ + Test: []string{"NONE"}, + } + } + if err := setNamespaces(s, c); err != nil { + return err + } + userNS := namespaces.UsernsMode(s.UserNS.NSMode) + tempIDMap, err := util.ParseIDMapping(namespaces.UsernsMode(c.UserNS), []string{}, []string{}, "", "") + if err != nil { + return err + } + s.IDMappings, err = util.ParseIDMapping(userNS, c.UIDMap, c.GIDMap, c.SubUIDName, c.SubGIDName) + if err != nil { + return err + } + if len(s.IDMappings.GIDMap) == 0 { + s.IDMappings.AutoUserNsOpts.AdditionalGIDMappings = tempIDMap.AutoUserNsOpts.AdditionalGIDMappings + if s.UserNS.NSMode == specgen.NamespaceMode("auto") { + s.IDMappings.AutoUserNs = true + } + } + if len(s.IDMappings.UIDMap) == 0 { + s.IDMappings.AutoUserNsOpts.AdditionalUIDMappings = tempIDMap.AutoUserNsOpts.AdditionalUIDMappings + if s.UserNS.NSMode == specgen.NamespaceMode("auto") { + s.IDMappings.AutoUserNs = true + } + } + if tempIDMap.AutoUserNsOpts.Size != 0 { + s.IDMappings.AutoUserNsOpts.Size = tempIDMap.AutoUserNsOpts.Size + } + // If some mappings are specified, assume a private user namespace + if userNS.IsDefaultValue() && (!s.IDMappings.HostUIDMapping || !s.IDMappings.HostGIDMapping) { + s.UserNS.NSMode = specgen.Private + } else { + s.UserNS.NSMode = specgen.NamespaceMode(userNS) + } + + s.Terminal = c.TTY + + if err := verifyExpose(c.Expose); err != nil { + return err + } + // We are not handling the Expose flag yet. + // s.PortsExpose = c.Expose + if c.Net != nil { + s.PortMappings = c.Net.PublishPorts + } + s.PublishExposedPorts = c.PublishAll + s.Pod = c.Pod + + if len(c.PodIDFile) > 0 { + if len(s.Pod) > 0 { + return errors.New("Cannot specify both --pod and --pod-id-file") + } + podID, err := ReadPodIDFile(c.PodIDFile) + if err != nil { + return err + } + s.Pod = podID + } + + expose, err := createExpose(c.Expose) + if err != nil { + return err + } + s.Expose = expose + + if sig := c.StopSignal; len(sig) > 0 { + stopSignal, err := util.ParseSignal(sig) + if err != nil { + return err + } + s.StopSignal = &stopSignal + } + + // ENVIRONMENT VARIABLES + // + // Precedence order (higher index wins): + // 1) containers.conf (EnvHost, EnvHTTP, Env) 2) image data, 3 User EnvHost/EnvHTTP, 4) env-file, 5) env + // containers.conf handled and image data handled on the server side + // user specified EnvHost and EnvHTTP handled on Server Side relative to Server + // env-file and env handled on client side + var env map[string]string + + // First transform the os env into a map. We need it for the labels later in + // any case. + osEnv, err := envLib.ParseSlice(os.Environ()) + if err != nil { + return errors.Wrap(err, "error parsing host environment variables") + } + + s.EnvHost = c.EnvHost + s.HTTPProxy = c.HTTPProxy + + // env-file overrides any previous variables + for _, f := range c.EnvFile { + fileEnv, err := envLib.ParseFile(f) + if err != nil { + return err + } + // File env is overridden by env. + env = envLib.Join(env, fileEnv) + } + + parsedEnv, err := envLib.ParseSlice(c.Env) + if err != nil { + return err + } + + s.Env = envLib.Join(env, parsedEnv) + + // LABEL VARIABLES + labels, err := parse.GetAllLabels(c.LabelFile, c.Label) + if err != nil { + return errors.Wrapf(err, "unable to process labels") + } + + if systemdUnit, exists := osEnv[systemdDefine.EnvVariable]; exists { + labels[systemdDefine.EnvVariable] = systemdUnit + } + + s.Labels = labels + + // ANNOTATIONS + annotations := make(map[string]string) + + // First, add our default annotations + annotations[ann.TTY] = "false" + if c.TTY { + annotations[ann.TTY] = "true" + } + + // Last, add user annotations + for _, annotation := range c.Annotation { + splitAnnotation := strings.SplitN(annotation, "=", 2) + if len(splitAnnotation) < 2 { + return errors.Errorf("Annotations must be formatted KEY=VALUE") + } + annotations[splitAnnotation[0]] = splitAnnotation[1] + } + s.Annotations = annotations + + s.WorkDir = c.Workdir + if c.Entrypoint != nil { + entrypoint := []string{} + // Check if entrypoint specified is json + if err := json.Unmarshal([]byte(*c.Entrypoint), &entrypoint); err != nil { + entrypoint = append(entrypoint, *c.Entrypoint) + } + s.Entrypoint = entrypoint + } + + // Include the command used to create the container. + + s.ContainerCreateCommand = os.Args + + if len(inputCommand) > 0 { + s.Command = inputCommand + } + + // SHM Size + if c.ShmSize != "" { + shmSize, err := units.FromHumanSize(c.ShmSize) + if err != nil { + return errors.Wrapf(err, "unable to translate --shm-size") + } + s.ShmSize = &shmSize + } + + if c.Net != nil { + s.CNINetworks = c.Net.CNINetworks + } + + // Network aliases + if c.Net != nil { + if len(c.Net.Aliases) > 0 { + // build a map of aliases where key=cniName + aliases := make(map[string][]string, len(s.CNINetworks)) + for _, cniNetwork := range s.CNINetworks { + aliases[cniNetwork] = c.Net.Aliases + } + s.Aliases = aliases + } + } + + if c.Net != nil { + s.HostAdd = c.Net.AddHosts + s.UseImageResolvConf = c.Net.UseImageResolvConf + s.DNSServers = c.Net.DNSServers + s.DNSSearch = c.Net.DNSSearch + s.DNSOptions = c.Net.DNSOptions + s.StaticIP = c.Net.StaticIP + s.StaticMAC = c.Net.StaticMAC + s.NetworkOptions = c.Net.NetworkOptions + s.UseImageHosts = c.Net.NoHosts + } + s.ImageVolumeMode = c.ImageVolume + if s.ImageVolumeMode == "bind" { + s.ImageVolumeMode = "anonymous" + } + + s.Systemd = strings.ToLower(c.Systemd) + s.SdNotifyMode = c.SdNotifyMode + if s.ResourceLimits == nil { + s.ResourceLimits = &specs.LinuxResources{} + } + s.ResourceLimits.Memory, err = getMemoryLimits(s, c) + if err != nil { + return err + } + s.ResourceLimits.BlockIO, err = getIOLimits(s, c) + if err != nil { + return err + } + if c.PIDsLimit != nil { + pids := specs.LinuxPids{ + Limit: *c.PIDsLimit, + } + + s.ResourceLimits.Pids = &pids + } + s.ResourceLimits.CPU = getCPULimits(c) + + unifieds := make(map[string]string) + for _, unified := range c.CgroupConf { + splitUnified := strings.SplitN(unified, "=", 2) + if len(splitUnified) < 2 { + return errors.Errorf("--cgroup-conf must be formatted KEY=VALUE") + } + unifieds[splitUnified[0]] = splitUnified[1] + } + if len(unifieds) > 0 { + s.ResourceLimits.Unified = unifieds + } + + if s.ResourceLimits.CPU == nil && s.ResourceLimits.Pids == nil && s.ResourceLimits.BlockIO == nil && s.ResourceLimits.Memory == nil && s.ResourceLimits.Unified == nil { + s.ResourceLimits = nil + } + + if s.LogConfiguration == nil { + s.LogConfiguration = &specgen.LogConfig{} + } + + if ld := c.LogDriver; len(ld) > 0 { + s.LogConfiguration.Driver = ld + } + s.CgroupParent = c.CGroupParent + s.CgroupsMode = c.CGroupsMode + s.Groups = c.GroupAdd + + s.Hostname = c.Hostname + sysctl := map[string]string{} + if ctl := c.Sysctl; len(ctl) > 0 { + sysctl, err = util.ValidateSysctls(ctl) + if err != nil { + return err + } + } + s.Sysctl = sysctl + + s.CapAdd = c.CapAdd + s.CapDrop = c.CapDrop + s.Privileged = c.Privileged + s.ReadOnlyFilesystem = c.ReadOnly + s.ConmonPidFile = c.ConmonPIDFile + + s.DependencyContainers = c.Requires + + // TODO + // outside of specgen and oci though + // defaults to true, check spec/storage + // s.readonly = c.ReadOnlyTmpFS + // TODO convert to map? + // check if key=value and convert + sysmap := make(map[string]string) + for _, ctl := range c.Sysctl { + splitCtl := strings.SplitN(ctl, "=", 2) + if len(splitCtl) < 2 { + return errors.Errorf("invalid sysctl value %q", ctl) + } + sysmap[splitCtl[0]] = splitCtl[1] + } + s.Sysctl = sysmap + + if c.CIDFile != "" { + s.Annotations[define.InspectAnnotationCIDFile] = c.CIDFile + } + + for _, opt := range c.SecurityOpt { + if opt == "no-new-privileges" { + s.ContainerSecurityConfig.NoNewPrivileges = true + } else { + con := strings.SplitN(opt, "=", 2) + if len(con) != 2 { + return fmt.Errorf("invalid --security-opt 1: %q", opt) + } + switch con[0] { + case "apparmor": + s.ContainerSecurityConfig.ApparmorProfile = con[1] + s.Annotations[define.InspectAnnotationApparmor] = con[1] + case "label": + // TODO selinux opts and label opts are the same thing + s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1]) + s.Annotations[define.InspectAnnotationLabel] = strings.Join(s.ContainerSecurityConfig.SelinuxOpts, ",label=") + case "mask": + s.ContainerSecurityConfig.Mask = append(s.ContainerSecurityConfig.Mask, strings.Split(con[1], ":")...) + case "proc-opts": + s.ProcOpts = strings.Split(con[1], ",") + case "seccomp": + s.SeccompProfilePath = con[1] + s.Annotations[define.InspectAnnotationSeccomp] = con[1] + // this option is for docker compatibility, it is the same as unmask=ALL + case "systempaths": + if con[1] == "unconfined" { + s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, []string{"ALL"}...) + } else { + return fmt.Errorf("invalid systempaths option %q, only `unconfined` is supported", con[1]) + } + case "unmask": + s.ContainerSecurityConfig.Unmask = append(s.ContainerSecurityConfig.Unmask, con[1:]...) + default: + return fmt.Errorf("invalid --security-opt 2: %q", opt) + } + } + } + + s.SeccompPolicy = c.SeccompPolicy + + s.VolumesFrom = c.VolumesFrom + + // Only add read-only tmpfs mounts in case that we are read-only and the + // read-only tmpfs flag has been set. + mounts, volumes, overlayVolumes, imageVolumes, err := parseVolumes(c.Volume, c.Mount, c.TmpFS, c.ReadOnlyTmpFS && c.ReadOnly) + if err != nil { + return err + } + s.Mounts = mounts + s.Volumes = volumes + s.OverlayVolumes = overlayVolumes + s.ImageVolumes = imageVolumes + + for _, dev := range c.Devices { + s.Devices = append(s.Devices, specs.LinuxDevice{Path: dev}) + } + + for _, rule := range c.DeviceCGroupRule { + dev, err := parseLinuxResourcesDeviceAccess(rule) + if err != nil { + return err + } + s.DeviceCGroupRule = append(s.DeviceCGroupRule, dev) + } + + s.Init = c.Init + s.InitPath = c.InitPath + s.Stdin = c.Interactive + // quiet + // DeviceCgroupRules: c.StringSlice("device-cgroup-rule"), + + // Rlimits/Ulimits + for _, u := range c.Ulimit { + if u == "host" { + s.Rlimits = nil + break + } + ul, err := units.ParseUlimit(u) + if err != nil { + return errors.Wrapf(err, "ulimit option %q requires name=SOFT:HARD, failed to be parsed", u) + } + rl := specs.POSIXRlimit{ + Type: ul.Name, + Hard: uint64(ul.Hard), + Soft: uint64(ul.Soft), + } + s.Rlimits = append(s.Rlimits, rl) + } + + logOpts := make(map[string]string) + for _, o := range c.LogOptions { + split := strings.SplitN(o, "=", 2) + if len(split) < 2 { + return errors.Errorf("invalid log option %q", o) + } + switch strings.ToLower(split[0]) { + case "driver": + s.LogConfiguration.Driver = split[1] + case "path": + s.LogConfiguration.Path = split[1] + case "max-size": + logSize, err := units.FromHumanSize(split[1]) + if err != nil { + return err + } + s.LogConfiguration.Size = logSize + default: + logOpts[split[0]] = split[1] + } + } + s.LogConfiguration.Options = logOpts + s.Name = c.Name + s.PreserveFDs = c.PreserveFDs + + s.OOMScoreAdj = &c.OOMScoreAdj + if c.Restart != "" { + splitRestart := strings.Split(c.Restart, ":") + switch len(splitRestart) { + case 1: + // No retries specified + case 2: + if strings.ToLower(splitRestart[0]) != "on-failure" { + return errors.Errorf("restart policy retries can only be specified with on-failure restart policy") + } + retries, err := strconv.Atoi(splitRestart[1]) + if err != nil { + return errors.Wrapf(err, "error parsing restart policy retry count") + } + if retries < 0 { + return errors.Errorf("must specify restart policy retry count as a number greater than 0") + } + var retriesUint = uint(retries) + s.RestartRetries = &retriesUint + default: + return errors.Errorf("invalid restart policy: may specify retries at most once") + } + s.RestartPolicy = splitRestart[0] + } + + s.Secrets, s.EnvSecrets, err = parseSecrets(c.Secrets) + if err != nil { + return err + } + + if c.Personality != "" { + s.Personality = &specs.LinuxPersonality{} + s.Personality.Domain = specs.LinuxPersonalityDomain(c.Personality) + } + + s.Remove = c.Rm + s.StopTimeout = &c.StopTimeout + s.Timeout = c.Timeout + s.Timezone = c.Timezone + s.Umask = c.Umask + s.PidFile = c.PidFile + s.Volatile = c.Rm + + // Initcontainers + s.InitContainerType = c.InitContainerType + return nil +} + +func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, startPeriod string) (*manifest.Schema2HealthConfig, error) { + cmdArr := []string{} + isArr := true + err := json.Unmarshal([]byte(inCmd), &cmdArr) // array unmarshalling + if err != nil { + cmdArr = strings.SplitN(inCmd, " ", 2) // default for compat + isArr = false + } + // Every healthcheck requires a command + if len(cmdArr) == 0 { + return nil, errors.New("Must define a healthcheck command for all healthchecks") + } + concat := "" + if cmdArr[0] == "CMD" || cmdArr[0] == "none" { // this is for compat, we are already split properly for most compat cases + cmdArr = strings.Fields(inCmd) + } else if cmdArr[0] != "CMD-SHELL" { // this is for podman side of things, won't contain the keywords + if isArr && len(cmdArr) > 1 { // an array of consecutive commands + cmdArr = append([]string{"CMD"}, cmdArr...) + } else { // one singular command + if len(cmdArr) == 1 { + concat = cmdArr[0] + } else { + concat = strings.Join(cmdArr[0:], " ") + } + cmdArr = append([]string{"CMD-SHELL"}, concat) + } + } + + if cmdArr[0] == "none" { // if specified to remove healtcheck + cmdArr = []string{"NONE"} + } + + // healthcheck is by default an array, so we simply pass the user input + hc := manifest.Schema2HealthConfig{ + Test: cmdArr, + } + + if interval == "disable" { + interval = "0" + } + intervalDuration, err := time.ParseDuration(interval) + if err != nil { + return nil, errors.Wrapf(err, "invalid healthcheck-interval") + } + + hc.Interval = intervalDuration + + if retries < 1 { + return nil, errors.New("healthcheck-retries must be greater than 0") + } + hc.Retries = int(retries) + timeoutDuration, err := time.ParseDuration(timeout) + if err != nil { + return nil, errors.Wrapf(err, "invalid healthcheck-timeout") + } + if timeoutDuration < time.Duration(1) { + return nil, errors.New("healthcheck-timeout must be at least 1 second") + } + hc.Timeout = timeoutDuration + + startPeriodDuration, err := time.ParseDuration(startPeriod) + if err != nil { + return nil, errors.Wrapf(err, "invalid healthcheck-start-period") + } + if startPeriodDuration < time.Duration(0) { + return nil, errors.New("healthcheck-start-period must be 0 seconds or greater") + } + hc.StartPeriod = startPeriodDuration + + return &hc, nil +} + +func parseWeightDevices(s *specgen.SpecGenerator, weightDevs []string) error { + for _, val := range weightDevs { + split := strings.SplitN(val, ":", 2) + if len(split) != 2 { + return fmt.Errorf("bad format: %s", val) + } + if !strings.HasPrefix(split[0], "/dev/") { + return fmt.Errorf("bad format for device path: %s", val) + } + weight, err := strconv.ParseUint(split[1], 10, 0) + if err != nil { + return fmt.Errorf("invalid weight for device: %s", val) + } + if weight > 0 && (weight < 10 || weight > 1000) { + return fmt.Errorf("invalid weight for device: %s", val) + } + w := uint16(weight) + s.WeightDevice[split[0]] = specs.LinuxWeightDevice{ + Weight: &w, + LeafWeight: nil, + } + } + return nil +} + +func parseThrottleBPSDevices(bpsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { + td := make(map[string]specs.LinuxThrottleDevice) + for _, val := range bpsDevices { + split := strings.SplitN(val, ":", 2) + if len(split) != 2 { + return nil, fmt.Errorf("bad format: %s", val) + } + if !strings.HasPrefix(split[0], "/dev/") { + return nil, fmt.Errorf("bad format for device path: %s", val) + } + rate, err := units.RAMInBytes(split[1]) + if err != nil { + return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :[]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) + } + if rate < 0 { + return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :[]. Number must be a positive integer. Unit is optional and can be kb, mb, or gb", val) + } + td[split[0]] = specs.LinuxThrottleDevice{Rate: uint64(rate)} + } + return td, nil +} + +func parseThrottleIOPsDevices(iopsDevices []string) (map[string]specs.LinuxThrottleDevice, error) { + td := make(map[string]specs.LinuxThrottleDevice) + for _, val := range iopsDevices { + split := strings.SplitN(val, ":", 2) + if len(split) != 2 { + return nil, fmt.Errorf("bad format: %s", val) + } + if !strings.HasPrefix(split[0], "/dev/") { + return nil, fmt.Errorf("bad format for device path: %s", val) + } + rate, err := strconv.ParseUint(split[1], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid rate for device: %s. The correct format is :. Number must be a positive integer", val) + } + td[split[0]] = specs.LinuxThrottleDevice{Rate: rate} + } + return td, nil +} + +func parseSecrets(secrets []string) ([]specgen.Secret, map[string]string, error) { + secretParseError := errors.New("error parsing secret") + var mount []specgen.Secret + envs := make(map[string]string) + for _, val := range secrets { + // mount only tells if user has set an option that can only be used with mount secret type + mountOnly := false + source := "" + secretType := "" + target := "" + var uid, gid uint32 + // default mode 444 octal = 292 decimal + var mode uint32 = 292 + split := strings.Split(val, ",") + + // --secret mysecret + if len(split) == 1 { + mountSecret := specgen.Secret{ + Source: val, + Target: target, + UID: uid, + GID: gid, + Mode: mode, + } + mount = append(mount, mountSecret) + continue + } + // --secret mysecret,opt=opt + if !strings.Contains(split[0], "=") { + source = split[0] + split = split[1:] + } + + for _, val := range split { + kv := strings.SplitN(val, "=", 2) + if len(kv) < 2 { + return nil, nil, errors.Wrapf(secretParseError, "option %s must be in form option=value", val) + } + switch kv[0] { + case "source": + source = kv[1] + case "type": + if secretType != "" { + return nil, nil, errors.Wrap(secretParseError, "cannot set more tha one secret type") + } + if kv[1] != "mount" && kv[1] != "env" { + return nil, nil, errors.Wrapf(secretParseError, "type %s is invalid", kv[1]) + } + secretType = kv[1] + case "target": + target = kv[1] + case "mode": + mountOnly = true + mode64, err := strconv.ParseUint(kv[1], 8, 32) + if err != nil { + return nil, nil, errors.Wrapf(secretParseError, "mode %s invalid", kv[1]) + } + mode = uint32(mode64) + case "uid", "UID": + mountOnly = true + uid64, err := strconv.ParseUint(kv[1], 10, 32) + if err != nil { + return nil, nil, errors.Wrapf(secretParseError, "UID %s invalid", kv[1]) + } + uid = uint32(uid64) + case "gid", "GID": + mountOnly = true + gid64, err := strconv.ParseUint(kv[1], 10, 32) + if err != nil { + return nil, nil, errors.Wrapf(secretParseError, "GID %s invalid", kv[1]) + } + gid = uint32(gid64) + + default: + return nil, nil, errors.Wrapf(secretParseError, "option %s invalid", val) + } + } + + if secretType == "" { + secretType = "mount" + } + if source == "" { + return nil, nil, errors.Wrapf(secretParseError, "no source found %s", val) + } + if secretType == "mount" { + mountSecret := specgen.Secret{ + Source: source, + Target: target, + UID: uid, + GID: gid, + Mode: mode, + } + mount = append(mount, mountSecret) + } + if secretType == "env" { + if mountOnly { + return nil, nil, errors.Wrap(secretParseError, "UID, GID, Mode options cannot be set with secret type env") + } + if target == "" { + target = source + } + envs[target] = source + } + } + return mount, envs, nil +} + +var cgroupDeviceType = map[string]bool{ + "a": true, // all + "b": true, // block device + "c": true, // character device +} + +var cgroupDeviceAccess = map[string]bool{ + "r": true, //read + "w": true, //write + "m": true, //mknod +} + +// parseLinuxResourcesDeviceAccess parses the raw string passed with the --device-access-add flag +func parseLinuxResourcesDeviceAccess(device string) (specs.LinuxDeviceCgroup, error) { + var devType, access string + var major, minor *int64 + + value := strings.Split(device, " ") + if len(value) != 3 { + return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device cgroup rule requires type, major:Minor, and access rules: %q", device) + } + + devType = value[0] + if !cgroupDeviceType[devType] { + return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device type in device-access-add: %s", devType) + } + + number := strings.SplitN(value[1], ":", 2) + i, err := strconv.ParseInt(number[0], 10, 64) + if err != nil { + return specs.LinuxDeviceCgroup{}, err + } + major = &i + if len(number) == 2 && number[1] != "*" { + i, err := strconv.ParseInt(number[1], 10, 64) + if err != nil { + return specs.LinuxDeviceCgroup{}, err + } + minor = &i + } + access = value[2] + for _, c := range strings.Split(access, "") { + if !cgroupDeviceAccess[c] { + return specs.LinuxDeviceCgroup{}, fmt.Errorf("invalid device access in device-access-add: %s", c) + } + } + return specs.LinuxDeviceCgroup{ + Allow: true, + Type: devType, + Major: major, + Minor: minor, + Access: access, + }, nil +} diff -Nru libpod-3.2.1+ds1/pkg/specgenutil/util.go libpod-3.4.4+ds1/pkg/specgenutil/util.go --- libpod-3.2.1+ds1/pkg/specgenutil/util.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgenutil/util.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,274 @@ +package specgenutil + +import ( + "io/ioutil" + "net" + "strconv" + "strings" + + "github.com/containers/podman/v3/libpod/network/types" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// ReadPodIDFile reads the specified file and returns its content (i.e., first +// line). +func ReadPodIDFile(path string) (string, error) { + content, err := ioutil.ReadFile(path) + if err != nil { + return "", errors.Wrap(err, "error reading pod ID file") + } + return strings.Split(string(content), "\n")[0], nil +} + +// ReadPodIDFiles reads the specified files and returns their content (i.e., +// first line). +func ReadPodIDFiles(files []string) ([]string, error) { + ids := []string{} + for _, file := range files { + id, err := ReadPodIDFile(file) + if err != nil { + return nil, err + } + ids = append(ids, id) + } + return ids, nil +} + +// ParseFilters transforms one filter format to another and validates input +func ParseFilters(filter []string) (map[string][]string, error) { + // TODO Remove once filter refactor is finished and url.Values done. + filters := map[string][]string{} + for _, f := range filter { + t := strings.SplitN(f, "=", 2) + filters = make(map[string][]string) + if len(t) < 2 { + return map[string][]string{}, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) + } + filters[t[0]] = append(filters[t[0]], t[1]) + } + return filters, nil +} + +// createExpose parses user-provided exposed port definitions and converts them +// into SpecGen format. +// TODO: The SpecGen format should really handle ranges more sanely - we could +// be massively inflating what is sent over the wire with a large range. +func createExpose(expose []string) (map[uint16]string, error) { + toReturn := make(map[uint16]string) + + for _, e := range expose { + // Check for protocol + proto := "tcp" + splitProto := strings.Split(e, "/") + if len(splitProto) > 2 { + return nil, errors.Errorf("invalid expose format - protocol can only be specified once") + } else if len(splitProto) == 2 { + proto = splitProto[1] + } + + // Check for a range + start, len, err := parseAndValidateRange(splitProto[0]) + if err != nil { + return nil, err + } + + var index uint16 + for index = 0; index < len; index++ { + portNum := start + index + protocols, ok := toReturn[portNum] + if !ok { + toReturn[portNum] = proto + } else { + newProto := strings.Join(append(strings.Split(protocols, ","), strings.Split(proto, ",")...), ",") + toReturn[portNum] = newProto + } + } + } + + return toReturn, nil +} + +// CreatePortBindings iterates ports mappings into SpecGen format. +func CreatePortBindings(ports []string) ([]types.PortMapping, error) { + // --publish is formatted as follows: + // [[hostip:]hostport[-endPort]:]containerport[-endPort][/protocol] + toReturn := make([]types.PortMapping, 0, len(ports)) + + for _, p := range ports { + var ( + ctrPort string + proto, hostIP, hostPort *string + ) + + splitProto := strings.Split(p, "/") + switch len(splitProto) { + case 1: + // No protocol was provided + case 2: + proto = &(splitProto[1]) + default: + return nil, errors.Errorf("invalid port format - protocol can only be specified once") + } + + remainder := splitProto[0] + haveV6 := false + + // Check for an IPv6 address in brackets + splitV6 := strings.Split(remainder, "]") + switch len(splitV6) { + case 1: + // Do nothing, proceed as before + case 2: + // We potentially have an IPv6 address + haveV6 = true + if !strings.HasPrefix(splitV6[0], "[") { + return nil, errors.Errorf("invalid port format - IPv6 addresses must be enclosed by []") + } + if !strings.HasPrefix(splitV6[1], ":") { + return nil, errors.Errorf("invalid port format - IPv6 address must be followed by a colon (':')") + } + ipNoPrefix := strings.TrimPrefix(splitV6[0], "[") + hostIP = &ipNoPrefix + remainder = strings.TrimPrefix(splitV6[1], ":") + default: + return nil, errors.Errorf("invalid port format - at most one IPv6 address can be specified in a --publish") + } + + splitPort := strings.Split(remainder, ":") + switch len(splitPort) { + case 1: + if haveV6 { + return nil, errors.Errorf("invalid port format - must provide host and destination port if specifying an IP") + } + ctrPort = splitPort[0] + case 2: + hostPort = &(splitPort[0]) + ctrPort = splitPort[1] + case 3: + if haveV6 { + return nil, errors.Errorf("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") + } + hostIP = &(splitPort[0]) + hostPort = &(splitPort[1]) + ctrPort = splitPort[2] + default: + return nil, errors.Errorf("invalid port format - format is [[hostIP:]hostPort:]containerPort") + } + + newPort, err := parseSplitPort(hostIP, hostPort, ctrPort, proto) + if err != nil { + return nil, err + } + + toReturn = append(toReturn, newPort) + } + + return toReturn, nil +} + +// parseSplitPort parses individual components of the --publish flag to produce +// a single port mapping in SpecGen format. +func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string) (types.PortMapping, error) { + newPort := types.PortMapping{} + if ctrPort == "" { + return newPort, errors.Errorf("must provide a non-empty container port to publish") + } + ctrStart, ctrLen, err := parseAndValidateRange(ctrPort) + if err != nil { + return newPort, errors.Wrapf(err, "error parsing container port") + } + newPort.ContainerPort = ctrStart + newPort.Range = ctrLen + + if protocol != nil { + if *protocol == "" { + return newPort, errors.Errorf("must provide a non-empty protocol to publish") + } + newPort.Protocol = *protocol + } + if hostIP != nil { + if *hostIP == "" { + return newPort, errors.Errorf("must provide a non-empty container host IP to publish") + } else if *hostIP != "0.0.0.0" { + // If hostIP is 0.0.0.0, leave it unset - CNI treats + // 0.0.0.0 and empty differently, Docker does not. + testIP := net.ParseIP(*hostIP) + if testIP == nil { + return newPort, errors.Errorf("cannot parse %q as an IP address", *hostIP) + } + newPort.HostIP = testIP.String() + } + } + if hostPort != nil { + if *hostPort == "" { + // Set 0 as a placeholder. The server side of Specgen + // will find a random, open, unused port to use. + newPort.HostPort = 0 + } else { + hostStart, hostLen, err := parseAndValidateRange(*hostPort) + if err != nil { + return newPort, errors.Wrapf(err, "error parsing host port") + } + if hostLen != ctrLen { + return newPort, errors.Errorf("host and container port ranges have different lengths: %d vs %d", hostLen, ctrLen) + } + newPort.HostPort = hostStart + } + } + + hport := newPort.HostPort + logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol) + + return newPort, nil +} + +// Parse and validate a port range. +// Returns start port, length of range, error. +func parseAndValidateRange(portRange string) (uint16, uint16, error) { + splitRange := strings.Split(portRange, "-") + if len(splitRange) > 2 { + return 0, 0, errors.Errorf("invalid port format - port ranges are formatted as startPort-stopPort") + } + + if splitRange[0] == "" { + return 0, 0, errors.Errorf("port numbers cannot be negative") + } + + startPort, err := parseAndValidatePort(splitRange[0]) + if err != nil { + return 0, 0, err + } + + var rangeLen uint16 = 1 + if len(splitRange) == 2 { + if splitRange[1] == "" { + return 0, 0, errors.Errorf("must provide ending number for port range") + } + endPort, err := parseAndValidatePort(splitRange[1]) + if err != nil { + return 0, 0, err + } + if endPort <= startPort { + return 0, 0, errors.Errorf("the end port of a range must be higher than the start port - %d is not higher than %d", endPort, startPort) + } + // Our range is the total number of ports + // involved, so we need to add 1 (8080:8081 is + // 2 ports, for example, not 1) + rangeLen = endPort - startPort + 1 + } + + return startPort, rangeLen, nil +} + +// Turn a single string into a valid U16 port. +func parseAndValidatePort(port string) (uint16, error) { + num, err := strconv.Atoi(port) + if err != nil { + return 0, errors.Wrapf(err, "invalid port number") + } + if num < 1 || num > 65535 { + return 0, errors.Errorf("port numbers must be between 1 and 65535 (inclusive), got %d", num) + } + return uint16(num), nil +} diff -Nru libpod-3.2.1+ds1/pkg/specgenutil/volumes.go libpod-3.4.4+ds1/pkg/specgenutil/volumes.go --- libpod-3.2.1+ds1/pkg/specgenutil/volumes.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/specgenutil/volumes.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,630 @@ +package specgenutil + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/containers/common/pkg/parse" + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/specgen" + "github.com/containers/podman/v3/pkg/util" + spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" +) + +var ( + errDuplicateDest = errors.Errorf("duplicate mount destination") + optionArgError = errors.Errorf("must provide an argument for option") + noDestError = errors.Errorf("must set volume destination") + errInvalidSyntax = errors.Errorf("incorrect mount format: should be --mount type=,[src=,]target=[,options]") +) + +// Parse all volume-related options in the create config into a set of mounts +// and named volumes to add to the container. +// Handles --volumes, --mount, and --tmpfs flags. +// Does not handle image volumes, init, and --volumes-from flags. +// Can also add tmpfs mounts from read-only tmpfs. +// TODO: handle options parsing/processing via containers/storage/pkg/mount +func parseVolumes(volumeFlag, mountFlag, tmpfsFlag []string, addReadOnlyTmpfs bool) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, []*specgen.ImageVolume, error) { + // Get mounts from the --mounts flag. + unifiedMounts, unifiedVolumes, unifiedImageVolumes, err := getMounts(mountFlag) + if err != nil { + return nil, nil, nil, nil, err + } + + // Next --volumes flag. + volumeMounts, volumeVolumes, overlayVolumes, err := specgen.GenVolumeMounts(volumeFlag) + if err != nil { + return nil, nil, nil, nil, err + } + + // Next --tmpfs flag. + tmpfsMounts, err := getTmpfsMounts(tmpfsFlag) + if err != nil { + return nil, nil, nil, nil, err + } + + // Unify mounts from --mount, --volume, --tmpfs. + // Start with --volume. + for dest, mount := range volumeMounts { + if _, ok := unifiedMounts[dest]; ok { + return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + } + unifiedMounts[dest] = mount + } + for dest, volume := range volumeVolumes { + if _, ok := unifiedVolumes[dest]; ok { + return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + } + unifiedVolumes[dest] = volume + } + // Now --tmpfs + for dest, tmpfs := range tmpfsMounts { + if _, ok := unifiedMounts[dest]; ok { + return nil, nil, nil, nil, errors.Wrapf(errDuplicateDest, dest) + } + unifiedMounts[dest] = tmpfs + } + + // If requested, add tmpfs filesystems for read-only containers. + if addReadOnlyTmpfs { + readonlyTmpfs := []string{"/tmp", "/var/tmp", "/run"} + options := []string{"rw", "rprivate", "nosuid", "nodev", "tmpcopyup"} + for _, dest := range readonlyTmpfs { + if _, ok := unifiedMounts[dest]; ok { + continue + } + if _, ok := unifiedVolumes[dest]; ok { + continue + } + unifiedMounts[dest] = spec.Mount{ + Destination: dest, + Type: define.TypeTmpfs, + Source: "tmpfs", + Options: options, + } + } + } + + // Check for conflicts between named volumes, overlay & image volumes, + // and mounts + allMounts := make(map[string]bool) + testAndSet := func(dest string) error { + if _, ok := allMounts[dest]; ok { + return errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) + } + allMounts[dest] = true + return nil + } + for dest := range unifiedMounts { + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err + } + } + for dest := range unifiedVolumes { + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err + } + } + for dest := range overlayVolumes { + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err + } + } + for dest := range unifiedImageVolumes { + if err := testAndSet(dest); err != nil { + return nil, nil, nil, nil, err + } + } + + // Final step: maps to arrays + finalMounts := make([]spec.Mount, 0, len(unifiedMounts)) + for _, mount := range unifiedMounts { + if mount.Type == define.TypeBind { + absSrc, err := filepath.Abs(mount.Source) + if err != nil { + return nil, nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) + } + mount.Source = absSrc + } + finalMounts = append(finalMounts, mount) + } + finalVolumes := make([]*specgen.NamedVolume, 0, len(unifiedVolumes)) + for _, volume := range unifiedVolumes { + finalVolumes = append(finalVolumes, volume) + } + finalOverlayVolume := make([]*specgen.OverlayVolume, 0) + for _, volume := range overlayVolumes { + finalOverlayVolume = append(finalOverlayVolume, volume) + } + finalImageVolumes := make([]*specgen.ImageVolume, 0, len(unifiedImageVolumes)) + for _, volume := range unifiedImageVolumes { + finalImageVolumes = append(finalImageVolumes, volume) + } + + return finalMounts, finalVolumes, finalOverlayVolume, finalImageVolumes, nil +} + +// findMountType parses the input and extracts the type of the mount type and +// the remaining non-type tokens. +func findMountType(input string) (mountType string, tokens []string, err error) { + // Split by comma, iterate over the slice and look for + // "type=$mountType". Everything else is appended to tokens. + found := false + for _, s := range strings.Split(input, ",") { + kv := strings.Split(s, "=") + if found || !(len(kv) == 2 && kv[0] == "type") { + tokens = append(tokens, s) + continue + } + mountType = kv[1] + found = true + } + if !found { + err = errInvalidSyntax + } + return +} + +// getMounts takes user-provided input from the --mount flag and creates OCI +// spec mounts and Libpod named volumes. +// podman run --mount type=bind,src=/etc/resolv.conf,target=/etc/resolv.conf ... +// podman run --mount type=tmpfs,target=/dev/shm ... +// podman run --mount type=volume,source=test-volume, ... +func getMounts(mountFlag []string) (map[string]spec.Mount, map[string]*specgen.NamedVolume, map[string]*specgen.ImageVolume, error) { + finalMounts := make(map[string]spec.Mount) + finalNamedVolumes := make(map[string]*specgen.NamedVolume) + finalImageVolumes := make(map[string]*specgen.ImageVolume) + + for _, mount := range mountFlag { + // TODO: Docker defaults to "volume" if no mount type is specified. + mountType, tokens, err := findMountType(mount) + if err != nil { + return nil, nil, nil, err + } + switch mountType { + case define.TypeBind: + mount, err := getBindMount(tokens) + if err != nil { + return nil, nil, nil, err + } + if _, ok := finalMounts[mount.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + } + finalMounts[mount.Destination] = mount + case define.TypeTmpfs: + mount, err := getTmpfsMount(tokens) + if err != nil { + return nil, nil, nil, err + } + if _, ok := finalMounts[mount.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + } + finalMounts[mount.Destination] = mount + case define.TypeDevpts: + mount, err := getDevptsMount(tokens) + if err != nil { + return nil, nil, nil, err + } + if _, ok := finalMounts[mount.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, mount.Destination) + } + finalMounts[mount.Destination] = mount + case "image": + volume, err := getImageVolume(tokens) + if err != nil { + return nil, nil, nil, err + } + if _, ok := finalImageVolumes[volume.Destination]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Destination) + } + finalImageVolumes[volume.Destination] = volume + case "volume": + volume, err := getNamedVolume(tokens) + if err != nil { + return nil, nil, nil, err + } + if _, ok := finalNamedVolumes[volume.Dest]; ok { + return nil, nil, nil, errors.Wrapf(errDuplicateDest, volume.Dest) + } + finalNamedVolumes[volume.Dest] = volume + default: + return nil, nil, nil, errors.Errorf("invalid filesystem type %q", mountType) + } + } + + return finalMounts, finalNamedVolumes, finalImageVolumes, nil +} + +// Parse a single bind mount entry from the --mount flag. +func getBindMount(args []string) (spec.Mount, error) { + newMount := spec.Mount{ + Type: define.TypeBind, + } + + var setSource, setDest, setRORW, setSuid, setDev, setExec, setRelabel bool + + for _, val := range args { + kv := strings.SplitN(val, "=", 2) + switch kv[0] { + case "bind-nonrecursive": + newMount.Options = append(newMount.Options, "bind") + case "readonly", "ro", "rw": + if setRORW { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'readonly', 'ro', or 'rw' options more than once") + } + setRORW = true + // Can be formatted as one of: + // readonly + // readonly=[true|false] + // ro + // ro=[true|false] + // rw + // rw=[true|false] + if kv[0] == "readonly" { + kv[0] = "ro" + } + switch len(kv) { + case 1: + newMount.Options = append(newMount.Options, kv[0]) + case 2: + switch strings.ToLower(kv[1]) { + case "true": + newMount.Options = append(newMount.Options, kv[0]) + case "false": + // Set the opposite only for rw + // ro's opposite is the default + if kv[0] == "rw" { + newMount.Options = append(newMount.Options, "ro") + } + default: + return newMount, errors.Wrapf(optionArgError, "'readonly', 'ro', or 'rw' must be set to true or false, instead received %q", kv[1]) + } + default: + return newMount, errors.Wrapf(optionArgError, "badly formatted option %q", val) + } + case "nosuid", "suid": + if setSuid { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") + } + setSuid = true + newMount.Options = append(newMount.Options, kv[0]) + case "nodev", "dev": + if setDev { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") + } + setDev = true + newMount.Options = append(newMount.Options, kv[0]) + case "noexec", "exec": + if setExec { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") + } + setExec = true + newMount.Options = append(newMount.Options, kv[0]) + case "shared", "rshared", "private", "rprivate", "slave", "rslave", "unbindable", "runbindable", "Z", "z": + newMount.Options = append(newMount.Options, kv[0]) + case "bind-propagation": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + newMount.Options = append(newMount.Options, kv[1]) + case "src", "source": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if len(kv[1]) == 0 { + return newMount, errors.Wrapf(optionArgError, "host directory cannot be empty") + } + newMount.Source = kv[1] + setSource = true + case "target", "dst", "destination": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + return newMount, err + } + newMount.Destination = filepath.Clean(kv[1]) + setDest = true + case "relabel": + if setRelabel { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'relabel' option more than once") + } + setRelabel = true + if len(kv) != 2 { + return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) + } + switch kv[1] { + case "private": + newMount.Options = append(newMount.Options, "Z") + case "shared": + newMount.Options = append(newMount.Options, "z") + default: + return newMount, errors.Wrapf(util.ErrBadMntOption, "%s mount option must be 'private' or 'shared'", kv[0]) + } + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue + default: + return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) + } + } + + if !setDest { + return newMount, noDestError + } + + if !setSource { + newMount.Source = newMount.Destination + } + + options, err := parse.ValidateVolumeOpts(newMount.Options) + if err != nil { + return newMount, err + } + newMount.Options = options + return newMount, nil +} + +// Parse a single tmpfs mount entry from the --mount flag +func getTmpfsMount(args []string) (spec.Mount, error) { + newMount := spec.Mount{ + Type: define.TypeTmpfs, + Source: define.TypeTmpfs, + } + + var setDest, setRORW, setSuid, setDev, setExec, setTmpcopyup bool + + for _, val := range args { + kv := strings.SplitN(val, "=", 2) + switch kv[0] { + case "tmpcopyup", "notmpcopyup": + if setTmpcopyup { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'tmpcopyup' and 'notmpcopyup' options more than once") + } + setTmpcopyup = true + newMount.Options = append(newMount.Options, kv[0]) + case "ro", "rw": + if setRORW { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") + } + setRORW = true + newMount.Options = append(newMount.Options, kv[0]) + case "nosuid", "suid": + if setSuid { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") + } + setSuid = true + newMount.Options = append(newMount.Options, kv[0]) + case "nodev", "dev": + if setDev { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") + } + setDev = true + newMount.Options = append(newMount.Options, kv[0]) + case "noexec", "exec": + if setExec { + return newMount, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") + } + setExec = true + newMount.Options = append(newMount.Options, kv[0]) + case "tmpfs-mode": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + newMount.Options = append(newMount.Options, fmt.Sprintf("mode=%s", kv[1])) + case "tmpfs-size": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + newMount.Options = append(newMount.Options, fmt.Sprintf("size=%s", kv[1])) + case "src", "source": + return newMount, errors.Errorf("source is not supported with tmpfs mounts") + case "target", "dst", "destination": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + return newMount, err + } + newMount.Destination = filepath.Clean(kv[1]) + setDest = true + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue + default: + return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) + } + } + + if !setDest { + return newMount, noDestError + } + + return newMount, nil +} + +// Parse a single devpts mount entry from the --mount flag +func getDevptsMount(args []string) (spec.Mount, error) { + newMount := spec.Mount{ + Type: define.TypeDevpts, + Source: define.TypeDevpts, + } + + var setDest bool + + for _, val := range args { + kv := strings.SplitN(val, "=", 2) + switch kv[0] { + case "target", "dst", "destination": + if len(kv) == 1 { + return newMount, errors.Wrapf(optionArgError, kv[0]) + } + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + return newMount, err + } + newMount.Destination = filepath.Clean(kv[1]) + setDest = true + default: + return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0]) + } + } + + if !setDest { + return newMount, noDestError + } + + return newMount, nil +} + +// Parse a single volume mount entry from the --mount flag. +// Note that the volume-label option for named volumes is currently NOT supported. +// TODO: add support for --volume-label +func getNamedVolume(args []string) (*specgen.NamedVolume, error) { + newVolume := new(specgen.NamedVolume) + + var setSource, setDest, setRORW, setSuid, setDev, setExec bool + + for _, val := range args { + kv := strings.SplitN(val, "=", 2) + switch kv[0] { + case "ro", "rw": + if setRORW { + return nil, errors.Wrapf(optionArgError, "cannot pass 'ro' and 'rw' options more than once") + } + setRORW = true + newVolume.Options = append(newVolume.Options, kv[0]) + case "nosuid", "suid": + if setSuid { + return nil, errors.Wrapf(optionArgError, "cannot pass 'nosuid' and 'suid' options more than once") + } + setSuid = true + newVolume.Options = append(newVolume.Options, kv[0]) + case "nodev", "dev": + if setDev { + return nil, errors.Wrapf(optionArgError, "cannot pass 'nodev' and 'dev' options more than once") + } + setDev = true + newVolume.Options = append(newVolume.Options, kv[0]) + case "noexec", "exec": + if setExec { + return nil, errors.Wrapf(optionArgError, "cannot pass 'noexec' and 'exec' options more than once") + } + setExec = true + newVolume.Options = append(newVolume.Options, kv[0]) + case "volume-label": + return nil, errors.Errorf("the --volume-label option is not presently implemented") + case "src", "source": + if len(kv) == 1 { + return nil, errors.Wrapf(optionArgError, kv[0]) + } + newVolume.Name = kv[1] + setSource = true + case "target", "dst", "destination": + if len(kv) == 1 { + return nil, errors.Wrapf(optionArgError, kv[0]) + } + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + return nil, err + } + newVolume.Dest = filepath.Clean(kv[1]) + setDest = true + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue + default: + return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) + } + } + + if !setSource { + return nil, errors.Errorf("must set source volume") + } + if !setDest { + return nil, noDestError + } + + return newVolume, nil +} + +// Parse the arguments into an image volume. An image volume is a volume based +// on a container image. The container image is first mounted on the host and +// is then bind-mounted into the container. An ImageVolume is always mounted +// read only. +func getImageVolume(args []string) (*specgen.ImageVolume, error) { + newVolume := new(specgen.ImageVolume) + + for _, val := range args { + kv := strings.SplitN(val, "=", 2) + switch kv[0] { + case "src", "source": + if len(kv) == 1 { + return nil, errors.Wrapf(optionArgError, kv[0]) + } + newVolume.Source = kv[1] + case "target", "dst", "destination": + if len(kv) == 1 { + return nil, errors.Wrapf(optionArgError, kv[0]) + } + if err := parse.ValidateVolumeCtrDir(kv[1]); err != nil { + return nil, err + } + newVolume.Destination = filepath.Clean(kv[1]) + case "rw", "readwrite": + switch kv[1] { + case "true": + newVolume.ReadWrite = true + case "false": + // Nothing to do. RO is default. + default: + return nil, errors.Wrapf(util.ErrBadMntOption, "invalid rw value %q", kv[1]) + } + case "consistency": + // Often used on MACs and mistakenly on Linux platforms. + // Since Docker ignores this option so shall we. + continue + default: + return nil, errors.Wrapf(util.ErrBadMntOption, kv[0]) + } + } + + if len(newVolume.Source)*len(newVolume.Destination) == 0 { + return nil, errors.Errorf("must set source and destination for image volume") + } + + return newVolume, nil +} + +// GetTmpfsMounts creates spec.Mount structs for user-requested tmpfs mounts +func getTmpfsMounts(tmpfsFlag []string) (map[string]spec.Mount, error) { + m := make(map[string]spec.Mount) + for _, i := range tmpfsFlag { + // Default options if nothing passed + var options []string + spliti := strings.Split(i, ":") + destPath := spliti[0] + if err := parse.ValidateVolumeCtrDir(spliti[0]); err != nil { + return nil, err + } + if len(spliti) > 1 { + options = strings.Split(spliti[1], ",") + } + + if _, ok := m[destPath]; ok { + return nil, errors.Wrapf(errDuplicateDest, destPath) + } + + mount := spec.Mount{ + Destination: filepath.Clean(destPath), + Type: define.TypeTmpfs, + Options: options, + Source: define.TypeTmpfs, + } + m[destPath] = mount + } + return m, nil +} diff -Nru libpod-3.2.1+ds1/pkg/systemd/activation.go libpod-3.4.4+ds1/pkg/systemd/activation.go --- libpod-3.2.1+ds1/pkg/systemd/activation.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/activation.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,11 +25,5 @@ if err != nil || nfds == 0 { return false } - - // "github.com/coreos/go-systemd/v22/activation" will use and validate this variable's - // value. We're just providing a fast fail - if _, found = os.LookupEnv("LISTEN_FDNAMES"); !found { - return false - } return true } diff -Nru libpod-3.2.1+ds1/pkg/systemd/activation_test.go libpod-3.4.4+ds1/pkg/systemd/activation_test.go --- libpod-3.2.1+ds1/pkg/systemd/activation_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/activation_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,32 @@ +package systemd + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestSocketActivated(t *testing.T) { + assert := assert.New(t) + + assert.False(SocketActivated()) + + // different pid + assert.NoError(os.Setenv("LISTEN_PID", "1")) + assert.False(SocketActivated()) + + // same pid no fds + assert.NoError(os.Setenv("LISTEN_PID", fmt.Sprintf("%d", os.Getpid()))) + assert.NoError(os.Setenv("LISTEN_FDS", "0")) + assert.False(SocketActivated()) + + // same pid some fds + assert.NoError(os.Setenv("LISTEN_FDS", "1")) + assert.True(SocketActivated()) + + // FDNAME is ok too (but not required) + assert.NoError(os.Setenv("LISTEN_FDNAMES", "/meshuggah/rocks")) + assert.True(SocketActivated()) +} diff -Nru libpod-3.2.1+ds1/pkg/systemd/dbus.go libpod-3.4.4+ds1/pkg/systemd/dbus.go --- libpod-3.2.1+ds1/pkg/systemd/dbus.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/dbus.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,8 +9,106 @@ "github.com/containers/podman/v3/pkg/rootless" "github.com/coreos/go-systemd/v22/dbus" godbus "github.com/godbus/dbus/v5" + "github.com/sirupsen/logrus" ) +// IsSystemdSessionValid checks if sessions is valid for provided rootless uid. +func IsSystemdSessionValid(uid int) bool { + var conn *godbus.Conn + var err error + var object godbus.BusObject + var seat0Path godbus.ObjectPath + dbusDest := "org.freedesktop.login1" + dbusInterface := "org.freedesktop.login1.Manager" + dbusPath := "/org/freedesktop/login1" + + if rootless.IsRootless() { + conn, err = GetLogindConnection(rootless.GetRootlessUID()) + object = conn.Object(dbusDest, godbus.ObjectPath(dbusPath)) + if err != nil { + //unable to fetch systemd object for logind + logrus.Debugf("systemd-logind: %s", err) + return false + } + object = conn.Object(dbusDest, godbus.ObjectPath(dbusPath)) + if err := object.Call(dbusInterface+".GetSeat", 0, "seat0").Store(&seat0Path); err != nil { + //unable to get seat0 path. + logrus.Debugf("systemd-logind: %s", err) + return false + } + seat0Obj := conn.Object(dbusDest, seat0Path) + activeSession, err := seat0Obj.GetProperty(dbusDest + ".Seat.ActiveSession") + if err != nil { + //unable to get active sessions. + logrus.Debugf("systemd-logind: %s", err) + return false + } + activeSessionMap, ok := activeSession.Value().([]interface{}) + if !ok || len(activeSessionMap) < 2 { + //unable to get active session map. + logrus.Debugf("systemd-logind: %s", err) + return false + } + activeSessionPath, ok := activeSessionMap[1].(godbus.ObjectPath) + if !ok { + //unable to fetch active session path. + logrus.Debugf("systemd-logind: %s", err) + return false + } + activeSessionObj := conn.Object(dbusDest, activeSessionPath) + sessionUser, err := activeSessionObj.GetProperty(dbusDest + ".Session.User") + if err != nil { + //unable to fetch session user from activeSession path. + logrus.Debugf("systemd-logind: %s", err) + return false + } + dbusUser, ok := sessionUser.Value().([]interface{}) + if !ok { + // not a valid user. + return false + } + if len(dbusUser) < 2 { + // not a valid session user. + return false + } + activeUID, ok := dbusUser[0].(uint32) + if !ok { + return false + } + //active session found which belongs to following rootless user + if activeUID == uint32(uid) { + return true + } + return false + } + return true +} + +// GetDbusConnection returns a user connection to D-BUS +func GetLogindConnection(uid int) (*godbus.Conn, error) { + return dbusAuthConnectionLogind(uid) +} + +func dbusAuthConnectionLogind(uid int) (*godbus.Conn, error) { + var conn *godbus.Conn + var err error + conn, err = godbus.SystemBusPrivate() + if err != nil { + return nil, err + } + methods := []godbus.Auth{godbus.AuthExternal(strconv.Itoa(uid))} + if err = conn.Auth(methods); err != nil { + conn.Close() + return nil, err + } + err = conn.Hello() + if err != nil { + conn.Close() + return nil, err + } + return conn, nil +} + func dbusAuthRootlessConnection(createBus func(opts ...godbus.ConnOption) (*godbus.Conn, error)) (*godbus.Conn, error) { conn, err := createBus() if err != nil { diff -Nru libpod-3.2.1+ds1/pkg/systemd/define/const.go libpod-3.4.4+ds1/pkg/systemd/define/const.go --- libpod-3.2.1+ds1/pkg/systemd/define/const.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/define/const.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,8 +1,13 @@ package define -// EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and -// is set to the unit's (unique) name. -const EnvVariable = "PODMAN_SYSTEMD_UNIT" +const ( + // Default restart policy for generated unit files. + DefaultRestartPolicy = "on-failure" + + // EnvVariable "PODMAN_SYSTEMD_UNIT" is set in all generated systemd units and + // is set to the unit's (unique) name. + EnvVariable = "PODMAN_SYSTEMD_UNIT" +) // RestartPolicies includes all valid restart policies to be used in a unit // file. diff -Nru libpod-3.2.1+ds1/pkg/systemd/generate/common.go libpod-3.4.4+ds1/pkg/systemd/generate/common.go --- libpod-3.2.1+ds1/pkg/systemd/generate/common.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/generate/common.go 2021-12-26 00:45:51.000000000 +0000 @@ -34,7 +34,7 @@ [Unit] Description=Podman {{{{.ServiceName}}}}.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor={{{{.RunRoot}}}} ` @@ -60,7 +60,7 @@ return processed } -// filterCommonContainerFlags removes --conmon-pidfile, --cidfile and --cgroups from the specified command. +// filterCommonContainerFlags removes --sdnotify, --rm and --cgroups from the specified command. // argCount is the number of last arguments which should not be filtered, e.g. the container entrypoint. func filterCommonContainerFlags(command []string, argCount int) []string { processed := []string{} @@ -68,12 +68,16 @@ s := command[i] switch { - case s == "--conmon-pidfile", s == "--cidfile", s == "--cgroups": + case s == "--rm": + // Boolean flags support --flag and --flag={true,false}. + continue + case s == "--sdnotify", s == "--cgroups", s == "--cidfile", s == "--restart": i++ continue - case strings.HasPrefix(s, "--conmon-pidfile="), + case strings.HasPrefix(s, "--rm="), + strings.HasPrefix(s, "--cgroups="), strings.HasPrefix(s, "--cidfile="), - strings.HasPrefix(s, "--cgroups="): + strings.HasPrefix(s, "--restart="): continue } processed = append(processed, s) diff -Nru libpod-3.2.1+ds1/pkg/systemd/generate/common_test.go libpod-3.4.4+ds1/pkg/systemd/generate/common_test.go --- libpod-3.2.1+ds1/pkg/systemd/generate/common_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/generate/common_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -93,12 +93,12 @@ }, { []string{"podman", "run", "--conmon-pidfile", "foo", "alpine"}, - []string{"podman", "run", "alpine"}, + []string{"podman", "run", "--conmon-pidfile", "foo", "alpine"}, 1, }, { []string{"podman", "run", "--conmon-pidfile=foo", "alpine"}, - []string{"podman", "run", "alpine"}, + []string{"podman", "run", "--conmon-pidfile=foo", "alpine"}, 1, }, { @@ -117,30 +117,20 @@ 1, }, { - []string{"podman", "run", "--cgroups=foo", "alpine"}, - []string{"podman", "run", "alpine"}, - 1, - }, - { - []string{"podman", "run", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "alpine"}, + []string{"podman", "run", "--cgroups=foo", "--restart=foo", "alpine"}, []string{"podman", "run", "alpine"}, 1, }, { - []string{"podman", "run", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo", "alpine"}, + []string{"podman", "run", "--cgroups=foo", "--rm", "--restart", "foo", "alpine"}, []string{"podman", "run", "alpine"}, 1, }, { - []string{"podman", "run", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo"}, - []string{"podman", "run", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo"}, + []string{"podman", "run", "--cgroups", "--rm=bogus", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "--rm"}, + []string{"podman", "run", "alpine", "--cgroups", "foo", "--conmon-pidfile", "foo", "--cidfile", "foo", "--rm"}, 7, }, - { - []string{"podman", "run", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo", "alpine", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo"}, - []string{"podman", "run", "alpine", "--cgroups=foo", "--conmon-pidfile=foo", "--cidfile=foo"}, - 4, - }, } for _, test := range tests { diff -Nru libpod-3.2.1+ds1/pkg/systemd/generate/containers.go libpod-3.4.4+ds1/pkg/systemd/generate/containers.go --- libpod-3.2.1+ds1/pkg/systemd/generate/containers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/generate/containers.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ "time" "github.com/containers/podman/v3/libpod" + libpodDefine "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/systemd/define" "github.com/containers/podman/v3/version" @@ -25,11 +26,17 @@ ServiceName string // Name or ID of the container. ContainerNameOrID string + // Type of the unit. + Type string + // NotifyAccess of the unit. + NotifyAccess string // StopTimeout sets the timeout Podman waits before killing the container // during service stop. StopTimeout uint // RestartPolicy of the systemd unit (e.g., no, on-failure, always). RestartPolicy string + // Custom number of restart attempts. + StartLimitBurst string // PIDFile of the service. Required for forking services. Must point to the // PID of the associated conmon process. PIDFile string @@ -97,18 +104,30 @@ Environment={{{{- range $index, $value := .ExtraEnvs -}}}}{{{{if $index}}}} {{{{end}}}}{{{{ $value }}}}{{{{end}}}} {{{{- end}}}} Restart={{{{.RestartPolicy}}}} +{{{{- if .StartLimitBurst}}}} +StartLimitBurst={{{{.StartLimitBurst}}}} +{{{{- end}}}} TimeoutStopSec={{{{.TimeoutStopSec}}}} {{{{- if .ExecStartPre}}}} ExecStartPre={{{{.ExecStartPre}}}} {{{{- end}}}} ExecStart={{{{.ExecStart}}}} +{{{{- if .ExecStop}}}} ExecStop={{{{.ExecStop}}}} +{{{{- end}}}} +{{{{- if .ExecStopPost}}}} ExecStopPost={{{{.ExecStopPost}}}} +{{{{- end}}}} +{{{{- if .PIDFile}}}} PIDFile={{{{.PIDFile}}}} -Type=forking +{{{{- end}}}} +Type={{{{.Type}}}} +{{{{- if .NotifyAccess}}}} +NotifyAccess={{{{.NotifyAccess}}}} +{{{{- end}}}} [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` // ContainerUnit generates a systemd unit for the specified container. Based @@ -142,21 +161,16 @@ if config.CreateCommand != nil { createCommand = config.CreateCommand } else if options.New { - return nil, errors.Errorf("cannot use --new on container %q: no create command found", ctr.ID()) + return nil, errors.Errorf("cannot use --new on container %q: no create command found: only works on containers created directly with podman but not via REST API", ctr.ID()) } nameOrID, serviceName := containerServiceName(ctr, options) - store := ctr.Runtime().GetStore() - if store == nil { - return nil, errors.Errorf("could not determine storage store for container") - } - var runRoot string if options.New { runRoot = "%t/containers" } else { - runRoot = store.RunRoot() + runRoot = ctr.Runtime().RunRoot() if runRoot == "" { return nil, errors.Errorf("could not lookup container's runroot: got empty string") } @@ -167,7 +181,7 @@ info := containerInfo{ ServiceName: serviceName, ContainerNameOrID: nameOrID, - RestartPolicy: options.RestartPolicy, + RestartPolicy: define.DefaultRestartPolicy, PIDFile: conmonPidFile, StopTimeout: timeout, GenerateTimestamp: true, @@ -194,8 +208,11 @@ // containerInfo. Note that the containerInfo is also post processed and // completed, which allows for an easier unit testing. func executeContainerTemplate(info *containerInfo, options entities.GenerateSystemdOptions) (string, error) { - if err := validateRestartPolicy(info.RestartPolicy); err != nil { - return "", err + if options.RestartPolicy != nil { + if err := validateRestartPolicy(*options.RestartPolicy); err != nil { + return "", err + } + info.RestartPolicy = *options.RestartPolicy } // Make sure the executable is set. @@ -208,6 +225,7 @@ info.Executable = executable } + info.Type = "forking" info.EnvVariable = define.EnvVariable info.ExecStart = "{{{{.Executable}}}} start {{{{.ContainerNameOrID}}}}" info.ExecStop = "{{{{.Executable}}}} stop {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}} {{{{.ContainerNameOrID}}}}" @@ -221,8 +239,13 @@ // invalid `info.CreateCommand`. Hence, we're doing a best effort unit // generation and don't try aiming at completeness. if options.New { - info.PIDFile = "%t/" + info.ServiceName + ".pid" - info.ContainerIDFile = "%t/" + info.ServiceName + ".ctr-id" + info.Type = "notify" + info.NotifyAccess = "all" + info.PIDFile = "" + info.ContainerIDFile = "%t/%n.ctr-id" + info.ExecStartPre = "/bin/rm -f {{{{.ContainerIDFile}}}}" + info.ExecStop = "{{{{.Executable}}}} stop --ignore --cidfile={{{{.ContainerIDFile}}}}" + info.ExecStopPost = "{{{{.Executable}}}} rm -f --ignore --cidfile={{{{.ContainerIDFile}}}}" // The create command must at least have three arguments: // /usr/bin/podman run $IMAGE index := 0 @@ -245,9 +268,9 @@ } startCommand = append(startCommand, "run", - "--conmon-pidfile", "{{{{.PIDFile}}}}", - "--cidfile", "{{{{.ContainerIDFile}}}}", + "--cidfile={{{{.ContainerIDFile}}}}", "--cgroups=no-conmon", + "--rm", ) remainingCmd := info.CreateCommand[index:] @@ -260,6 +283,8 @@ fs.String("name", "", "") fs.Bool("replace", false, "") fs.StringArrayP("env", "e", nil, "") + fs.String("sdnotify", "", "") + fs.String("restart", "", "") fs.Parse(remainingCmd) remainingCmd = filterCommonContainerFlags(remainingCmd, fs.NArg()) @@ -281,6 +306,13 @@ return "", err } + // Default to --sdnotify=conmon unless already set by the + // container. + hasSdnotifyParam := fs.Lookup("sdnotify").Changed + if !hasSdnotifyParam { + startCommand = append(startCommand, "--sdnotify=conmon") + } + if !hasDetachParam { // Enforce detaching // @@ -317,6 +349,27 @@ } } + // Unless the user explicitly set a restart policy, check + // whether the container was created with a custom one and use + // it instead of the default. + if options.RestartPolicy == nil { + restartPolicy, err := fs.GetString("restart") + if err != nil { + return "", err + } + if restartPolicy != "" { + if strings.HasPrefix(restartPolicy, "on-failure:") { + // Special case --restart=on-failure:5 + spl := strings.Split(restartPolicy, ":") + restartPolicy = spl[0] + info.StartLimitBurst = spl[1] + } else if restartPolicy == libpodDefine.RestartPolicyUnlessStopped { + restartPolicy = libpodDefine.RestartPolicyAlways + } + info.RestartPolicy = restartPolicy + } + } + envs, err := fs.GetStringArray("env") if err != nil { return "", err @@ -336,11 +389,7 @@ startCommand = append(startCommand, remainingCmd...) startCommand = escapeSystemdArguments(startCommand) - - info.ExecStartPre = "/bin/rm -f {{{{.PIDFile}}}} {{{{.ContainerIDFile}}}}" info.ExecStart = strings.Join(startCommand, " ") - info.ExecStop = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}stop --ignore --cidfile {{{{.ContainerIDFile}}}} {{{{if (ge .StopTimeout 0)}}}}-t {{{{.StopTimeout}}}}{{{{end}}}}" - info.ExecStopPost = "{{{{.Executable}}}} {{{{if .RootFlags}}}}{{{{ .RootFlags}}}} {{{{end}}}}rm --ignore -f --cidfile {{{{.ContainerIDFile}}}}" } info.TimeoutStopSec = minTimeoutStopSec + info.StopTimeout diff -Nru libpod-3.2.1+ds1/pkg/systemd/generate/containers_test.go libpod-3.4.4+ds1/pkg/systemd/generate/containers_test.go --- libpod-3.2.1+ds1/pkg/systemd/generate/containers_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/generate/containers_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -46,13 +46,13 @@ [Unit] Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=82 ExecStart=/usr/bin/podman start 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 ExecStop=/usr/bin/podman stop -t 22 639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401 @@ -61,7 +61,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodID := serviceInfo + headerInfo + goodIDContent goodIDNoHeaderInfo := serviceInfo + goodIDContent @@ -72,13 +72,13 @@ [Unit] Description=Podman container-foobar.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar @@ -87,7 +87,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNameBoundTo := `# container-foobar.service @@ -96,7 +96,7 @@ [Unit] Description=Podman container-foobar.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage BindsTo=a.service b.service c.service pod.service @@ -104,7 +104,7 @@ [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 ExecStart=/usr/bin/podman start foobar ExecStop=/usr/bin/podman stop -t 10 foobar @@ -113,7 +113,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodWithNameAndGeneric := `# jadda-jadda.service @@ -122,23 +122,48 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" -ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman container run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --replace --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target +` + + goodWithNameAndSdnotify := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman container run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm -d --replace --sdnotify=container --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN "foo=arg \"with \" space" +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all + +[Install] +WantedBy=default.target ` goodWithExplicitShortDetachParam := `# jadda-jadda.service @@ -147,23 +172,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNameNewWithPodFile := `# jadda-jadda.service @@ -172,23 +197,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file %t/pod-foobar.pod-id-file --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --pod-id-file %t/pod-foobar.pod-id-file --sdnotify=conmon --replace -d --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNameNewDetach := `# jadda-jadda.service @@ -197,23 +222,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon --replace --detach --name jadda-jadda --hostname hello-world awesome-image:latest command arg1 ... argN +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service @@ -222,23 +247,23 @@ [Unit] Description=Podman container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.ctr-id -PIDFile=%t/container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` genGoodNewDetach := func(detachparam string) string { @@ -248,25 +273,25 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon ` + +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon ` + detachparam + ` awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` return goodNewDetach } @@ -277,23 +302,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test -p 80:80 awesome-image:latest somecmd --detach=false -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --replace --name test -p 80:80 awesome-image:latest somecmd --detach=false +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewRootFlags := `# jadda-jadda.service @@ -302,23 +327,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=102 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman --events-backend none --runroot /root run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman --events-backend none --runroot /root stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 42 -ExecStopPost=/usr/bin/podman --events-backend none --runroot /root rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman --events-backend none --runroot /root run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodContainerCreate := `# jadda-jadda.service @@ -327,23 +352,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman container run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest -ExecStop=/usr/bin/podman container stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman container rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman container run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithJournaldTag := `# jadda-jadda.service @@ -352,23 +377,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test --log-driver=journald --log-opt=tag={{.Name}} awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --replace --name test --log-driver=journald --log-opt=tag={{.Name}} awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithSpecialChars := `# jadda-jadda.service @@ -377,23 +402,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --replace --name test awesome-image:latest sh -c "kill $$$$ && echo %%\\" -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --replace --name test awesome-image:latest sh -c "kill $$$$ && echo %%\\" +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithIDFiles := `# jadda-jadda.service @@ -402,23 +427,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo alpine -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --conmon-pidfile=foo awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo alpine +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithPodIDFiles := `# jadda-jadda.service @@ -427,23 +452,23 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon --pod-id-file %t/pod-foobar.pod-id-file -d awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo --pod-id-file /tmp/pod-foobar.pod-id-file alpine -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --pod-id-file %t/pod-foobar.pod-id-file --sdnotify=conmon -d --conmon-pidfile=foo awesome-image:latest podman run --cgroups=foo --conmon-pidfile=foo --cidfile=foo --pod-id-file /tmp/pod-foobar.pod-id-file alpine +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` goodNewWithEnvar := `# jadda-jadda.service @@ -452,24 +477,50 @@ [Unit] Description=Podman jadda-jadda.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage [Service] Environment=PODMAN_SYSTEMD_UNIT=%n Environment=FOO=abc "BAR=my test" USER=%%a -Restart=always +Restart=on-failure TimeoutStopSec=70 -ExecStartPre=/bin/rm -f %t/jadda-jadda.pid %t/jadda-jadda.ctr-id -ExecStart=/usr/bin/podman run --conmon-pidfile %t/jadda-jadda.pid --cidfile %t/jadda-jadda.ctr-id --cgroups=no-conmon -d --env FOO --env=BAR --env=MYENV=2 -e USER awesome-image:latest -ExecStop=/usr/bin/podman stop --ignore --cidfile %t/jadda-jadda.ctr-id -t 10 -ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/jadda-jadda.ctr-id -PIDFile=%t/jadda-jadda.pid -Type=forking +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d --env FOO --env=BAR --env=MYENV=2 -e USER awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target +` + + goodNewWithRestartPolicy := `# jadda-jadda.service +# autogenerated by Podman CI + +[Unit] +Description=Podman jadda-jadda.service +Documentation=man:podman-generate-systemd(1) +Wants=network-online.target +After=network-online.target +RequiresMountsFor=/var/run/containers/storage + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +Restart=on-failure +StartLimitBurst=42 +TimeoutStopSec=70 +ExecStartPre=/bin/rm -f %t/%n.ctr-id +ExecStart=/usr/bin/podman run --cidfile=%t/%n.ctr-id --cgroups=no-conmon --rm --sdnotify=conmon -d awesome-image:latest +ExecStop=/usr/bin/podman stop --ignore --cidfile=%t/%n.ctr-id +ExecStopPost=/usr/bin/podman rm -f --ignore --cidfile=%t/%n.ctr-id +Type=notify +NotifyAccess=all + +[Install] +WantedBy=default.target ` tests := []struct { name string @@ -485,7 +536,6 @@ Executable: "/usr/bin/podman", ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 22, PodmanVersion: "CI", @@ -503,7 +553,6 @@ Executable: "/usr/bin/podman", ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 22, PodmanVersion: "CI", @@ -521,7 +570,6 @@ Executable: "/usr/bin/podman", ServiceName: "container-foobar", ContainerNameOrID: "foobar", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -539,7 +587,6 @@ Executable: "/usr/bin/podman", ServiceName: "container-foobar", ContainerNameOrID: "foobar", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -553,38 +600,38 @@ false, false, }, - {"bad restart policy", + {"good with name and generic", containerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "never", - PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 10, - PodmanVersion: "CI", - EnvVariable: define.EnvVariable, - GraphRoot: "/var/lib/containers/storage", - RunRoot: "/var/run/containers/storage", + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, + EnvVariable: define.EnvVariable, + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", }, - "", + goodWithNameAndGeneric, + true, false, false, - true, }, - {"good with name and generic", + {"good with name and sdnotify", containerInfo{ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "container", "run", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, + CreateCommand: []string{"I'll get stripped", "container", "run", "--sdnotify=container", "--name", "jadda-jadda", "--hostname", "hello-world", "awesome-image:latest", "command", "arg1", "...", "argN", "foo=arg \"with \" space"}, EnvVariable: define.EnvVariable, GraphRoot: "/var/lib/containers/storage", RunRoot: "/var/run/containers/storage", }, - goodWithNameAndGeneric, + goodWithNameAndSdnotify, true, false, false, @@ -594,7 +641,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -613,7 +659,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -635,7 +680,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -654,7 +698,6 @@ Executable: "/usr/bin/podman", ServiceName: "container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", ContainerNameOrID: "639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -673,7 +716,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -692,7 +734,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -711,7 +752,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -730,7 +770,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -749,7 +788,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -768,26 +806,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", - PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", - StopTimeout: 42, - PodmanVersion: "CI", - CreateCommand: []string{"I'll get stripped", "run", "-tid", "awesome-image:latest"}, - EnvVariable: define.EnvVariable, - GraphRoot: "/var/lib/containers/storage", - RunRoot: "/var/run/containers/storage", - }, - genGoodNewDetach("-tid"), - true, - false, - false, - }, - {"good with root flags", - containerInfo{ - Executable: "/usr/bin/podman", - ServiceName: "jadda-jadda", - ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -806,7 +824,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -825,7 +842,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -844,7 +860,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -863,7 +878,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -882,7 +896,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -904,7 +917,6 @@ Executable: "/usr/bin/podman", ServiceName: "jadda-jadda", ContainerNameOrID: "jadda-jadda", - RestartPolicy: "always", PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -919,6 +931,24 @@ false, false, }, + {"good with restart policy", + containerInfo{ + Executable: "/usr/bin/podman", + ServiceName: "jadda-jadda", + ContainerNameOrID: "jadda-jadda", + PIDFile: "/var/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", + StopTimeout: 10, + PodmanVersion: "CI", + GraphRoot: "/var/lib/containers/storage", + RunRoot: "/var/run/containers/storage", + CreateCommand: []string{"I'll get stripped", "create", "--restart", "on-failure:42", "awesome-image:latest"}, + EnvVariable: define.EnvVariable, + }, + goodNewWithRestartPolicy, + true, + false, + false, + }, } for _, tt := range tests { test := tt @@ -927,12 +957,13 @@ New: test.new, NoHeader: test.noHeader, } + test.info.RestartPolicy = define.DefaultRestartPolicy got, err := executeContainerTemplate(&test.info, opts) if (err != nil) != test.wantErr { - t.Errorf("CreateContainerSystemdUnit() error = \n%v, wantErr \n%v", err, test.wantErr) + t.Errorf("CreateContainerSystemdUnit() %s error = \n%v, wantErr \n%v", test.name, err, test.wantErr) return } - assert.Equal(t, test.want, got) + assert.Equal(t, test.want, got, test.name) }) } } diff -Nru libpod-3.2.1+ds1/pkg/systemd/generate/pods.go libpod-3.4.4+ds1/pkg/systemd/generate/pods.go --- libpod-3.2.1+ds1/pkg/systemd/generate/pods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/generate/pods.go 2021-12-26 00:45:51.000000000 +0000 @@ -101,7 +101,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` // PodUnits generates systemd units for the specified pod and its containers. @@ -217,7 +217,6 @@ info := podInfo{ ServiceName: serviceName, InfraNameOrID: ctrNameOrID, - RestartPolicy: options.RestartPolicy, PIDFile: conmonPidFile, StopTimeout: timeout, GenerateTimestamp: true, @@ -230,8 +229,12 @@ // that the podInfo is also post processed and completed, which allows for an // easier unit testing. func executePodTemplate(info *podInfo, options entities.GenerateSystemdOptions) (string, error) { - if err := validateRestartPolicy(info.RestartPolicy); err != nil { - return "", err + info.RestartPolicy = define.DefaultRestartPolicy + if options.RestartPolicy != nil { + if err := validateRestartPolicy(*options.RestartPolicy); err != nil { + return "", err + } + info.RestartPolicy = *options.RestartPolicy } // Make sure the executable is set. diff -Nru libpod-3.2.1+ds1/pkg/systemd/generate/pods_test.go libpod-3.4.4+ds1/pkg/systemd/generate/pods_test.go --- libpod-3.2.1+ds1/pkg/systemd/generate/pods_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/systemd/generate/pods_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -45,7 +45,7 @@ [Unit] Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service @@ -53,7 +53,7 @@ [Service] Environment=PODMAN_SYSTEMD_UNIT=%n -Restart=always +Restart=on-failure TimeoutStopSec=102 ExecStart=/usr/bin/podman start jadda-jadda-infra ExecStop=/usr/bin/podman stop -t 42 jadda-jadda-infra @@ -62,7 +62,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podGood := serviceInfo + headerInfo + podContent podGoodNoHeaderInfo := serviceInfo + podContent @@ -73,7 +73,7 @@ [Unit] Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service @@ -92,7 +92,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podGoodNamedNewWithRootArgs := `# pod-123abc.service @@ -101,7 +101,7 @@ [Unit] Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service @@ -120,7 +120,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podGoodNamedNewWithReplaceFalse := `# pod-123abc.service @@ -129,7 +129,7 @@ [Unit] Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service @@ -148,7 +148,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` podNewLabelWithCurlyBraces := `# pod-123abc.service @@ -157,7 +157,7 @@ [Unit] Description=Podman pod-123abc.service Documentation=man:podman-generate-systemd(1) -Wants=network.target +Wants=network-online.target After=network-online.target RequiresMountsFor=/var/run/containers/storage Requires=container-1.service container-2.service @@ -176,7 +176,7 @@ Type=forking [Install] -WantedBy=multi-user.target default.target +WantedBy=default.target ` tests := []struct { @@ -192,7 +192,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -211,7 +210,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -230,7 +228,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "always", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 42, PodmanVersion: "CI", @@ -249,7 +246,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "on-failure", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -268,7 +264,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "on-failure", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -287,7 +282,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "on-failure", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -306,7 +300,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "on-failure", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", @@ -325,7 +318,6 @@ Executable: "/usr/bin/podman", ServiceName: "pod-123abc", InfraNameOrID: "jadda-jadda-infra", - RestartPolicy: "on-failure", PIDFile: "/run/containers/storage/overlay-containers/639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401/userdata/conmon.pid", StopTimeout: 10, PodmanVersion: "CI", diff -Nru libpod-3.2.1+ds1/pkg/terminal/console_windows.go libpod-3.4.4+ds1/pkg/terminal/console_windows.go --- libpod-3.2.1+ds1/pkg/terminal/console_windows.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/terminal/console_windows.go 2021-12-26 00:45:51.000000000 +0000 @@ -25,7 +25,7 @@ var mode uint32 err := windows.GetConsoleMode(handle, &mode) if err != nil { - return err + return nil // not a terminal } if err := windows.SetConsoleMode(handle, mode|flags); err != nil { // In similar code, it is not considered an error if we cannot set the diff -Nru libpod-3.2.1+ds1/pkg/util/camelcase/camelcase.go libpod-3.4.4+ds1/pkg/util/camelcase/camelcase.go --- libpod-3.2.1+ds1/pkg/util/camelcase/camelcase.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/util/camelcase/camelcase.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,7 +9,7 @@ // Split splits the camelcase word and returns a list of words. It also // supports digits. Both lower camel case and upper camel case are supported. -// For more info please check: http://en.wikipedia.org/wiki/CamelCase +// For more info please check: https://en.wikipedia.org/wiki/CamelCase // // Examples // diff -Nru libpod-3.2.1+ds1/pkg/util/camelcase/README.md libpod-3.4.4+ds1/pkg/util/camelcase/README.md --- libpod-3.2.1+ds1/pkg/util/camelcase/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/util/camelcase/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -# CamelCase [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/camelcase) [![Build Status](http://img.shields.io/travis/fatih/camelcase.svg?style=flat-square)](https://travis-ci.org/fatih/camelcase) +# CamelCase [![GoDoc](https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://godoc.org/github.com/fatih/camelcase) [![Build Status](https://img.shields.io/travis/fatih/camelcase.svg?style=flat-square)](https://travis-ci.org/fatih/camelcase) CamelCase is a Golang (Go) package to split the words of a camelcase type string into a slice of words. It can be used to convert a camelcase word (lower @@ -33,7 +33,7 @@ ``` Both lower camel case and upper camel case are supported. For more info please -check: [http://en.wikipedia.org/wiki/CamelCase](http://en.wikipedia.org/wiki/CamelCase) +check: [https://en.wikipedia.org/wiki/CamelCase](https://en.wikipedia.org/wiki/CamelCase) Below are some example cases: diff -Nru libpod-3.2.1+ds1/pkg/util/utils.go libpod-3.4.4+ds1/pkg/util/utils.go --- libpod-3.2.1+ds1/pkg/util/utils.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/util/utils.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,6 +3,7 @@ import ( "encoding/json" "fmt" + "math" "os" "os/user" "path/filepath" @@ -519,7 +520,7 @@ // ParseInputTime takes the users input and to determine if it is valid and // returns a time format and error. The input is compared to known time formats // or a duration which implies no-duration -func ParseInputTime(inputTime string) (time.Time, error) { +func ParseInputTime(inputTime string, since bool) (time.Time, error) { timeFormats := []string{time.RFC3339Nano, time.RFC3339, "2006-01-02T15:04:05", "2006-01-02T15:04:05.999999999", "2006-01-02Z07:00", "2006-01-02"} // iterate the supported time formats @@ -530,9 +531,10 @@ } } - unixTimestamp, err := strconv.ParseInt(inputTime, 10, 64) + unixTimestamp, err := strconv.ParseFloat(inputTime, 64) if err == nil { - return time.Unix(unixTimestamp, 0), nil + iPart, fPart := math.Modf(unixTimestamp) + return time.Unix(int64(iPart), int64(fPart*1_000_000_000)).UTC(), nil } // input might be a duration @@ -540,7 +542,10 @@ if err != nil { return time.Time{}, errors.Errorf("unable to interpret time value") } - return time.Now().Add(-duration), nil + if since { + return time.Now().Add(-duration), nil + } + return time.Now().Add(duration), nil } // OpenExclusiveFile opens a file for writing and ensure it doesn't already exist @@ -616,6 +621,12 @@ if len(arr) < 2 { return nil, errors.Errorf("%s is invalid, sysctl values must be in the form of KEY=VALUE", val) } + + trimmed := fmt.Sprintf("%s=%s", strings.TrimSpace(arr[0]), strings.TrimSpace(arr[1])) + if trimmed != val { + return nil, errors.Errorf("'%s' is invalid, extra spaces found", val) + } + if validSysctlMap[arr[0]] { sysctl[arr[0]] = arr[1] continue diff -Nru libpod-3.2.1+ds1/pkg/util/utils_supported.go libpod-3.4.4+ds1/pkg/util/utils_supported.go --- libpod-3.2.1+ds1/pkg/util/utils_supported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/util/utils_supported.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,4 +1,4 @@ -// +build linux darwin +// +build !windows package util diff -Nru libpod-3.2.1+ds1/pkg/util/utils_test.go libpod-3.4.4+ds1/pkg/util/utils_test.go --- libpod-3.2.1+ds1/pkg/util/utils_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/pkg/util/utils_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,9 @@ package util import ( + "fmt" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -258,6 +260,28 @@ assert.Error(t, err) } +func TestValidateSysctlBadSysctlWithExtraSpaces(t *testing.T) { + expectedError := "'%s' is invalid, extra spaces found" + + // should fail fast on first sysctl + strSlice1 := []string{ + "net.ipv4.ping_group_range = 0 0", + "net.ipv4.ping_group_range=0 0 ", + } + _, err := ValidateSysctls(strSlice1) + assert.Error(t, err) + assert.Equal(t, err.Error(), fmt.Sprintf(expectedError, strSlice1[0])) + + // should fail on second sysctl + strSlice2 := []string{ + "net.ipv4.ping_group_range=0 0", + "net.ipv4.ping_group_range=0 0 ", + } + _, err = ValidateSysctls(strSlice2) + assert.Error(t, err) + assert.Equal(t, err.Error(), fmt.Sprintf(expectedError, strSlice2[1])) +} + func TestCoresToPeriodAndQuota(t *testing.T) { cores := 1.0 expectedPeriod := DefaultCPUPeriod @@ -277,3 +301,17 @@ assert.Equal(t, PeriodAndQuotaToCores(period, quota), expectedCores) } + +func TestParseInputTime(t *testing.T) { + tm, err := ParseInputTime("1.5", true) + if err != nil { + t.Errorf("expected error to be nil but was: %v", err) + } + + expected, err := time.ParseInLocation(time.RFC3339Nano, "1970-01-01T00:00:01.500000000Z", time.UTC) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, expected, tm) +} diff -Nru libpod-3.2.1+ds1/.pre-commit-config.yaml libpod-3.4.4+ds1/.pre-commit-config.yaml --- libpod-3.2.1+ds1/.pre-commit-config.yaml 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/.pre-commit-config.yaml 2021-12-26 00:45:51.000000000 +0000 @@ -4,8 +4,15 @@ - repo: https://github.com/pre-commit/pre-commit-hooks.git rev: v3.4.0 hooks: + # buildah-tests.diff is generated by 'git format-patch' and includes + # trailing whitespace as part of its format. We can work around that, + # but unfortunately the buildah repo has some files with tabs, which + # git-diff formats as '[+/-]', which these hooks choke on. + # Just disable checks on this diff file as a special case. - id: end-of-file-fixer + exclude: test/buildah-bud/buildah-tests.diff - id: trailing-whitespace + exclude: test/buildah-bud/buildah-tests.diff - id: mixed-line-ending - id: check-byte-order-marker - id: check-executables-have-shebangs diff -Nru libpod-3.2.1+ds1/README.md libpod-3.4.4+ds1/README.md --- libpod-3.2.1+ds1/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -5,9 +5,9 @@ Podman (the POD MANager) is a tool for managing containers and images, volumes mounted into those containers, and pods made from groups of containers. Podman is based on libpod, a library for container lifecycle management that is also contained in this repository. The libpod library provides APIs for managing containers, pods, container images, and volumes. -* [Latest Version: 3.1.0](https://github.com/containers/podman/releases/latest) +* [Latest Version: 3.3.1](https://github.com/containers/podman/releases/latest) * Latest Remote client for Windows - * Latest Remote client for MacOs + * Latest Remote client for macOS * Latest Static Remote client for Linux * Continuous Integration: [![Build Status](https://api.cirrus-ci.com/github/containers/podman.svg)](https://cirrus-ci.com/github/containers/podman/master) @@ -28,7 +28,7 @@ * Support for a REST API providing both a Docker-compatible interface and an improved interface exposing advanced Podman functionality. * In the future, integration with [CRI-O](https://github.com/cri-o/cri-o) to share containers and backend code. -Podman presently only supports running containers on Linux. However, we are building a remote client which can run on Windows and OS X and manage Podman containers on a Linux system via the REST API using SSH tunneling. +Podman presently only supports running containers on Linux. However, we are building a remote client which can run on Windows and macOS and manage Podman containers on a Linux system via the REST API using SSH tunneling. ## Roadmap @@ -42,7 +42,7 @@ Instead, send an email with as many details as possible to `security@lists.podman.io`. This is a private mailing list for the core maintainers. For general questions and discussion, please use the -IRC `#podman` channel on `irc.freenode.net`. +IRC `#podman` channel on `irc.libera.chat`. For discussions around issues/bugs and features, you can use the GitHub [issues](https://github.com/containers/podman/issues) @@ -93,7 +93,7 @@ **[OCI Hooks Support](pkg/hooks/README.md)** Information on how Podman configures [OCI Hooks][spec-hooks] to run when launching a container. -**[Podman API](http://docs.podman.io/en/latest/_static/api.html)** +**[Podman API](https://docs.podman.io/en/latest/_static/api.html)** Documentation on the Podman REST API. **[Podman Commands](https://podman.readthedocs.io/en/latest/Commands.html)** diff -Nru libpod-3.2.1+ds1/RELEASE_NOTES.md libpod-3.4.4+ds1/RELEASE_NOTES.md --- libpod-3.2.1+ds1/RELEASE_NOTES.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/RELEASE_NOTES.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,5 +1,351 @@ # Release Notes +## 3.4.4 +### Bugfixes +- Fixed a bug where the `podman exec` command would, under some circumstances, print a warning message about failing to move `conmon` to the appropriate cgroup ([#12535](https://github.com/containers/podman/issues/12535)). +- Fixed a bug where named volumes created as part of container creation (e.g. `podman run --volume avolume:/a/mountpoint` or similar) would be mounted with incorrect permissions ([#12523](https://github.com/containers/podman/issues/12523)). +- Fixed a bug where the `podman-remote create` and `podman-remote run` commands did not properly handle the `--entrypoint=""` option (to clear the container's entrypoint) ([#12521](https://github.com/containers/podman/issues/12521)). + +## 3.4.3 +### Security +- This release addresses CVE-2021-4024, where the `podman machine` command opened the `gvproxy` API (used to forward ports to `podman machine` VMs) to the public internet on port 7777. +- This release addresses CVE-2021-41190, where incomplete specification of behavior regarding image manifests could lead to inconsistent decoding on different clients. + +### Features +- The `--secret type=mount` option to `podman create` and `podman run` supports a new option, `target=`, which specifies where in the container the secret will be mounted ([#12287](https://github.com/containers/podman/issues/12287)). + +### Bugfixes +- Fixed a bug where rootless Podman would occasionally print warning messages about failing to move the pause process to a new cgroup ([#12065](https://github.com/containers/podman/issues/12065)). +- Fixed a bug where the `podman run` and `podman create` commands would, when pulling images, still require TLS even with registries set to Insecure via config file ([#11933](https://github.com/containers/podman/issues/11933)). +- Fixed a bug where the `podman generate systemd` command generated units that depended on `multi-user.target`, which has been removed from some distributions ([#12438](https://github.com/containers/podman/issues/12438)). +- Fixed a bug where Podman could not run containers with images that had `/etc/` as a symlink ([#12189](https://github.com/containers/podman/issues/12189)). +- Fixed a bug where the `podman logs -f` command would, when using the `journald` logs backend, exit immediately if the container had previously been restarted ([#12263](https://github.com/containers/podman/issues/12263)). +- Fixed a bug where, in containers on VMs created by `podman machine`, the `host.containers.internal` name pointed to the VM, not the host system ([#11642](https://github.com/containers/podman/issues/11642)). +- Fixed a bug where containers and pods created by the `podman play kube` command in VMs managed by `podman machine` would not automatically forward ports from the host machine ([#12248](https://github.com/containers/podman/issues/12248)). +- Fixed a bug where `podman machine init` would fail on OS X when GNU Coreutils was installed ([#12329](https://github.com/containers/podman/issues/12329)). +- Fixed a bug where `podman machine start` would exit before SSH on the started VM was accepting connections ([#11532](https://github.com/containers/podman/issues/11532)). +- Fixed a bug where the `podman run` command with signal proxying (`--sig-proxy`) enabled could print an error if it attempted to send a signal to a container that had just exited ([#8086](https://github.com/containers/podman/issues/8086)). +- Fixed a bug where the `podman stats` command would not return correct information for containers running Systemd as PID1 ([#12400](https://github.com/containers/podman/issues/12400)). +- Fixed a bug where the `podman image save` command would fail on OS X when writing the image to STDOUT ([#12402](https://github.com/containers/podman/issues/12402)). +- Fixed a bug where the `podman ps` command did not properly handle PS arguments which contained whitespace ([#12452](https://github.com/containers/podman/issues/12452)). +- Fixed a bug where the `podman-remote wait` command could fail to detect that the container exited and return an error under some circumstances ([#12457](https://github.com/containers/podman/issues/12457)). +- Fixed a bug where the Windows MSI installer for `podman-remote` would break the PATH environment variable by adding an extra `"` ([#11416](https://github.com/containers/podman/issues/11416)). + +### API +- Updated the containers/image library to v5.17.0 +- The Libpod Play Kube endpoint now also accepts `ConfigMap` YAML as part of its payload, and will use provided any `ConfigMap` to configure provided pods and services. +- Fixed a bug where the Compat Create endpoint for Containers would not always create the container's working directory if it did not exist ([#11842](https://github.com/containers/podman/issues/11842)). +- Fixed a bug where the Compat Create endpoint for Containers returned an incorrect error message with 404 errors when the requested image was not found ([#12315](https://github.com/containers/podman/pull/12315)). +- Fixed a bug where the Compat Create endpoint for Containers did not properly handle the `HostConfig.Mounts` field ([#12419](https://github.com/containers/podman/issues/12419)). +- Fixed a bug where the Compat Archive endpoint for Containers did not properly report errors when the operation failed ([#12420](https://github.com/containers/podman/issues/12420)). +- Fixed a bug where the Compat Build endpoint for Images ignored the `layers` query parameter (for caching intermediate layers from the build) ([#12378](https://github.com/containers/podman/issues/12378)). +- Fixed a bug where the Compat Build endpoint for Images did not report errors in a manner compatible with Docker ([#12392](https://github.com/containers/podman/issues/12392)). +- Fixed a bug where the Compat Build endpoint for Images would fail to build if the context directory was a symlink ([#12409](https://github.com/containers/podman/issues/12409)). +- Fixed a bug where the Compat List endpoint for Images included manifest lists (and not just images) in returned results ([#12453](https://github.com/containers/podman/issues/12453)). + +### Misc +- Updated the containers/image library to v5.17.0 +- Updated the containers/storage library to v1.37.0 +- Podman now builds by default with cgo enabled on OS X, resolving some issues with SSH ([#10737](https://github.com/containers/podman/issues/10737)). + +## 3.4.2 +### Bugfixes +- Fixed a bug where `podman tag` could not tag manifest lists ([#12046](https://github.com/containers/podman/issues/12046)). +- Fixed a bug where built-in volumes specified by images would not be created correctly under some circumstances. +- Fixed a bug where, when using Podman Machine on OS X, containers in pods did not have working port forwarding from the host ([#12207](https://github.com/containers/podman/issues/12207)). +- Fixed a bug where the `podman network reload` command command on containers using the `slirp4netns` network mode and the `rootlessport` port forwarding driver would make an unnecessary attempt to restart `rootlessport` on containers that did not forward ports. +- Fixed a bug where the `podman generate kube` command would generate YAML including some unnecessary (set to default) fields (e.g. empty SELinux and DNS configuration blocks, and the `privileged` flag when set to false) ([#11995](https://github.com/containers/podman/issues/11995)). +- Fixed a bug where the `podman pod rm` command could, if interrupted at the right moment, leave a reference to an already-removed infra container behind ([#12034](https://github.com/containers/podman/issues/12034)). +- Fixed a bug where the `podman pod rm` command would not remove pods with more than one container if all containers save for the infra container were stopped unless `--force` was specified ([#11713](https://github.com/containers/podman/issues/11713)). +- Fixed a bug where the `--memory` flag to `podman run` and `podman create` did not accept a limit of 0 (which should specify unlimited memory) ([#12002](https://github.com/containers/podman/issues/12002)). +- Fixed a bug where the remote Podman client's `podman build` command could attempt to build a Dockerfile in the working directory of the `podman system service` instance instead of the Dockerfile specified by the user ([#12054](https://github.com/containers/podman/issues/12054)). +- Fixed a bug where the `podman logs --tail` command could function improperly (printing more output than requested) when the `journald` log driver was used. +- Fixed a bug where containers run using the `slirp4netns` network mode with IPv6 enabled would not have IPv6 connectivity until several seconds after they started ([#11062](https://github.com/containers/podman/issues/11062)). +- Fixed a bug where some Podman commands could cause an extra `dbus-daemon` process to be created ([#9727](https://github.com/containers/podman/issues/9727)). +- Fixed a bug where rootless Podman would sometimes print warnings about a failure to move the pause process into a given CGroup ([#12065](https://github.com/containers/podman/issues/12065)). +- Fixed a bug where the `checkpointed` field in `podman inspect` on a container was not set to false after a container was restored. +- Fixed a bug where the `podman system service` command would print overly-verbose logs about request IDs ([#12181](https://github.com/containers/podman/issues/12181)). +- Fixed a bug where Podman could, when creating a new container without a name explicitly specified by the user, sometimes use an auto-generated name already in use by another container if multiple containers were being created in parallel ([#11735](https://github.com/containers/podman/issues/11735)). + +## 3.4.1 +### Bugfixes +- Fixed a bug where `podman machine init` could, under some circumstances, create invalid machine configurations which could not be started ([#11824](https://github.com/containers/podman/issues/11824)). +- Fixed a bug where the `podman machine list` command would not properly populate some output fields. +- Fixed a bug where `podman machine rm` could leave dangling sockets from the removed machine ([#11393](https://github.com/containers/podman/issues/11393)). +- Fixed a bug where `podman run --pids-limit=-1` was not supported (it now sets the PID limit in the container to unlimited) ([#11782](https://github.com/containers/podman/issues/11782)). +- Fixed a bug where `podman run` and `podman attach` could throw errors about a closed network connection when STDIN was closed by the client ([#11856](https://github.com/containers/podman/issues/11856)). +- Fixed a bug where the `podman stop` command could fail when run on a container that had another `podman stop` command run on it previously. +- Fixed a bug where the `--sync` flag to `podman ps` was nonfunctional. +- Fixed a bug where the Windows and OS X remote clients' `podman stats` command would fail ([#11909](https://github.com/containers/podman/issues/11909)). +- Fixed a bug where the `podman play kube` command did not properly handle environment variables whose values contained an `=` ([#11891](https://github.com/containers/podman/issues/11891)). +- Fixed a bug where the `podman generate kube` command could generate invalid annotations when run on containers with volumes that use SELinux relabelling (`:z` or `:Z`) ([#11929](https://github.com/containers/podman/issues/11929)). +- Fixed a bug where the `podman generate kube` command would generate YAML including some unnecessary (set to default) fields (e.g. user and group, entrypoint, default protocol for forwarded ports) ([#11914](https://github.com/containers/podman/issues/11914), [#11915](https://github.com/containers/podman/issues/11915), and [#11965](https://github.com/containers/podman/issues/11965)). +- Fixed a bug where the `podman generate kube` command could, under some circumstances, generate YAML including an invalid `targetPort` field for forwarded ports ([#11930](https://github.com/containers/podman/issues/11930)). +- Fixed a bug where rootless Podman's `podman info` command could, under some circumstances, not read available CGroup controllers ([#11931](https://github.com/containers/podman/issues/11931)). +- Fixed a bug where `podman container checkpoint --export` would fail to checkpoint any container created with `--log-driver=none` ([#11974](https://github.com/containers/podman/issues/11974)). + +### API +- Fixed a bug where the Compat Create endpoint for Containers could panic when no options were passed to a bind mount of tmpfs ([#11961](https://github.com/containers/podman/issues/11961)). + +## 3.4.0 +### Features +- Pods now support init containers! Init containers are containers which run before the rest of the pod starts. There are two types of init containers: "always", which always run before the pod is started, and "once", which only run the first time the pod starts and are subsequently removed. They can be added using the `podman create` command's `--init-ctr` option. +- Support for init containers has also been added to `podman play kube` and `podman generate kube` - init containers contained in Kubernetes YAML will be created as Podman init containers, and YAML generated by Podman will include any init containers created. +- The `podman play kube` command now supports building images. If the `--build` option is given and a directory with the name of the specified image exists in the current working directory and contains a valid Containerfile or Dockerfile, the image will be built and used for the container. +- The `podman play kube` command now supports a new option, `--teardown`, which removes any pods and containers created by the given Kubernetes YAML. +- The `podman generate kube` command now generates annotations for SELinux mount options on volume (`:z` and `:Z`) that are respected by the `podman play kube` command. +- A new command has been added, `podman pod logs`, to return logs for all containers in a pod at the same time. +- Two new commands have been added, `podman volume export` (to export a volume to a tar file) and `podman volume import`) (to populate a volume from a given tar file). +- The `podman auto-update` command now supports simple rollbacks. If a container fails to start after an automatic update, it will be rolled back to the previous image and restarted again. +- Pods now share their user namespace by default, and the `podman pod create` command now supports the `--userns` option. This allows rootless pods to be created with the `--userns=keep-id` option. +- The `podman pod ps` command now supports a new filter with its `--filter` option, `until`, which returns pods created before a given timestamp. +- The `podman image scp` command has been added. This command allows images to be transferred between different hosts. +- The `podman stats` command supports a new option, `--interval`, to specify the amount of time before the information is refreshed. +- The `podman inspect` command now includes ports exposed (but not published) by containers (e.g. ports from `--expose` when `--publish-all` is not specified). +- The `podman inspect` command now has a new boolean value, `Checkpointed`, which indicates that a container was stopped as a result of a `podman container checkpoint` operation. +- Volumes created by `podman volume create` now support setting quotas when run atop XFS. The `size` and `inode` options allow the maximum size and maximum number of inodes consumed by a volume to be limited. +- The `podman info` command now outputs information on what log drivers, network drivers, and volume plugins are available for use ([#11265](https://github.com/containers/podman/issues/11265)). +- The `podman info` command now outputs the current log driver in use, and the variant and codename of the distribution in use. +- The parameters of the VM created by `podman machine init` (amount of disk space, memory, CPUs) can now be set in `containers.conf`. +- The `podman machine ls` command now shows additional information (CPUs, memory, disk size) about VMs managed by `podman machine`. +- The `podman ps` command now includes healthcheck status in container state for containers that have healthchecks ([#11527](https://github.com/containers/podman/issues/11527)). + +### Changes +- The `podman build` command has a new alias, `podman buildx`, to improve compatibility with Docker. We have already added support for many `docker buildx` flags to `podman build` and aim to continue to do so. +- Cases where Podman is run without a user session or a writable temporary files directory will now produce better error messages. +- The default log driver has been changed from `file` to `journald`. The `file` driver did not properly support log rotation, so this should lead to a better experience. If journald is not available on the system, Podman will automatically revert to the `file`. +- Podman no longer depends on `ip` for removing networks ([#11403](https://github.com/containers/podman/issues/11403)). +- The deprecated `--macvlan` flag to `podman network create` now warns when it is used. It will be removed entirely in the Podman 4.0 release. +- The `podman machine start` command now prints a message when the VM is successfully started. +- The `podman stats` command can now be used on containers that are paused. +- The `podman unshare` command will now return the exit code of the command that was run in the user namespace (assuming the command was successfully run). +- Successful healthchecks will no longer add a `healthy` line to the system log to reduce log spam. +- As a temporary workaround for a lack of shortname prompts in the Podman remote client, VMs created by `podman machine` now default to only using the `docker.io` registry. + +### Bugfixes +- Fixed a bug where whitespace in the definition of sysctls (particularly default sysctls specified in `containers.conf`) would cause them to be parsed incorrectly. +- Fixed a bug where the Windows remote client improperly validated volume paths ([#10900](https://github.com/containers/podman/issues/10900)). +- Fixed a bug where the first line of logs from a container run with the `journald` log driver could be skipped. +- Fixed a bug where images created by `podman commit` did not include ports exposed by the container. +- Fixed a bug where the `podman auto-update` command would ignore the `io.containers.autoupdate.authfile` label when pulling images ([#11171](https://github.com/containers/podman/issues/11171)). +- Fixed a bug where the `--workdir` option to `podman create` and `podman run` could not be set to a directory where a volume was mounted ([#11352](https://github.com/containers/podman/issues/11352)). +- Fixed a bug where systemd socket-activation did not properly work with systemd-managed Podman containers ([#10443](https://github.com/containers/podman/issues/10443)). +- Fixed a bug where environment variable secrets added to a container were not available to exec sessions launched in the container. +- Fixed a bug where rootless containers could fail to start the `rootlessport` port-forwarding service when `XDG_RUNTIME_DIR` was set to a long path. +- Fixed a bug where arguments to the `--systemd` option to `podman create` and `podman run` were case-sensitive ([#11387](https://github.com/containers/podman/issues/11387)). +- Fixed a bug where the `podman manifest rm` command would also remove images referenced by the manifest, not just the manifest itself ([#11344](https://github.com/containers/podman/issues/11344)). +- Fixed a bug where the Podman remote client on OS X would not function properly if the `TMPDIR` environment variable was not set ([#11418](https://github.com/containers/podman/issues/11418)). +- Fixed a bug where the `/etc/hosts` file was not guaranteed to contain an entry for `localhost` (this is still not guaranteed if `--net=host` is used; such containers will exactly match the host's `/etc/hosts`) ([#11411](https://github.com/containers/podman/issues/11411)). +- Fixed a bug where the `podman machine start` command could print warnings about unsupported CPU features ([#11421](https://github.com/containers/podman/issues/11421)). +- Fixed a bug where the `podman info` command could segfault when accessing cgroup information. +- Fixed a bug where the `podman logs -f` command could hang when a container exited ([#11461](https://github.com/containers/podman/issues/11461)). +- Fixed a bug where the `podman generate systemd` command could not be used on containers that specified a restart policy ([#11438](https://github.com/containers/podman/issues/11438)). +- Fixed a bug where the remote Podman client's `podman build` command would fail to build containers if the UID and GID on the client were higher than 65536 ([#11474](https://github.com/containers/podman/issues/11474)). +- Fixed a bug where the remote Podman client's `podman build` command would fail to build containers if the context directory was a symlink ([#11732](https://github.com/containers/podman/issues/11732)). +- Fixed a bug where the `--network` flag to `podman play kube` was not properly parsed when a non-bridge network configuration was specified. +- Fixed a bug where the `podman inspect` command could error when the container being inspected was removed as it was being inspected ([#11392](https://github.com/containers/podman/issues/11392)). +- Fixed a bug where the `podman play kube` command ignored the default pod infra image specified in `containers.conf`. +- Fixed a bug where the `--format` option to `podman inspect` was nonfunctional under some circumstances ([#8785](https://github.com/containers/podman/issues/8785)). +- Fixed a bug where the remote Podman client's `podman run` and `podman exec` commands could skip a byte of output every 8192 bytes ([#11496](https://github.com/containers/podman/issues/11496)). +- Fixed a bug where the `podman stats` command would print nonsensical results if the container restarted while it was running ([#11469](https://github.com/containers/podman/issues/11469)). +- Fixed a bug where the remote Podman client would error when STDOUT was redirected on a Windows client ([#11444](https://github.com/containers/podman/issues/11444)). +- Fixed a bug where the `podman run` command could return 0 when the application in the container exited with 125 ([#11540](https://github.com/containers/podman/issues/11540)). +- Fixed a bug where containers with `--restart=always` set using the rootlessport port-forwarding service could not be restarted automatically. +- Fixed a bug where the `--cgroups=split` option to `podman create` and `podman run` was silently discarded if the container was part of a pod. +- Fixed a bug where the `podman container runlabel` command could fail if the image name given included a tag. +- Fixed a bug where Podman could add an extra `127.0.0.1` entry to `/etc/hosts` under some circumstances ([#11596](https://github.com/containers/podman/issues/11596)). +- Fixed a bug where the remote Podman client's `podman untag` command did not properly handle tags including a digest ([#11557](https://github.com/containers/podman/issues/11557)). +- Fixed a bug where the `--format` option to `podman ps` did not properly support the `table` argument for tabular output. +- Fixed a bug where the `--filter` option to `podman ps` did not properly handle filtering by healthcheck status ([#11687](https://github.com/containers/podman/issues/11687)). +- Fixed a bug where the `podman run` and `podman start --attach` commands could race when retrieving the exit code of a container that had already been removed resulting in an error (e.g. by an external `podman rm -f`) ([#11633](https://github.com/containers/podman/issues/11633)). +- Fixed a bug where the `podman generate kube` command would add default environment variables to generated YAML. +- Fixed a bug where the `podman generate kube` command would add the default CMD from the image to generated YAML ([#11672](https://github.com/containers/podman/issues/11672)). +- Fixed a bug where the `podman rm --storage` command could fail to remove containers under some circumstances ([#11207](https://github.com/containers/podman/issues/11207)). +- Fixed a bug where the `podman machine ssh` command could fail when run on Linux ([#11731](https://github.com/containers/podman/issues/11731)). +- Fixed a bug where the `podman stop` command would error when used on a container that was already stopped ([#11740](https://github.com/containers/podman/issues/11740)). +- Fixed a bug where renaming a container in a pod using the `podman rename` command, then removing the pod using `podman pod rm`, could cause Podman to believe the new name of the container was permanently in use, despite the container being removed ([#11750](https://github.com/containers/podman/issues/11750)). + +### API +- The Libpod Pull endpoint for Images now has a new query parameter, `quiet`, which (when set to true) suppresses image pull progress reports ([#10612](https://github.com/containers/podman/issues/10612)). +- The Compat Events endpoint now includes several deprecated fields from the Docker v1.21 API for improved compatibility with older clients. +- The Compat List and Inspect endpoints for Images now prefix image IDs with `sha256:` for improved Docker compatibility ([#11623](https://github.com/containers/podman/issues/11623)). +- The Compat Create endpoint for Containers now properly sets defaults for healthcheck-related fields ([#11225](https://github.com/containers/podman/issues/11225)). +- The Compat Create endpoint for Containers now supports volume options provided by the `Mounts` field ([#10831](https://github.com/containers/podman/issues/10831)). +- The Compat List endpoint for Secrets now supports a new query parameter, `filter`, which allows returned results to be filtered. +- The Compat Auth endpoint now returns the correct response code (500 instead of 400) when logging into a registry fails. +- The Version endpoint now includes information about the OCI runtime and Conmon in use ([#11227](https://github.com/containers/podman/issues/11227)). +- Fixed a bug where the X-Registry-Config header was not properly handled, leading to errors when pulling images ([#11235](https://github.com/containers/podman/issues/11235)). +- Fixed a bug where invalid query parameters could cause a null pointer dereference when creating error messages. +- Logging of API requests and responses at trace level has been greatly improved, including the addition of an X-Reference-Id header to correlate requests and responses ([#10053](https://github.com/containers/podman/issues/10053)). + +### Misc +- Updated Buildah to v1.23.0 +- Updated the containers/storage library to v1.36.0 +- Updated the containers/image library to v5.16.0 +- Updated the containers/common library to v0.44.0 + +## 3.3.1 +### Bugfixes +- Fixed a bug where unit files created by `podman generate systemd` could not cleanup shut down containers when stopped by `systemctl stop` ([#11304](https://github.com/containers/podman/issues/11304)). +- Fixed a bug where `podman machine` commands would not properly locate the `gvproxy` binary in some circumstances. +- Fixed a bug where containers created as part of a pod using the `--pod-id-file` option would not join the pod's network namespace ([#11303](https://github.com/containers/podman/issues/11303)). +- Fixed a bug where Podman, when using the systemd cgroups driver, could sometimes leak dbus sessions. +- Fixed a bug where the `until` filter to `podman logs` and `podman events` was improperly handled, requiring input to be negated ([#11158](https://github.com/containers/podman/issues/11158)). +- Fixed a bug where rootless containers using CNI networking run on systems using `systemd-resolved` for DNS would fail to start if resolved symlinked `/etc/resolv.conf` to an absolute path ([#11358](https://github.com/containers/podman/issues/11358)). + +### API +- A large number of potential file descriptor leaks from improperly closing client connections have been fixed. + +## 3.3.0 +### Features +- Containers inside VMs created by `podman machine` will now automatically handle port forwarding - containers in `podman machine` VMs that publish ports via `--publish` or `--publish-all` will have these ports not just forwarded on the VM, but also on the host system. +- The `podman play kube` command's `--network` option now accepts advanced network options (e.g. `--network slirp4netns:port_handler=slirp4netns`) ([#10807](https://github.com/containers/podman/issues/10807)). +- The `podman play kube` command now supports Kubernetes liveness probes, which will be created as Podman healthchecks. +- Podman now provides a systemd unit, `podman-restart.service`, which, when enabled, will restart all containers that were started with `--restart=always` after the system reboots. +- Rootless Podman can now be configured to use CNI networking by default by using the `rootless_networking` option in `containers.conf`. +- Images can now be pulled using `image:tag@digest` syntax (e.g. `podman pull fedora:34@sha256:1b0d4ddd99b1a8c8a80e885aafe6034c95f266da44ead992aab388e6aa91611a`) ([#6721](https://github.com/containers/podman/issues/6721)). +- The `podman container checkpoint` and `podman container restore` commands can now be used to checkpoint containers that are in pods, and restore those containers into pods. +- The `podman container restore` command now features a new option, `--publish`, to change the ports that are forwarded to a container that is being restored from an exported checkpoint. +- The `podman container checkpoint` command now features a new option, `--compress`, to specify the compression algorithm that will be used on the generated checkpoint. +- The `podman pull` command can now pull multiple images at once (e.g. `podman pull fedora:34 ubi8:latest` will pull both specified images). +- THe `podman cp` command can now copy files from one container into another directly (e.g. `podman cp containera:/etc/hosts containerb:/etc/`) ([#7370](https://github.com/containers/podman/issues/7370)). +- The `podman cp` command now supports a new option, `--archive`, which controls whether copied files will be chown'd to the UID and GID of the user of the destination container. +- The `podman stats` command now provides two additional metrics: Average CPU, and CPU time. +- The `podman pod create` command supports a new flag, `--pid`, to specify the PID namespace of the pod. If specified, containers that join the pod will automatically share its PID namespace. +- The `podman pod create` command supports a new flag, `--infra-name`, which allows the name of the pod's infra container to be set ([#10794](https://github.com/containers/podman/issues/10794)). +- The `podman auto-update` command has had its output reformatted - it is now much clearer what images were pulled and what containers were updated. +- The `podman auto-update` command now supports a new option, `--dry-run`, which reports what would be updated but does not actually perform the update ([#9949](https://github.com/containers/podman/issues/9949)). +- The `podman build` command now supports a new option, `--secret`, to mount secrets into build containers. +- The `podman manifest remove` command now has a new alias, `podman manifest rm`. +- The `podman login` command now supports a new option, `--verbose`, to print detailed information about where the credentials entered were stored. +- The `podman events` command now supports a new event, `exec_died`, which is produced when an exec session exits, and includes the exit code of the exec session. +- The `podman system connection add` command now supports adding connections that connect using the `tcp://` and `unix://` URL schemes. +- The `podman system connection list` command now supports a new flag, `--format`, to determine how the output is printed. +- The `podman volume prune` and `podman volume ls` commands' `--filter` option now support a new filter, `until`, that matches volumes created before a certain time ([#10579](https://github.com/containers/podman/issues/10579)). +- The `podman ps --filter` option's `network` filter now accepts a new value: `container:`, which matches containers that share a network namespace with a specific container ([#10361](https://github.com/containers/podman/issues/10361)). +- The `podman diff` command can now accept two arguments, allowing two images or two containers to be specified; the diff between the two will be printed ([#10649](https://github.com/containers/podman/issues/10649)). +- Podman can now optionally copy-up content from containers into volumes mounted into those containers earlier (at creation time, instead of at runtime) via the `prepare_on_create` option in `containers.conf` ([#10262](https://github.com/containers/podman/issues/10262)). +- A new option, `--gpus`, has been added to `podman create` and `podman run` as a no-op for better compatibility with Docker. If the nvidia-container-runtime package is installed, GPUs should be automatically added to containers without using the flag. +- If an invalid subcommand is provided, similar commands to try will now be suggested in the error message. + +### Changes +- The `podman system reset` command now removes non-Podman (e.g. Buildah and CRI-O) containers as well. +- The new port forwarding offered by `podman machine` requires [gvproxy](https://github.com/containers/gvisor-tap-vsock) in order to function. +- Podman will now automatically create the default CNI network if it does not exist, for both root and rootless users. This will only be done once per user - if the network is subsequently removed, it will not be recreated. +- The `install.cni` makefile option has been removed. It is no longer required to distribute the default `87-podman.conflist` CNI configuration file, as Podman will now automatically create it. +- The `--root` option to Podman will not automatically clear all default storage options when set. Storage options can be set manually using `--storage-opt` ([#10393](https://github.com/containers/podman/issues/10393)). +- The output of `podman system connection list` is now deterministic, with connections being sorted alpabetically by their name. +- The auto-update service (`podman-auto-update.service`) has had its default timer adjusted so it now starts at a random time up to 15 minutes after midnight, to help prevent system congestion from numerous daily services run at once. +- Systemd unit files generated by `podman generate systemd` now depend on `network-online.target` by default ([#10655](https://github.com/containers/podman/issues/10655)). +- Systemd unit files generated by `podman generate systemd` now use `Type=notify` by default, instead of using PID files. +- The `podman info` command's logic for detecting package versions on Gentoo has been improved, and should be significantly faster. + +### Bugfixes +- Fixed a bug where the `podman play kube` command did not perform SELinux relabelling of volumes specified with a `mountPath` that included the `:z` or `:Z` options ([#9371](https://github.com/containers/podman/issues/9371)). +- Fixed a bug where the `podman play kube` command would ignore the `USER` and `EXPOSE` directives in images ([#9609](https://github.com/containers/podman/issues/9609)). +- Fixed a bug where the `podman play kube` command would only accept lowercase pull policies. +- Fixed a bug where named volumes mounted into containers with the `:z` or `:Z` options were not appropriately relabelled for access from the container ([#10273](https://github.com/containers/podman/issues/10273)). +- Fixed a bug where the `podman logs -f` command, with the `journald` log driver, could sometimes fail to pick up the last line of output from a container ([#10323](https://github.com/containers/podman/issues/10323)). +- Fixed a bug where running `podman rm` on a container created with the `--rm` option would occasionally emit an error message saying the container failed to be removed, when it was successfully removed. +- Fixed a bug where starting a Podman container would segfault if the `LISTEN_PID` and `LISTEN_FDS` environment variables were set, but `LISTEN_FDNAMES` was not ([#10435](https://github.com/containers/podman/issues/10435)). +- Fixed a bug where exec sessions in containers were sometimes not cleaned up when run without `-d` and when the associated `podman exec` process was killed before completion. +- Fixed a bug where `podman system service` could, when run in a systemd unit file with sdnotify in use, drop some connections when it was starting up. +- Fixed a bug where containers run using the REST API using the `slirp4netns` network mode would leave zombie processes that were not cleaned up until `podman system service` exited ([#9777](https://github.com/containers/podman/issues/9777)). +- Fixed a bug where the `podman system service` command would leave zombie processes after its initial launch that were not cleaned up until it exited ([#10575](https://github.com/containers/podman/issues/10575)). +- Fixed a bug where VMs created by `podman machine` could not be started after the host system restarted ([#10824](https://github.com/containers/podman/issues/10824)). +- Fixed a bug where the `podman pod ps` command would not show headers for optional information (e.g. container names when the `--ctr-names` option was given). +- Fixed a bug where the remote Podman client's `podman create` and `podman run` commands would ignore timezone configuration from the server's `containers.conf` file ([#11124](https://github.com/containers/podman/issues/11124)). +- Fixed a bug where the remote Podman client's `podman build` command would only respect `.containerignore` and not `.dockerignore` files (when both are present, `.containerignore` will be preferred) ([#10907](https://github.com/containers/podman/issues/10907)). +- Fixed a bug where the remote Podman client's `podman build` command would fail to send the Dockerfile being built to the server when it was excluded by the `.dockerignore` file, resulting in an error ([#9867](https://github.com/containers/podman/issues/9867)). +- Fixed a bug where the remote Podman client's `podman build` command could unexpectedly stop streaming the output of the build ([#10154](https://github.com/containers/podman/issues/10154)). +- Fixed a bug where the remote Podman client's `podman build` command would fail to build when run on Windows ([#11259](https://github.com/containers/podman/issues/11259)). +- Fixed a bug where the `podman manifest create` command accepted at most two arguments (an arbitrary number of images are allowed as arguments, which will be added to the manifest). +- Fixed a bug where named volumes would not be properly chowned to the UID and GID of the directory they were mounted over when first mounted into a container ([#10776](https://github.com/containers/podman/issues/10776)). +- Fixed a bug where named volumes created using a volume plugin would be removed from Podman, even if the plugin reported a failure to remove the volume ([#11214](https://github.com/containers/podman/issues/11214)). +- Fixed a bug where the remote Podman client's `podman exec -i` command would hang when input was provided via shell redirection (e.g. `podman --remote exec -i foo cat <<<"hello"`) ([#7360](https://github.com/containers/podman/issues/7360)). +- Fixed a bug where containers created with `--rm` were not immediately removed after being started by `podman start` if they failed to start ([#10935](https://github.com/containers/podman/issues/10935)). +- Fixed a bug where the `--storage-opt` flag to `podman create` and `podman run` was nonfunctional ([#10264](https://github.com/containers/podman/issues/10264)). +- Fixed a bug where the `--device-cgroup-rule` option to `podman create` and `podman run` was nonfunctional ([#10302](https://github.com/containers/podman/issues/10302)). +- Fixed a bug where the `--tls-verify` option to `podman manifest push` was nonfunctional. +- Fixed a bug where the `podman import` command could, in some circumstances, produce empty images ([#10994](https://github.com/containers/podman/issues/10994)). +- Fixed a bug where images pulled using the `docker-daemon:` transport had the wrong registry (`localhost` instead of `docker.io/library`) ([#10998](https://github.com/containers/podman/issues/10998)). +- Fixed a bug where operations that pruned images (`podman image prune` and `podman system prune`) would prune untagged images with children ([#10832](https://github.com/containers/podman/issues/10832)). +- Fixed a bug where dual-stack networks created by `podman network create` did not properly auto-assign an IPv4 subnet when one was not explicitly specified ([#11032](https://github.com/containers/podman/issues/11032)). +- Fixed a bug where port forwarding using the `rootlessport` port forwarder would break when a network was disconnected and then reconnected ([#10052](https://github.com/containers/podman/issues/10052)). +- Fixed a bug where Podman would ignore user-specified SELinux policies for containers using the Kata OCI runtime, or containers using systemd as PID 1 ([#11100](https://github.com/containers/podman/issues/11100)). +- Fixed a bug where Podman containers created using `--net=host` would add an entry to `/etc/hosts` for the container's hostname pointing to `127.0.1.1` ([#10319](https://github.com/containers/podman/issues/10319)). +- Fixed a bug where the `podman unpause --all` command would throw an error for every container that was not paused ([#11098](https://github.com/containers/podman/issues/11098)). +- Fixed a bug where timestamps for the `since` and `until` filters using Unix timestamps with a nanoseconds portion could not be parsed ([#11131](https://github.com/containers/podman/issues/11131)). +- Fixed a bug where the `podman info` command would sometimes print the wrong path for the `slirp4netns` binary. +- Fixed a bug where rootless Podman containers joined to a CNI network would not have functional DNS when the host used systemd-resolved without the resolved stub resolver being enabled ([#11222](https://github.com/containers/podman/issues/11222)). +- Fixed a bug where `podman network connect` and `podman network disconnect` of rootless containers could sometimes break port forwarding to the container ([#11248](https://github.com/containers/podman/issues/11248)). +- Fixed a bug where joining a container to a CNI network by ID and adding network aliases to this network would cause the container to fail to start ([#11285](https://github.com/containers/podman/issues/11285)). + +### API +- Fixed a bug where the Compat List endpoint for Containers included healthcheck information for all containers, even those that did not have a configured healthcheck. +- Fixed a bug where the Compat Create endpoint for Containers would fail to create containers with the `NetworkMode` parameter set to `default` ([#10569](https://github.com/containers/podman/issues/10569)). +- Fixed a bug where the Compat Create endpoint for Containers did not properly handle healthcheck commands ([#10617](https://github.com/containers/podman/issues/10617)). +- Fixed a bug where the Compat Wait endpoint for Containers would always send an empty string error message when no error occurred. +- Fixed a bug where the Libpod Stats endpoint for Containers would not error when run on rootless containers on cgroups v1 systems (nonsensical results would be returned, as this configuration cannot be supportable). +- Fixed a bug where the Compat List endpoint for Images omitted the `ContainerConfig` field ([#10795](https://github.com/containers/podman/issues/10795)). +- Fixed a bug where the Compat Build endpoint for Images was too strict when validating the `Content-Type` header, rejecting content that Docker would have accepted ([#11022](https://github.com/containers/podman/issues/11012)). +- Fixed a bug where the Compat Pull endpoint for Images could fail, but return a 200 status code, if an image name that could not be parsed was provided. +- Fixed a bug where the Compat Pull endpoint for Images would continue to pull images after the client disconnected. +- Fixed a bug where the Compat List endpoint for Networks would fail for non-bridge (e.g. macvlan) networks ([#10266](https://github.com/containers/podman/issues/10266)). +- Fixed a bug where the Libpod List endpoint for Networks would return nil, instead of an empty list, when no networks were present ([#10495](https://github.com/containers/podman/issues/10495)). +- The Compat and Libpod Logs endpoints for Containers now support the `until` query parameter ([#10859](https://github.com/containers/podman/issues/10859)). +- The Compat Import endpoint for Images now supports the `platform`, `message`, and `repo` query parameters. +- The Compat Pull endpoint for Images now supports the `platform` query parameter. + +### Misc +- Updated Buildah to v1.22.3 +- Updated the containers/storage library to v1.34.1 +- Updated the containers/image library to v5.15.2 +- Updated the containers/common library to v0.42.1 + +## 3.2.3 +### Security +- This release addresses CVE-2021-3602, an issue with the `podman build` command with the `--isolation chroot` flag that results in environment variables from the host leaking into build containers. + +### Bugfixes +- Fixed a bug where events related to images could occur before the relevant operation had completed (e.g. an image pull event could be written before the pull was finished) ([#10812](https://github.com/containers/podman/issues/10812)). +- Fixed a bug where `podman save` would refuse to save images with an architecture different from that of the host ([#10835](https://github.com/containers/podman/issues/10835)). +- Fixed a bug where the `podman import` command did not correctly handle images without tags ([#10854](https://github.com/containers/podman/issues/10854)). +- Fixed a bug where Podman's journald events backend would fail and prevent Podman from running when run on a host with systemd as PID1 but in an environment (e.g. a container) without systemd ([#10863](https://github.com/containers/podman/issues/10863)). +- Fixed a bug where containers using rootless CNI networking would fail to start when the `dnsname` CNI plugin was in use and the host system's `/etc/resolv.conf` was a symlink ([#10855](https://github.com/containers/podman/issues/10855) and [#10929](https://github.com/containers/podman/issues/10929)). +- Fixed a bug where containers using rootless CNI networking could fail to start due to a race in rootless CNI initialization ([#10930](https://github.com/containers/podman/issues/10930)). + +### Misc +- Updated Buildah to v1.21.3 +- Updated the containers/common library to v0.38.16 + +## 3.2.2 +### Changes +- Podman's handling of the Architecture field of images has been relaxed. Since 3.2.0, Podman required that the architecture of the image match the architecture of the system to run containers based on an image, but images often incorrectly report architecture, causing Podman to reject valid images ([#10648](https://github.com/containers/podman/issues/10648) and [#10682](https://github.com/containers/podman/issues/10682)). +- Podman no longer uses inotify to monitor for changes to CNI configurations. This removes potential issues where Podman cannot be run because a user has exhausted their available inotify sessions ([#10686](https://github.com/containers/podman/issues/10686)). + +### Bugfixes +- Fixed a bug where the `podman cp` would, when given a directory as its source and a target that existed and was a file, copy the contents of the directory into the parent directory of the file; this now results in an error. +- Fixed a bug where the `podman logs` command would, when following a running container's logs, not include the last line of output from the container when it exited when the `k8s-file` driver was in use ([#10675](https://github.com/containers/podman/issues/10675)). +- Fixed a bug where Podman would fail to run containers if `systemd-resolved` was incorrectly detected as the system's DNS server ([#10733](https://github.com/containers/podman/issues/10733)). +- Fixed a bug where the `podman exec -t` command would only resize the exec session's TTY after the session started, leading to a race condition where the terminal would initially not have a size set ([#10560](https://github.com/containers/podman/issues/10560)). +- Fixed a bug where Podman containers using the `slirp4netns` network mode would add an incorrect entry to `/etc/hosts` pointing the container's hostname to the wrong IP address. +- Fixed a bug where Podman would create volumes specified by images with incorrect permissions ([#10188](https://github.com/containers/podman/issues/10188) and [#10606](https://github.com/containers/podman/issues/10606)). +- Fixed a bug where Podman would not respect the `uid` and `gid` options to `podman volume create -o` ([#10620](https://github.com/containers/podman/issues/10620)). +- Fixed a bug where the `podman run` command could panic when parsing the system's cgroup configuration ([#10666](https://github.com/containers/podman/issues/10666)). +- Fixed a bug where the remote Podman client's `podman build -f - ...` command did not read a Containerfile from STDIN ([#10621](https://github.com/containers/podman/issues/10621)). +- Fixed a bug where the `podman container restore --import` command would fail to restore checkpoints created from privileged containers ([#10615](https://github.com/containers/podman/issues/10615)). +- Fixed a bug where Podman was not respecting the `TMPDIR` environment variable when pulling images ([#10698](https://github.com/containers/podman/issues/10698)). +- Fixed a bug where a number of Podman commands did not properly support using Go templates as an argument to the `--format` option. + +### API +- Fixed a bug where the Compat Inspect endpoint for Containers did not include information on container healthchecks ([#10457](https://github.com/containers/podman/issues/10457)). +- Fixed a bug where the Libpod and Compat Build endpoints for Images did not properly handle the `devices` query parameter ([#10614](https://github.com/containers/podman/issues/10614)). + +### Misc +- Fixed a bug where the Makefile's `make podman-remote-static` target to build a statically-linked `podman-remote` binary was instead producing dynamic binaries ([#10656](https://github.com/containers/podman/issues/10656)). +- Updated the containers/common library to v0.38.11 + ## 3.2.1 ### Changes - Podman now allows corrupt images (e.g. from restarting the system during an image pull) to be replaced by a `podman pull` of the same image (instead of requiring they be removed first, then re-pulled). @@ -110,7 +456,7 @@ - Fixed a bug where images with empty layers were stored incorrectly, causing them to be unable to be pushed or saved. - Fixed a bug where the `podman rmi` command could fail to remove corrupt images from storage. - Fixed a bug where the remote Podman client's `podman save` command did not support the `oci-dir` and `docker-dir` formats ([#9742](https://github.com/containers/podman/issues/9742)). -- Fixed a bug where volume mounts from `podman play kube` created with a trailing `/` in the container path were were not properly superceding named volumes from the image ([#9618](https://github.com/containers/podman/issues/9618)). +- Fixed a bug where volume mounts from `podman play kube` created with a trailing `/` in the container path were were not properly superseding named volumes from the image ([#9618](https://github.com/containers/podman/issues/9618)). - Fixed a bug where Podman could fail to build on 32-bit architectures. ### Misc @@ -755,7 +1101,7 @@ ## 2.0.5 ### Features - Rootless Podman will now add an entry to `/etc/passwd` for the user who ran Podman if run with `--userns=keep-id`. -- The `podman system connection` command has been reworked to support multiple connections, and reenabled for use! +- The `podman system connection` command has been reworked to support multiple connections, and re-enabled for use! - Podman now has a new global flag, `--connection`, to specify a connection to a remote Podman API instance. ### Changes diff -Nru libpod-3.2.1+ds1/RELEASE_PROCESS.md libpod-3.4.4+ds1/RELEASE_PROCESS.md --- libpod-3.2.1+ds1/RELEASE_PROCESS.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/RELEASE_PROCESS.md 2021-12-26 00:45:51.000000000 +0000 @@ -164,10 +164,6 @@ `git checkout -b bump_vX.Y.Z`. 1. Lookup the *COMMIT ID* of the last release, `git log -1 $(git tag | sort -V | tail -1)`. - 1. Run `make changelog CHANGELOG_BASE=`*COMMIT ID*. This will modify the - `changelog.txt` file. Manually edit it to change the first line - (“Changelog for …”) to include the current (new) release version number. - For example, `- Changelog for v2.1.0 (2020-09-22):`. 1. Edit `version/version.go` and bump the `Version` value to the new release version. If there were API changes, also bump `APIVersion` value. 1. Commit this and sign the commit (`git commit -a -s -S`). The commit message diff -Nru libpod-3.2.1+ds1/rootless.md libpod-3.4.4+ds1/rootless.md --- libpod-3.2.1+ds1/rootless.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/rootless.md 2021-12-26 00:45:51.000000000 +0000 @@ -29,7 +29,7 @@ * Ubuntu supports non root overlay, but no other Linux distros do. * Only other supported driver is VFS. * Cannot use ping out of the box. - * [(Can be fixed by setting sysctl on host)](https://github.com/containers/podman/blob/master/troubleshooting.md#6-rootless-containers-cannot-ping-hosts) + * [(Can be fixed by setting sysctl on host)](https://github.com/containers/podman/blob/master/troubleshooting.md#5-rootless-containers-cannot-ping-hosts) * Requires new shadow-utils (not found in older (RHEL7/Centos7 distros) Should be fixed in RHEL7.7 release) * A few commands do not work. * mount/unmount (on fuse-overlay) diff -Nru libpod-3.2.1+ds1/SECURITY.md libpod-3.4.4+ds1/SECURITY.md --- libpod-3.2.1+ds1/SECURITY.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/SECURITY.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,3 +1,3 @@ ## Security and Disclosure Information Policy for the Libpod Project -The Libpod Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/master/SECURITY.md) for the Containers Projects. +The Libpod Project follows the [Security and Disclosure Information Policy](https://github.com/containers/common/blob/main/SECURITY.md) for the Containers Projects. diff -Nru libpod-3.2.1+ds1/test/apiv2/10-images.at libpod-3.4.4+ds1/test/apiv2/10-images.at --- libpod-3.2.1+ds1/test/apiv2/10-images.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/10-images.at 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,13 @@ .[0].Id~[0-9a-f]\\{64\\} iid=$(jq -r '.[0].Id' <<<"$output") +# Create an empty manifest and make sure it is not listed +# in the compat endpoint. +t GET images/json 200 length=1 +podman manifest create foo +t GET images/json 200 length=1 +t GET libpod/images/json 200 length=2 + t GET libpod/images/$iid/exists 204 t GET libpod/images/$PODMAN_TEST_IMAGE_NAME/exists 204 t GET libpod/images/${iid}abcdef/exists 404 \ @@ -45,6 +52,11 @@ t POST "images/create?fromImage=alpine&tag=latest" 200 +# 10977 - handle platform parameter correctly +t POST "images/create?fromImage=alpine&platform=linux/arm64" 200 +t GET "images/alpine/json" 200 \ + .Architecture=arm64 + # Make sure that new images are pulled old_iid=$(podman image inspect --format "{{.ID}}" docker.io/library/alpine:latest) podman rmi -f docker.io/library/alpine:latest @@ -89,6 +101,10 @@ t GET libpod/images/json?filters='{"label":["testl' 500 \ .cause="unexpected end of JSON input" +# Prune images - bad all input +t POST libpod/images/prune?all='garb1age' 500 \ + .cause="schema: error converting value for \"all\"" + # Prune images - bad filter input t POST images/prune?filters='garb1age}' 500 \ .cause="invalid character 'g' looking for beginning of value" @@ -168,7 +184,7 @@ BUILD_TEST_ERROR="" if ! grep -q '200 OK' "${TMPD}/headers.txt"; then - echo -e "${red}NOK: Image build from tar failed response was not 200 OK" + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/x-tar)" BUILD_TEST_ERROR="1" fi @@ -177,6 +193,38 @@ BUILD_TEST_ERROR="1" fi +curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/tar" \ + --dump-header "${TMPD}/headers.txt" \ + -o /dev/null \ + "http://$HOST:$PORT/v1.40/libpod/build?dockerfile=containerfile" &> /dev/null +if ! grep -q '200 OK' "${TMPD}/headers.txt"; then + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/tar)" + BUILD_TEST_ERROR="1" +fi + +# Yes, this is very un-RESTful re: Content-Type header ignored when compatibility endpoint used +# See https://github.com/containers/podman/issues/11012 +curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/json" \ + --dump-header "${TMPD}/headers.txt" \ + -o /dev/null \ + "http://$HOST:$PORT/v1.40/build?dockerfile=containerfile" &> /dev/null +if ! grep -q '200 OK' "${TMPD}/headers.txt"; then + echo -e "${red}NOK: Image build from tar failed response was not 200 OK (application/tar)" + BUILD_TEST_ERROR="1" +fi + +curl -XPOST --data-binary @<(cat $CONTAINERFILE_TAR) \ + -H "content-type: application/json" \ + --dump-header "${TMPD}/headers.txt" \ + -o /dev/null \ + "http://$HOST:$PORT/v1.40/libpod/build?dockerfile=containerfile" &> /dev/null +if ! grep -q '400 Bad Request' "${TMPD}/headers.txt"; then + echo -e "${red}NOK: Image build should have failed with 400 (wrong Content-Type)" + BUILD_TEST_ERROR="1" +fi + cleanBuildTest if [[ "${BUILD_TEST_ERROR}" ]]; then exit 1 diff -Nru libpod-3.2.1+ds1/test/apiv2/20-containers.at libpod-3.4.4+ds1/test/apiv2/20-containers.at --- libpod-3.2.1+ds1/test/apiv2/20-containers.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/20-containers.at 2021-12-26 00:45:51.000000000 +0000 @@ -22,6 +22,10 @@ t GET libpod/containers/json 200 length=0 +# bad all input +t GET libpod/containers/json?all='garb1age' 500 \ + .cause="schema: error converting value for \"all\"" + t GET libpod/containers/json?all=true 200 \ length=1 \ .[0].Id~[0-9a-f]\\{64\\} \ @@ -65,6 +69,16 @@ cid=$(jq -r '.[0].Id' <<<"$output") +if root; then + t GET libpod/containers/stats?containers='[$cid]' 200 +else + if have_cgroupsv2; then + t GET libpod/containers/stats?containers='[$cid]' 200 + else + t GET libpod/containers/stats?containers='[$cid]' 409 + fi +fi + t DELETE libpod/containers/$cid 204 # Issue #6799: it should be possible to start a container, even w/o args. @@ -211,15 +225,11 @@ t POST containers/create Image=$IMAGE Entrypoint='["top"]' 201 \ .Id~[0-9a-f]\\{64\\} cid_top=$(jq -r '.Id' <<<"$output") -network_expect="{}" -if root; then - network_expect='.podman.NetworkID=podman' -fi t GET containers/${cid_top}/json 200 \ .Config.Entrypoint[0]="top" \ .Config.Cmd='[]' \ - .Path="top" - .NetworkSettings.Networks="$network_expect" + .Path="top" \ + .NetworkSettings.Networks.podman.NetworkID=podman t POST containers/${cid_top}/start 204 # make sure the container is running t GET containers/${cid_top}/json 200 \ @@ -341,3 +351,49 @@ .HostConfig.NanoCpus=500000 t DELETE containers/$cid?v=true 204 + +# Test Compat Create with default network mode (#10569) +t POST containers/create Image=$IMAGE HostConfig='{"NetworkMode":"default"}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .HostConfig.NetworkMode="bridge" + +t DELETE containers/$cid?v=true 204 + +# Test Compat Create with healthcheck, check default values +t POST containers/create Image=$IMAGE Cmd='["top"]' Healthcheck='{"Test":["true"]}' 201 \ + .Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.Id' <<<"$output") +t GET containers/$cid/json 200 \ + .Config.Healthcheck.Interval=30000000000 \ + .Config.Healthcheck.Timeout=30000000000 \ + .Config.Healthcheck.Retries=3 + +# compat api: Test for mount options support +payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch","TmpfsOptions":{"SizeBytes":1024,"Mode":755}}]}' +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 \ + .HostConfig.Tmpfs['"/mnt/scratch"']~.*size=1024.* \ + .HostConfig.Tmpfs['"/mnt/scratch"']~.*mode=755.* + +t DELETE containers/$cid?v=true 204 + +# compat api: tmpfs without mount options +payload='{"Mounts":[{"Type":"tmpfs","Target":"/mnt/scratch"}]}' +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 \ + .HostConfig.Tmpfs['"/mnt/scratch"']~.*tmpcopyup.* \ + +t DELETE containers/$cid?v=true 204 + +# compat api: bind mount without mount options +payload='{"Mounts":[{"Type":"bind","Source":"/tmp","Target":"/mnt"}]}' +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 \ + .HostConfig.Binds[0]~/tmp:/mnt:.* \ + +t DELETE containers/$cid?v=true 204 diff -Nru libpod-3.2.1+ds1/test/apiv2/23-containersArchive.at libpod-3.4.4+ds1/test/apiv2/23-containersArchive.at --- libpod-3.2.1+ds1/test/apiv2/23-containersArchive.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/23-containersArchive.at 2021-12-26 00:45:51.000000000 +0000 @@ -16,7 +16,7 @@ TMPD=$(mktemp -d podman-apiv2-test.archive.XXXXXXXX) HELLO_TAR="${TMPD}/hello.tar" echo "Hello" > $TMPD/hello.txt -tar --format=posix -C $TMPD -cvf ${HELLO_TAR} hello.txt &> /dev/null +tar --owner=1042 --group=1043 --format=posix -C $TMPD -cvf ${HELLO_TAR} hello.txt &> /dev/null podman run -d --name "${CTR}" "${IMAGE}" top @@ -72,6 +72,20 @@ ARCHIVE_TEST_ERROR="1" fi +# test if uid/gid was set correctly in the server +uidngid=$($PODMAN_BIN --root $WORKDIR/server_root exec "${CTR}" stat -c "%u:%g" "/tmp/hello.txt") +if [[ "${uidngid}" != "1042:1043" ]]; then + echo -e "${red}NOK: UID/GID of the file doesn't match.${nc}" 1>&2; + ARCHIVE_TEST_ERROR="1" +fi + +# TODO: uid/gid should be also preserved on way back (GET request) +# right now it ends up as root:root instead of 1042:1043 +#if [[ "$(tar -tvf "${TMPD}/body.tar")" != *"1042/1043"* ]]; then +# echo -e "${red}NOK: UID/GID of the file doesn't match.${nc}" 1>&2; +# ARCHIVE_TEST_ERROR="1" +#fi + cleanUpArchiveTest if [[ "${ARCHIVE_TEST_ERROR}" ]] ; then exit 1; diff -Nru libpod-3.2.1+ds1/test/apiv2/26-containersWait.at libpod-3.4.4+ds1/test/apiv2/26-containersWait.at --- libpod-3.2.1+ds1/test/apiv2/26-containersWait.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/26-containersWait.at 2021-12-26 00:45:51.000000000 +0000 @@ -21,7 +21,9 @@ t POST "containers/${CTR}/wait?condition=not-running" 200 -t POST "containers/${CTR}/wait?condition=next-exit" 200 & +t POST "containers/${CTR}/wait?condition=next-exit" 200 \ + .StatusCode=0 \ + .Error=null & child_pid=$! podman start "${CTR}" wait "${child_pid}" diff -Nru libpod-3.2.1+ds1/test/apiv2/30-volumes.at libpod-3.4.4+ds1/test/apiv2/30-volumes.at --- libpod-3.2.1+ds1/test/apiv2/30-volumes.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/30-volumes.at 2021-12-26 00:45:51.000000000 +0000 @@ -13,13 +13,14 @@ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ .Options={} +# TODO(mwhahaha): there might be a bug here since options is null and not {} t POST volumes/create 201 \ - .Name~[0-9a-f]\\{64\\} + .Name~[0-9a-f]\\{64\\} \ .Driver=local \ - .Mountpoint=$volumepath/~[0-9a-f]\\{64\\}/_data \ + .Mountpoint~$volumepath/[0-9a-f]\\{64\\}/_data \ .CreatedAt~[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}.* \ .Labels={} \ - .Options={} + .Options=null t POST libpod/volumes/create 201 t POST libpod/volumes/create \ Name=foo2 \ @@ -125,11 +126,6 @@ t POST libpod/volumes/prune?filters='{"label":["testlabel"]}' 200 t GET libpod/volumes/json?filters='{"label":["testlabel"]}' 200 length=0 -## Prune volumes -t POST libpod/volumes/prune 200 -#After prune volumes, there should be no volume existing -t GET libpod/volumes/json 200 length=0 - # libpod api: do not use list filters for prune t POST libpod/volumes/prune?filters='{"name":["anyname"]}' 500 \ .cause="\"name\" is an invalid volume filter" @@ -146,4 +142,48 @@ t POST volumes/prune?filters='{"scope":["anyscope"]}' 500 \ .cause="\"scope\" is an invalid volume filter" +## Prune volumes using until filter +t POST libpod/volumes/create \ + Name=foo5 \ + Label='{"testuntil":""}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo5 \ + .Labels.testuntil="" \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec + +# with date way back in the past, volume should not be deleted +t POST libpod/volumes/prune?filters='{"until":["500000"]}' 200 +t GET libpod/volumes/json?filters='{"label":["testuntil"]}' 200 length=1 + +# with date far in the future, volume should be deleted +t POST libpod/volumes/prune?filters='{"until":["5000000000"]}' 200 +t GET libpod/volumes/json?filters='{"label":["testuntil"]}' 200 length=0 + +t POST libpod/volumes/create \ + Name=foo6 \ + Label='{"testuntilcompat":""}' \ + Options='{"type":"tmpfs","o":"nodev,noexec"}}' \ + 201 \ + .Name=foo6 \ + .Labels.testuntilcompat="" \ + .Options.type=tmpfs \ + .Options.o=nodev,noexec + +# with date way back in the past, volume should not be deleted (compat api) +t POST volumes/prune?filters='{"until":["500000"]}' 200 +t GET libpod/volumes/json?filters='{"label":["testuntilcompat"]}' 200 length=1 +t GET libpod/volumes/json?filters='{"until":["500000"]}' 200 length=0 +t GET libpod/volumes/json?filters='{"until":["5000000000"]}' 200 length=1 + +# with date far in the future, volume should be deleted (compat api) +t POST volumes/prune?filters='{"until":["5000000000"]}' 200 +t GET libpod/volumes/json?filters='{"label":["testuntilcompat"]}' 200 length=0 + +## Prune volumes +t POST libpod/volumes/prune 200 +#After prune volumes, there should be no volume existing +t GET libpod/volumes/json 200 length=0 + # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/apiv2/35-networks.at libpod-3.4.4+ds1/test/apiv2/35-networks.at --- libpod-3.2.1+ds1/test/apiv2/35-networks.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/35-networks.at 2021-12-26 00:45:51.000000000 +0000 @@ -142,4 +142,18 @@ t POST networks/prune?filters='{"until":["5000000000"]}' 200 t GET networks?filters='{"label":["zaq"]}' 200 length=0 +# test macvlan network response +podman network create --driver macvlan macvlan1 + +# libpod api inspect the macvlan network +t GET libpod/networks/macvlan1/json 200 .name="macvlan1" + +# compat api inspect the macvlan network +t GET networks/macvlan1 200 .Name="macvlan1" + +# clean the macvlan network +t DELETE libpod/networks/macvlan1 200 \ + .[0].Name~macvlan1 \ + .[0].Err=null + # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/apiv2/40-pods.at libpod-3.4.4+ds1/test/apiv2/40-pods.at --- libpod-3.2.1+ds1/test/apiv2/40-pods.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/40-pods.at 2021-12-26 00:45:51.000000000 +0000 @@ -19,6 +19,9 @@ .[0].Id=$pod_id \ .[0].Containers\|length=1 +t GET libpod/pods/json?filters='{"until":["500000"]}' 200 length=0 +t GET libpod/pods/json?filters='{"until":["5000000000"]}' 200 length=1 + # Cannot create a dup pod with the same name t POST "libpod/pods/create (dup pod)" name=foo 409 \ .cause="pod already exists" diff -Nru libpod-3.2.1+ds1/test/apiv2/50-secrets.at libpod-3.4.4+ds1/test/apiv2/50-secrets.at --- libpod-3.2.1+ds1/test/apiv2/50-secrets.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/50-secrets.at 2021-12-26 00:45:51.000000000 +0000 @@ -27,8 +27,37 @@ .[0].Spec.Name=mysecret \ .[0].Version.Index=1 -# secret list unsupported filters -t GET secrets?filters='{"name":["foo1"]}' 400 +# secret list with filters +t GET secrets?filters='{"name":["mysecret"]}' 200 \ + length=1 \ + .[0].Spec.Name=mysecret \ + .[0].Version.Index=1 + +t GET secrets?filters='{"name":["mysecret2"]}' 200 \ + length=0 \ + +# secret libpod list with filters +t GET libpod/secrets/json?filters='{"name":["mysecret"]}' 200 \ + length=1 \ + .[0].Spec.Name=mysecret \ + +t GET libpod/secrets/json?filters='{"name":["mysecret2"]}' 200 \ + length=0 \ + +# secret list with unsupported filters +t GET secrets?filters='{"label":["xyz"]}' 500 + +#compat api list secrets sanity checks +t GET secrets?filters='garb1age}' 500 \ + .cause="invalid character 'g' looking for beginning of value" +t GET secrets?filters='{"label":["testl' 500 \ + .cause="unexpected end of JSON input" + +#libpod api list secrets sanity checks +t GET libpod/secrets/json?filters='garb1age}' 500 \ + .cause="invalid character 'g' looking for beginning of value" +t GET libpod/secrets/json?filters='{"label":["testl' 500 \ + .cause="unexpected end of JSON input" # secret rm t DELETE secrets/mysecret 204 diff -Nru libpod-3.2.1+ds1/test/apiv2/60-auth.at libpod-3.4.4+ds1/test/apiv2/60-auth.at --- libpod-3.2.1+ds1/test/apiv2/60-auth.at 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/60-auth.at 2021-12-26 00:45:51.000000000 +0000 @@ -5,10 +5,15 @@ start_registry +# Test unreachable +t POST /v1.40/auth username=$REGISTRY_USERNAME password=WrOnGPassWord serveraddress=does.not.exist.io:1234/ \ + 500 \ + .message~'.*no such host.*' + # Test with wrong password. Confirm bad status and appropriate error message t POST /v1.40/auth username=$REGISTRY_USERNAME password=WrOnGPassWord serveraddress=localhost:$REGISTRY_PORT/ \ - 400 \ - .Status~'.* invalid username/password' + 500 \ + .message~'.* 401 Unauthorized' # Test with the right password. Confirm status message t POST /v1.40/auth username=$REGISTRY_USERNAME password=$REGISTRY_PASSWORD serveraddress=localhost:$REGISTRY_PORT/ \ diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/fixtures/api_testcase.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/fixtures/api_testcase.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/fixtures/api_testcase.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/fixtures/api_testcase.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,103 @@ +import json +import subprocess +import unittest + +import requests +import sys +import time + +from .podman import Podman + + +class APITestCase(unittest.TestCase): + PODMAN_URL = "http://localhost:8080" + podman = None # initialized podman configuration for tests + service = None # podman service instance + + @classmethod + def setUpClass(cls): + super().setUpClass() + + APITestCase.podman = Podman() + APITestCase.service = APITestCase.podman.open( + "system", "service", "tcp:localhost:8080", "--time=0" + ) + # give the service some time to be ready... + time.sleep(2) + + returncode = APITestCase.service.poll() + if returncode is not None: + raise subprocess.CalledProcessError(returncode, "podman system service") + + r = requests.post( + APITestCase.uri("/images/pull?reference=quay.io%2Flibpod%2Falpine%3Alatest") + ) + if r.status_code != 200: + raise subprocess.CalledProcessError( + r.status_code, f"podman images pull quay.io/libpod/alpine:latest {r.text}" + ) + + @classmethod + def tearDownClass(cls): + APITestCase.service.terminate() + stdout, stderr = APITestCase.service.communicate(timeout=0.5) + if stdout: + sys.stdout.write("\nService Stdout:\n" + stdout.decode("utf-8")) + if stderr: + sys.stderr.write("\nService Stderr:\n" + stderr.decode("utf-8")) + return super().tearDownClass() + + def setUp(self): + super().setUp() + APITestCase.podman.run("run", "-d", "alpine", "top", check=True) + + def tearDown(self) -> None: + APITestCase.podman.run("pod", "rm", "--all", "--force", check=True) + APITestCase.podman.run("rm", "--all", "--force", check=True) + super().tearDown() + + @property + def podman_url(self): + return "http://localhost:8080" + + @staticmethod + def uri(path): + return APITestCase.PODMAN_URL + "/v2.0.0/libpod" + path + + def resolve_container(self, path): + """Find 'first' container and return 'Id' formatted into given URI path.""" + + try: + r = requests.get(self.uri("/containers/json?all=true")) + containers = r.json() + except Exception as e: + msg = f"Bad container response: {e}" + if r is not None: + msg += ": " + r.text + raise self.failureException(msg) + return path.format(containers[0]["Id"]) + + def assertContainerExists(self, member, msg=None): # pylint: disable=invalid-name + r = requests.get(self.uri(f"/containers/{member}/exists")) + if r.status_code == 404: + if msg is None: + msg = f"Container '{member}' does not exist." + self.failureException(msg) + + def assertContainerNotExists(self, member, msg=None): # pylint: disable=invalid-name + r = requests.get(self.uri(f"/containers/{member}/exists")) + if r.status_code == 204: + if msg is None: + msg = f"Container '{member}' exists." + self.failureException(msg) + + def assertId(self, content): # pylint: disable=invalid-name + objects = json.loads(content) + try: + if isinstance(objects, dict): + _ = objects["Id"] + else: + for item in objects: + _ = item["Id"] + except KeyError: + self.failureException("Failed in find 'Id' in return value.") diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/fixtures/__init__.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/fixtures/__init__.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/fixtures/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/fixtures/__init__.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,3 @@ +from .api_testcase import APITestCase + +__all__ = ["APITestCase"] diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/fixtures/podman.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/fixtures/podman.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/fixtures/podman.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/fixtures/podman.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,136 @@ +import configparser +import json +import os +import shutil +import subprocess +import sys +import tempfile + + +class Podman: + """ + Instances hold the configuration and setup for running podman commands + """ + + def __init__(self): + """Initialize a Podman instance with global options""" + binary = os.getenv("PODMAN", "bin/podman") + self.cmd = [binary, "--storage-driver=vfs"] + + cgroupfs = os.getenv("CGROUP_MANAGER", "systemd") + self.cmd.append(f"--cgroup-manager={cgroupfs}") + + if os.getenv("DEBUG"): + self.cmd.append("--log-level=debug") + self.cmd.append("--syslog=true") + + self.anchor_directory = tempfile.mkdtemp(prefix="podman_restapi_") + self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio")) + self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run")) + + os.environ["CONTAINERS_REGISTRIES_CONF"] = os.path.join( + self.anchor_directory, "registry.conf" + ) + p = configparser.ConfigParser() + p.read_dict( + { + "registries.search": {"registries": "['quay.io']"}, + "registries.insecure": {"registries": "[]"}, + "registries.block": {"registries": "[]"}, + } + ) + with open(os.environ["CONTAINERS_REGISTRIES_CONF"], "w") as w: + p.write(w) + + 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("--cni-config-dir=" + os.environ["CNI_CONFIG_PATH"]) + 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( + """ + { + "cniVersion": "0.3.0", + "name": "podman", + "plugins": [{ + "type": "bridge", + "bridge": "cni0", + "isGateway": true, + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "10.88.0.0/16", + "routes": [{ + "dst": "0.0.0.0/0" + }] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] + } + """ + ) + with open(cni_cfg, "w") as w: + json.dump(buf, w) + + def open(self, command, *args, **kwargs): + """Podman initialized instance to run a given command + + :param self: Podman instance + :param command: podman sub-command to run + :param args: arguments and options for command + :param kwargs: See subprocess.Popen() for shell keyword + :return: subprocess.Popen() instance configured to run podman instance + """ + cmd = self.cmd.copy() + cmd.append(command) + cmd.extend(args) + + shell = kwargs.get("shell", False) + + return subprocess.Popen( + cmd, + shell=shell, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + def run(self, command, *args, **kwargs): + """Run given podman command + + :param self: Podman instance + :param command: podman sub-command to run + :param args: arguments and options for command + :param kwargs: See subprocess.Popen() for shell and check keywords + :return: subprocess.Popen() instance configured to run podman instance + """ + cmd = self.cmd.copy() + cmd.append(command) + cmd.extend(args) + + check = kwargs.get("check", False) + shell = kwargs.get("shell", False) + + try: + return subprocess.run( + cmd, + shell=shell, + check=check, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + except subprocess.CalledProcessError as e: + if e.stdout: + sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8")) + if e.stderr: + sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8")) + raise + + def tear_down(self): + shutil.rmtree(self.anchor_directory, ignore_errors=True) diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_container.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_container.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_container.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_container.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,262 @@ +import random +import unittest +import json + +import requests +from dateutil.parser import parse + +from .fixtures import APITestCase + + +class ContainerTestCase(APITestCase): + def test_list(self): + r = requests.get(self.uri("/containers/json"), timeout=5) + self.assertEqual(r.status_code, 200, r.text) + obj = r.json() + self.assertEqual(len(obj), 1) + + def test_list_filters(self): + r = requests.get(self.podman_url + "/v1.40/containers/json?filters%3D%7B%22status%22%3A%5B%22running%22%5D%7D") + self.assertEqual(r.status_code, 200, r.text) + payload = r.json() + containerAmnt = len(payload) + self.assertGreater(containerAmnt, 0) + + def test_list_all(self): + r = requests.get(self.uri("/containers/json?all=true")) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + + def test_inspect(self): + r = requests.get(self.uri(self.resolve_container("/containers/{}/json"))) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + _ = parse(r.json()["Created"]) + + + r = requests.post( + self.podman_url + "/v1.40/containers/create?name=topcontainer", + json={"Cmd": ["top"], + "Image": "alpine:latest", + "Healthcheck": { + "Test": ["CMD", "pidof", "top"], + "Interval": 5000000000, + "Timeout": 2000000000, + "Retries": 3, + "StartPeriod": 5000000000 + } + }, + ) + self.assertEqual(r.status_code, 201, r.text) + payload = r.json() + container_id = payload["Id"] + self.assertIsNotNone(container_id) + + r = requests.get(self.podman_url + f"/v1.40/containers/{container_id}/json") + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + out = r.json() + self.assertIsNone(out["State"].get("Health")) + self.assertListEqual(["CMD", "pidof", "top"], out["Config"]["Healthcheck"]["Test"]) + self.assertEqual(5000000000, out["Config"]["Healthcheck"]["Interval"]) + self.assertEqual(2000000000, out["Config"]["Healthcheck"]["Timeout"]) + self.assertEqual(3, out["Config"]["Healthcheck"]["Retries"]) + self.assertEqual(5000000000, out["Config"]["Healthcheck"]["StartPeriod"]) + + r = requests.get(self.uri(f"/containers/{container_id}/json")) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + out = r.json() + hc = out["Config"]["Healthcheck"]["Test"] + self.assertListEqual(["CMD", "pidof", "top"], hc) + + r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get(self.podman_url + f"/v1.40/containers/{container_id}/json") + self.assertEqual(r.status_code, 200, r.text) + out = r.json() + state = out["State"]["Health"] + self.assertIsInstance(state, dict) + + def test_stats(self): + r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false"))) + self.assertIn(r.status_code, (200, 409), r.text) + if r.status_code == 200: + self.assertId(r.content) + r = requests.get(self.uri(self.resolve_container("/containers/{}/stats?stream=false&one-shot=true"))) + self.assertIn(r.status_code, (200, 409), r.text) + if r.status_code == 200: + self.assertId(r.content) + + def test_delete(self): + r = requests.delete(self.uri(self.resolve_container("/containers/{}?force=true"))) + self.assertEqual(r.status_code, 204, r.text) + + def test_stop(self): + r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.post(self.uri(self.resolve_container("/containers/{}/stop"))) + self.assertIn(r.status_code, (204, 304), r.text) + + def test_start(self): + r = requests.post(self.uri(self.resolve_container("/containers/{}/stop"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + def test_restart(self): + r = requests.post(self.uri(self.resolve_container("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.post(self.uri(self.resolve_container("/containers/{}/restart")), timeout=5) + self.assertEqual(r.status_code, 204, r.text) + + def test_resize(self): + r = requests.post(self.uri(self.resolve_container("/containers/{}/resize?h=43&w=80"))) + self.assertIn(r.status_code, (200, 409), r.text) + if r.status_code == 200: + self.assertEqual(r.text, "", r.text) + + def test_attach(self): + self.skipTest("FIXME: Test timeouts") + r = requests.post(self.uri(self.resolve_container("/containers/{}/attach")), timeout=5) + self.assertIn(r.status_code, (101, 500), r.text) + + def test_logs(self): + r = requests.get(self.uri(self.resolve_container("/containers/{}/logs?stdout=true"))) + self.assertEqual(r.status_code, 200, r.text) + r = requests.post( + self.podman_url + "/v1.40/containers/create?name=topcontainer", + json={"Cmd": ["top", "ls"], "Image": "alpine:latest"}, + ) + self.assertEqual(r.status_code, 201, r.text) + payload = r.json() + container_id = payload["Id"] + self.assertIsNotNone(container_id) + r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=0") + self.assertEqual(r.status_code, 200, r.text) + r = requests.get(self.podman_url + f"/v1.40/containers/{payload['Id']}/logs?follow=false&stdout=true&until=1") + self.assertEqual(r.status_code, 200, r.text) + + def test_commit(self): + r = requests.post(self.uri(self.resolve_container("/commit?container={}"))) + self.assertEqual(r.status_code, 200, r.text) + self.assertId(r.content) + + obj = r.json() + self.assertIsInstance(obj, dict) + + def test_prune(self): + name = f"Container_{random.getrandbits(160):x}" + + r = requests.post( + self.podman_url + f"/v1.40/containers/create?name={name}", + json={ + "Cmd": ["cp", "/etc/motd", "/motd.size_test"], + "Image": "alpine:latest", + "NetworkDisabled": True, + }, + ) + self.assertEqual(r.status_code, 201, r.text) + create = r.json() + + r = requests.post(self.podman_url + f"/v1.40/containers/{create['Id']}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.post(self.podman_url + f"/v1.40/containers/{create['Id']}/wait") + self.assertEqual(r.status_code, 200, r.text) + wait = r.json() + self.assertEqual(wait["StatusCode"], 0, wait["Error"]) + + prune = requests.post(self.podman_url + "/v1.40/containers/prune") + self.assertEqual(prune.status_code, 200, prune.status_code) + prune_payload = prune.json() + self.assertGreater(prune_payload["SpaceReclaimed"], 0) + self.assertIn(create["Id"], prune_payload["ContainersDeleted"]) + + # Delete any orphaned containers + r = requests.get(self.podman_url + "/v1.40/containers/json?all=true") + self.assertEqual(r.status_code, 200, r.text) + for self.resolve_container in r.json(): + requests.delete( + self.podman_url + f"/v1.40/containers/{self.resolve_container['Id']}?force=true" + ) + + # Image prune here tied to containers freeing up + prune = requests.post(self.podman_url + "/v1.40/images/prune") + self.assertEqual(prune.status_code, 200, prune.text) + prune_payload = prune.json() + self.assertGreater(prune_payload["SpaceReclaimed"], 0) + + # FIXME need method to determine which image is going to be "pruned" to fix test + # TODO should handler be recursive when deleting images? + # self.assertIn(img["Id"], prune_payload["ImagesDeleted"][1]["Deleted"]) + + # FIXME (@vrothberg): I commented this line out during the `libimage` migration. + # It doesn't make sense to report anything to be deleted if the reclaimed space + # is zero. I think the test needs some rewrite. + # self.assertIsNotNone(prune_payload["ImagesDeleted"][1]["Deleted"]) + + def test_status(self): + r = requests.post( + self.podman_url + "/v1.40/containers/create?name=topcontainer", + json={"Cmd": ["top"], "Image": "alpine:latest"}, + ) + self.assertEqual(r.status_code, 201, r.text) + payload = r.json() + container_id = payload["Id"] + self.assertIsNotNone(container_id) + + r = requests.get( + self.podman_url + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = r.json() + self.assertEqual(payload[0]["Status"], "Created") + + r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get( + self.podman_url + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = r.json() + self.assertTrue(str(payload[0]["Status"]).startswith("Up")) + + r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/pause") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get( + self.podman_url + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = r.json() + self.assertTrue(str(payload[0]["Status"]).startswith("Up")) + self.assertTrue(str(payload[0]["Status"]).endswith("(Paused)")) + + r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/unpause") + self.assertEqual(r.status_code, 204, r.text) + r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/stop") + self.assertEqual(r.status_code, 204, r.text) + + r = requests.get( + self.podman_url + "/v1.40/containers/json", + params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, + ) + self.assertEqual(r.status_code, 200, r.text) + payload = r.json() + self.assertTrue(str(payload[0]["Status"]).startswith("Exited")) + + r = requests.delete(self.podman_url + f"/v1.40/containers/{container_id}") + self.assertEqual(r.status_code, 204, r.text) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_image.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_image.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_image.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_image.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,210 @@ +import json +import unittest +from multiprocessing import Process + +import requests +from dateutil.parser import parse +from .fixtures import APITestCase + + +class ImageTestCase(APITestCase): + def test_list(self): + r = requests.get(self.podman_url + "/v1.40/images/json") + self.assertEqual(r.status_code, 200, r.text) + + # See https://docs.docker.com/engine/api/v1.40/#operation/ImageList + required_keys = ( + "Id", + "ParentId", + "RepoTags", + "RepoDigests", + "Created", + "Size", + "SharedSize", + "VirtualSize", + "Labels", + "Containers", + ) + images = r.json() + self.assertIsInstance(images, list) + for item in images: + self.assertIsInstance(item, dict) + for k in required_keys: + self.assertIn(k, item) + + # Id should be prefixed with sha256: (#11645) + self.assertIn("sha256:",item['Id']) + + def test_inspect(self): + r = requests.get(self.podman_url + "/v1.40/images/alpine/json") + self.assertEqual(r.status_code, 200, r.text) + + # See https://docs.docker.com/engine/api/v1.40/#operation/ImageInspect + required_keys = ( + "Id", + "Parent", + "Comment", + "Created", + "Container", + "DockerVersion", + "Author", + "Architecture", + "Os", + "Size", + "VirtualSize", + "GraphDriver", + "RootFS", + "Metadata", + ) + + image = r.json() + self.assertIsInstance(image, dict) + for item in required_keys: + self.assertIn(item, image) + _ = parse(image["Created"]) + # Id should be prefixed with sha256: (#11645) + self.assertIn("sha256:",image['Id']) + + def test_delete(self): + r = requests.delete(self.podman_url + "/v1.40/images/alpine?force=true") + self.assertEqual(r.status_code, 200, r.text) + self.assertIsInstance(r.json(), list) + + def test_pull(self): + r = requests.post(self.uri("/images/pull?reference=alpine"), timeout=15) + self.assertEqual(r.status_code, 200, r.status_code) + text = r.text + keys = { + "error": False, + "id": False, + "images": False, + "stream": False, + } + # Read and record stanza's from pull + for line in str.splitlines(text): + obj = json.loads(line) + key_list = list(obj.keys()) + for k in key_list: + keys[k] = True + + self.assertFalse(keys["error"], "Expected no errors") + self.assertTrue(keys["id"], "Expected to find id stanza") + self.assertTrue(keys["images"], "Expected to find images stanza") + self.assertTrue(keys["stream"], "Expected to find stream progress stanza's") + + r = requests.post(self.uri("/images/pull?reference=alpine&quiet=true"), timeout=15) + self.assertEqual(r.status_code, 200, r.status_code) + text = r.text + keys = { + "error": False, + "id": False, + "images": False, + "stream": False, + } + # Read and record stanza's from pull + for line in str.splitlines(text): + obj = json.loads(line) + key_list = list(obj.keys()) + for k in key_list: + keys[k] = True + + self.assertFalse(keys["error"], "Expected no errors") + self.assertTrue(keys["id"], "Expected to find id stanza") + self.assertTrue(keys["images"], "Expected to find images stanza") + self.assertFalse(keys["stream"], "Expected to find stream progress stanza's") + + def test_create(self): + r = requests.post( + self.podman_url + "/v1.40/images/create?fromImage=alpine&platform=linux/amd64/v8", + timeout=15, + ) + self.assertEqual(r.status_code, 200, r.text) + r = requests.post( + self.podman_url + + "/v1.40/images/create?fromSrc=-&repo=fedora&message=testing123&platform=linux/amd64", + timeout=15, + ) + self.assertEqual(r.status_code, 200, r.text) + + def test_search_compat(self): + url = self.podman_url + "/v1.40/images/search" + + # Had issues with this test hanging when repositories not happy + def do_search1(): + payload = {"term": "alpine"} + r = requests.get(url, params=payload, timeout=5) + self.assertEqual(r.status_code, 200, f"#1: {r.text}") + self.assertIsInstance(r.json(), list) + + def do_search2(): + payload = {"term": "alpine", "limit": 1} + r = requests.get(url, params=payload, timeout=5) + self.assertEqual(r.status_code, 200, f"#2: {r.text}") + + results = r.json() + self.assertIsInstance(results, list) + self.assertEqual(len(results), 1) + + def do_search3(): + # FIXME: Research if quay.io supports is-official and which image is "official" + return + payload = {"term": "thanos", "filters": '{"is-official":["true"]}'} + r = requests.get(url, params=payload, timeout=5) + self.assertEqual(r.status_code, 200, f"#3: {r.text}") + + results = r.json() + self.assertIsInstance(results, list) + + # There should be only one official image + self.assertEqual(len(results), 1) + + def do_search4(): + headers = {"X-Registry-Auth": "null"} + payload = {"term": "alpine"} + r = requests.get(url, params=payload, headers=headers, timeout=5) + self.assertEqual(r.status_code, 200, f"#4: {r.text}") + + def do_search5(): + headers = {"X-Registry-Auth": "invalid value"} + payload = {"term": "alpine"} + r = requests.get(url, params=payload, headers=headers, timeout=5) + self.assertEqual(r.status_code, 400, f"#5: {r.text}") + + i = 1 + for fn in [do_search1, do_search2, do_search3, do_search4, do_search5]: + with self.subTest(i=i): + search = Process(target=fn) + search.start() + search.join(timeout=10) + self.assertFalse(search.is_alive(), f"#{i} /images/search took too long") + + # search_methods = [do_search1, do_search2, do_search3, do_search4, do_search5] + # for search_method in search_methods: + # search = Process(target=search_method) + # search.start() + # search.join(timeout=10) + # self.assertFalse(search.is_alive(), "/images/search took too long") + + def test_history(self): + r = requests.get(self.podman_url + "/v1.40/images/alpine/history") + self.assertEqual(r.status_code, 200, r.text) + + # See https://docs.docker.com/engine/api/v1.40/#operation/ImageHistory + required_keys = ("Id", "Created", "CreatedBy", "Tags", "Size", "Comment") + + changes = r.json() + self.assertIsInstance(changes, list) + for change in changes: + self.assertIsInstance(change, dict) + for k in required_keys: + self.assertIn(k, change) + + def test_tree(self): + r = requests.get(self.uri("/images/alpine/tree")) + self.assertEqual(r.status_code, 200, r.text) + tree = r.json() + self.assertTrue(tree["Tree"].startswith("Image ID:"), r.text) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_manifest.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_manifest.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_manifest.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_manifest.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,14 @@ +import unittest + +import requests +from .fixtures import APITestCase + + +class ManifestTestCase(APITestCase): + def test_manifest_409(self): + r = requests.post(self.uri("/manifests/create"), params={"name": "ThisIsAnInvalidImage"}) + self.assertEqual(r.status_code, 400, r.text) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_network.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_network.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_network.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_network.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,182 @@ +import random +import unittest + +import requests + +from .fixtures import APITestCase + + +class NetworkTestCase(APITestCase): + # TODO Need to support Docker-py order of network/container creates + def test_connect(self): + """Create network and container then connect to network""" + net_default = requests.post( + self.podman_url + "/v1.40/networks/create", json={"Name": "TestDefaultNetwork"} + ) + self.assertEqual(net_default.status_code, 201, net_default.text) + + create = requests.post( + self.podman_url + "/v1.40/containers/create?name=postCreateConnect", + json={ + "Cmd": ["top"], + "Image": "alpine:latest", + "NetworkDisabled": False, + # FIXME adding these 2 lines cause: (This is sampled from docker-py) + # "network already exists","message":"container + # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI + # network \"TestDefaultNetwork\": network already exists" + # "HostConfig": {"NetworkMode": "TestDefaultNetwork"}, + # "NetworkingConfig": {"EndpointsConfig": {"TestDefaultNetwork": None}}, + # FIXME These two lines cause: + # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container + # 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not + # found" + # "HostConfig": {"NetworkMode": "TestNetwork"}, + # "NetworkingConfig": {"EndpointsConfig": {"TestNetwork": None}}, + # FIXME no networking defined cause: (note this error is from the container inspect below) + # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [ + # TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error" + }, + ) + self.assertEqual(create.status_code, 201, create.text) + self.assertId(create.content) + + payload = create.json() + start = requests.post(self.podman_url + f"/v1.40/containers/{payload['Id']}/start") + self.assertEqual(start.status_code, 204, start.text) + + connect = requests.post( + self.podman_url + "/v1.40/networks/TestDefaultNetwork/connect", + json={"Container": payload["Id"]}, + ) + self.assertEqual(connect.status_code, 200, connect.text) + self.assertEqual(connect.text, "OK\n") + + inspect = requests.get(f"{self.podman_url}/v1.40/containers/{payload['Id']}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + + payload = inspect.json() + self.assertFalse(payload["Config"].get("NetworkDisabled", False)) + + self.assertEqual( + "TestDefaultNetwork", + payload["NetworkSettings"]["Networks"]["TestDefaultNetwork"]["NetworkID"], + ) + # TODO restore this to test, when joining multiple networks possible + # self.assertEqual( + # "TestNetwork", + # payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"], + # ) + # TODO Need to support network aliases + # self.assertIn( + # "test_post_create", + # payload["NetworkSettings"]["Networks"]["TestNetwork"]["Aliases"], + # ) + + def test_create(self): + """Create network and connect container during create""" + net = requests.post( + self.podman_url + "/v1.40/networks/create", json={"Name": "TestNetwork"} + ) + self.assertEqual(net.status_code, 201, net.text) + + create = requests.post( + self.podman_url + "/v1.40/containers/create?name=postCreate", + json={ + "Cmd": ["date"], + "Image": "alpine:latest", + "NetworkDisabled": False, + "HostConfig": {"NetworkMode": "TestNetwork"}, + }, + ) + self.assertEqual(create.status_code, 201, create.text) + self.assertId(create.content) + + payload = create.json() + inspect = requests.get(f"{self.podman_url}/v1.40/containers/{payload['Id']}/json") + self.assertEqual(inspect.status_code, 200, inspect.text) + + payload = inspect.json() + self.assertFalse(payload["Config"].get("NetworkDisabled", False)) + self.assertEqual( + "TestNetwork", + payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"], + ) + def test_inspect(self): + name = f"Network_{random.getrandbits(160):x}" + create = requests.post(self.podman_url + "/v1.40/networks/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.text) + self.assertId(create.content) + + net = create.json() + self.assertIsInstance(net, dict) + self.assertNotEqual(net["Id"], name) + ident = net["Id"] + + ls = requests.get(self.podman_url + "/v1.40/networks") + self.assertEqual(ls.status_code, 200, ls.text) + + networks = ls.json() + self.assertIsInstance(networks, list) + + found = False + for net in networks: + if net["Name"] == name: + found = True + break + self.assertTrue(found, f"Network '{name}' not found") + + inspect = requests.get(self.podman_url + f"/v1.40/networks/{ident}?verbose=false&scope=local") + self.assertEqual(inspect.status_code, 200, inspect.text) + + + def test_crud(self): + name = f"Network_{random.getrandbits(160):x}" + + # Cannot test for 0 existing networks because default "podman" network always exists + + create = requests.post(self.podman_url + "/v1.40/networks/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.text) + self.assertId(create.content) + + net = create.json() + self.assertIsInstance(net, dict) + self.assertNotEqual(net["Id"], name) + ident = net["Id"] + + ls = requests.get(self.podman_url + "/v1.40/networks") + self.assertEqual(ls.status_code, 200, ls.text) + + networks = ls.json() + self.assertIsInstance(networks, list) + + found = False + for net in networks: + if net["Name"] == name: + found = True + break + self.assertTrue(found, f"Network '{name}' not found") + + inspect = requests.get(self.podman_url + f"/v1.40/networks/{ident}") + self.assertEqual(inspect.status_code, 200, inspect.text) + self.assertIsInstance(inspect.json(), dict) + + inspect = requests.delete(self.podman_url + f"/v1.40/networks/{ident}") + self.assertEqual(inspect.status_code, 204, inspect.text) + inspect = requests.get(self.podman_url + f"/v1.40/networks/{ident}") + self.assertEqual(inspect.status_code, 404, inspect.text) + + # network prune + prune_name = f"Network_{random.getrandbits(160):x}" + prune_create = requests.post( + self.podman_url + "/v1.40/networks/create", json={"Name": prune_name} + ) + self.assertEqual(create.status_code, 201, prune_create.text) + + prune = requests.post(self.podman_url + "/v1.40/networks/prune") + self.assertEqual(prune.status_code, 200, prune.text) + self.assertTrue(prune_name in prune.json()["NetworksDeleted"]) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_pod.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_pod.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_pod.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_pod.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,65 @@ +import random +import unittest + +import requests +from .fixtures import APITestCase + + +class TestApi(APITestCase): + def test_pod_start_conflict(self): + """Verify issue #8865""" + + pod_name = list() + pod_name.append(f"Pod_{random.getrandbits(160):x}") + pod_name.append(f"Pod_{random.getrandbits(160):x}") + + r = requests.post( + self.uri("/pods/create"), + json={ + "name": pod_name[0], + "no_infra": False, + "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + r = requests.post( + self.uri("/containers/create"), + json={ + "pod": pod_name[0], + "image": "quay.io/libpod/alpine:latest", + "command": ["top"], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + + r = requests.post( + self.uri("/pods/create"), + json={ + "name": pod_name[1], + "no_infra": False, + "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + r = requests.post( + self.uri("/containers/create"), + json={ + "pod": pod_name[1], + "image": "quay.io/libpod/alpine:latest", + "command": ["top"], + }, + ) + self.assertEqual(r.status_code, 201, r.text) + + r = requests.post(self.uri(f"/pods/{pod_name[0]}/start")) + self.assertEqual(r.status_code, 200, r.text) + + r = requests.post(self.uri(f"/pods/{pod_name[1]}/start")) + self.assertEqual(r.status_code, 409, r.text) + + start = r.json() + self.assertGreater(len(start["Errs"]), 0, r.text) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_system.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_system.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_system.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_system.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,117 @@ +import json +import unittest +import uuid + +import requests +from .fixtures import APITestCase + + +class SystemTestCase(APITestCase): + def test_info(self): + r = requests.get(self.uri("/info")) + self.assertEqual(r.status_code, 200, r.text) + self.assertIsNotNone(r.content) + _ = r.json() + + r = requests.get(self.podman_url + "/v1.40/info") + self.assertEqual(r.status_code, 200, r.text) + self.assertIsNotNone(r.content) + _ = r.json() + + def test_events(self): + r = requests.get(self.uri("/events?stream=false")) + self.assertEqual(r.status_code, 200, r.text) + self.assertIsNotNone(r.content) + + report = r.text.splitlines() + self.assertGreater(len(report), 0, "No events found!") + for line in report: + obj = json.loads(line) + # Actor.ID is uppercase for compatibility + self.assertIn("ID", obj["Actor"]) + # Verify 1.22+ deprecated variants are present if current originals are + if obj["Actor"]["ID"]: + self.assertEqual(obj["Actor"]["ID"], obj["id"]) + if obj["Action"]: + self.assertEqual(obj["Action"], obj["status"]) + if obj["Actor"].get("Attributes") and obj["Actor"]["Attributes"].get("image"): + self.assertEqual(obj["Actor"]["Attributes"]["image"], obj["from"]) + + def test_ping(self): + required_headers = ( + "API-Version", + "Builder-Version", + "Docker-Experimental", + "Cache-Control", + "Pragma", + "Pragma", + ) + + def check_headers(req): + for k in required_headers: + self.assertIn(k, req.headers) + + r = requests.get(self.podman_url + "/_ping") + self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "OK") + check_headers(r) + + r = requests.head(self.podman_url + "/_ping") + self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "") + check_headers(r) + + r = requests.get(self.uri("/_ping")) + self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "OK") + check_headers(r) + + r = requests.head(self.uri("/_ping")) + self.assertEqual(r.status_code, 200, r.text) + self.assertEqual(r.text, "") + check_headers(r) + + def test_version(self): + r = requests.get(self.podman_url + "/v1.40/version") + self.assertEqual(r.status_code, 200, r.text) + + r = requests.get(self.uri("/version")) + self.assertEqual(r.status_code, 200, r.text) + + body = r.json() + names = [d.get("Name", "") for d in body["Components"]] + + self.assertIn("Conmon", names) + for n in names: + if n.startswith("OCI Runtime"): + oci_name = n + self.assertIsNotNone(oci_name, "OCI Runtime not found in version components.") + + def test_df(self): + r = requests.get(self.podman_url + "/v1.40/system/df") + self.assertEqual(r.status_code, 200, r.text) + + obj = r.json() + self.assertIn("Images", obj) + self.assertIn("Containers", obj) + self.assertIn("Volumes", obj) + self.assertIn("BuildCache", obj) + + r = requests.get(self.uri("/system/df")) + self.assertEqual(r.status_code, 200, r.text) + + def test_reference_id(self): + rid = str(uuid.uuid4()) + r = requests.get(self.uri("/info"), headers={"X-Reference-Id": rid}) + self.assertEqual(r.status_code, 200, r.text) + + self.assertIn("X-Reference-Id", r.headers) + self.assertEqual(r.headers["X-Reference-Id"], rid) + + r = requests.get(self.uri("/info")) + self.assertIn("X-Reference-Id", r.headers) + self.assertNotEqual(r.headers["X-Reference-Id"], rid) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_volume.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_volume.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/test_v2_0_0_volume.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/test_v2_0_0_volume.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,75 @@ +import os +import random +import unittest + +import requests +from .fixtures import APITestCase + + +class VolumeTestCase(APITestCase): + def test_volume(self): + name = f"Volume_{random.getrandbits(160):x}" + + ls = requests.get(self.podman_url + "/v1.40/volumes") + self.assertEqual(ls.status_code, 200, ls.text) + + # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeList + required_keys = ( + "Volumes", + "Warnings", + ) + + volumes = ls.json() + self.assertIsInstance(volumes, dict) + for key in required_keys: + self.assertIn(key, volumes) + + create = requests.post(self.podman_url + "/v1.40/volumes/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.text) + + # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeCreate + # and https://docs.docker.com/engine/api/v1.40/#operation/VolumeInspect + required_keys = ( + "Name", + "Driver", + "Mountpoint", + "Labels", + "Scope", + "Options", + ) + + volume = create.json() + self.assertIsInstance(volume, dict) + for k in required_keys: + self.assertIn(k, volume) + self.assertEqual(volume["Name"], name) + + inspect = requests.get(self.podman_url + f"/v1.40/volumes/{name}") + self.assertEqual(inspect.status_code, 200, inspect.text) + + volume = inspect.json() + self.assertIsInstance(volume, dict) + for k in required_keys: + self.assertIn(k, volume) + + rm = requests.delete(self.podman_url + f"/v1.40/volumes/{name}") + self.assertEqual(rm.status_code, 204, rm.text) + + # recreate volume with data and then prune it + r = requests.post(self.podman_url + "/v1.40/volumes/create", json={"Name": name}) + self.assertEqual(create.status_code, 201, create.text) + + create = r.json() + with open(os.path.join(create["Mountpoint"], "test_prune"), "w") as file: + file.writelines(["This is a test\n", "This is a good test\n"]) + + prune = requests.post(self.podman_url + "/v1.40/volumes/prune") + self.assertEqual(prune.status_code, 200, prune.text) + + payload = prune.json() + self.assertIn(name, payload["VolumesDeleted"]) + self.assertGreater(payload["SpaceReclaimed"], 0) + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py libpod-3.4.4+ds1/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py --- libpod-3.2.1+ds1/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/python/rest_api/v1_test_rest_v1_0_0.py 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,238 @@ +import json +import os +import shlex +import signal +import string +import subprocess +import sys +import time +import unittest +from collections.abc import Iterable +from multiprocessing import Process + +import requests +from dateutil.parser import parse + +PODMAN_URL = "http://localhost:8080" + + +def _url(path): + return PODMAN_URL + "/v1.0.0/libpod" + path + + +def podman(): + binary = os.getenv("PODMAN_BINARY") + if binary is None: + binary = "bin/podman" + return binary + + +def ctnr(path): + r = requests.get(_url("/containers/json?all=true")) + try: + ctnrs = json.loads(r.text) + except Exception as e: + sys.stderr.write("Bad container response: {}/{}".format(r.text, e)) + raise e + return path.format(ctnrs[0]["Id"]) + + +class TestApi(unittest.TestCase): + podman = None + + def setUp(self): + super().setUp() + if TestApi.podman.poll() is not None: + sys.stderr.write("podman service returned {}", TestApi.podman.returncode) + sys.exit(2) + requests.get(_url("/images/create?fromSrc=quay.io%2Flibpod%2Falpine%3Alatest")) + # calling out to podman is easier than the API for running a container + subprocess.run( + [podman(), "run", "alpine", "/bin/ls"], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + @classmethod + def setUpClass(cls): + super().setUpClass() + + TestApi.podman = subprocess.Popen( + [ + podman(), + "system", + "service", + "tcp:localhost:8080", + "--log-level=debug", + "--time=0", + ], + shell=False, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + time.sleep(2) + + @classmethod + def tearDownClass(cls): + TestApi.podman.terminate() + stdout, stderr = TestApi.podman.communicate(timeout=0.5) + if stdout: + print("\nService Stdout:\n" + stdout.decode("utf-8")) + if stderr: + print("\nService Stderr:\n" + stderr.decode("utf-8")) + + if TestApi.podman.returncode > 0: + sys.stderr.write("podman exited with error code {}\n".format(TestApi.podman.returncode)) + sys.exit(2) + + return super().tearDownClass() + + def test_info(self): + r = requests.get(_url("/info")) + self.assertEqual(r.status_code, 200) + self.assertIsNotNone(r.content) + _ = json.loads(r.text) + + def test_events(self): + r = requests.get(_url("/events?stream=false")) + self.assertEqual(r.status_code, 200, r.text) + self.assertIsNotNone(r.content) + for line in r.text.splitlines(): + obj = json.loads(line) + # Actor.ID is uppercase for compatibility + _ = obj["Actor"]["ID"] + + def test_containers(self): + r = requests.get(_url("/containers/json"), timeout=5) + self.assertEqual(r.status_code, 200, r.text) + obj = json.loads(r.text) + self.assertEqual(len(obj), 0) + + def test_containers_all(self): + r = requests.get(_url("/containers/json?all=true")) + self.assertEqual(r.status_code, 200, r.text) + self.validateObjectFields(r.text) + + def test_inspect_container(self): + r = requests.get(_url(ctnr("/containers/{}/json"))) + self.assertEqual(r.status_code, 200, r.text) + obj = self.validateObjectFields(r.content) + _ = parse(obj["Created"]) + + def test_stats(self): + r = requests.get(_url(ctnr("/containers/{}/stats?stream=false"))) + self.assertIn(r.status_code, (200, 409), r.text) + if r.status_code == 200: + self.validateObjectFields(r.text) + + def test_delete_containers(self): + r = requests.delete(_url(ctnr("/containers/{}"))) + self.assertEqual(r.status_code, 204, r.text) + + def test_stop_containers(self): + r = requests.post(_url(ctnr("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.post(_url(ctnr("/containers/{}/stop"))) + self.assertIn(r.status_code, (204, 304), r.text) + + def test_start_containers(self): + r = requests.post(_url(ctnr("/containers/{}/stop"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.post(_url(ctnr("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + def test_restart_containers(self): + r = requests.post(_url(ctnr("/containers/{}/start"))) + self.assertIn(r.status_code, (204, 304), r.text) + + r = requests.post(_url(ctnr("/containers/{}/restart")), timeout=5) + self.assertEqual(r.status_code, 204, r.text) + + def test_resize(self): + r = requests.post(_url(ctnr("/containers/{}/resize?h=43&w=80"))) + self.assertIn(r.status_code, (200, 409), r.text) + if r.status_code == 200: + self.assertIsNone(r.text) + + def test_attach_containers(self): + r = requests.post(_url(ctnr("/containers/{}/attach"))) + self.assertIn(r.status_code, (101, 409), r.text) + + def test_logs_containers(self): + r = requests.get(_url(ctnr("/containers/{}/logs?stdout=true"))) + self.assertEqual(r.status_code, 200, r.text) + + def test_post_create(self): + self.skipTest("TODO: create request body") + r = requests.post(_url("/containers/create?args=True")) + self.assertEqual(r.status_code, 200, r.text) + json.loads(r.text) + + def test_commit(self): + r = requests.post(_url(ctnr("/commit?container={}"))) + self.assertEqual(r.status_code, 200, r.text) + self.validateObjectFields(r.text) + + def test_images(self): + r = requests.get(_url("/images/json")) + self.assertEqual(r.status_code, 200, r.text) + self.validateObjectFields(r.content) + + def test_inspect_image(self): + r = requests.get(_url("/images/alpine/json")) + self.assertEqual(r.status_code, 200, r.text) + obj = self.validateObjectFields(r.content) + _ = parse(obj["Created"]) + + def test_delete_image(self): + r = requests.delete(_url("/images/alpine?force=true")) + self.assertEqual(r.status_code, 200, r.text) + json.loads(r.text) + + def test_pull(self): + r = requests.post(_url("/images/pull?reference=alpine"), timeout=5) + self.assertEqual(r.status_code, 200, r.text) + json.loads(r.text) + + def test_search(self): + # Had issues with this test hanging when repositories not happy + def do_search(): + r = requests.get(_url("/images/search?term=alpine"), timeout=5) + self.assertEqual(r.status_code, 200, r.text) + json.loads(r.text) + + search = Process(target=do_search) + search.start() + search.join(timeout=10) + self.assertFalse(search.is_alive(), "/images/search took too long") + + def test_ping(self): + r = requests.get(PODMAN_URL + "/_ping") + self.assertEqual(r.status_code, 200, r.text) + + r = requests.head(PODMAN_URL + "/_ping") + self.assertEqual(r.status_code, 200, r.text) + + r = requests.get(_url("/_ping")) + self.assertEqual(r.status_code, 200, r.text) + + r = requests.get(_url("/_ping")) + self.assertEqual(r.status_code, 200, r.text) + + +def validateObjectFields(self, buffer): + objs = json.loads(buffer) + if not isinstance(objs, dict): + for o in objs: + _ = o["Id"] + else: + _ = objs["Id"] + return objs + + +if __name__ == "__main__": + unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/rest_api/__init__.py libpod-3.4.4+ds1/test/apiv2/rest_api/__init__.py --- libpod-3.2.1+ds1/test/apiv2/rest_api/__init__.py 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/rest_api/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,136 +0,0 @@ -import configparser -import json -import os -import shutil -import subprocess -import sys -import tempfile - - -class Podman(object): - """ - Instances hold the configuration and setup for running podman commands - """ - - def __init__(self): - """Initialize a Podman instance with global options""" - binary = os.getenv("PODMAN", "bin/podman") - self.cmd = [binary, "--storage-driver=vfs"] - - cgroupfs = os.getenv("CGROUP_MANAGER", "systemd") - self.cmd.append(f"--cgroup-manager={cgroupfs}") - - if os.getenv("DEBUG"): - self.cmd.append("--log-level=debug") - self.cmd.append("--syslog=true") - - self.anchor_directory = tempfile.mkdtemp(prefix="podman_restapi_") - self.cmd.append("--root=" + os.path.join(self.anchor_directory, "crio")) - self.cmd.append("--runroot=" + os.path.join(self.anchor_directory, "crio-run")) - - os.environ["CONTAINERS_REGISTRIES_CONF"] = os.path.join( - self.anchor_directory, "registry.conf" - ) - p = configparser.ConfigParser() - p.read_dict( - { - "registries.search": {"registries": "['docker.io']"}, - "registries.insecure": {"registries": "[]"}, - "registries.block": {"registries": "[]"}, - } - ) - with open(os.environ["CONTAINERS_REGISTRIES_CONF"], "w") as w: - p.write(w) - - 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("--cni-config-dir=" + os.environ["CNI_CONFIG_PATH"]) - 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( - """ - { - "cniVersion": "0.3.0", - "name": "podman", - "plugins": [{ - "type": "bridge", - "bridge": "cni0", - "isGateway": true, - "ipMasq": true, - "ipam": { - "type": "host-local", - "subnet": "10.88.0.0/16", - "routes": [{ - "dst": "0.0.0.0/0" - }] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - } - ] - } - """ - ) - with open(cni_cfg, "w") as w: - json.dump(buf, w) - - def open(self, command, *args, **kwargs): - """Podman initialized instance to run a given command - - :param self: Podman instance - :param command: podman sub-command to run - :param args: arguments and options for command - :param kwargs: See subprocess.Popen() for shell keyword - :return: subprocess.Popen() instance configured to run podman instance - """ - cmd = self.cmd.copy() - cmd.append(command) - cmd.extend(args) - - shell = kwargs.get("shell", False) - - return subprocess.Popen( - cmd, - shell=shell, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - - def run(self, command, *args, **kwargs): - """Podman initialized instance to run a given command - - :param self: Podman instance - :param command: podman sub-command to run - :param args: arguments and options for command - :param kwargs: See subprocess.Popen() for shell and check keywords - :return: subprocess.Popen() instance configured to run podman instance - """ - cmd = self.cmd.copy() - cmd.append(command) - cmd.extend(args) - - check = kwargs.get("check", False) - shell = kwargs.get("shell", False) - - try: - return subprocess.run( - cmd, - shell=shell, - check=check, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) - except subprocess.CalledProcessError as e: - if e.stdout: - sys.stdout.write("\nRun Stdout:\n" + e.stdout.decode("utf-8")) - if e.stderr: - sys.stderr.write("\nRun Stderr:\n" + e.stderr.decode("utf-8")) - raise - - def tear_down(self): - shutil.rmtree(self.anchor_directory, ignore_errors=True) diff -Nru libpod-3.2.1+ds1/test/apiv2/rest_api/test_rest_v2_0_0.py libpod-3.4.4+ds1/test/apiv2/rest_api/test_rest_v2_0_0.py --- libpod-3.2.1+ds1/test/apiv2/rest_api/test_rest_v2_0_0.py 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/rest_api/test_rest_v2_0_0.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,744 +0,0 @@ -import json -import os -import random -import string -import subprocess -import sys -import time -import unittest -from multiprocessing import Process - -import requests -from dateutil.parser import parse - -from test.apiv2.rest_api import Podman - -PODMAN_URL = "http://localhost:8080" - - -def _url(path): - return PODMAN_URL + "/v2.0.0/libpod" + path - - -def ctnr(path): - try: - r = requests.get(_url("/containers/json?all=true")) - ctnrs = json.loads(r.text) - except Exception as e: - msg = f"Bad container response: {e}" - if r is not None: - msg = msg + " " + r.text - sys.stderr.write(msg + "\n") - raise - return path.format(ctnrs[0]["Id"]) - - -def validateObjectFields(buffer): - objs = json.loads(buffer) - if not isinstance(objs, dict): - for o in objs: - _ = o["Id"] - else: - _ = objs["Id"] - return objs - - -class TestApi(unittest.TestCase): - podman = None # initialized podman configuration for tests - service = None # podman service instance - - def setUp(self): - super().setUp() - - TestApi.podman.run("run", "alpine", "/bin/ls", check=True) - - def tearDown(self) -> None: - super().tearDown() - - TestApi.podman.run("pod", "rm", "--all", "--force", check=True) - TestApi.podman.run("rm", "--all", "--force", check=True) - - @classmethod - def setUpClass(cls): - super().setUpClass() - - TestApi.podman = Podman() - TestApi.service = TestApi.podman.open("system", "service", "tcp:localhost:8080", "--time=0") - # give the service some time to be ready... - time.sleep(2) - - returncode = TestApi.service.poll() - if returncode is not None: - raise subprocess.CalledProcessError(returncode, "podman system service") - - r = requests.post(_url("/images/pull?reference=docker.io%2Falpine%3Alatest")) - if r.status_code != 200: - raise subprocess.CalledProcessError( - r.status_code, f"podman images pull docker.io/alpine:latest {r.text}" - ) - - @classmethod - def tearDownClass(cls): - TestApi.service.terminate() - stdout, stderr = TestApi.service.communicate(timeout=0.5) - if stdout: - sys.stdout.write("\nService Stdout:\n" + stdout.decode("utf-8")) - if stderr: - sys.stderr.write("\nService Stderr:\n" + stderr.decode("utf-8")) - return super().tearDownClass() - - def test_info(self): - r = requests.get(_url("/info")) - self.assertEqual(r.status_code, 200) - self.assertIsNotNone(r.content) - _ = json.loads(r.text) - - info = requests.get(PODMAN_URL + "/v1.40/info") - self.assertEqual(info.status_code, 200, info.content) - _ = json.loads(info.text) - - def test_events(self): - r = requests.get(_url("/events?stream=false")) - self.assertEqual(r.status_code, 200, r.text) - self.assertIsNotNone(r.content) - - report = r.text.splitlines() - self.assertGreater(len(report), 0, "No events found!") - for line in report: - obj = json.loads(line) - # Actor.ID is uppercase for compatibility - self.assertIn("ID", obj["Actor"]) - - def test_containers(self): - r = requests.get(_url("/containers/json"), timeout=5) - self.assertEqual(r.status_code, 200, r.text) - obj = json.loads(r.text) - self.assertEqual(len(obj), 0) - - def test_containers_all(self): - r = requests.get(_url("/containers/json?all=true")) - self.assertEqual(r.status_code, 200, r.text) - validateObjectFields(r.text) - - def test_inspect_container(self): - r = requests.get(_url(ctnr("/containers/{}/json"))) - self.assertEqual(r.status_code, 200, r.text) - obj = validateObjectFields(r.content) - _ = parse(obj["Created"]) - - def test_stats(self): - r = requests.get(_url(ctnr("/containers/{}/stats?stream=false"))) - self.assertIn(r.status_code, (200, 409), r.text) - if r.status_code == 200: - validateObjectFields(r.text) - - def test_delete_containers(self): - r = requests.delete(_url(ctnr("/containers/{}"))) - self.assertEqual(r.status_code, 204, r.text) - - def test_stop_containers(self): - r = requests.post(_url(ctnr("/containers/{}/start"))) - self.assertIn(r.status_code, (204, 304), r.text) - - r = requests.post(_url(ctnr("/containers/{}/stop"))) - self.assertIn(r.status_code, (204, 304), r.text) - - def test_start_containers(self): - r = requests.post(_url(ctnr("/containers/{}/stop"))) - self.assertIn(r.status_code, (204, 304), r.text) - - r = requests.post(_url(ctnr("/containers/{}/start"))) - self.assertIn(r.status_code, (204, 304), r.text) - - def test_restart_containers(self): - r = requests.post(_url(ctnr("/containers/{}/start"))) - self.assertIn(r.status_code, (204, 304), r.text) - - r = requests.post(_url(ctnr("/containers/{}/restart")), timeout=5) - self.assertEqual(r.status_code, 204, r.text) - - def test_resize(self): - r = requests.post(_url(ctnr("/containers/{}/resize?h=43&w=80"))) - self.assertIn(r.status_code, (200, 409), r.text) - if r.status_code == 200: - self.assertEqual(r.text, "", r.text) - - def test_attach_containers(self): - self.skipTest("FIXME: Test timeouts") - r = requests.post(_url(ctnr("/containers/{}/attach")), timeout=5) - self.assertIn(r.status_code, (101, 500), r.text) - - def test_logs_containers(self): - r = requests.get(_url(ctnr("/containers/{}/logs?stdout=true"))) - self.assertEqual(r.status_code, 200, r.text) - - # TODO Need to support Docker-py order of network/container creates - def test_post_create_compat_connect(self): - """Create network and container then connect to network""" - net_default = requests.post( - PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestDefaultNetwork"} - ) - self.assertEqual(net_default.status_code, 201, net_default.text) - - create = requests.post( - PODMAN_URL + "/v1.40/containers/create?name=postCreateConnect", - json={ - "Cmd": ["top"], - "Image": "alpine:latest", - "NetworkDisabled": False, - # FIXME adding these 2 lines cause: (This is sampled from docker-py) - # "network already exists","message":"container - # 01306e499df5441560d70071a54342611e422a94de20865add50a9565fd79fb9 is already connected to CNI - # network \"TestDefaultNetwork\": network already exists" - # "HostConfig": {"NetworkMode": "TestDefaultNetwork"}, - # "NetworkingConfig": {"EndpointsConfig": {"TestDefaultNetwork": None}}, - # FIXME These two lines cause: - # CNI network \"TestNetwork\" not found","message":"error configuring network namespace for container - # 369ddfa7d3211ebf1fbd5ddbff91bd33fa948858cea2985c133d6b6507546dff: CNI network \"TestNetwork\" not - # found" - # "HostConfig": {"NetworkMode": "TestNetwork"}, - # "NetworkingConfig": {"EndpointsConfig": {"TestNetwork": None}}, - # FIXME no networking defined cause: (note this error is from the container inspect below) - # "internal libpod error","message":"network inspection mismatch: asked to join 2 CNI network(s) [ - # TestDefaultNetwork podman], but have information on 1 network(s): internal libpod error" - }, - ) - self.assertEqual(create.status_code, 201, create.text) - payload = json.loads(create.text) - self.assertIsNotNone(payload["Id"]) - - start = requests.post(PODMAN_URL + f"/v1.40/containers/{payload['Id']}/start") - self.assertEqual(start.status_code, 204, start.text) - - connect = requests.post( - PODMAN_URL + "/v1.40/networks/TestDefaultNetwork/connect", - json={"Container": payload["Id"]}, - ) - self.assertEqual(connect.status_code, 200, connect.text) - self.assertEqual(connect.text, "OK\n") - - inspect = requests.get(f"{PODMAN_URL}/v1.40/containers/{payload['Id']}/json") - self.assertEqual(inspect.status_code, 200, inspect.text) - - payload = json.loads(inspect.text) - self.assertFalse(payload["Config"].get("NetworkDisabled", False)) - - self.assertEqual( - "TestDefaultNetwork", - payload["NetworkSettings"]["Networks"]["TestDefaultNetwork"]["NetworkID"], - ) - # TODO restore this to test, when joining multiple networks possible - # self.assertEqual( - # "TestNetwork", - # payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"], - # ) - # TODO Need to support network aliases - # self.assertIn( - # "test_post_create", - # payload["NetworkSettings"]["Networks"]["TestNetwork"]["Aliases"], - # ) - - def test_post_create_compat(self): - """Create network and connect container during create""" - net = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": "TestNetwork"}) - self.assertEqual(net.status_code, 201, net.text) - - create = requests.post( - PODMAN_URL + "/v1.40/containers/create?name=postCreate", - json={ - "Cmd": ["date"], - "Image": "alpine:latest", - "NetworkDisabled": False, - "HostConfig": {"NetworkMode": "TestNetwork"}, - }, - ) - self.assertEqual(create.status_code, 201, create.text) - payload = json.loads(create.text) - self.assertIsNotNone(payload["Id"]) - - inspect = requests.get(f"{PODMAN_URL}/v1.40/containers/{payload['Id']}/json") - self.assertEqual(inspect.status_code, 200, inspect.text) - payload = json.loads(inspect.text) - self.assertFalse(payload["Config"].get("NetworkDisabled", False)) - self.assertEqual( - "TestNetwork", - payload["NetworkSettings"]["Networks"]["TestNetwork"]["NetworkID"], - ) - - def test_commit(self): - r = requests.post(_url(ctnr("/commit?container={}"))) - self.assertEqual(r.status_code, 200, r.text) - - obj = json.loads(r.content) - self.assertIsInstance(obj, dict) - self.assertIn("Id", obj) - - def test_images_compat(self): - r = requests.get(PODMAN_URL + "/v1.40/images/json") - self.assertEqual(r.status_code, 200, r.text) - - # See https://docs.docker.com/engine/api/v1.40/#operation/ImageList - required_keys = ( - "Id", - "ParentId", - "RepoTags", - "RepoDigests", - "Created", - "Size", - "SharedSize", - "VirtualSize", - "Labels", - "Containers", - ) - objs = json.loads(r.content) - self.assertIn(type(objs), (list,)) - for o in objs: - self.assertIsInstance(o, dict) - for k in required_keys: - self.assertIn(k, o) - - def test_inspect_image_compat(self): - r = requests.get(PODMAN_URL + "/v1.40/images/alpine/json") - self.assertEqual(r.status_code, 200, r.text) - - # See https://docs.docker.com/engine/api/v1.40/#operation/ImageInspect - required_keys = ( - "Id", - "Parent", - "Comment", - "Created", - "Container", - "DockerVersion", - "Author", - "Architecture", - "Os", - "Size", - "VirtualSize", - "GraphDriver", - "RootFS", - "Metadata", - ) - - obj = json.loads(r.content) - self.assertIn(type(obj), (dict,)) - for k in required_keys: - self.assertIn(k, obj) - _ = parse(obj["Created"]) - - def test_delete_image_compat(self): - r = requests.delete(PODMAN_URL + "/v1.40/images/alpine?force=true") - self.assertEqual(r.status_code, 200, r.text) - obj = json.loads(r.content) - self.assertIn(type(obj), (list,)) - - def test_pull(self): - r = requests.post(_url("/images/pull?reference=alpine"), timeout=15) - self.assertEqual(r.status_code, 200, r.status_code) - text = r.text - keys = { - "error": False, - "id": False, - "images": False, - "stream": False, - } - # Read and record stanza's from pull - for line in str.splitlines(text): - obj = json.loads(line) - key_list = list(obj.keys()) - for k in key_list: - keys[k] = True - - self.assertFalse(keys["error"], "Expected no errors") - self.assertTrue(keys["id"], "Expected to find id stanza") - self.assertTrue(keys["images"], "Expected to find images stanza") - self.assertTrue(keys["stream"], "Expected to find stream progress stanza's") - - def test_search_compat(self): - url = PODMAN_URL + "/v1.40/images/search" - - # Had issues with this test hanging when repositories not happy - def do_search1(): - payload = {"term": "alpine"} - r = requests.get(url, params=payload, timeout=5) - self.assertEqual(r.status_code, 200, r.text) - objs = json.loads(r.text) - self.assertIn(type(objs), (list,)) - - def do_search2(): - payload = {"term": "alpine", "limit": 1} - r = requests.get(url, params=payload, timeout=5) - self.assertEqual(r.status_code, 200, r.text) - objs = json.loads(r.text) - self.assertIn(type(objs), (list,)) - self.assertEqual(len(objs), 1) - - def do_search3(): - payload = {"term": "alpine", "filters": '{"is-official":["true"]}'} - r = requests.get(url, params=payload, timeout=5) - self.assertEqual(r.status_code, 200, r.text) - objs = json.loads(r.text) - self.assertIn(type(objs), (list,)) - # There should be only one official image - self.assertEqual(len(objs), 1) - - def do_search4(): - headers = {"X-Registry-Auth": "null"} - payload = {"term": "alpine"} - r = requests.get(url, params=payload, headers=headers, timeout=5) - self.assertEqual(r.status_code, 200, r.text) - - def do_search5(): - headers = {"X-Registry-Auth": "invalid value"} - payload = {"term": "alpine"} - r = requests.get(url, params=payload, headers=headers, timeout=5) - self.assertEqual(r.status_code, 400, r.text) - - search_methods = [do_search1, do_search2, do_search3, do_search4, do_search5] - for search_method in search_methods: - search = Process(target=search_method) - search.start() - search.join(timeout=10) - self.assertFalse(search.is_alive(), "/images/search took too long") - - def test_ping(self): - required_headers = ( - "API-Version", - "Builder-Version", - "Docker-Experimental", - "Cache-Control", - "Pragma", - "Pragma", - ) - - def check_headers(req): - for k in required_headers: - self.assertIn(k, req.headers) - - r = requests.get(PODMAN_URL + "/_ping") - self.assertEqual(r.status_code, 200, r.text) - self.assertEqual(r.text, "OK") - check_headers(r) - - r = requests.head(PODMAN_URL + "/_ping") - self.assertEqual(r.status_code, 200, r.text) - self.assertEqual(r.text, "") - check_headers(r) - - r = requests.get(_url("/_ping")) - self.assertEqual(r.status_code, 200, r.text) - self.assertEqual(r.text, "OK") - check_headers(r) - - r = requests.head(_url("/_ping")) - self.assertEqual(r.status_code, 200, r.text) - self.assertEqual(r.text, "") - check_headers(r) - - def test_history_compat(self): - r = requests.get(PODMAN_URL + "/v1.40/images/alpine/history") - self.assertEqual(r.status_code, 200, r.text) - - # See https://docs.docker.com/engine/api/v1.40/#operation/ImageHistory - required_keys = ("Id", "Created", "CreatedBy", "Tags", "Size", "Comment") - - objs = json.loads(r.content) - self.assertIn(type(objs), (list,)) - for o in objs: - self.assertIsInstance(o, dict) - for k in required_keys: - self.assertIn(k, o) - - def test_network_compat(self): - name = "Network_" + "".join(random.choice(string.ascii_letters) for i in range(10)) - - # Cannot test for 0 existing networks because default "podman" network always exists - - create = requests.post(PODMAN_URL + "/v1.40/networks/create", json={"Name": name}) - self.assertEqual(create.status_code, 201, create.content) - obj = json.loads(create.content) - self.assertIn(type(obj), (dict,)) - self.assertIn("Id", obj) - ident = obj["Id"] - self.assertNotEqual(name, ident) - - ls = requests.get(PODMAN_URL + "/v1.40/networks") - self.assertEqual(ls.status_code, 200, ls.content) - objs = json.loads(ls.content) - self.assertIn(type(objs), (list,)) - - found = False - for network in objs: - if network["Name"] == name: - found = True - self.assertTrue(found, f"Network {name} not found") - - inspect = requests.get(PODMAN_URL + f"/v1.40/networks/{ident}") - self.assertEqual(inspect.status_code, 200, inspect.content) - obj = json.loads(create.content) - self.assertIn(type(obj), (dict,)) - - inspect = requests.delete(PODMAN_URL + f"/v1.40/networks/{ident}") - self.assertEqual(inspect.status_code, 204, inspect.content) - inspect = requests.get(PODMAN_URL + f"/v1.40/networks/{ident}") - self.assertEqual(inspect.status_code, 404, inspect.content) - - # network prune - prune_name = "Network_" + "".join(random.choice(string.ascii_letters) for i in range(10)) - prune_create = requests.post( - PODMAN_URL + "/v1.40/networks/create", json={"Name": prune_name} - ) - self.assertEqual(create.status_code, 201, prune_create.content) - - prune = requests.post(PODMAN_URL + "/v1.40/networks/prune") - self.assertEqual(prune.status_code, 200, prune.content) - obj = json.loads(prune.content) - self.assertTrue(prune_name in obj["NetworksDeleted"]) - - def test_volumes_compat(self): - name = "Volume_" + "".join(random.choice(string.ascii_letters) for i in range(10)) - - ls = requests.get(PODMAN_URL + "/v1.40/volumes") - self.assertEqual(ls.status_code, 200, ls.content) - - # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeList - required_keys = ( - "Volumes", - "Warnings", - ) - - obj = json.loads(ls.content) - self.assertIn(type(obj), (dict,)) - for k in required_keys: - self.assertIn(k, obj) - - create = requests.post(PODMAN_URL + "/v1.40/volumes/create", json={"Name": name}) - self.assertEqual(create.status_code, 201, create.content) - - # See https://docs.docker.com/engine/api/v1.40/#operation/VolumeCreate - # and https://docs.docker.com/engine/api/v1.40/#operation/VolumeInspect - required_keys = ( - "Name", - "Driver", - "Mountpoint", - "Labels", - "Scope", - "Options", - ) - - obj = json.loads(create.content) - self.assertIn(type(obj), (dict,)) - for k in required_keys: - self.assertIn(k, obj) - self.assertEqual(obj["Name"], name) - - inspect = requests.get(PODMAN_URL + f"/v1.40/volumes/{name}") - self.assertEqual(inspect.status_code, 200, inspect.content) - - obj = json.loads(create.content) - self.assertIn(type(obj), (dict,)) - for k in required_keys: - self.assertIn(k, obj) - - rm = requests.delete(PODMAN_URL + f"/v1.40/volumes/{name}") - self.assertEqual(rm.status_code, 204, rm.content) - - # recreate volume with data and then prune it - r = requests.post(PODMAN_URL + "/v1.40/volumes/create", json={"Name": name}) - self.assertEqual(create.status_code, 201, create.content) - create = json.loads(r.content) - with open(os.path.join(create["Mountpoint"], "test_prune"), "w") as file: - file.writelines(["This is a test\n", "This is a good test\n"]) - - prune = requests.post(PODMAN_URL + "/v1.40/volumes/prune") - self.assertEqual(prune.status_code, 200, prune.content) - payload = json.loads(prune.content) - self.assertIn(name, payload["VolumesDeleted"]) - self.assertGreater(payload["SpaceReclaimed"], 0) - - def test_version(self): - r = requests.get(PODMAN_URL + "/v1.40/version") - self.assertEqual(r.status_code, 200, r.content) - - r = requests.get(_url("/version")) - self.assertEqual(r.status_code, 200, r.content) - - def test_df_compat(self): - r = requests.get(PODMAN_URL + "/v1.40/system/df") - self.assertEqual(r.status_code, 200, r.content) - - obj = json.loads(r.content) - self.assertIn("Images", obj) - self.assertIn("Containers", obj) - self.assertIn("Volumes", obj) - self.assertIn("BuildCache", obj) - - def test_prune_compat(self): - name = "Ctnr_" + "".join(random.choice(string.ascii_letters) for i in range(10)) - - r = requests.post( - PODMAN_URL + f"/v1.40/containers/create?name={name}", - json={ - "Cmd": ["cp", "/etc/motd", "/motd.size_test"], - "Image": "alpine:latest", - "NetworkDisabled": True, - }, - ) - self.assertEqual(r.status_code, 201, r.text) - create = json.loads(r.text) - - r = requests.post(PODMAN_URL + f"/v1.40/containers/{create['Id']}/start") - self.assertEqual(r.status_code, 204, r.text) - - r = requests.post(PODMAN_URL + f"/v1.40/containers/{create['Id']}/wait") - self.assertEqual(r.status_code, 200, r.text) - wait = json.loads(r.text) - self.assertEqual(wait["StatusCode"], 0, wait["Error"]["Message"]) - - prune = requests.post(PODMAN_URL + "/v1.40/containers/prune") - self.assertEqual(prune.status_code, 200, prune.status_code) - prune_payload = json.loads(prune.text) - self.assertGreater(prune_payload["SpaceReclaimed"], 0) - self.assertIn(create["Id"], prune_payload["ContainersDeleted"]) - - # Delete any orphaned containers - r = requests.get(PODMAN_URL + "/v1.40/containers/json?all=true") - self.assertEqual(r.status_code, 200, r.text) - for ctnr in json.loads(r.text): - requests.delete(PODMAN_URL + f"/v1.40/containers/{ctnr['Id']}?force=true") - - prune = requests.post(PODMAN_URL + "/v1.40/images/prune") - self.assertEqual(prune.status_code, 200, prune.text) - prune_payload = json.loads(prune.text) - self.assertGreater(prune_payload["SpaceReclaimed"], 0) - - # FIXME need method to determine which image is going to be "pruned" to fix test - # TODO should handler be recursive when deleting images? - # self.assertIn(img["Id"], prune_payload["ImagesDeleted"][1]["Deleted"]) - - # FIXME (@vrothberg): I commented this line out during the `libimage` migration. - # It doesn't make sense to report anything to be deleted if the reclaimed space - # is zero. I think the test needs some rewrite. - # self.assertIsNotNone(prune_payload["ImagesDeleted"][1]["Deleted"]) - - def test_status_compat(self): - r = requests.post( - PODMAN_URL + "/v1.40/containers/create?name=topcontainer", - json={"Cmd": ["top"], "Image": "alpine:latest"}, - ) - self.assertEqual(r.status_code, 201, r.text) - payload = json.loads(r.text) - container_id = payload["Id"] - self.assertIsNotNone(container_id) - - r = requests.get( - PODMAN_URL + "/v1.40/containers/json", - params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, - ) - self.assertEqual(r.status_code, 200, r.text) - payload = json.loads(r.text) - self.assertEqual(payload[0]["Status"], "Created") - - r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/start") - self.assertEqual(r.status_code, 204, r.text) - - r = requests.get( - PODMAN_URL + "/v1.40/containers/json", - params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, - ) - self.assertEqual(r.status_code, 200, r.text) - payload = json.loads(r.text) - self.assertTrue(str(payload[0]["Status"]).startswith("Up")) - - r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/pause") - self.assertEqual(r.status_code, 204, r.text) - - r = requests.get( - PODMAN_URL + "/v1.40/containers/json", - params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, - ) - self.assertEqual(r.status_code, 200, r.text) - payload = json.loads(r.text) - self.assertTrue(str(payload[0]["Status"]).startswith("Up")) - self.assertTrue(str(payload[0]["Status"]).endswith("(Paused)")) - - r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/unpause") - self.assertEqual(r.status_code, 204, r.text) - r = requests.post(PODMAN_URL + f"/v1.40/containers/{container_id}/stop") - self.assertEqual(r.status_code, 204, r.text) - - r = requests.get( - PODMAN_URL + "/v1.40/containers/json", - params={"all": "true", "filters": f'{{"id":["{container_id}"]}}'}, - ) - self.assertEqual(r.status_code, 200, r.text) - payload = json.loads(r.text) - self.assertTrue(str(payload[0]["Status"]).startswith("Exited")) - - r = requests.delete(PODMAN_URL + f"/v1.40/containers/{container_id}") - self.assertEqual(r.status_code, 204, r.text) - - def test_pod_start_conflict(self): - """Verify issue #8865""" - - pod_name = list() - pod_name.append("Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10))) - pod_name.append("Pod_" + "".join(random.choice(string.ascii_letters) for i in range(10))) - - r = requests.post( - _url("/pods/create"), - json={ - "name": pod_name[0], - "no_infra": False, - "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], - }, - ) - self.assertEqual(r.status_code, 201, r.text) - r = requests.post( - _url("/containers/create"), - json={ - "pod": pod_name[0], - "image": "docker.io/alpine:latest", - "command": ["top"], - }, - ) - self.assertEqual(r.status_code, 201, r.text) - - r = requests.post( - _url("/pods/create"), - json={ - "name": pod_name[1], - "no_infra": False, - "portmappings": [{"host_ip": "127.0.0.1", "host_port": 8889, "container_port": 89}], - }, - ) - self.assertEqual(r.status_code, 201, r.text) - r = requests.post( - _url("/containers/create"), - json={ - "pod": pod_name[1], - "image": "docker.io/alpine:latest", - "command": ["top"], - }, - ) - self.assertEqual(r.status_code, 201, r.text) - - r = requests.post(_url(f"/pods/{pod_name[0]}/start")) - self.assertEqual(r.status_code, 200, r.text) - - r = requests.post(_url(f"/pods/{pod_name[1]}/start")) - self.assertEqual(r.status_code, 409, r.text) - - start = json.loads(r.text) - self.assertGreater(len(start["Errs"]), 0, r.text) - - def test_manifest_409(self): - r = requests.post(_url("/manifests/create"), params={"name": "ThisIsAnInvalidImage"}) - self.assertEqual(r.status_code, 400, r.text) - - def test_df(self): - r = requests.get(_url("/system/df")) - self.assertEqual(r.status_code, 200, r.text) - - -if __name__ == "__main__": - unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/rest_api/v1_test_rest_v1_0_0.py libpod-3.4.4+ds1/test/apiv2/rest_api/v1_test_rest_v1_0_0.py --- libpod-3.2.1+ds1/test/apiv2/rest_api/v1_test_rest_v1_0_0.py 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/rest_api/v1_test_rest_v1_0_0.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,238 +0,0 @@ -import json -import os -import shlex -import signal -import string -import subprocess -import sys -import time -import unittest -from collections.abc import Iterable -from multiprocessing import Process - -import requests -from dateutil.parser import parse - -PODMAN_URL = "http://localhost:8080" - - -def _url(path): - return PODMAN_URL + "/v1.0.0/libpod" + path - - -def podman(): - binary = os.getenv("PODMAN_BINARY") - if binary is None: - binary = "bin/podman" - return binary - - -def ctnr(path): - r = requests.get(_url("/containers/json?all=true")) - try: - ctnrs = json.loads(r.text) - except Exception as e: - sys.stderr.write("Bad container response: {}/{}".format(r.text, e)) - raise e - return path.format(ctnrs[0]["Id"]) - - -class TestApi(unittest.TestCase): - podman = None - - def setUp(self): - super().setUp() - if TestApi.podman.poll() is not None: - sys.stderr.write("podman service returned {}", TestApi.podman.returncode) - sys.exit(2) - requests.get(_url("/images/create?fromSrc=docker.io%2Falpine%3Alatest")) - # calling out to podman is easier than the API for running a container - subprocess.run( - [podman(), "run", "alpine", "/bin/ls"], - check=True, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - - @classmethod - def setUpClass(cls): - super().setUpClass() - - TestApi.podman = subprocess.Popen( - [ - podman(), - "system", - "service", - "tcp:localhost:8080", - "--log-level=debug", - "--time=0", - ], - shell=False, - stdin=subprocess.DEVNULL, - stdout=subprocess.DEVNULL, - stderr=subprocess.DEVNULL, - ) - time.sleep(2) - - @classmethod - def tearDownClass(cls): - TestApi.podman.terminate() - stdout, stderr = TestApi.podman.communicate(timeout=0.5) - if stdout: - print("\nService Stdout:\n" + stdout.decode("utf-8")) - if stderr: - print("\nService Stderr:\n" + stderr.decode("utf-8")) - - if TestApi.podman.returncode > 0: - sys.stderr.write("podman exited with error code {}\n".format(TestApi.podman.returncode)) - sys.exit(2) - - return super().tearDownClass() - - def test_info(self): - r = requests.get(_url("/info")) - self.assertEqual(r.status_code, 200) - self.assertIsNotNone(r.content) - _ = json.loads(r.text) - - def test_events(self): - r = requests.get(_url("/events?stream=false")) - self.assertEqual(r.status_code, 200, r.text) - self.assertIsNotNone(r.content) - for line in r.text.splitlines(): - obj = json.loads(line) - # Actor.ID is uppercase for compatibility - _ = obj["Actor"]["ID"] - - def test_containers(self): - r = requests.get(_url("/containers/json"), timeout=5) - self.assertEqual(r.status_code, 200, r.text) - obj = json.loads(r.text) - self.assertEqual(len(obj), 0) - - def test_containers_all(self): - r = requests.get(_url("/containers/json?all=true")) - self.assertEqual(r.status_code, 200, r.text) - self.validateObjectFields(r.text) - - def test_inspect_container(self): - r = requests.get(_url(ctnr("/containers/{}/json"))) - self.assertEqual(r.status_code, 200, r.text) - obj = self.validateObjectFields(r.content) - _ = parse(obj["Created"]) - - def test_stats(self): - r = requests.get(_url(ctnr("/containers/{}/stats?stream=false"))) - self.assertIn(r.status_code, (200, 409), r.text) - if r.status_code == 200: - self.validateObjectFields(r.text) - - def test_delete_containers(self): - r = requests.delete(_url(ctnr("/containers/{}"))) - self.assertEqual(r.status_code, 204, r.text) - - def test_stop_containers(self): - r = requests.post(_url(ctnr("/containers/{}/start"))) - self.assertIn(r.status_code, (204, 304), r.text) - - r = requests.post(_url(ctnr("/containers/{}/stop"))) - self.assertIn(r.status_code, (204, 304), r.text) - - def test_start_containers(self): - r = requests.post(_url(ctnr("/containers/{}/stop"))) - self.assertIn(r.status_code, (204, 304), r.text) - - r = requests.post(_url(ctnr("/containers/{}/start"))) - self.assertIn(r.status_code, (204, 304), r.text) - - def test_restart_containers(self): - r = requests.post(_url(ctnr("/containers/{}/start"))) - self.assertIn(r.status_code, (204, 304), r.text) - - r = requests.post(_url(ctnr("/containers/{}/restart")), timeout=5) - self.assertEqual(r.status_code, 204, r.text) - - def test_resize(self): - r = requests.post(_url(ctnr("/containers/{}/resize?h=43&w=80"))) - self.assertIn(r.status_code, (200, 409), r.text) - if r.status_code == 200: - self.assertIsNone(r.text) - - def test_attach_containers(self): - r = requests.post(_url(ctnr("/containers/{}/attach"))) - self.assertIn(r.status_code, (101, 409), r.text) - - def test_logs_containers(self): - r = requests.get(_url(ctnr("/containers/{}/logs?stdout=true"))) - self.assertEqual(r.status_code, 200, r.text) - - def test_post_create(self): - self.skipTest("TODO: create request body") - r = requests.post(_url("/containers/create?args=True")) - self.assertEqual(r.status_code, 200, r.text) - json.loads(r.text) - - def test_commit(self): - r = requests.post(_url(ctnr("/commit?container={}"))) - self.assertEqual(r.status_code, 200, r.text) - self.validateObjectFields(r.text) - - def test_images(self): - r = requests.get(_url("/images/json")) - self.assertEqual(r.status_code, 200, r.text) - self.validateObjectFields(r.content) - - def test_inspect_image(self): - r = requests.get(_url("/images/alpine/json")) - self.assertEqual(r.status_code, 200, r.text) - obj = self.validateObjectFields(r.content) - _ = parse(obj["Created"]) - - def test_delete_image(self): - r = requests.delete(_url("/images/alpine?force=true")) - self.assertEqual(r.status_code, 200, r.text) - json.loads(r.text) - - def test_pull(self): - r = requests.post(_url("/images/pull?reference=alpine"), timeout=5) - self.assertEqual(r.status_code, 200, r.text) - json.loads(r.text) - - def test_search(self): - # Had issues with this test hanging when repositories not happy - def do_search(): - r = requests.get(_url("/images/search?term=alpine"), timeout=5) - self.assertEqual(r.status_code, 200, r.text) - json.loads(r.text) - - search = Process(target=do_search) - search.start() - search.join(timeout=10) - self.assertFalse(search.is_alive(), "/images/search took too long") - - def test_ping(self): - r = requests.get(PODMAN_URL + "/_ping") - self.assertEqual(r.status_code, 200, r.text) - - r = requests.head(PODMAN_URL + "/_ping") - self.assertEqual(r.status_code, 200, r.text) - - r = requests.get(_url("/_ping")) - self.assertEqual(r.status_code, 200, r.text) - - r = requests.get(_url("/_ping")) - self.assertEqual(r.status_code, 200, r.text) - - -def validateObjectFields(self, buffer): - objs = json.loads(buffer) - if not isinstance(objs, dict): - for o in objs: - _ = o["Id"] - else: - _ = objs["Id"] - return objs - - -if __name__ == "__main__": - unittest.main() diff -Nru libpod-3.2.1+ds1/test/apiv2/test-apiv2 libpod-3.4.4+ds1/test/apiv2/test-apiv2 --- libpod-3.2.1+ds1/test/apiv2/test-apiv2 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/apiv2/test-apiv2 2021-12-26 00:45:51.000000000 +0000 @@ -327,7 +327,9 @@ die "Cannot start service on non-localhost ($HOST)" fi - $PODMAN_BIN --root $WORKDIR/server_root system service \ + echo $WORKDIR + $PODMAN_BIN --root $WORKDIR/server_root --syslog=true \ + system service \ --time 15 \ tcp:127.0.0.1:$PORT \ &> $WORKDIR/server.log & @@ -442,10 +444,10 @@ function wait_for_port() { local host=$1 # Probably "localhost" local port=$2 # Numeric port - local timeout=${3:-5} # Optional; default to 5 seconds + local _timeout=${3:-5} # Optional; default to 5 seconds # Wait - while [ $timeout -gt 0 ]; do + while [ $_timeout -gt 0 ]; do { exec 3<> /dev/tcp/$host/$port; } &>/dev/null && return sleep 1 _timeout=$(( $_timeout - 1 )) diff -Nru libpod-3.2.1+ds1/test/buildah-bud/apply-podman-deltas libpod-3.4.4+ds1/test/buildah-bud/apply-podman-deltas --- libpod-3.2.1+ds1/test/buildah-bud/apply-podman-deltas 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/buildah-bud/apply-podman-deltas 2021-12-26 00:45:51.000000000 +0000 @@ -56,22 +56,31 @@ done } -# skip: used to add a 'skip' to one specific test -function skip() { +# _skip: used to add a 'skip' or 'skip_if_remote' to one specific test +function _skip() { + local skip=$1; shift local reason=$1; shift # All further arguments are test names for t in "$@"; do if fgrep -qx "@test \"$t\" {" $BUD; then - $ECHO "@test \"$t\" : skip \"$reason\"" + $ECHO "@test \"$t\" : $skip \"$reason\"" t=${t//\//\\/} - sed -i -e "/^\@test \"$t\" {/ a \ \ skip \"$reason\"" $BUD + sed -i -e "/^\@test \"$t\" {/ a \ \ $skip \"$reason\"" $BUD else - warn "[skip] Did not find test \"$t\" in $BUD" + warn "[$skip] Did not find test \"$t\" in $BUD" fi done } +function skip() { + _skip "skip" "$@" +} + +function skip_if_remote() { + _skip "skip_if_remote" "$@" +} + # END handlers ############################################################################### # BEGIN user-customizable section @@ -79,14 +88,14 @@ # These are the hand-maintained exceptions. This is what you want to edit # or update as needed. # -# There are two directives you can use below: +# There are three directives you can use below: # # errmsg "old-message" "new-message" "test name" ["test name"...] # # This replaced "old-message" with "new-message" in @test "test name". # It is used when a podman error message differs from buildah's. # -# skip "reason" "test name" ["test name"...] +# [skip | skip_if_remote] "reason" "test name" ["test name"...] # # This adds a 'skip' statement as the first line of @test "test name". # It is used when a test does not work in podman, either for permanent @@ -126,6 +135,7 @@ ############################################################################### # BEGIN tests that don't make sense under podman due to fundamental differences + skip "N/A under podman" \ "bud-flags-order-verification" @@ -147,17 +157,38 @@ skip "FIXME FIXME FIXME: argument-order incompatible with podman" \ "bud-squash-hardlinks" -skip "FIXME FIXME FIXME we'll figure these out later" \ - "bud-multi-stage-nocache-nocommit" \ - "bud with --cgroup-parent" +skip "FIXME FIXME FIXME: this passes on Ed's laptop, fails in CI??" \ + "bud-multi-stage-nocache-nocommit" -# see https://github.com/containers/podman/pull/10147#issuecomment-832503633 -skip "FIXME FIXME FIXME podman save/load has been fixed (but not yet used in Buildah CI)" \ - "bud with --layers and --no-cache flags" +# This will probably never work: buildah and podman have incompatible defaults +# Documented in https://github.com/containers/podman/issues/10412 +skip "buildah runs with --cgroup-manager=cgroupfs, podman with systemd" \ + "bud with --cgroup-parent" ############################################################################### -# BEGIN tests which are skipped due to actual podman bugs. +# BEGIN tests which are skipped because they make no sense under podman-remote + +skip_if_remote "--target does not work with podman-remote" \ + "bud-target" +skip_if_remote "--runtime not meaningful under podman-remote" \ + "bud with --runtime and --runtime-flag" + +skip_if_remote "secret files not implemented under podman-remote" \ + "bud with containerfile secret" \ + "bud with containerfile secret accessed on second RUN" \ + "bud with containerfile secret options" + +skip_if_remote "volumes don't work with podman-remote" \ + "buildah bud --volume" \ + "buildah-bud-policy" + +# See podman #9890 for discussion +skip_if_remote "--stdin option will not be implemented in podman-remote" \ + "bud test no --stdin" + +############################################################################### +# BEGIN tests which are skipped due to actual podman-remote bugs. ############################################################################### # Done. diff -Nru libpod-3.2.1+ds1/test/buildah-bud/buildah-tests.diff libpod-3.4.4+ds1/test/buildah-bud/buildah-tests.diff --- libpod-3.2.1+ds1/test/buildah-bud/buildah-tests.diff 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/buildah-bud/buildah-tests.diff 2021-12-26 00:45:51.000000000 +0000 @@ -1,24 +1,76 @@ -From 90c93048c9f3bdedf39c3c5ce3216ee2518220b4 Mon Sep 17 00:00:00 2001 +From 5baab334cac9853c1d4bae9466d46dbbe5ff7158 Mon Sep 17 00:00:00 2001 From: Ed Santiago Date: Tue, 9 Feb 2021 17:28:05 -0700 Subject: [PATCH] tweaks for running buildah tests under podman Signed-off-by: Ed Santiago --- - tests/helpers.bash | 28 +++++++++++++++++++++++++--- - 1 file changed, 25 insertions(+), 3 deletions(-) + tests/helpers.bash | 71 +++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/tests/helpers.bash b/tests/helpers.bash -index b85f99d3..1a827cd7 100644 +index 166316d5..775d7c9b 100644 --- a/tests/helpers.bash +++ b/tests/helpers.bash -@@ -147,15 +147,37 @@ function run_buildah() { +@@ -43,6 +43,23 @@ EOF + ROOTDIR_OPTS="--root ${TESTDIR}/root --runroot ${TESTDIR}/runroot --storage-driver ${STORAGE_DRIVER}" + BUILDAH_REGISTRY_OPTS="--registries-conf ${TESTSDIR}/registries.conf --registries-conf-dir ${TESTDIR}/registries.d --short-name-alias-conf ${TESTDIR}/cache/shortnames.conf" + PODMAN_REGISTRY_OPTS="--registries-conf ${TESTSDIR}/registries.conf" ++ ++ PODMAN_SERVER_PID= ++ PODMAN_NATIVE="${PODMAN_BINARY} ${ROOTDIR_OPTS} ${PODMAN_REGISTRY_OPTS}" ++ if [[ -n "$REMOTE" ]]; then ++ PODMAN_NATIVE="${PODMAN_BINARY%%-remote} ${ROOTDIR_OPTS} ${PODMAN_REGISTRY_OPTS}" ++ # static CONTAINERS_CONF needed for capabilities test. As of 2021-07-01 ++ # no tests in bud.bats override this; if at some point any test does ++ # so, it will probably need to be skip_if_remote()d. ++ env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} $PODMAN_NATIVE system service --timeout=0 & ++ PODMAN_SERVER_PID=$! ++ local timeout=10 ++ while ((timeout > 0)); do ++ test -S /run/podman/podman.sock && return ++ sleep 0.2 ++ done ++ die "podman server never came up" ++ fi + } + + function starthttpd() { +@@ -84,6 +101,12 @@ function teardown(){ + function teardown_tests() { + stophttpd + ++ if [[ -n "$PODMAN_SERVER_PID" ]]; then ++ kill $PODMAN_SERVER_PID ++ wait $PODMAN_SERVER_PID ++ rm -f /run/podman/podman.sock ++ fi ++ + # Workaround for #1991 - buildah + overlayfs leaks mount points. + # Many tests leave behind /var/tmp/.../root/overlay and sub-mounts; + # let's find those and clean them up, otherwise 'rm -rf' fails. +@@ -156,7 +179,13 @@ function copy() { + } + + function podman() { +- command podman ${PODMAN_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" ++ echo "# ... podman $*" >&3 ++ ${PODMAN_BINARY} ${PODMAN_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" ++} ++ ++function podman-remote() { ++ echo "# ... podman-remote $*" >&3 ++ ${PODMAN_BINARY} ${ROOTDIR_OPTS} "$@" + } + + ################# +@@ -191,15 +220,40 @@ function run_buildah() { --retry) retry=3; shift;; # retry network flakes esac - + + local podman_or_buildah=${BUILDAH_BINARY} -+ local registry_opts=${BUILDAH_REGISTRY_OPTS} -+ if [[ $1 == "bud" || $1 == "build-using-dockerfile" ]]; then ++ local _opts="${ROOTDIR_OPTS} ${BUILDAH_REGISTRY_OPTS}" ++ if [[ $1 == "build" || $1 == "build-using-dockerfile" ]]; then + shift + # podman defaults to --layers=true; buildah to --false. + # If command line includes explicit --layers, leave it untouched, @@ -29,7 +81,10 @@ + set "build" "--force-rm=false" "--layers=false" "$@" + fi + podman_or_buildah=${PODMAN_BINARY} -+ registry_opts=${PODMAN_REGISTRY_OPTS} ++ _opts="${ROOTDIR_OPTS} ${PODMAN_REGISTRY_OPTS}" ++ if [[ -n "$REMOTE" ]]; then ++ _opts= ++ fi + + # podman always exits 125 where buildah exits 1 or 2 + case $expected_rc in @@ -41,17 +96,31 @@ # Remember command args, for possible use in later diagnostic messages - MOST_RECENT_BUILDAH_COMMAND="buildah $*" + MOST_RECENT_BUILDAH_COMMAND="$cmd_basename $*" - + while [ $retry -gt 0 ]; do retry=$(( retry - 1 )) - + # stdout is only emitted upon error; this echo is to help a debugger - echo "\$ $BUILDAH_BINARY $*" -- run timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${BUILDAH_BINARY} ${BUILDAH_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" +- run env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${BUILDAH_BINARY} ${BUILDAH_REGISTRY_OPTS} ${ROOTDIR_OPTS} "$@" + echo "\$ $cmd_basename $*" -+ run timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${podman_or_buildah} ${registry_opts} ${ROOTDIR_OPTS} "$@" ++ run env CONTAINERS_CONF=${CONTAINERS_CONF:-$(dirname ${BASH_SOURCE})/containers.conf} timeout --foreground --kill=10 $BUILDAH_TIMEOUT ${podman_or_buildah} ${_opts} "$@" # without "quotes", multiple lines are glommed together into one if [ -n "$output" ]; then echo "$output" --- -2.31.1 +@@ -477,3 +531,12 @@ function skip_if_no_docker() { + skip "this test needs actual docker, not podman-docker" + fi + } ++ ++#################### ++# skip_if_remote # (only applicable for podman) ++#################### ++function skip_if_remote() { ++ if [[ -n "$REMOTE" ]]; then ++ skip "${1:-test does not work with podman-remote}" ++ fi ++} +-- +2.25.1 + diff -Nru libpod-3.2.1+ds1/test/buildah-bud/make-new-buildah-diffs libpod-3.4.4+ds1/test/buildah-bud/make-new-buildah-diffs --- libpod-3.2.1+ds1/test/buildah-bud/make-new-buildah-diffs 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/buildah-bud/make-new-buildah-diffs 2021-12-26 00:45:51.000000000 +0000 @@ -56,8 +56,6 @@ die "Internal error: I thought I checked for squashed commits, but still see $patch2" fi -# All looks good. Now write that patch into its proper place in the -# podman repo. The sed and tac mess strips trailing whitespace and -# empty lines; we need to do this to pass github CI checks. -sed -e 's/ \+$//' <0001-*.patch |\ - tac | sed -e '/./,$!d' | tac >| ../test/buildah-bud/buildah-tests.diff +# All looks good. We can now copy that patch into its proper place in the +# podman repo. +cp 0001-*.patch ../test/buildah-bud/buildah-tests.diff diff -Nru libpod-3.2.1+ds1/test/buildah-bud/run-buildah-bud-tests libpod-3.4.4+ds1/test/buildah-bud/run-buildah-bud-tests --- libpod-3.2.1+ds1/test/buildah-bud/run-buildah-bud-tests 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/buildah-bud/run-buildah-bud-tests 2021-12-26 00:45:51.000000000 +0000 @@ -69,8 +69,7 @@ # If remote, start server & change path if [[ "${PODBIN_NAME:-}" = "remote" ]]; then REMOTE=1 - echo "$ME: remote tests are not working yet" >&2 - exit 1 + PODMAN_BINARY+="-remote" fi function die() { @@ -145,9 +144,11 @@ # the set of patches (set -x;git tag $BASE_TAG) - # Build buildah + # Build buildah and the copy helper failhint="error building buildah. This should never happen." (set -x;make bin/buildah) + failhint="error building buildah's copy helper. This should never happen." + (set -x;make bin/copy) # The upcoming patch may fail. Before we try it, create a helper script # for a developer to push a new set of diffs to podman-land. @@ -212,6 +213,8 @@ (set -x;sudo env TMPDIR=/var/tmp \ PODMAN_BINARY=$PODMAN_BINARY \ + REMOTE=$REMOTE \ BUILDAH_BINARY=$(pwd)/bin/buildah \ + COPY_BINARY=$(pwd)/bin/copy \ bats "${bats_filter[@]}" tests/bud.bats) fi diff -Nru libpod-3.2.1+ds1/test/compose/mount_and_label/docker-compose.yml libpod-3.4.4+ds1/test/compose/mount_and_label/docker-compose.yml --- libpod-3.2.1+ds1/test/compose/mount_and_label/docker-compose.yml 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/compose/mount_and_label/docker-compose.yml 2021-12-26 00:45:51.000000000 +0000 @@ -6,5 +6,7 @@ - '5000:5000' volumes: - /tmp/data:/data:ro + security_opt: + - label=disable labels: - "io.podman=the_best" diff -Nru libpod-3.2.1+ds1/test/compose/test-compose libpod-3.4.4+ds1/test/compose/test-compose --- libpod-3.2.1+ds1/test/compose/test-compose 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/compose/test-compose 2021-12-26 00:45:51.000000000 +0000 @@ -174,12 +174,12 @@ if [ $curl_rc -ne 0 ]; then _show_ok 0 "$testname - curl (port $port) failed with status $curl_rc" echo "# podman ps -a:" - $PODMAN_BIN --root $WORKDIR/root --runroot $WORKDIR/runroot ps -a + $PODMAN_BIN --storage-driver=vfs --root $WORKDIR/root --runroot $WORKDIR/runroot ps -a if type -p ss; then echo "# ss -tulpn:" ss -tulpn echo "# podman unshare --rootless-cni ss -tulpn:" - $PODMAN_BIN --root $WORKDIR/root --runroot $WORKDIR/runroot unshare --rootless-cni ss -tulpn + $PODMAN_BIN --storage-driver=vfs --root $WORKDIR/root --runroot $WORKDIR/runroot unshare --rootless-cni ss -tulpn fi echo "# cat $WORKDIR/server.log:" cat $WORKDIR/server.log @@ -216,6 +216,7 @@ $PODMAN_BIN \ --log-level debug \ + --storage-driver=vfs \ --root $WORKDIR/root \ --runroot $WORKDIR/runroot \ --cgroup-manager=systemd \ @@ -243,6 +244,7 @@ function podman() { echo "\$ podman $*" >>$WORKDIR/output.log output=$($PODMAN_BIN \ + --storage-driver=vfs \ --root $WORKDIR/root \ --runroot $WORKDIR/runroot \ "$@") diff -Nru libpod-3.2.1+ds1/test/compose/uptwice/docker-compose.yml libpod-3.4.4+ds1/test/compose/uptwice/docker-compose.yml --- libpod-3.2.1+ds1/test/compose/uptwice/docker-compose.yml 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/compose/uptwice/docker-compose.yml 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,5 @@ +version: '3' +services: + app: + build: . + command: sleep 10002 diff -Nru libpod-3.2.1+ds1/test/compose/uptwice/Dockerfile libpod-3.4.4+ds1/test/compose/uptwice/Dockerfile --- libpod-3.2.1+ds1/test/compose/uptwice/Dockerfile 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/compose/uptwice/Dockerfile 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,2 @@ +FROM alpine +WORKDIR /app diff -Nru libpod-3.2.1+ds1/test/compose/uptwice/tests.sh libpod-3.4.4+ds1/test/compose/uptwice/tests.sh --- libpod-3.2.1+ds1/test/compose/uptwice/tests.sh 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/compose/uptwice/tests.sh 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,4 @@ +# -*- bash -*- + +sed -i -e 's/10001/10002/' docker-compose.yml +docker-compose up -d diff -Nru libpod-3.2.1+ds1/test/e2e/attach_test.go libpod-3.4.4+ds1/test/e2e/attach_test.go --- libpod-3.2.1+ds1/test/e2e/attach_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/attach_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman attach", func() { @@ -36,48 +37,48 @@ It("podman attach to bogus container", func() { session := podmanTest.Podman([]string{"attach", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman attach to non-running container", func() { session := podmanTest.Podman([]string{"create", "--name", "test1", "-i", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"attach", "test1"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(125)) + Expect(results).Should(Exit(125)) }) It("podman container attach to non-running container", func() { session := podmanTest.Podman([]string{"container", "create", "--name", "test1", "-i", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"container", "attach", "test1"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(125)) + Expect(results).Should(Exit(125)) }) It("podman attach to multiple containers", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainer("test2") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"attach", "test1", "test2"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(125)) + Expect(results).Should(Exit(125)) }) It("podman attach to a running container", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test", ALPINE, "/bin/sh", "-c", "while true; do echo test; sleep 1; done"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"attach", "test"}) time.Sleep(2 * time.Second) @@ -88,11 +89,11 @@ It("podman attach to the latest container", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", ALPINE, "/bin/sh", "-c", "while true; do echo test1; sleep 1; done"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--name", "test2", ALPINE, "/bin/sh", "-c", "while true; do echo test2; sleep 1; done"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := "-l" if IsRemote() { @@ -108,7 +109,7 @@ It("podman attach to a container with --sig-proxy set to false", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test", ALPINE, "/bin/sh", "-c", "while true; do echo test; sleep 1; done"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"attach", "--sig-proxy=false", "test"}) time.Sleep(2 * time.Second) diff -Nru libpod-3.2.1+ds1/test/e2e/build/envwithtab/Dockerfile libpod-3.4.4+ds1/test/e2e/build/envwithtab/Dockerfile --- libpod-3.2.1+ds1/test/e2e/build/envwithtab/Dockerfile 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/build/envwithtab/Dockerfile 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,3 @@ +FROM alpine + +ENV TEST=" t" diff -Nru libpod-3.2.1+ds1/test/e2e/build_test.go libpod-3.4.4+ds1/test/e2e/build_test.go --- libpod-3.2.1+ds1/test/e2e/build_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/build_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman build", func() { @@ -42,7 +43,7 @@ podmanTest.AddImageToRWStore(ALPINE) session := podmanTest.Podman([]string{"build", "--pull-never", "build/basicalpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) iid := session.OutputToStringArray()[len(session.OutputToStringArray())-1] @@ -55,14 +56,14 @@ session = podmanTest.Podman([]string{"rmi", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman build with logfile", func() { logfile := filepath.Join(podmanTest.TempDir, "logfile") session := podmanTest.Podman([]string{"build", "--pull-never", "--tag", "test", "--logfile", logfile, "build/basicalpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Verify that OS and Arch are being set inspect := podmanTest.Podman([]string{"inspect", "test"}) @@ -77,7 +78,7 @@ session = podmanTest.Podman([]string{"rmi", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) // If the context directory is pointing at a file and not a directory, @@ -85,7 +86,7 @@ It("podman build context directory a file", func() { session := podmanTest.Podman([]string{"build", "--pull-never", "build/context_dir_a_file"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) // Check that builds with different values for the squash options @@ -93,47 +94,47 @@ It("podman build basic alpine with squash", func() { session := podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/squash/Dockerfile.squash-a", "-t", "test-squash-a:latest", "build/squash"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Check for two layers Expect(len(strings.Fields(session.OutputToString()))).To(Equal(2)) session = podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/squash/Dockerfile.squash-b", "--squash", "-t", "test-squash-b:latest", "build/squash"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-b"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Check for three layers Expect(len(strings.Fields(session.OutputToString()))).To(Equal(3)) session = podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/squash/Dockerfile.squash-c", "--squash", "-t", "test-squash-c:latest", "build/squash"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-c"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Check for two layers Expect(len(strings.Fields(session.OutputToString()))).To(Equal(2)) session = podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/squash/Dockerfile.squash-c", "--squash-all", "-t", "test-squash-d:latest", "build/squash"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.RootFS.Layers}}", "test-squash-d"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Check for one layers Expect(len(strings.Fields(session.OutputToString()))).To(Equal(1)) session = podmanTest.Podman([]string{"rm", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman build Containerfile locations", func() { @@ -168,7 +169,7 @@ session.WaitWithDefaultTimeout() // Then - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(strings.Fields(session.OutputToString())). To(ContainElement("scratch")) }) @@ -188,7 +189,7 @@ session := podmanTest.Podman([]string{"build", "--pull-never", "build/basicalpine", "--iidfile", targetFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) id, _ := ioutil.ReadFile(targetFile) // Verify that id is correct @@ -204,11 +205,11 @@ "build", "--pull-never", "-f", "build/basicalpine/Containerfile.path", "-t", "test-path", }) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "test-path", "printenv", "PATH"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stdoutLines := session.OutputToStringArray() Expect(stdoutLines[0]).Should(Equal(path)) }) @@ -228,7 +229,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"build", "--pull-never", "--http-proxy", "--file", dockerfilePath, podmanTest.TempDir}) session.Wait(120) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("1.2.3.4") Expect(ok).To(BeTrue()) os.Unsetenv("http_proxy") @@ -237,7 +238,7 @@ It("podman build and check identity", func() { session := podmanTest.Podman([]string{"build", "--pull-never", "-f", "build/basicalpine/Containerfile.path", "--no-cache", "-t", "test", "build/basicalpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Verify that OS and Arch are being set inspect := podmanTest.Podman([]string{"image", "inspect", "--format", "{{ index .Config.Labels }}", "test"}) @@ -282,7 +283,7 @@ session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "-f", "Containerfile", targetSubPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("/test/dummy") Expect(ok).To(BeTrue()) }) @@ -324,7 +325,7 @@ session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "-f", "subdir/Containerfile", "."}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman remote test .dockerignore", func() { @@ -388,7 +389,7 @@ session := podmanTest.Podman([]string{"build", "-t", "test", "."}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("/testfilter/dummy1") Expect(ok).NotTo(BeTrue()) ok, _ = session.GrepString("/testfilter/dummy2") @@ -448,7 +449,7 @@ session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", targetSubPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("/test/dummy") Expect(ok).To(BeTrue()) ok, _ = session.GrepString("/test/emptyDir") @@ -481,7 +482,7 @@ session.WaitWithDefaultTimeout() // Then - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(strings.Fields(session.OutputToString())). To(ContainElement(ALPINE)) Expect(strings.Fields(session.OutputToString())). @@ -507,7 +508,7 @@ }) session.WaitWithDefaultTimeout() // Then - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // When session = podmanTest.Podman([]string{ @@ -515,7 +516,7 @@ }) session.WaitWithDefaultTimeout() // Then - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // When session = podmanTest.Podman([]string{ @@ -523,7 +524,7 @@ }) session.WaitWithDefaultTimeout() // Then - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // When session = podmanTest.Podman([]string{ @@ -531,7 +532,7 @@ }) session.WaitWithDefaultTimeout() // Then - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman build --timestamp flag", func() { @@ -543,7 +544,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "--timestamp", "0", "--file", containerfilePath, podmanTest.TempDir}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"image", "inspect", "--format", "{{ .Created }}", "test"}) inspect.WaitWithDefaultTimeout() @@ -561,7 +562,7 @@ session := podmanTest.Podman([]string{"build", "--log-rusage", "--pull-never", targetPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("(system)")) Expect(session.OutputToString()).To(ContainSubstring("(user)")) Expect(session.OutputToString()).To(ContainSubstring("(elapsed)")) @@ -574,7 +575,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "--arch", "foo", "--os", "bar", "--file", containerfilePath, podmanTest.TempDir}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"image", "inspect", "--format", "{{ .Architecture }}", "test"}) inspect.WaitWithDefaultTimeout() @@ -593,7 +594,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "--os", "windows", "--file", containerfilePath, podmanTest.TempDir}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"image", "inspect", "--format", "{{ .Architecture }}", "test"}) inspect.WaitWithDefaultTimeout() @@ -604,4 +605,38 @@ Expect(inspect.OutputToString()).To(Equal("windows")) }) + + It("podman build device test", func() { + if _, err := os.Lstat("/dev/fuse"); err != nil { + Skip(fmt.Sprintf("test requires stat /dev/fuse to work: %v", err)) + } + containerfile := fmt.Sprintf(`FROM %s +RUN ls /dev/fuse`, ALPINE) + containerfilePath := filepath.Join(podmanTest.TempDir, "Containerfile") + err := ioutil.WriteFile(containerfilePath, []byte(containerfile), 0755) + Expect(err).To(BeNil()) + session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "--file", containerfilePath, podmanTest.TempDir}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + + session = podmanTest.Podman([]string{"build", "--pull-never", "--device", "/dev/fuse", "-t", "test", "--file", containerfilePath, podmanTest.TempDir}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("podman build device rename test", func() { + SkipIfRootless("rootless builds do not currently support renaming devices") + containerfile := fmt.Sprintf(`FROM %s +RUN ls /dev/test1`, ALPINE) + containerfilePath := filepath.Join(podmanTest.TempDir, "Containerfile") + err := ioutil.WriteFile(containerfilePath, []byte(containerfile), 0755) + Expect(err).To(BeNil()) + session := podmanTest.Podman([]string{"build", "--pull-never", "-t", "test", "--file", containerfilePath, podmanTest.TempDir}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + + session = podmanTest.Podman([]string{"build", "--pull-never", "--device", "/dev/zero:/dev/test1", "-t", "test", "--file", containerfilePath, podmanTest.TempDir}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/checkpoint_test.go libpod-3.4.4+ds1/test/e2e/checkpoint_test.go --- libpod-3.2.1+ds1/test/e2e/checkpoint_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/checkpoint_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,15 +1,20 @@ package integration import ( + "fmt" "net" "os" "os/exec" "strings" + "time" + "github.com/containers/podman/v3/pkg/checkpoint/crutils" "github.com/containers/podman/v3/pkg/criu" . "github.com/containers/podman/v3/test/utils" + "github.com/containers/podman/v3/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) func getRunString(input []string) []string { @@ -45,7 +50,7 @@ Skip("OCI runtime does not support checkpoint/restore") } - if !criu.CheckForCriu() { + if !criu.CheckForCriu(criu.MinCriuVersion) { Skip("CRIU is missing or too old.") } // Only Fedora 29 and newer has a new enough selinux-policy and @@ -80,20 +85,26 @@ localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "checkpoint", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) + inspect := podmanTest.Podman([]string{"inspect", cid}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + inspectOut := inspect.InspectContainerToJSON() + Expect(inspectOut[0].State.Checkpointed).To(BeTrue()) + result = podmanTest.Podman([]string{"container", "restore", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) }) @@ -102,19 +113,19 @@ localRunString := getRunString([]string{"--name", "test_name", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) result = podmanTest.Podman([]string{"container", "restore", "test_name"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) }) @@ -123,36 +134,36 @@ localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "checkpoint", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) result = podmanTest.Podman([]string{"pause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) result = podmanTest.Podman([]string{"container", "restore", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) result = podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(2)) + Expect(result).Should(Exit(2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) result = podmanTest.Podman([]string{"rm", "-f", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -161,36 +172,36 @@ localRunString := getRunString([]string{"--name", "first", ALPINE, "top"}) session1 := podmanTest.Podman(localRunString) session1.WaitWithDefaultTimeout() - Expect(session1.ExitCode()).To(Equal(0)) + Expect(session1).Should(Exit(0)) localRunString = getRunString([]string{"--name", "second", ALPINE, "top"}) session2 := podmanTest.Podman(localRunString) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) result := podmanTest.Podman([]string{"container", "checkpoint", "-l"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeTrue()) Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse()) result = podmanTest.Podman([]string{"container", "restore", "-l"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -198,36 +209,36 @@ localRunString := getRunString([]string{"--name", "first", ALPINE, "top"}) session1 := podmanTest.Podman(localRunString) session1.WaitWithDefaultTimeout() - Expect(session1.ExitCode()).To(Equal(0)) + Expect(session1).Should(Exit(0)) localRunString = getRunString([]string{"--name", "second", ALPINE, "top"}) session2 := podmanTest.Podman(localRunString) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) result := podmanTest.Podman([]string{"container", "checkpoint", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) ps := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.LineInOutputContains(session1.OutputToString())).To(BeFalse()) Expect(ps.LineInOutputContains(session2.OutputToString())).To(BeFalse()) result = podmanTest.Podman([]string{"container", "restore", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited"))) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -237,22 +248,25 @@ localRunString := getRunString([]string{redis}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + if !WaitContainerReady(podmanTest, cid, "Ready to accept connections", 20, 1) { + Fail("Container failed to get ready") + } IP := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"}) IP.WaitWithDefaultTimeout() - Expect(IP.ExitCode()).To(Equal(0)) + Expect(IP).Should(Exit(0)) // Open a network connection to the redis server - conn, err := net.Dial("tcp", IP.OutputToString()+":6379") - if err != nil { - os.Exit(1) - } + conn, err := net.DialTimeout("tcp4", IP.OutputToString()+":6379", time.Duration(3)*time.Second) + Expect(err).To(BeNil()) + // This should fail as the container has established TCP connections result := podmanTest.Podman([]string{"container", "checkpoint", "-l"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -260,7 +274,7 @@ result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "--tcp-established"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -268,7 +282,7 @@ result = podmanTest.Podman([]string{"container", "restore", "-l"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -276,13 +290,13 @@ result = podmanTest.Podman([]string{"container", "restore", "-l", "--tcp-established"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) conn.Close() @@ -292,14 +306,14 @@ localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Checkpoint container, but leave it running result := podmanTest.Podman([]string{"container", "checkpoint", "--leave-running", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Make sure it is still running Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -308,7 +322,7 @@ result = podmanTest.Podman([]string{"container", "stop", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -316,13 +330,13 @@ result = podmanTest.Podman([]string{"container", "restore", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -330,20 +344,20 @@ localRunString := getRunString([]string{"--name", "test_name", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) IPBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"}) IPBefore.WaitWithDefaultTimeout() - Expect(IPBefore.ExitCode()).To(Equal(0)) + Expect(IPBefore).Should(Exit(0)) MACBefore := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"}) MACBefore.WaitWithDefaultTimeout() - Expect(MACBefore.ExitCode()).To(Equal(0)) + Expect(MACBefore).Should(Exit(0)) result := podmanTest.Podman([]string{"container", "checkpoint", "test_name"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) @@ -352,11 +366,11 @@ IPAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.IPAddress}}"}) IPAfter.WaitWithDefaultTimeout() - Expect(IPAfter.ExitCode()).To(Equal(0)) + Expect(IPAfter).Should(Exit(0)) MACAfter := podmanTest.Podman([]string{"inspect", "-l", "--format={{.NetworkSettings.MacAddress}}"}) MACAfter.WaitWithDefaultTimeout() - Expect(MACAfter.ExitCode()).To(Equal(0)) + Expect(MACAfter).Should(Exit(0)) // Check that IP address did not change between checkpointing and restoring Expect(IPBefore.OutputToString()).To(Equal(IPAfter.OutputToString())) @@ -364,13 +378,13 @@ // Check that MAC address did not change between checkpointing and restoring Expect(MACBefore.OutputToString()).To(Equal(MACAfter.OutputToString())) - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -380,7 +394,7 @@ localRunString := getRunString([]string{"--rm", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) cid := session.OutputToString() fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -390,7 +404,7 @@ // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -405,20 +419,120 @@ result = podmanTest.Podman([]string{"container", "restore", "-i", fileName, "-n", "restore_again", "--ignore-static-ip"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Remove exported checkpoint + os.Remove(fileName) + }) + // This test does the same steps which are necessary for migrating + // a container from one host to another + It("podman checkpoint container with export and different compression algorithms", func() { + localRunString := getRunString([]string{"--rm", ALPINE, "top"}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + cid := session.OutputToString() + fileName := "/tmp/checkpoint-" + cid + ".tar" + + // Checkpoint with the default algorithm + result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container + result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Checkpoint with the zstd algorithm + result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "--compress", "zstd"}) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container + result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Checkpoint with the none algorithm + result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "-c", "none"}) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container + result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Checkpoint with the gzip algorithm + result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "-c", "gzip"}) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container + result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Checkpoint with the non-existing algorithm + result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName, "-c", "non-existing"}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(125)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + result = podmanTest.Podman([]string{"rm", "-fa"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -431,7 +545,7 @@ localRunString := getRunString([]string{"--rm", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) cid := session.OutputToString() fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -439,15 +553,15 @@ // Change the container's root file-system result := podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo test" + cid + "test > /test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "rm /etc/motd"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"diff", "-l"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("C /etc")) Expect(result.OutputToString()).To(ContainSubstring("A /test.output")) Expect(result.OutputToString()).To(ContainSubstring("D /etc/motd")) @@ -457,7 +571,7 @@ result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -465,7 +579,7 @@ result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -473,12 +587,12 @@ // Verify the changes to the container's root file-system result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("test" + cid + "test")) result = podmanTest.Podman([]string{"diff", "-l"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("C /etc")) Expect(result.OutputToString()).To(ContainSubstring("A /test.output")) Expect(result.OutputToString()).To(ContainSubstring("D /etc/motd")) @@ -492,7 +606,7 @@ localRunString := getRunString([]string{"--rm", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) cid := session.OutputToString() fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -500,13 +614,13 @@ // Change the container's root file-system result := podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo test" + cid + "test > /test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Checkpoint the container result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -514,7 +628,7 @@ result = podmanTest.Podman([]string{"container", "restore", "--ignore-rootfs", "-i", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -522,7 +636,7 @@ // Verify the changes to the container's root file-system result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(1)) + Expect(result).Should(Exit(1)) Expect(result.ErrorToString()).To(ContainSubstring("cat: can't open '/test.output': No such file or directory")) // Remove exported checkpoint @@ -533,7 +647,7 @@ localRunString := getRunString([]string{"--rm", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) cid := session.OutputToString() fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -541,13 +655,13 @@ // Change the container's root file-system result := podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo test" + cid + "test > /test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Checkpoint the container result = podmanTest.Podman([]string{"container", "checkpoint", "--ignore-rootfs", "-l", "-e", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -555,7 +669,7 @@ result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -563,7 +677,7 @@ // Verify the changes to the container's root file-system result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(1)) + Expect(result).Should(Exit(1)) Expect(result.ErrorToString()).To(ContainSubstring("cat: can't open '/test.output': No such file or directory")) // Remove exported checkpoint @@ -575,7 +689,7 @@ localRunString := getRunString([]string{"--rm", ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) cid := session.OutputToString() fileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -584,7 +698,7 @@ result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -592,7 +706,7 @@ result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -600,11 +714,11 @@ // Exec in the container result = podmanTest.Podman([]string{"exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"exec", "-l", "cat", "/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(cid)) // Remove exported checkpoint @@ -633,20 +747,20 @@ // As the container has been started with '--rm' it will be completely // cleaned up after checkpointing. - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) result = podmanTest.Podman([]string{"container", "restore", "-i", fileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -659,7 +773,7 @@ "build", "-f", "build/basicalpine/Containerfile.volume", "-t", "test-cr-volume", }) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Start the container localRunString := getRunString([]string{ @@ -671,7 +785,7 @@ }) session = podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) cid := session.OutputToString() @@ -681,28 +795,28 @@ "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume0/test.output", }) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Add file in volume1 result = podmanTest.Podman([]string{ "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume1/test.output", }) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Add file in volume2 result = podmanTest.Podman([]string{ "exec", "-l", "/bin/sh", "-c", "echo " + cid + " > /volume2/test.output", }) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz" // Checkpoint the container result = podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", checkpointFileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -717,12 +831,12 @@ // Remove named volume session = podmanTest.Podman([]string{"volume", "rm", "my-test-vol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Restoring container result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) @@ -730,19 +844,19 @@ // Validate volume0 content result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume0/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(cid)) // Validate volume1 content result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume1/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(cid)) // Validate volume2 content result = podmanTest.Podman([]string{"exec", "-l", "cat", "/volume2/test.output"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(cid)) // Remove exported checkpoint @@ -756,27 +870,27 @@ localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "checkpoint", "-P", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) result = podmanTest.Podman([]string{"container", "restore", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) }) @@ -788,7 +902,7 @@ localRunString := getRunString([]string{ALPINE, "top"}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() preCheckpointFileName := "/tmp/pre-checkpoint-" + cid + ".tar.gz" checkpointFileName := "/tmp/checkpoint-" + cid + ".tar.gz" @@ -796,30 +910,250 @@ result := podmanTest.Podman([]string{"container", "checkpoint", "-P", "-e", preCheckpointFileName, cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) result = podmanTest.Podman([]string{"container", "checkpoint", "--with-previous", "-e", checkpointFileName, cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Exited")) result = podmanTest.Podman([]string{"rm", "-f", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) result = podmanTest.Podman([]string{"container", "restore", "-i", checkpointFileName, "--import-previous", preCheckpointFileName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) os.Remove(checkpointFileName) os.Remove(preCheckpointFileName) }) + + It("podman checkpoint and restore container with different port mappings", func() { + randomPort, err := utils.GetRandomPort() + Expect(err).ShouldNot(HaveOccurred()) + localRunString := getRunString([]string{"-p", fmt.Sprintf("%d:6379", randomPort), "--rm", redis}) + session := podmanTest.Podman(localRunString) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + cid := session.OutputToString() + fileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + if !WaitContainerReady(podmanTest, cid, "Ready to accept connections", 20, 1) { + Fail("Container failed to get ready") + } + + fmt.Fprintf(os.Stderr, "Trying to connect to redis server at localhost:%d", randomPort) + // Open a network connection to the redis server via initial port mapping + conn, err := net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", randomPort), time.Duration(3)*time.Second) + Expect(err).ShouldNot(HaveOccurred()) + conn.Close() + + // Checkpoint the container + result := podmanTest.Podman([]string{"container", "checkpoint", "-l", "-e", fileName}) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Restore container with different port mapping + newRandomPort, err := utils.GetRandomPort() + Expect(err).ShouldNot(HaveOccurred()) + result = podmanTest.Podman([]string{"container", "restore", "-p", fmt.Sprintf("%d:6379", newRandomPort), "-i", fileName}) + result.WaitWithDefaultTimeout() + + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + // Open a network connection to the redis server via initial port mapping + // This should fail + conn, err = net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", randomPort), time.Duration(3)*time.Second) + Expect(err.Error()).To(ContainSubstring("connection refused")) + // Open a network connection to the redis server via new port mapping + fmt.Fprintf(os.Stderr, "Trying to reconnect to redis server at localhost:%d", newRandomPort) + conn, err = net.DialTimeout("tcp4", fmt.Sprintf("localhost:%d", newRandomPort), time.Duration(3)*time.Second) + Expect(err).ShouldNot(HaveOccurred()) + conn.Close() + + result = podmanTest.Podman([]string{"rm", "-fa"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(0)) + + // Remove exported checkpoint + os.Remove(fileName) + }) + + namespaceCombination := []string{ + "cgroup,ipc,net,uts,pid", + "cgroup,ipc,net,uts", + "cgroup,ipc,net", + "cgroup,ipc", + "ipc,net,uts,pid", + "ipc,net,uts", + "ipc,net", + "net,uts,pid", + "net,uts", + "uts,pid", + } + for _, share := range namespaceCombination { + testName := fmt.Sprintf( + "podman checkpoint and restore container out of and into pod (%s)", + share, + ) + It(testName, func() { + if !criu.CheckForCriu(criu.PodCriuVersion) { + Skip("CRIU is missing or too old.") + } + if !crutils.CRRuntimeSupportsPodCheckpointRestore(podmanTest.OCIRuntime) { + Skip("runtime does not support pod restore") + } + // Create a pod + session := podmanTest.Podman([]string{ + "pod", + "create", + "--share", + share, + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + podID := session.OutputToString() + + session = podmanTest.Podman([]string{ + "run", + "-d", + "--rm", + "--pod", + podID, + ALPINE, + "top", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + cid := session.OutputToString() + + fileName := "/tmp/checkpoint-" + cid + ".tar.gz" + + // Checkpoint the container + result := podmanTest.Podman([]string{ + "container", + "checkpoint", + "-e", + fileName, + cid, + }) + result.WaitWithDefaultTimeout() + + // As the container has been started with '--rm' it will be completely + // cleaned up after checkpointing. + Expect(result).To(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + // Remove the pod and create a new pod + result = podmanTest.Podman([]string{ + "pod", + "rm", + podID, + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + + // First create a pod with different shared namespaces. + // Restore should fail + + wrongShare := share[:strings.LastIndex(share, ",")] + + session = podmanTest.Podman([]string{ + "pod", + "create", + "--share", + wrongShare, + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + podID = session.OutputToString() + + // Restore container with different port mapping + result = podmanTest.Podman([]string{ + "container", + "restore", + "--pod", + podID, + "-i", + fileName, + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(125)) + Expect(result.ErrorToString()).To(ContainSubstring("does not share the")) + + // Remove the pod and create a new pod + result = podmanTest.Podman([]string{ + "pod", + "rm", + podID, + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + + session = podmanTest.Podman([]string{ + "pod", + "create", + "--share", + share, + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + podID = session.OutputToString() + + // Restore container with different port mapping + result = podmanTest.Podman([]string{ + "container", + "restore", + "--pod", + podID, + "-i", + fileName, + }) + result.WaitWithDefaultTimeout() + + Expect(result).To(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) + Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up")) + + result = podmanTest.Podman([]string{ + "rm", + "-f", + result.OutputToString(), + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) + Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + + result = podmanTest.Podman([]string{ + "pod", + "rm", + "-fa", + }) + result.WaitWithDefaultTimeout() + Expect(result).To(Exit(0)) + + // Remove exported checkpoint + os.Remove(fileName) + }) + } }) diff -Nru libpod-3.2.1+ds1/test/e2e/commit_test.go libpod-3.4.4+ds1/test/e2e/commit_test.go --- libpod-3.2.1+ds1/test/e2e/commit_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/commit_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman commit", func() { @@ -41,7 +42,7 @@ session := podmanTest.Podman([]string{"commit", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() @@ -56,7 +57,7 @@ session := podmanTest.Podman([]string{"commit", "test1", "a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "localhost/a:latest"}) check.WaitWithDefaultTimeout() @@ -71,7 +72,7 @@ session := podmanTest.Podman([]string{"container", "commit", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"image", "inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() @@ -86,7 +87,7 @@ session := podmanTest.Podman([]string{"commit", "-f", "docker", "--message", "testing-commit", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() @@ -101,7 +102,7 @@ session := podmanTest.Podman([]string{"commit", "--author", "snoopy", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() @@ -112,12 +113,12 @@ It("podman commit container with change flag", func() { test := podmanTest.Podman([]string{"run", "--name", "test1", "-d", ALPINE, "ls"}) test.WaitWithDefaultTimeout() - Expect(test.ExitCode()).To(Equal(0)) + Expect(test).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session := podmanTest.Podman([]string{"commit", "--change", "LABEL=image=blue", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() @@ -135,12 +136,12 @@ It("podman commit container with change flag and JSON entrypoint with =", func() { test := podmanTest.Podman([]string{"run", "--name", "test1", "-d", ALPINE, "ls"}) test.WaitWithDefaultTimeout() - Expect(test.ExitCode()).To(Equal(0)) + Expect(test).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session := podmanTest.Podman([]string{"commit", "--change", `ENTRYPOINT ["foo", "bar=baz"]`, "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() @@ -154,25 +155,25 @@ It("podman commit container with change CMD flag", func() { test := podmanTest.Podman([]string{"run", "--name", "test1", "-d", ALPINE, "ls"}) test.WaitWithDefaultTimeout() - Expect(test.ExitCode()).To(Equal(0)) + Expect(test).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session := podmanTest.Podman([]string{"commit", "--change", "CMD a b c", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Config.Cmd}}", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("sh -c a b c")) session = podmanTest.Podman([]string{"commit", "--change", "CMD=[\"a\",\"b\",\"c\"]", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Config.Cmd}}", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("sh -c"))) }) @@ -183,25 +184,25 @@ session := podmanTest.Podman([]string{"commit", "--pause=false", "test1", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) }) It("podman commit with volumes mounts and no include-volumes", func() { s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"}) s.WaitWithDefaultTimeout() - Expect(s.ExitCode()).To(Equal(0)) + Expect(s).Should(Exit(0)) c := podmanTest.Podman([]string{"commit", "test1", "newimage"}) c.WaitWithDefaultTimeout() - Expect(c.ExitCode()).To(Equal(0)) + Expect(c).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "newimage"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) image := inspect.InspectImageJSON() _, ok := image[0].Config.Volumes["/foo"] Expect(ok).To(BeFalse()) @@ -213,36 +214,36 @@ SkipIfRemote("--testing Remote Volumes") s := podmanTest.Podman([]string{"run", "--name", "test1", "-v", "/tmp:/foo", "alpine", "date"}) s.WaitWithDefaultTimeout() - Expect(s.ExitCode()).To(Equal(0)) + Expect(s).Should(Exit(0)) c := podmanTest.Podman([]string{"commit", "--include-volumes", "test1", "newimage"}) c.WaitWithDefaultTimeout() - Expect(c.ExitCode()).To(Equal(0)) + Expect(c).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "newimage"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) image := inspect.InspectImageJSON() _, ok := image[0].Config.Volumes["/foo"] Expect(ok).To(BeTrue()) r := podmanTest.Podman([]string{"run", "newimage"}) r.WaitWithDefaultTimeout() - Expect(r.ExitCode()).To(Equal(0)) + Expect(r).Should(Exit(0)) }) It("podman commit container check env variables", func() { s := podmanTest.Podman([]string{"run", "--name", "test1", "-e", "TEST=1=1-01=9.01", "-it", "alpine", "true"}) s.WaitWithDefaultTimeout() - Expect(s.ExitCode()).To(Equal(0)) + Expect(s).Should(Exit(0)) c := podmanTest.Podman([]string{"commit", "test1", "newimage"}) c.WaitWithDefaultTimeout() - Expect(c.ExitCode()).To(Equal(0)) + Expect(c).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "newimage"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) image := inspect.InspectImageJSON() envMap := make(map[string]bool) @@ -271,7 +272,7 @@ session := podmanTest.Podman([]string{"commit", "test1", "foobar.com/test1-image:latest", "--iidfile", targetFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) id, _ := ioutil.ReadFile(targetFile) check := podmanTest.Podman([]string{"inspect", "foobar.com/test1-image:latest"}) @@ -288,20 +289,20 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--secret", "mysecret", "--name", "secr", ALPINE, "cat", "/run/secrets/mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) session = podmanTest.Podman([]string{"commit", "secr", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "foobar.com/test1-image:latest", "cat", "/run/secrets/mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) @@ -313,19 +314,55 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=env", "--name", "secr", ALPINE, "printenv", "mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) session = podmanTest.Podman([]string{"commit", "secr", "foobar.com/test1-image:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "foobar.com/test1-image:latest", "printenv", "mysecret"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(Not(ContainSubstring(secretsString))) }) + + It("podman commit adds exposed ports", func() { + name := "testcon" + s := podmanTest.Podman([]string{"run", "--name", name, "-p", "8585:80", ALPINE, "true"}) + s.WaitWithDefaultTimeout() + Expect(s).Should(Exit(0)) + + newImageName := "newimage" + c := podmanTest.Podman([]string{"commit", name, newImageName}) + c.WaitWithDefaultTimeout() + Expect(c).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", newImageName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + images := inspect.InspectImageJSON() + Expect(images).To(HaveLen(1)) + Expect(images[0].Config.ExposedPorts).To(HaveKey("80/tcp")) + + name = "testcon2" + s = podmanTest.Podman([]string{"run", "--name", name, "-d", nginx}) + s.WaitWithDefaultTimeout() + Expect(s).Should(Exit(0)) + + newImageName = "newimage2" + c = podmanTest.Podman([]string{"commit", name, newImageName}) + c.WaitWithDefaultTimeout() + Expect(c).Should(Exit(0)) + + inspect = podmanTest.Podman([]string{"inspect", newImageName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + images = inspect.InspectImageJSON() + Expect(images).To(HaveLen(1)) + Expect(images[0].Config.ExposedPorts).To(HaveKey("80/tcp")) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/common_test.go libpod-3.4.4+ds1/test/e2e/common_test.go --- libpod-3.2.1+ds1/test/e2e/common_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/common_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -264,6 +264,11 @@ if rootless.IsRootless() { storageFs = ROOTLESS_STORAGE_FS } + if os.Getenv("STORAGE_FS") != "" { + storageFs = os.Getenv("STORAGE_FS") + storageOptions = "--storage-driver " + storageFs + } + p := &PodmanTestIntegration{ PodmanTest: PodmanTest{ PodmanBinary: podmanBinary, @@ -321,11 +326,11 @@ if _, err := os.Stat(destName); os.IsNotExist(err) { pull := p.PodmanNoCache([]string{"pull", image}) pull.Wait(440) - Expect(pull.ExitCode()).To(Equal(0)) + Expect(pull).Should(Exit(0)) save := p.PodmanNoCache([]string{"save", "-o", destName, image}) save.Wait(90) - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) fmt.Printf("\n") } else { fmt.Printf(" already exists.\n") @@ -645,9 +650,13 @@ return os.Geteuid() != 0 } +func isCgroupsV1() bool { + return !CGROUPSV2 +} + func SkipIfCgroupV1(reason string) { checkReason(reason) - if !CGROUPSV2 { + if isCgroupsV1() { Skip(reason) } } @@ -676,7 +685,7 @@ // PodmanAsUser is the exec call to podman on the filesystem with the specified uid/gid and environment func (p *PodmanTestIntegration) PodmanAsUser(args []string, uid, gid uint32, cwd string, env []string) *PodmanSessionIntegration { - podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil) + podmanSession := p.PodmanAsUserBase(args, uid, gid, cwd, env, false, false, nil, nil) return &PodmanSessionIntegration{podmanSession} } @@ -811,7 +820,7 @@ func (p *PodmanTestIntegration) removeCNINetwork(name string) { session := p.Podman([]string{"network", "rm", "-f", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeNumerically("<=", 1)) + Expect(session.ExitCode()).To(BeNumerically("<=", 1), "Exit code must be 0 or 1") } func (p *PodmanSessionIntegration) jq(jqCommand string) (string, error) { @@ -841,3 +850,18 @@ output := session.OutputToStringArray() return output[len(output)-1] } + +func writeYaml(content string, fileName string) error { + f, err := os.Create(fileName) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString(content) + if err != nil { + return err + } + + return nil +} diff -Nru libpod-3.2.1+ds1/test/e2e/config/containers-journald.conf libpod-3.4.4+ds1/test/e2e/config/containers-journald.conf --- libpod-3.2.1+ds1/test/e2e/config/containers-journald.conf 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/config/containers-journald.conf 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,4 @@ +[containers] + +log_driver="journald" +log_tag="{{.ImageName}}" diff -Nru libpod-3.2.1+ds1/test/e2e/config/containers-netns2.conf libpod-3.4.4+ds1/test/e2e/config/containers-netns2.conf --- libpod-3.2.1+ds1/test/e2e/config/containers-netns2.conf 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/config/containers-netns2.conf 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,3 @@ +[containers] + +netns = "bridge" diff -Nru libpod-3.2.1+ds1/test/e2e/config/containers-netns.conf libpod-3.4.4+ds1/test/e2e/config/containers-netns.conf --- libpod-3.2.1+ds1/test/e2e/config/containers-netns.conf 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/config/containers-netns.conf 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,3 @@ +[containers] + +netns = "host" diff -Nru libpod-3.2.1+ds1/test/e2e/config.go libpod-3.4.4+ds1/test/e2e/config.go --- libpod-3.2.1+ds1/test/e2e/config.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/config.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,7 +2,7 @@ var ( redis = "quay.io/libpod/redis:alpine" - fedoraMinimal = "quay.io/libpod/fedora-minimal:latest" + fedoraMinimal = "registry.fedoraproject.org/fedora-minimal:34" ALPINE = "quay.io/libpod/alpine:latest" ALPINELISTTAG = "quay.io/libpod/alpine:3.10.2" ALPINELISTDIGEST = "quay.io/libpod/alpine@sha256:fa93b01658e3a5a1686dc3ae55f170d8de487006fb53a28efcd12ab0710a2e5f" diff -Nru libpod-3.2.1+ds1/test/e2e/container_create_volume_test.go libpod-3.4.4+ds1/test/e2e/container_create_volume_test.go --- libpod-3.2.1+ds1/test/e2e/container_create_volume_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/container_create_volume_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,127 @@ +package integration + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +func buildDataVolumeImage(pTest *PodmanTestIntegration, image, data, dest string) { + // Create a dummy file for data volume + dummyFile := filepath.Join(pTest.TempDir, data) + err := ioutil.WriteFile(dummyFile, []byte(data), 0644) + Expect(err).To(BeNil()) + + // Create a data volume container image but no CMD binary in it + containerFile := fmt.Sprintf(`FROM scratch +CMD doesnotexist.sh +ADD %s %s/ +VOLUME %s/`, data, dest, dest) + pTest.BuildImage(containerFile, image, "false") +} + +func createContainersConfFile(pTest *PodmanTestIntegration) { + configPath := filepath.Join(pTest.TempDir, "containers.conf") + containersConf := []byte(fmt.Sprintf("[containers]\nprepare_volume_on_create = true\n")) + err := ioutil.WriteFile(configPath, containersConf, os.ModePerm) + Expect(err).To(BeNil()) + + // Set custom containers.conf file + os.Setenv("CONTAINERS_CONF", configPath) + if IsRemote() { + pTest.RestartRemoteService() + } +} + +func checkDataVolumeContainer(pTest *PodmanTestIntegration, image, cont, dest, data string) { + create := pTest.Podman([]string{"create", "--name", cont, image}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + inspect := pTest.InspectContainer(cont) + Expect(len(inspect)).To(Equal(1)) + Expect(len(inspect[0].Mounts)).To(Equal(1)) + Expect(inspect[0].Mounts[0].Destination).To(Equal(dest)) + + mntName, mntSource := inspect[0].Mounts[0].Name, inspect[0].Mounts[0].Source + + volList := pTest.Podman([]string{"volume", "list", "--quiet"}) + volList.WaitWithDefaultTimeout() + Expect(volList).Should(Exit(0)) + Expect(len(volList.OutputToStringArray())).To(Equal(1)) + Expect(volList.OutputToStringArray()[0]).To(Equal(mntName)) + + // Check the mount source directory + files, err := ioutil.ReadDir(mntSource) + Expect(err).To(BeNil()) + + if data == "" { + Expect(len(files)).To(Equal(0)) + } else { + Expect(len(files)).To(Equal(1)) + Expect(files[0].Name()).To(Equal(data)) + } +} + +var _ = Describe("Podman create data volume", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + os.Unsetenv("CONTAINERS_CONF") + }) + + It("podman create with volume data copy turned off", func() { + imgName, volData, volDest := "dataimg", "dummy", "/test" + + buildDataVolumeImage(podmanTest, imgName, volData, volDest) + + // Create a container with the default containers.conf and + // check that the volume is not copied from the image. + checkDataVolumeContainer(podmanTest, imgName, "ctr-nocopy", volDest, "") + }) + + It("podman create with volume data copy turned on", func() { + imgName, volData, volDest := "dataimg", "dummy", "/test" + + buildDataVolumeImage(podmanTest, imgName, volData, volDest) + + // Create a container with the custom containers.conf and + // check that the volume is copied from the image. + createContainersConfFile(podmanTest) + + checkDataVolumeContainer(podmanTest, imgName, "ctr-copy", volDest, volData) + }) + + It("podman run with volume data copy turned on", func() { + // Create a container with the custom containers.conf and + // check that the container is run successfully + createContainersConfFile(podmanTest) + + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "echo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) +}) diff -Nru libpod-3.2.1+ds1/test/e2e/container_inspect_test.go libpod-3.4.4+ds1/test/e2e/container_inspect_test.go --- libpod-3.2.1+ds1/test/e2e/container_inspect_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/container_inspect_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -3,10 +3,12 @@ import ( "os" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/annotations" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman container inspect", func() { @@ -36,10 +38,34 @@ const testContainer = "container-inspect-test-1" setup := podmanTest.RunTopContainer(testContainer) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := podmanTest.InspectContainer(testContainer) Expect(data[0].Config.Annotations[annotations.ContainerManager]). To(Equal(annotations.ContainerManagerLibpod)) }) + + It("podman inspect shows exposed ports", func() { + name := "testcon" + session := podmanTest.Podman([]string{"run", "-d", "--stop-timeout", "0", "--expose", "8787/udp", "--name", name, ALPINE, "sleep", "inf"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + data := podmanTest.InspectContainer(name) + + Expect(data).To(HaveLen(1)) + Expect(data[0].NetworkSettings.Ports). + To(Equal(map[string][]define.InspectHostPort{"8787/udp": nil})) + }) + + It("podman inspect shows exposed ports on image", func() { + name := "testcon" + session := podmanTest.Podman([]string{"run", "-d", "--expose", "8989", "--name", name, nginx}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + data := podmanTest.InspectContainer(name) + Expect(data).To(HaveLen(1)) + Expect(data[0].NetworkSettings.Ports). + To(Equal(map[string][]define.InspectHostPort{"80/tcp": nil, "8989/tcp": nil})) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/containers_conf_test.go libpod-3.4.4+ds1/test/e2e/containers_conf_test.go --- libpod-3.2.1+ds1/test/e2e/containers_conf_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/containers_conf_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run", func() { @@ -47,12 +48,12 @@ //containers.conf is set to "nofile=500:500" session := podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "ulimit", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("500")) session = podmanTest.Podman([]string{"run", "--rm", "--ulimit", "nofile=2048:2048", fedoraMinimal, "ulimit", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("2048")) }) @@ -60,7 +61,7 @@ //containers.conf default env includes foo session := podmanTest.Podman([]string{"run", ALPINE, "printenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("foo=bar")) }) @@ -68,7 +69,7 @@ //containers.conf devices includes notone session := podmanTest.Podman([]string{"run", "--device", "/dev/null:/dev/bar", ALPINE, "ls", "/dev"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("bar")) Expect(session.OutputToString()).To(ContainSubstring("notone")) }) @@ -77,7 +78,7 @@ //containers.conf default sets shm-size=201k, which ends up as 200k session := podmanTest.Podman([]string{"run", ALPINE, "grep", "shm", "/proc/self/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("size=200k")) }) @@ -85,7 +86,7 @@ SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") cap := podmanTest.Podman([]string{"run", ALPINE, "grep", "CapEff", "/proc/self/status"}) cap.WaitWithDefaultTimeout() - Expect(cap.ExitCode()).To(Equal(0)) + Expect(cap).Should(Exit(0)) os.Setenv("CONTAINERS_CONF", "config/containers-ns.conf") if IsRemote() { @@ -93,7 +94,7 @@ } session := podmanTest.Podman([]string{"run", BB, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).ToNot(Equal(cap.OutputToString())) }) @@ -102,7 +103,7 @@ setup.WaitWithDefaultTimeout() result := podmanTest.Podman([]string{"top", "test1", "capeff"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("SYS_CHROOT")) Expect(result.OutputToString()).To(ContainSubstring("NET_RAW")) }) @@ -116,7 +117,7 @@ setup.WaitWithDefaultTimeout() result := podmanTest.Podman([]string{"container", "top", "test1", "capeff"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).ToNot(ContainSubstring("SYS_CHROOT")) Expect(result.OutputToString()).ToNot(ContainSubstring("NET_RAW")) }) @@ -130,7 +131,7 @@ //containers.conf default ipcns to default to host session := podmanTest.Podman([]string{"run", ALPINE, "ls", "-l", nspath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fields := strings.Split(session.OutputToString(), " ") ctrNS := strings.TrimSuffix(fields[len(fields)-1], "\n") @@ -167,16 +168,34 @@ verifyNSHandling("/proc/self/ns/cgroup", "--cgroupns") }) + It("using journald for container with container log_tag", func() { + SkipIfInContainer("journalctl inside a container doesn't work correctly") + os.Setenv("CONTAINERS_CONF", "config/containers-journald.conf") + if IsRemote() { + podmanTest.RestartRemoteService() + } + logc := podmanTest.Podman([]string{"run", "-d", ALPINE, "sh", "-c", "echo podman; sleep 0.1; echo podman; sleep 0.1; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).Should(Exit(0)) + cid := logc.OutputToString() + + wait := podmanTest.Podman([]string{"wait", cid}) + wait.WaitWithDefaultTimeout() + Expect(wait).Should(Exit(0)) + + cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_TAG", fmt.Sprintf("CONTAINER_ID_FULL=%s", cid)) + out, err := cmd.CombinedOutput() + Expect(err).To(BeNil()) + Expect(string(out)).To(ContainSubstring("alpine")) + }) + It("podman containers.conf additionalvolumes", func() { conffile := filepath.Join(podmanTest.TempDir, "container.conf") tempdir, err = CreateTempDirInTempDir() - if err != nil { - os.Exit(1) - } + Expect(err).To(BeNil()) + err := ioutil.WriteFile(conffile, []byte(fmt.Sprintf("[containers]\nvolumes=[\"%s:%s:Z\",]\n", tempdir, tempdir)), 0755) - if err != nil { - os.Exit(1) - } + Expect(err).To(BeNil()) os.Setenv("CONTAINERS_CONF", conffile) if IsRemote() { @@ -184,55 +203,55 @@ } result := podmanTest.Podman([]string{"run", ALPINE, "ls", tempdir}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman run containers.conf sysctl test", func() { //containers.conf is set to "net.ipv4.ping_group_range=0 1000" session := podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "cat", "/proc/sys/net/ipv4/ping_group_range"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("1000")) // Ignore containers.conf setting if --net=host session = podmanTest.Podman([]string{"run", "--rm", "--net", "host", fedoraMinimal, "cat", "/proc/sys/net/ipv4/ping_group_range"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).ToNot((ContainSubstring("1000"))) }) It("podman run containers.conf search domain", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("search foobar.com") }) It("podman run add dns server", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("server 1.2.3.4") }) It("podman run add dns option", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("options debug") }) It("podman run containers.conf remove all search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) }) It("podman run use containers.conf search domain", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("search")).To(BeTrue()) Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) @@ -242,10 +261,16 @@ It("podman run containers.conf timezone", func() { //containers.conf timezone set to Pacific/Honolulu - session := podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"}) + session := podmanTest.Podman([]string{"run", "--tz", "", ALPINE, "date", "+'%H %Z'"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("HST")) + + // verify flag still overrides + session = podmanTest.Podman([]string{"run", "--tz", "EST", ALPINE, "date", "+'%H %Z'"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("EST")) }) It("podman run containers.conf umask", func() { @@ -256,14 +281,14 @@ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0002")) }) It("podman set network cmd options slirp options to allow host loopback", func() { session := podmanTest.Podman([]string{"run", "--network", "slirp4netns", ALPINE, "ping", "-c1", "10.0.2.2"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman-remote test localcontainers.conf versus remote containers.conf", func() { @@ -276,13 +301,13 @@ // env session := podmanTest.Podman([]string{"run", ALPINE, "printenv", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("bar")) // dns-search, server, options session = podmanTest.Podman([]string{"run", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("search")).To(BeTrue()) Expect(session.OutputToString()).To(ContainSubstring("foobar.com")) Expect(session.OutputToString()).To(ContainSubstring("1.2.3.4")) @@ -291,32 +316,32 @@ // sysctls session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "cat", "/proc/sys/net/ipv4/ping_group_range"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("1000")) // shm-size session = podmanTest.Podman([]string{"run", ALPINE, "grep", "shm", "/proc/self/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("size=200k")) // ulimits session = podmanTest.Podman([]string{"run", "--rm", fedoraMinimal, "ulimit", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("500")) // Configuration that comes from remote client // Timezone session = podmanTest.Podman([]string{"run", ALPINE, "date", "+'%H %Z'"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Or(ContainSubstring("EST"), ContainSubstring("EDT"))) // Umask session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0022")) }) @@ -324,7 +349,7 @@ //containers.conf is set to "run.oci.keep_original_groups=1" session := podmanTest.Podman([]string{"create", "--rm", "--name", "test", fedoraMinimal}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--format", "{{ .Config.Annotations }}", "test"}) inspect.WaitWithDefaultTimeout() @@ -339,18 +364,18 @@ session = podmanTest.Podman([]string{"run", "-dt", "--add-host", "test1:127.0.0.1", "--no-hosts=false", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run with no-hosts=true /etc/hosts does not include hostname", func() { session := podmanTest.Podman([]string{"run", "--rm", "--name", "test", ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("test"))) session = podmanTest.Podman([]string{"run", "--rm", "--name", "test", "--no-hosts=false", ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("test")) }) @@ -369,7 +394,7 @@ session := podmanTest.Podman([]string{"info", "--format", "{{.Host.Security.SECCOMPProfilePath}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(profile)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/cp_test.go libpod-3.4.4+ds1/test/e2e/cp_test.go --- libpod-3.2.1+ds1/test/e2e/cp_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/cp_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) // NOTE: Only smoke tests. The system tests (i.e., "./test/system/*") take @@ -55,7 +56,7 @@ // Create a container. NOTE that container mustn't be running for copying. session := podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) name := session.OutputToString() // Copy TO the container. @@ -68,7 +69,7 @@ // The file will now be created (and written to). session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Copy FROM the container. @@ -79,11 +80,11 @@ session = podmanTest.Podman([]string{"cp", name + ":foo", destFile.Name()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Now make sure the content matches. roundtripContent, err := ioutil.ReadFile(destFile.Name()) @@ -106,17 +107,17 @@ // Create a container. NOTE that container mustn't be running for copying. session := podmanTest.Podman([]string{"create", "--pid=host", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) name := session.OutputToString() session = podmanTest.Podman([]string{"start", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // The file will now be created (and written to). session = podmanTest.Podman([]string{"cp", srcFile.Name(), name + ":foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Copy FROM the container. @@ -127,7 +128,7 @@ session = podmanTest.Podman([]string{"cp", name + ":foo", destFile.Name()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Now make sure the content matches. roundtripContent, err := ioutil.ReadFile(destFile.Name()) @@ -150,25 +151,25 @@ session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) name := session.OutputToString() session = podmanTest.Podman([]string{"exec", name, "ln", "-s", "/tmp", "/test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"cp", "--pause=false", srcFile.Name(), name + ":/test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", name, "cat", "/tmp/" + filepath.Base(srcFile.Name())}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(string(originalContent))) session = podmanTest.Podman([]string{"exec", name, "cat", "/test/" + filepath.Base(srcFile.Name())}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(string(originalContent))) }) @@ -188,21 +189,21 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"volume", "create", "data"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "data:/data", "--name", "container1", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"cp", srcFile.Name(), "container1" + ":/data/file.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Now get the volume's mount point, read the file and make // sure the contents match. session = podmanTest.Podman([]string{"volume", "inspect", "data", "--format", "{{.Mountpoint}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) volumeMountPoint := session.OutputToString() copiedContent, err := ioutil.ReadFile(filepath.Join(volumeMountPoint, "file.txt")) @@ -221,19 +222,19 @@ setup := podmanTest.RunTopContainer("testctr") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "testctr", "adduser", "-S", "testuser"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "-u", "testuser", "testctr", "touch", "/tmp/testfile"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"cp", "--pause=false", "testctr:/tmp/testfile", srcFile.Name()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // owner of the file copied to local machine is not testuser u, err := user.Current() @@ -245,12 +246,12 @@ session = podmanTest.Podman([]string{"cp", "--pause=false", srcFile.Name(), "testctr:testfile2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // owner of the file copied to a container is the root user session = podmanTest.Podman([]string{"exec", "-it", "testctr", "ls", "-l", "testfile2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("root")) }) @@ -259,18 +260,18 @@ container := "copyroottohost" session := podmanTest.RunTopContainer(container) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", container, "touch", "/dummy.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) session = podmanTest.Podman([]string{"cp", container + ":/", tmpDir}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cmd := exec.Command("ls", "-la", tmpDir) output, err := cmd.Output() diff -Nru libpod-3.2.1+ds1/test/e2e/create_staticip_test.go libpod-3.4.4+ds1/test/e2e/create_staticip_test.go --- libpod-3.2.1+ds1/test/e2e/create_staticip_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/create_staticip_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman create with --ip flag", func() { @@ -52,7 +53,7 @@ SkipIfRootless("--ip not supported without network in rootless mode") result := podmanTest.Podman([]string{"create", "--name", "test", "--ip", "203.0.113.124", ALPINE, "ls"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"start", "test"}) result.WaitWithDefaultTimeout() @@ -67,17 +68,17 @@ result.WaitWithDefaultTimeout() // Rootless static ip assignment without network should error if rootless.IsRootless() { - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) } else { - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"start", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"logs", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(ip + "/16")) } }) @@ -87,19 +88,19 @@ ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test1", "--ip", ip, ALPINE, "sleep", "999"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test2", "--ip", ip, ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"start", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // race prevention: wait until IP address is assigned for i := 0; i < 5; i++ { result = podmanTest.Podman([]string{"inspect", "--format", "{{.NetworkSettings.IPAddress}}", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) if result.OutputToString() != "" { break } diff -Nru libpod-3.2.1+ds1/test/e2e/create_staticmac_test.go libpod-3.4.4+ds1/test/e2e/create_staticmac_test.go --- libpod-3.2.1+ds1/test/e2e/create_staticmac_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/create_staticmac_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run with --mac-address flag", func() { @@ -40,9 +41,9 @@ result := podmanTest.Podman([]string{"run", "--mac-address", "92:d0:c6:0a:29:34", ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() if rootless.IsRootless() { - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) } else { - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:0a:29:34")) } }) @@ -52,11 +53,11 @@ session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"run", "--network", net, "--mac-address", "92:d0:c6:00:29:34", ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("92:d0:c6:00:29:34")) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/create_test.go libpod-3.4.4+ds1/test/e2e/create_test.go --- libpod-3.2.1+ds1/test/e2e/create_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/create_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman create", func() { @@ -42,7 +43,7 @@ session := podmanTest.Podman([]string{"create", "--name", "local_image_test", ALPINE, "ls"}) session.WaitWithDefaultTimeout() cid := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) check := podmanTest.Podman([]string{"inspect", "local_image_test"}) @@ -54,39 +55,53 @@ It("podman create container based on a remote image", func() { session := podmanTest.Podman([]string{"create", BB_GLIBC, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) }) It("podman container create container based on a remote image", func() { - session := podmanTest.Podman([]string{"container", "create", BB_GLIBC, "ls"}) + containerCreate := podmanTest.Podman([]string{"container", "create", BB_GLIBC, "ls"}) + containerCreate.WaitWithDefaultTimeout() + Expect(containerCreate).Should(Exit(0)) + + lock := GetPortLock("5000") + defer lock.Unlock() + session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(podmanTest.NumberOfContainers()).To(Equal(1)) + Expect(session).Should(Exit(0)) + + if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { + Skip("Cannot start docker registry.") + } + + create := podmanTest.Podman([]string{"container", "create", "--tls-verify=false", ALPINE}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(3)) }) It("podman create using short options", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) }) It("podman create using existing name", func() { session := podmanTest.Podman([]string{"create", "--name=foo", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session = podmanTest.Podman([]string{"create", "--name=foo", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman create adds annotation", func() { session := podmanTest.Podman([]string{"create", "--annotation", "HELLO=WORLD", "--name", "annotate_test", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) check := podmanTest.Podman([]string{"inspect", "annotate_test"}) @@ -100,24 +115,24 @@ It("podman create --entrypoint command", func() { session := podmanTest.Podman([]string{"create", "--name", "entrypoint_test", "--entrypoint", "/bin/foobar", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) result := podmanTest.Podman([]string{"inspect", "entrypoint_test", "--format", "{{.Config.Entrypoint}}"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("/bin/foobar")) }) It("podman create --entrypoint \"\"", func() { session := podmanTest.Podman([]string{"create", "--entrypoint", "", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) result := podmanTest.Podman([]string{"inspect", session.OutputToString(), "--format", "{{.Config.Entrypoint}}"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("")) }) @@ -125,12 +140,12 @@ jsonString := `[ "/bin/foo", "-c"]` session := podmanTest.Podman([]string{"create", "--name", "entrypoint_json", "--entrypoint", jsonString, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) result := podmanTest.Podman([]string{"inspect", "entrypoint_json", "--format", "{{.Config.Entrypoint}}"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("/bin/foo -c")) }) @@ -144,15 +159,15 @@ session := podmanTest.Podman([]string{"create", "--name", "test", "--mount", "type=bind,src=" + vol1 + ",target=/myvol1,z", "--mount", "type=bind,src=" + vol2 + ",target=/myvol2,z", ALPINE, "touch", "/myvol2/foo.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).ToNot(ContainSubstring("cannot touch")) }) @@ -167,35 +182,35 @@ os.Mkdir(mountPath, 0755) session := podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/create/test rw")) session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test_ro", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,ro", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test_ro"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logs", "test_ro"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/create/test ro")) session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test_shared", "--mount", fmt.Sprintf("type=bind,src=%s,target=/create/test,shared", mountPath), ALPINE, "grep", "/create/test", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test_shared"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logs", "test_shared"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches := session.GrepString("/create/test") Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("rw")) @@ -205,20 +220,20 @@ os.Mkdir(mountPath, 0755) session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test_tmpfs", "--mount", "type=tmpfs,target=/create/test", ALPINE, "grep", "/create/test", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test_tmpfs"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logs", "test_tmpfs"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/create/test rw,nosuid,nodev,relatime - tmpfs")) }) It("podman create --pod automatically", func() { session := podmanTest.Podman([]string{"create", "--pod", "new:foobar", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) check.WaitWithDefaultTimeout() @@ -231,7 +246,7 @@ // if used together. session := podmanTest.Podman([]string{"create", "--pod", "foo", "--pod-id-file", "bar", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) @@ -244,11 +259,11 @@ // Now, let's create a pod with --pod-id-file. session = podmanTest.Podman([]string{"pod", "create", "--pod-id-file", podIDFile, "--name", podName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "inspect", podName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) podData := session.InspectPodToJSON() @@ -256,7 +271,7 @@ // some checks to make sure it's working as expected. session = podmanTest.Podman([]string{"create", "--pod-id-file", podIDFile, "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ctrJSON := podmanTest.InspectContainer(ctrName) Expect(podData.ID).To(Equal(ctrJSON[0].Pod)) // Make sure the container's pod matches the pod's ID @@ -266,7 +281,7 @@ name := "test101" create := podmanTest.Podman([]string{"create", "--name", name, redis}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) ctrJSON := podmanTest.InspectContainer(name) Expect(len(ctrJSON)).To(Equal(1)) @@ -282,62 +297,62 @@ session = podmanTest.Podman([]string{"create", "--pull", "always", "--name=foo", "testimage:00000000"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman create using image list by tag", func() { session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINELISTTAG}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64ID)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.ImageName}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG)) }) It("podman create using image list by digest", func() { session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64ID)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.ImageName}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) }) It("podman create using image list instance by digest", func() { session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64ID)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.ImageName}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) }) It("podman create using cross-arch image list instance by digest", func() { session := podmanTest.Podman([]string{"create", "--pull=always", "--arch=arm64", "--name=foo", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Image}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64ID)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.ImageName}}", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) }) @@ -351,11 +366,11 @@ SkipIfRemote("SigPolicy not handled by remote") session := podmanTest.Podman([]string{"create", "--pull=always", "--signature-policy", "/no/such/file", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"create", "--pull=always", "--signature-policy", "/etc/containers/policy.json", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman create with unset label", func() { @@ -363,7 +378,7 @@ ctrName := "testctr" session := podmanTest.Podman([]string{"create", "--label", "TESTKEY1=", "--label", "TESTKEY2", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -381,7 +396,7 @@ ctrName := "testctr" session := podmanTest.Podman([]string{"create", "--label", "TESTKEY1=value1", "--label", "TESTKEY2=bar", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -400,7 +415,7 @@ ctrName := "testctr" session := podmanTest.Podman([]string{"create", "-t", "--restart", "on-failure:5", "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -413,7 +428,7 @@ It("podman create with --restart-policy=always:5 fails", func() { session := podmanTest.Podman([]string{"create", "-t", "--restart", "always:5", ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman create with --restart-policy unless-stopped", func() { @@ -421,7 +436,7 @@ unlessStopped := "unless-stopped" session := podmanTest.Podman([]string{"create", "-t", "--restart", unlessStopped, "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -435,7 +450,7 @@ ctrName := "testCtr" session := podmanTest.Podman([]string{"create", "-t", "-m", fmt.Sprintf("%db", numMem), "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -450,7 +465,7 @@ ctrName := "testCtr" session := podmanTest.Podman([]string{"create", "-t", "--cpus", fmt.Sprintf("%d", numCpus), "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -463,14 +478,14 @@ // Make sure we error out with --name. session := podmanTest.Podman([]string{"create", "--replace", ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) // Create and replace 5 times in a row the "same" container. ctrName := "testCtr" for i := 0; i < 5; i++ { session = podmanTest.Podman([]string{"create", "--replace", "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } }) @@ -478,7 +493,7 @@ ctrName := "testCtr" session := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() @@ -490,11 +505,11 @@ It("podman create --tz", func() { session := podmanTest.Podman([]string{"create", "--tz", "foo", "--name", "bad", ALPINE, "date"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"create", "--tz", "America", "--name", "dir", ALPINE, "date"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"create", "--tz", "Pacific/Honolulu", "--name", "zone", ALPINE, "date"}) session.WaitWithDefaultTimeout() @@ -552,7 +567,7 @@ session = podmanTest.Podman([]string{"create", "--umask", "9999", "--name", "bad", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask")) }) @@ -561,11 +576,11 @@ name := "createwithstaticip" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(BeZero()) + Expect(pod).Should(Exit(0)) session := podmanTest.Podman([]string{"create", "--pod", name, "--ip", "192.168.1.2", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) }) It("create container in pod with mac should fail", func() { @@ -573,112 +588,112 @@ name := "createwithstaticmac" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(BeZero()) + Expect(pod).Should(Exit(0)) session := podmanTest.Podman([]string{"create", "--pod", name, "--mac-address", "52:54:00:6d:2f:82", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) }) It("create container in pod with network should not fail", func() { name := "createwithnetwork" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(BeZero()) + Expect(pod).Should(Exit(0)) netName := "pod" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) session = podmanTest.Podman([]string{"create", "--pod", name, "--network", netName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) }) It("create container in pod with ports should fail", func() { name := "createwithports" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(BeZero()) + Expect(pod).Should(Exit(0)) - session := podmanTest.Podman([]string{"create", "--pod", name, "-p", "8080:80", ALPINE, "top"}) + session := podmanTest.Podman([]string{"create", "--pod", name, "-p", "8086:80", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) }) - It("create container in pod ppublish ports should fail", func() { + It("create container in pod publish ports should fail", func() { name := "createwithpublishports" pod := podmanTest.RunTopContainerInPod("", "new:"+name) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(BeZero()) + Expect(pod).Should(Exit(0)) session := podmanTest.Podman([]string{"create", "--pod", name, "-P", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) }) It("create use local store image if input image contains a manifest list", func() { session := podmanTest.Podman([]string{"pull", BB}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "create", "mylist"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--all", "mylist", BB}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "mylist"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) }) It("podman create -d should fail, can not detach create containers", func() { session := podmanTest.Podman([]string{"create", "-d", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("unknown shorthand flag")) session = podmanTest.Podman([]string{"create", "--detach", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("unknown flag")) session = podmanTest.Podman([]string{"create", "--detach-keys", "ctrl-x", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("unknown flag")) }) It("podman create --platform", func() { session := podmanTest.Podman([]string{"create", "--platform=linux/bogus", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError := "no image found in manifest list for architecture bogus" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"create", "--platform=linux/arm64", "--os", "windows", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError = "--platform option can not be specified with --arch or --os" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"create", "-q", "--platform=linux/arm64", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) setup := podmanTest.Podman([]string{"container", "inspect", session.OutputToString()}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := setup.InspectContainerToJSON() setup = podmanTest.Podman([]string{"image", "inspect", data[0].Image}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) idata := setup.InspectImageJSON() // returns []inspect.ImageData Expect(len(idata)).To(Equal(1)) diff -Nru libpod-3.2.1+ds1/test/e2e/diff_test.go libpod-3.4.4+ds1/test/e2e/diff_test.go --- libpod-3.2.1+ds1/test/e2e/diff_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/diff_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,12 +1,15 @@ package integration import ( + "fmt" "os" "sort" . "github.com/containers/podman/v3/test/utils" + "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman diff", func() { @@ -36,34 +39,27 @@ It("podman diff of image", func() { session := podmanTest.Podman([]string{"diff", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) - }) - - It("podman container diff of image", func() { - session := podmanTest.Podman([]string{"container", "diff", ALPINE}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) It("podman diff bogus image", func() { session := podmanTest.Podman([]string{"diff", "1234"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman diff image with json output", func() { session := podmanTest.Podman([]string{"diff", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) It("podman diff container and committed image", func() { session := podmanTest.Podman([]string{"run", "--name=diff-test", ALPINE, "touch", "/tmp/diff-test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"diff", "diff-test"}) session.WaitWithDefaultTimeout() containerDiff := session.OutputToStringArray() @@ -72,7 +68,7 @@ Expect(session.LineInOutputContains("A /tmp/diff-test")).To(BeTrue()) session = podmanTest.Podman([]string{"commit", "diff-test", "diff-test-img"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"diff", "diff-test-img"}) session.WaitWithDefaultTimeout() imageDiff := session.OutputToStringArray() @@ -83,13 +79,114 @@ It("podman diff latest container", func() { session := podmanTest.Podman([]string{"run", "--name", "diff-test", ALPINE, "touch", "/tmp/diff-test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"diff", "diff-test"}) + Expect(session).Should(Exit(0)) + if !IsRemote() { + session = podmanTest.Podman([]string{"diff", "-l"}) + } else { + session = podmanTest.Podman([]string{"diff", "diff-test"}) + } session.WaitWithDefaultTimeout() containerDiff := session.OutputToStringArray() sort.Strings(containerDiff) Expect(session.LineInOutputContains("C /tmp")).To(BeTrue()) Expect(session.LineInOutputContains("A /tmp/diff-test")).To(BeTrue()) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + }) + + It("podman image diff", func() { + file1 := "/" + stringid.GenerateNonCryptoID() + file2 := "/" + stringid.GenerateNonCryptoID() + file3 := "/" + stringid.GenerateNonCryptoID() + + // Create container image with the files + containerfile := fmt.Sprintf(` +FROM %s +RUN touch %s +RUN touch %s +RUN touch %s`, ALPINE, file1, file2, file3) + + image := "podman-diff-test" + podmanTest.BuildImage(containerfile, image, "true") + + // build a second image which used as base to compare against + // using ALPINE does not work in CI, most likely due the extra vfs.imagestore + containerfile = fmt.Sprintf(` +FROM %s +RUN echo test +`, ALPINE) + baseImage := "base-image" + podmanTest.BuildImage(containerfile, baseImage, "true") + + session := podmanTest.Podman([]string{"image", "diff", image}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 1)) + Expect(session.OutputToString()).To(Equal("A " + file3)) + + session = podmanTest.Podman([]string{"image", "diff", image, baseImage}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 4)) + Expect(session.LineInOutputContains("A " + file1)).To(BeTrue()) + Expect(session.LineInOutputContains("A " + file2)).To(BeTrue()) + Expect(session.LineInOutputContains("A " + file3)).To(BeTrue()) + }) + + It("podman image diff of single image", func() { + session := podmanTest.Podman([]string{"image", "diff", BB}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) + + It("podman image diff bogus image", func() { + session := podmanTest.Podman([]string{"image", "diff", "1234", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + }) + + It("podman image diff of the same image", func() { + session := podmanTest.Podman([]string{"image", "diff", ALPINE, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 0)) + }) + + It("podman diff container and image with same name", func() { + imagefile := "/" + stringid.GenerateNonCryptoID() + confile := "/" + stringid.GenerateNonCryptoID() + + // Create container image with the files + containerfile := fmt.Sprintf(` +FROM %s +RUN touch %s`, ALPINE, imagefile) + + name := "podman-diff-test" + podmanTest.BuildImage(containerfile, name, "false") + + session := podmanTest.Podman([]string{"run", "--name", name, ALPINE, "touch", confile}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // podman diff prefers image over container when they have the same name + session = podmanTest.Podman([]string{"diff", name}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 2)) + Expect(session.OutputToString()).To(ContainSubstring(imagefile)) + + session = podmanTest.Podman([]string{"image", "diff", name}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 2)) + Expect(session.OutputToString()).To(ContainSubstring(imagefile)) + + // container diff has to show the container + session = podmanTest.Podman([]string{"container", "diff", name}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(BeNumerically("==", 2)) + Expect(session.OutputToString()).To(ContainSubstring(confile)) + }) + }) diff -Nru libpod-3.2.1+ds1/test/e2e/events_test.go libpod-3.4.4+ds1/test/e2e/events_test.go --- libpod-3.2.1+ds1/test/e2e/events_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/events_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -52,7 +52,7 @@ Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(BeZero()) + Expect(result).Should(Exit(0)) }) It("podman events with an event filter", func() { @@ -61,7 +61,7 @@ Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray()) >= 1) }) @@ -75,7 +75,7 @@ time.Sleep(5 * time.Second) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "event=start", "--filter", fmt.Sprintf("container=%s", cid)}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(1)) Expect(!strings.Contains(result.OutputToString(), cid2)) }) @@ -86,7 +86,7 @@ Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "type=pod", "--filter", fmt.Sprintf("container=%s", cid)}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(0)) }) @@ -96,11 +96,11 @@ setup.WaitWithDefaultTimeout() stop := podmanTest.Podman([]string{"pod", "stop", "foobarpod"}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) - Expect(setup.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) + Expect(setup).Should(Exit(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--filter", "type=pod", "--filter", "pod=foobarpod"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) fmt.Println(result.OutputToStringArray()) Expect(len(result.OutputToStringArray()) >= 2) }) @@ -111,7 +111,7 @@ Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--since", "1m"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(BeZero()) + Expect(result).Should(Exit(0)) }) It("podman events --until", func() { @@ -120,7 +120,7 @@ Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"events", "--stream=false", "--until", "1h"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(BeZero()) + Expect(result).Should(Exit(0)) }) It("podman events format", func() { @@ -157,7 +157,7 @@ name3 := stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"create", "--name", name1, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) var wg sync.WaitGroup wg.Add(1) @@ -169,17 +169,30 @@ time.Sleep(time.Second * 2) session = podmanTest.Podman([]string{"create", "--name", name2, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "--name", name3, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }() // unix timestamp in 10 seconds until := time.Now().Add(time.Second * 10).Unix() result := podmanTest.Podman([]string{"events", "--since", "30s", "--until", fmt.Sprint(until)}) result.Wait(11) - Expect(result.ExitCode()).To(BeZero()) + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(name1)) + Expect(result.OutputToString()).To(ContainSubstring(name2)) + Expect(result.OutputToString()).To(ContainSubstring(name3)) + + // string duration in 10 seconds + untilT := time.Now().Add(time.Second * 9) + result = podmanTest.Podman([]string{"events", "--since", "30s", "--until", "10s"}) + result.Wait(11) + Expect(result).Should(Exit(0)) + tEnd := time.Now() + outDur := tEnd.Sub(untilT) + diff := outDur.Seconds() > 0 + Expect(diff).To(Equal(true)) Expect(result.OutputToString()).To(ContainSubstring(name1)) Expect(result.OutputToString()).To(ContainSubstring(name2)) Expect(result.OutputToString()).To(ContainSubstring(name3)) diff -Nru libpod-3.2.1+ds1/test/e2e/exec_test.go libpod-3.4.4+ds1/test/e2e/exec_test.go --- libpod-3.2.1+ds1/test/e2e/exec_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/exec_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,12 +2,15 @@ import ( "fmt" + "io/ioutil" "os" + "path/filepath" "strings" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman exec", func() { @@ -37,61 +40,61 @@ It("podman exec into bogus container", func() { session := podmanTest.Podman([]string{"exec", "foobar", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman exec without command", func() { session := podmanTest.Podman([]string{"exec", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman exec simple command", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container exec simple command", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"container", "exec", "test1", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman exec simple command using latest", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := "-l" if IsRemote() { cid = "test1" } session := podmanTest.Podman([]string{"exec", cid, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman exec environment test", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "--env", "FOO=BAR", "test1", "printenv", "FOO"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("BAR")) session = podmanTest.Podman([]string{"exec", "--env", "PATH=/bin", "test1", "printenv", "PATH"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/bin")) }) @@ -99,12 +102,12 @@ // remote doesn't properly interpret os.Setenv setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) os.Setenv("FOO", "BAR") session := podmanTest.Podman([]string{"exec", "--env", "FOO", "test1", "printenv", "FOO"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("BAR")) os.Unsetenv("FOO") }) @@ -112,11 +115,11 @@ It("podman exec exit code", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "sh", "-c", "exit 100"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(100)) + Expect(session).Should(Exit(100)) }) It("podman exec in keep-id container drops privileges", func() { @@ -124,63 +127,63 @@ ctrName := "testctr1" testCtr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "--userns=keep-id", ALPINE, "top"}) testCtr.WaitWithDefaultTimeout() - Expect(testCtr.ExitCode()).To(Equal(0)) + Expect(testCtr).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", ctrName, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) }) It("podman exec --privileged", func() { session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bndPerms := session.OutputToString() session = podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) effPerms := session.OutputToString() setup := podmanTest.RunTopContainer("test-privileged") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) }) It("podman exec --privileged", func() { session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bndPerms := session.OutputToString() session = podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) effPerms := session.OutputToString() setup := podmanTest.RunTopContainer("test-privileged") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) }) @@ -188,52 +191,52 @@ It("podman exec --privileged", func() { session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bndPerms := session.OutputToString() setup := podmanTest.RunTopContainer("test-privileged") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000")) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) }) It("podman exec --privileged container not running as root", func() { session := podmanTest.Podman([]string{"run", "--privileged", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bndPerms := session.OutputToString() setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000")) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000")) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=root", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) session = podmanTest.Podman([]string{"exec", "--privileged", "--user=bin", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) }) @@ -241,26 +244,26 @@ capAdd := "--cap-add=net_bind_service" session := podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bndPerms := session.OutputToString() session = podmanTest.Podman([]string{"run", "--user=bin", capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) effPerms := session.OutputToString() setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capAdd}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) }) @@ -269,111 +272,111 @@ capDrop := "--cap-drop=all" session := podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bndPerms := session.OutputToString() session = podmanTest.Podman([]string{"run", "--user=bin", capDrop, capAdd, "--rm", ALPINE, "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) effPerms := session.OutputToString() setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--user=bin", capDrop, capAdd}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bndPerms)) session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapInh /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapPrm /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) session = podmanTest.Podman([]string{"exec", "test-privileged", "sh", "-c", "grep ^CapAmb /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(effPerms)) }) It("podman exec --privileged with user", func() { session := podmanTest.Podman([]string{"run", "--privileged", "--user=bin", "--rm", ALPINE, "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bindPerms := session.OutputToString() setup := podmanTest.RunTopContainerWithArgs("test-privileged", []string{"--privileged", "--user=bin"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapBnd /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(bindPerms)) session = podmanTest.Podman([]string{"exec", "--privileged", "test-privileged", "sh", "-c", "grep ^CapEff /proc/self/status | cut -f 2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) }) It("podman exec terminal doesn't hang", func() { setup := podmanTest.Podman([]string{"run", "-dti", "--name", "test1", fedoraMinimal, "sleep", "+Inf"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) for i := 0; i < 5; i++ { session := podmanTest.Podman([]string{"exec", "-ti", "test1", "true"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } }) It("podman exec pseudo-terminal sanity check", func() { setup := podmanTest.Podman([]string{"run", "--detach", "--name", "test1", fedoraMinimal, "sleep", "+Inf"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "--interactive", "--tty", "test1", "/usr/bin/stty", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(" onlcr")) }) It("podman exec simple command with user", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "--user", "root", "test1", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman exec with user only in container", func() { testUser := "test123" setup := podmanTest.Podman([]string{"run", "--name", "test1", "-d", fedoraMinimal, "sleep", "60"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "useradd", testUser}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"exec", "--user", testUser, "test1", "whoami"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) Expect(session2.OutputToString()).To(Equal(testUser)) }) @@ -381,41 +384,41 @@ testUser := "guest" setup := podmanTest.Podman([]string{"run", "--user", testUser, "-d", ALPINE, "top"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) ctrID := setup.OutputToString() session := podmanTest.Podman([]string{"exec", ctrID, "whoami"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(testUser)) overrideUser := "root" session = podmanTest.Podman([]string{"exec", "--user", overrideUser, ctrID, "whoami"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(overrideUser)) }) It("podman exec simple working directory test", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "--workdir", "/tmp", "test1", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/tmp")) session = podmanTest.Podman([]string{"exec", "-w", "/tmp", "test1", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/tmp")) }) It("podman exec missing working directory test", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "--workdir", "/missing", "test1", "pwd"}) session.WaitWithDefaultTimeout() @@ -429,27 +432,27 @@ It("podman exec cannot be invoked", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "/etc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(126)) + Expect(session).Should(Exit(126)) }) It("podman exec command not found", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "notthere"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(127)) + Expect(session).Should(Exit(127)) }) It("podman exec preserve fds sanity check", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) devNull, err := os.Open("/dev/null") Expect(err).To(BeNil()) @@ -459,7 +462,7 @@ } session := podmanTest.PodmanExtraFiles([]string{"exec", "--preserve-fds", "1", "test1", "ls"}, files) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman exec preserves --group-add groups", func() { @@ -468,21 +471,21 @@ ctrName1 := "ctr1" ctr1 := podmanTest.Podman([]string{"run", "-ti", "--name", ctrName1, fedoraMinimal, "groupadd", "-g", gid, groupName}) ctr1.WaitWithDefaultTimeout() - Expect(ctr1.ExitCode()).To(Equal(0)) + Expect(ctr1).Should(Exit(0)) imgName := "img1" commit := podmanTest.Podman([]string{"commit", ctrName1, imgName}) commit.WaitWithDefaultTimeout() - Expect(commit.ExitCode()).To(Equal(0)) + Expect(commit).Should(Exit(0)) ctrName2 := "ctr2" ctr2 := podmanTest.Podman([]string{"run", "-d", "--name", ctrName2, "--group-add", groupName, imgName, "sleep", "300"}) ctr2.WaitWithDefaultTimeout() - Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-ti", ctrName2, "id"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(Equal(0)) + Expect(exec).Should(Exit(0)) Expect(exec.OutputToString()).To(ContainSubstring(fmt.Sprintf("%s(%s)", gid, groupName))) }) @@ -497,11 +500,11 @@ ctrName := "testctr" ctr := podmanTest.Podman([]string{"run", "-t", "-i", "-d", "--name", ctrName, "--user", "auser:first", "--group-add", "second", imgName, "sleep", "300"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-t", ctrName, "id"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(Equal(0)) + Expect(exec).Should(Exit(0)) output := exec.OutputToString() Expect(output).To(ContainSubstring("4000(first)")) Expect(output).To(ContainSubstring("4001(second)")) @@ -510,18 +513,18 @@ // Kill the container just so the test does not take 15 seconds to stop. kill := podmanTest.Podman([]string{"kill", ctrName}) kill.WaitWithDefaultTimeout() - Expect(kill.ExitCode()).To(Equal(0)) + Expect(kill).Should(Exit(0)) }) It("podman exec --detach", func() { ctrName := "testctr" ctr := podmanTest.Podman([]string{"run", "-t", "-i", "-d", "--name", ctrName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) exec1 := podmanTest.Podman([]string{"exec", "-t", "-i", "-d", ctrName, "top"}) exec1.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) data := podmanTest.InspectContainer(ctrName) Expect(len(data)).To(Equal(1)) @@ -530,13 +533,41 @@ exec2 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "ps", "-a"}) exec2.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) Expect(strings.Count(exec2.OutputToString(), "top")).To(Equal(2)) // Ensure that stop with a running detached exec session is // clean. stop := podmanTest.Podman([]string{"stop", ctrName}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) + }) + + It("podman exec with env var secret", func() { + secretsString := "somesecretdata" + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "-t", "-i", "-d", "--secret", "source=mysecret,type=env", "--name", "secr", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"exec", "secr", "printenv", "mysecret"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring(secretsString)) + + session = podmanTest.Podman([]string{"commit", "secr", "foobar.com/test1-image:latest"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "foobar.com/test1-image:latest", "printenv", "mysecret"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(Not(ContainSubstring(secretsString))) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/exists_test.go libpod-3.4.4+ds1/test/e2e/exists_test.go --- libpod-3.2.1+ds1/test/e2e/exists_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/exists_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -50,7 +50,7 @@ It("podman container exists in local storage by name", func() { setup := podmanTest.RunTopContainer("foobar") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"container", "exists", "foobar"}) session.WaitWithDefaultTimeout() @@ -69,7 +69,7 @@ It("podman container exists in local storage by short container ID", func() { setup := podmanTest.RunTopContainer("") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString()[0:12] session := podmanTest.Podman([]string{"container", "exists", cid}) diff -Nru libpod-3.2.1+ds1/test/e2e/export_test.go libpod-3.4.4+ds1/test/e2e/export_test.go --- libpod-3.2.1+ds1/test/e2e/export_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/export_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman export", func() { @@ -40,7 +41,7 @@ outfile := filepath.Join(podmanTest.TempDir, "container.tar") result := podmanTest.Podman([]string{"export", "-o", outfile, cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) _, err := os.Stat(outfile) Expect(err).To(BeNil()) @@ -55,7 +56,7 @@ outfile := filepath.Join(podmanTest.TempDir, "container.tar") result := podmanTest.Podman([]string{"container", "export", "-o", outfile, cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) _, err := os.Stat(outfile) Expect(err).To(BeNil()) diff -Nru libpod-3.2.1+ds1/test/e2e/generate_kube_test.go libpod-3.4.4+ds1/test/e2e/generate_kube_test.go --- libpod-3.2.1+ds1/test/e2e/generate_kube_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/generate_kube_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,12 +5,16 @@ "os" "path/filepath" "strconv" + "strings" + + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" "github.com/ghodss/yaml" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" v1 "k8s.io/api/core/v1" ) @@ -53,16 +57,20 @@ It("podman generate kube on container", func() { session := podmanTest.RunTopContainer("top") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "top"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) Expect(pod.Spec.HostNetwork).To(Equal(false)) + Expect(pod.Spec.SecurityContext).To(BeNil()) + Expect(pod.Spec.DNSConfig).To(BeNil()) + Expect(pod.Spec.Containers[0].WorkingDir).To(Equal("")) + Expect(pod.Spec.Containers[0].Env).To(BeNil()) numContainers := 0 for range pod.Spec.Containers { @@ -74,11 +82,11 @@ It("podman generate service kube on container with --security-opt level", func() { session := podmanTest.Podman([]string{"create", "--name", "test", "--security-opt", "label=level:s0:c100,c200", "alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "test"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -89,26 +97,27 @@ It("podman generate service kube on container with --security-opt disable", func() { session := podmanTest.Podman([]string{"create", "--name", "test-disable", "--security-opt", "label=disable", "alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "test-disable"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) Expect(kube.OutputToString()).To(ContainSubstring("type: spc_t")) + }) It("podman generate service kube on container with --security-opt type", func() { session := podmanTest.Podman([]string{"create", "--name", "test", "--security-opt", "label=type:foo_bar_t", "alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "test"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) @@ -116,20 +125,28 @@ Expect(kube.OutputToString()).To(ContainSubstring("type: foo_bar_t")) }) - It("podman generate service kube on container", func() { - session := podmanTest.RunTopContainer("top") + It("podman generate service kube on container - targetPort should match port name", func() { + session := podmanTest.Podman([]string{"create", "--name", "test-ctr", "-p", "3890:3890", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "-s", "top"}) + kube := podmanTest.Podman([]string{"generate", "kube", "-s", "test-ctr"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) + + // Separate out the Service and Pod yaml + arr := strings.Split(string(kube.Out.Contents()), "---") + Expect(len(arr)).To(Equal(2)) - // TODO - test generated YAML - service produces multiple - // structs. - // pod := new(v1.Pod) - // err := yaml.Unmarshal([]byte(kube.OutputToString()), pod) - // Expect(err).To(BeNil()) + svc := new(v1.Service) + err := yaml.Unmarshal([]byte(arr[0]), svc) + Expect(err).To(BeNil()) + Expect(len(svc.Spec.Ports)).To(Equal(1)) + Expect(svc.Spec.Ports[0].TargetPort.IntValue()).To(Equal(3890)) + + pod := new(v1.Pod) + err = yaml.Unmarshal([]byte(arr[1]), pod) + Expect(err).To(BeNil()) }) It("podman generate kube on pod", func() { @@ -138,11 +155,11 @@ session := podmanTest.RunTopContainerInPod("topcontainer", "toppod") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "toppod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -159,32 +176,104 @@ It("podman generate kube multiple pods", func() { pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", ALPINE, "top"}) pod1.WaitWithDefaultTimeout() - Expect(pod1.ExitCode()).To(Equal(0)) + Expect(pod1).Should(Exit(0)) pod2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod2", ALPINE, "top"}) pod2.WaitWithDefaultTimeout() - Expect(pod2.ExitCode()).To(Equal(0)) + Expect(pod2).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "pod2"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) Expect(string(kube.Out.Contents())).To(ContainSubstring(`name: pod1`)) Expect(string(kube.Out.Contents())).To(ContainSubstring(`name: pod2`)) }) + It("podman generate kube on pod with init containers", func() { + session := podmanTest.Podman([]string{"create", "--pod", "new:toppod", "--init-ctr", "always", ALPINE, "echo", "hello"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--pod", "toppod", "--init-ctr", "always", ALPINE, "echo", "world"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--pod", "toppod", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "toppod"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(false)) + + numContainers := len(pod.Spec.Containers) + len(pod.Spec.InitContainers) + Expect(numContainers).To(Equal(3)) + + // Init container should be in the generated kube yaml if created with "once" type and the pod has not been started + session = podmanTest.Podman([]string{"create", "--pod", "new:toppod-2", "--init-ctr", "once", ALPINE, "echo", "using once type"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--pod", "toppod-2", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "toppod-2"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(false)) + + numContainers = len(pod.Spec.Containers) + len(pod.Spec.InitContainers) + Expect(numContainers).To(Equal(2)) + + // Init container should not be in the generated kube yaml if created with "once" type and the pod has been started + session = podmanTest.Podman([]string{"create", "--pod", "new:toppod-3", "--init-ctr", "once", ALPINE, "echo", "using once type"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"create", "--pod", "toppod-3", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"pod", "start", "toppod-3"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "toppod-3"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + Expect(pod.Spec.HostNetwork).To(Equal(false)) + + numContainers = len(pod.Spec.Containers) + len(pod.Spec.InitContainers) + Expect(numContainers).To(Equal(1)) + }) + It("podman generate kube on pod with host network", func() { podSession := podmanTest.Podman([]string{"pod", "create", "--name", "testHostNetwork", "--network", "host"}) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) session := podmanTest.Podman([]string{"create", "--name", "topcontainer", "--pod", "testHostNetwork", "--network", "host", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "testHostNetwork"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -195,11 +284,11 @@ It("podman generate kube on container with host network", func() { session := podmanTest.RunTopContainerWithArgs("topcontainer", []string{"--network", "host"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "topcontainer"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -215,21 +304,21 @@ "--add-host", "test2.podman.io" + ":" + testIP, }) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) ctr1Name := "ctr1" ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, ALPINE, "top"}) ctr1Session.WaitWithDefaultTimeout() - Expect(ctr1Session.ExitCode()).To(Equal(0)) + Expect(ctr1Session).Should(Exit(0)) ctr2Name := "ctr2" ctr2Session := podmanTest.Podman([]string{"create", "--name", ctr2Name, "--pod", podName, ALPINE, "top"}) ctr2Session.WaitWithDefaultTimeout() - Expect(ctr2Session.ExitCode()).To(Equal(0)) + Expect(ctr2Session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -240,21 +329,28 @@ }) It("podman generate service kube on pod", func() { - _, rc, _ := podmanTest.CreatePod(map[string][]string{"--name": {"toppod"}}) - Expect(rc).To(Equal(0)) - - session := podmanTest.RunTopContainerInPod("topcontainer", "toppod") + session := podmanTest.Podman([]string{"create", "--pod", "new:test-pod", "-p", "4000:4000/udp", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) - kube := podmanTest.Podman([]string{"generate", "kube", "-s", "toppod"}) + kube := podmanTest.Podman([]string{"generate", "kube", "-s", "test-pod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) + + // Separate out the Service and Pod yaml + arr := strings.Split(string(kube.Out.Contents()), "---") + Expect(len(arr)).To(Equal(2)) + + svc := new(v1.Service) + err := yaml.Unmarshal([]byte(arr[0]), svc) + Expect(err).To(BeNil()) + Expect(len(svc.Spec.Ports)).To(Equal(1)) + Expect(svc.Spec.Ports[0].TargetPort.IntValue()).To(Equal(4000)) + Expect(svc.Spec.Ports[0].Protocol).To(Equal(v1.ProtocolUDP)) - // TODO: How do we test unmarshal with a service? We have two - // structs that need to be unmarshalled... - // _, err := yaml.Marshal(kube.OutputToString()) - // Expect(err).To(BeNil()) + pod := new(v1.Pod) + err = yaml.Unmarshal([]byte(arr[1]), pod) + Expect(err).To(BeNil()) }) It("podman generate kube on pod with restartPolicy", func() { @@ -270,17 +366,17 @@ podName := v[0] podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) ctrName := "ctr" + strconv.Itoa(k) ctr1Session := podmanTest.Podman([]string{"create", "--name", ctrName, "--pod", podName, "--restart", v[1], ALPINE, "top"}) ctr1Session.WaitWithDefaultTimeout() - Expect(ctr1Session.ExitCode()).To(Equal(0)) + Expect(ctr1Session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -294,16 +390,16 @@ podName := "testMemoryLimit" podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) ctr1Name := "ctr1" ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "--memory", "10Mi", ALPINE, "top"}) ctr1Session.WaitWithDefaultTimeout() - Expect(ctr1Session.ExitCode()).To(Equal(0)) + Expect(ctr1Session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -319,23 +415,23 @@ podName := "testCpuLimit" podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName}) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) ctr1Name := "ctr1" ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, "--cpus", "0.5", ALPINE, "top"}) ctr1Session.WaitWithDefaultTimeout() - Expect(ctr1Session.ExitCode()).To(Equal(0)) + Expect(ctr1Session).Should(Exit(0)) ctr2Name := "ctr2" ctr2Session := podmanTest.Podman([]string{"create", "--name", ctr2Name, "--pod", podName, "--cpu-period", "100000", "--cpu-quota", "50000", ALPINE, "top"}) ctr2Session.WaitWithDefaultTimeout() - Expect(ctr2Session.ExitCode()).To(Equal(0)) + Expect(ctr2Session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -351,21 +447,21 @@ podName := "test" podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "4000:4000", "-p", "5000:5000"}) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) ctr1Name := "ctr1" ctr1Session := podmanTest.Podman([]string{"create", "--name", ctr1Name, "--pod", podName, ALPINE, "top"}) ctr1Session.WaitWithDefaultTimeout() - Expect(ctr1Session.ExitCode()).To(Equal(0)) + Expect(ctr1Session).Should(Exit(0)) ctr2Name := "ctr2" ctr2Session := podmanTest.Podman([]string{"create", "--name", ctr2Name, "--pod", podName, ALPINE, "top"}) ctr2Session.WaitWithDefaultTimeout() - Expect(ctr2Session.ExitCode()).To(Equal(0)) + Expect(ctr2Session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -376,6 +472,10 @@ foundOtherPort := 0 for _, ctr := range pod.Spec.Containers { for _, port := range ctr.Ports { + // Since we are using tcp here, the generated kube yaml shouldn't + // have anything for protocol under the ports as tcp is the default + // for k8s + Expect(port.Protocol).To(BeEmpty()) if port.HostPort == 4000 { foundPort4000 = foundPort4000 + 1 } else if port.HostPort == 5000 { @@ -388,6 +488,24 @@ Expect(foundPort4000).To(Equal(1)) Expect(foundPort5000).To(Equal(1)) Expect(foundOtherPort).To(Equal(0)) + + // Create container with UDP port and check the generated kube yaml + ctrWithUDP := podmanTest.Podman([]string{"create", "--pod", "new:test-pod", "-p", "6666:66/udp", ALPINE, "top"}) + ctrWithUDP.WaitWithDefaultTimeout() + Expect(ctrWithUDP).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "test-pod"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers := pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(len(containers[0].Ports)).To(Equal(1)) + Expect(containers[0].Ports[0].Protocol).To(Equal(v1.ProtocolUDP)) }) It("podman generate and reimport kube on pod", func() { @@ -397,33 +515,33 @@ session := podmanTest.Podman([]string{"create", "--pod", podName, "--name", "test1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"create", "--pod", podName, "--name", "test2", ALPINE, "top"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") kube := podmanTest.Podman([]string{"generate", "kube", "-f", outputFile, podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) session3 := podmanTest.Podman([]string{"pod", "rm", "-af"}) session3.WaitWithDefaultTimeout() - Expect(session3.ExitCode()).To(Equal(0)) + Expect(session3).Should(Exit(0)) session4 := podmanTest.Podman([]string{"play", "kube", outputFile}) session4.WaitWithDefaultTimeout() - Expect(session4.ExitCode()).To(Equal(0)) + Expect(session4).Should(Exit(0)) session5 := podmanTest.Podman([]string{"pod", "ps"}) session5.WaitWithDefaultTimeout() - Expect(session5.ExitCode()).To(Equal(0)) + Expect(session5).Should(Exit(0)) Expect(session5.OutputToString()).To(ContainSubstring(podName)) session6 := podmanTest.Podman([]string{"ps", "-a"}) session6.WaitWithDefaultTimeout() - Expect(session6.ExitCode()).To(Equal(0)) + Expect(session6).Should(Exit(0)) psOut := session6.OutputToString() Expect(psOut).To(ContainSubstring("test1")) Expect(psOut).To(ContainSubstring("test2")) @@ -436,31 +554,31 @@ session := podmanTest.Podman([]string{"create", "--pod", podName, "--name", "test1", "--user", "100:200", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.Config.User}}", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring("100:200")) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") kube := podmanTest.Podman([]string{"generate", "kube", "-f", outputFile, podName}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "rm", "-af"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podmanTest.AddImageToRWStore(ALPINE) session = podmanTest.Podman([]string{"play", "kube", outputFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // container name in pod is - inspect1 := podmanTest.Podman([]string{"inspect", "--format", "{{.Config.User}}", "toppod-test1"}) inspect1.WaitWithDefaultTimeout() - Expect(inspect1.ExitCode()).To(Equal(0)) + Expect(inspect1).Should(Exit(0)) Expect(inspect1.OutputToString()).To(ContainSubstring(inspect.OutputToString())) }) @@ -475,24 +593,33 @@ session1 := podmanTest.Podman([]string{"run", "-d", "--pod", "new:test1", "--name", ctrName, "-v", vol1 + ":/volume/:z", "alpine", "top"}) session1.WaitWithDefaultTimeout() - Expect(session1.ExitCode()).To(Equal(0)) + Expect(session1).Should(Exit(0)) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") kube := podmanTest.Podman([]string{"generate", "kube", "test1", "-f", outputFile}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) + + b, err := ioutil.ReadFile(outputFile) + Expect(err).ShouldNot(HaveOccurred()) + pod := new(v1.Pod) + err = yaml.Unmarshal(b, pod) + Expect(err).To(BeNil()) + val, found := pod.Annotations[define.BindMountPrefix+vol1] + Expect(found).To(BeTrue()) + Expect(val).To(HaveSuffix("z")) rm := podmanTest.Podman([]string{"pod", "rm", "-f", "test1"}) rm.WaitWithDefaultTimeout() - Expect(rm.ExitCode()).To(Equal(0)) + Expect(rm).Should(Exit(0)) play := podmanTest.Podman([]string{"play", "kube", outputFile}) play.WaitWithDefaultTimeout() - Expect(play.ExitCode()).To(Equal(0)) + Expect(play).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrNameInKubePod}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(vol1)) }) @@ -505,11 +632,11 @@ "-v", "/root:/volume2/", "alpine", "top"}) session1.WaitWithDefaultTimeout() - Expect(session1.ExitCode()).To(Equal(0)) + Expect(session1).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "mount-root-conflict"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -528,24 +655,24 @@ session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:test1", "--name", ctrName, "-v", vol + ":/volume/:z", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") kube := podmanTest.Podman([]string{"generate", "kube", "test1", "-f", outputFile}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) rm := podmanTest.Podman([]string{"pod", "rm", "-f", "test1"}) rm.WaitWithDefaultTimeout() - Expect(rm.ExitCode()).To(Equal(0)) + Expect(rm).Should(Exit(0)) play := podmanTest.Podman([]string{"play", "kube", outputFile}) play.WaitWithDefaultTimeout() - Expect(play.ExitCode()).To(Equal(0)) + Expect(play).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrNameInKubePod}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(vol)) }) @@ -553,95 +680,95 @@ podName := "test" podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--share", "pid"}) podSession.WaitWithDefaultTimeout() - Expect(podSession.ExitCode()).To(Equal(0)) + Expect(podSession).Should(Exit(0)) session := podmanTest.Podman([]string{"create", "--pod", podName, "--name", "test1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml") kube := podmanTest.Podman([]string{"generate", "kube", podName, "-f", outputFile}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) rm := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) rm.WaitWithDefaultTimeout() - Expect(rm.ExitCode()).To(Equal(0)) + Expect(rm).Should(Exit(0)) play := podmanTest.Podman([]string{"play", "kube", outputFile}) play.WaitWithDefaultTimeout() - Expect(play.ExitCode()).To(Equal(0)) + Expect(play).Should(Exit(0)) inspect := podmanTest.Podman([]string{"pod", "inspect", podName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`"pid"`)) }) It("podman generate kube with pods and containers", func() { pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", ALPINE, "top"}) pod1.WaitWithDefaultTimeout() - Expect(pod1.ExitCode()).To(Equal(0)) + Expect(pod1).Should(Exit(0)) pod2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top", ALPINE, "top"}) pod2.WaitWithDefaultTimeout() - Expect(pod2.ExitCode()).To(Equal(0)) + Expect(pod2).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "pod1", "top"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman generate kube with containers in a pod should fail", func() { pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"}) pod1.WaitWithDefaultTimeout() - Expect(pod1.ExitCode()).To(Equal(0)) + Expect(pod1).Should(Exit(0)) con := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod1", "--name", "top", ALPINE, "top"}) con.WaitWithDefaultTimeout() - Expect(con.ExitCode()).To(Equal(0)) + Expect(con).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "top"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).ToNot(Equal(0)) + Expect(kube).To(ExitWithError()) }) It("podman generate kube with multiple containers", func() { con1 := podmanTest.Podman([]string{"run", "-dt", "--name", "con1", ALPINE, "top"}) con1.WaitWithDefaultTimeout() - Expect(con1.ExitCode()).To(Equal(0)) + Expect(con1).Should(Exit(0)) con2 := podmanTest.Podman([]string{"run", "-dt", "--name", "con2", ALPINE, "top"}) con2.WaitWithDefaultTimeout() - Expect(con2.ExitCode()).To(Equal(0)) + Expect(con2).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "con1", "con2"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman generate kube with containers in pods should fail", func() { pod1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod1", "--name", "top1", ALPINE, "top"}) pod1.WaitWithDefaultTimeout() - Expect(pod1.ExitCode()).To(Equal(0)) + Expect(pod1).Should(Exit(0)) pod2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:pod2", "--name", "top2", ALPINE, "top"}) pod2.WaitWithDefaultTimeout() - Expect(pod2.ExitCode()).To(Equal(0)) + Expect(pod2).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "top1", "top2"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).ToNot(Equal(0)) + Expect(kube).To(ExitWithError()) }) It("podman generate kube on a container with dns options", func() { top := podmanTest.Podman([]string{"run", "-dt", "--name", "top", "--dns", "8.8.8.8", "--dns-search", "foobar.com", "--dns-opt", "color:blue", ALPINE, "top"}) top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(BeZero()) + Expect(top).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "top"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -657,15 +784,15 @@ It("podman generate kube multiple container dns servers and options are cumulative", func() { top1 := podmanTest.Podman([]string{"run", "-dt", "--name", "top1", "--dns", "8.8.8.8", "--dns-search", "foobar.com", ALPINE, "top"}) top1.WaitWithDefaultTimeout() - Expect(top1.ExitCode()).To(BeZero()) + Expect(top1).Should(Exit(0)) top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--dns", "8.7.7.7", "--dns-search", "homer.com", ALPINE, "top"}) top2.WaitWithDefaultTimeout() - Expect(top2.ExitCode()).To(BeZero()) + Expect(top2).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "top1", "top2"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -680,11 +807,11 @@ It("podman generate kube on a pod with dns options", func() { top := podmanTest.Podman([]string{"run", "--pod", "new:pod1", "-dt", "--name", "top", "--dns", "8.8.8.8", "--dns-search", "foobar.com", "--dns-opt", "color:blue", ALPINE, "top"}) top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(BeZero()) + Expect(top).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "pod1"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -700,11 +827,11 @@ It("podman generate kube - set entrypoint as command", func() { session := podmanTest.Podman([]string{"create", "--pod", "new:testpod", "--entrypoint", "/bin/sleep", ALPINE, "10s"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "testpod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // Now make sure that the container's command is set to the // entrypoint and it's arguments to "10s". @@ -719,10 +846,49 @@ Expect(containers[0].Args).To(Equal([]string{"10s"})) }) - It("podman generate kube - use entrypoint from image", func() { + It("podman generate kube - use command from image unless explicitly set in the podman command", func() { + session := podmanTest.Podman([]string{"create", "--name", "test", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube := podmanTest.Podman([]string{"generate", "kube", "test"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Now make sure that the container's command in the kube yaml is not set to the + // image command. + pod := new(v1.Pod) + err := yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers := pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(len(containers[0].Command)).To(Equal(0)) + + cmd := []string{"echo", "hi"} + session = podmanTest.Podman(append([]string{"create", "--name", "test1", ALPINE}, cmd...)) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "test1"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Now make sure that the container's command in the kube yaml is set to the + // command passed via the cli to podman create. + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers = pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(containers[0].Command).To(Equal(cmd)) + }) + + It("podman generate kube - use entrypoint from image unless --entrypoint is set", func() { // Build an image with an entrypoint. containerfile := `FROM quay.io/libpod/alpine:latest -ENTRYPOINT /bin/sleep` +ENTRYPOINT ["sleep"]` targetPath, err := CreateTempDirInTempDir() Expect(err).To(BeNil()) @@ -733,37 +899,54 @@ image := "generatekube:test" session := podmanTest.Podman([]string{"build", "--pull-never", "-f", containerfilePath, "-t", image}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "--pod", "new:testpod", image, "10s"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "testpod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) - // Now make sure that the container's command is set to the - // entrypoint and it's arguments to "10s". + // Now make sure that the container's command in the kube yaml is NOT set to the + // entrypoint but the arguments should be set to "10s". pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) containers := pod.Spec.Containers Expect(len(containers)).To(Equal(1)) - - Expect(containers[0].Command).To(Equal([]string{"/bin/sh", "-c", "/bin/sleep"})) Expect(containers[0].Args).To(Equal([]string{"10s"})) + + session = podmanTest.Podman([]string{"create", "--pod", "new:testpod-2", "--entrypoint", "echo", image, "hello"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + kube = podmanTest.Podman([]string{"generate", "kube", "testpod-2"}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Now make sure that the container's command in the kube yaml is set to the + // entrypoint defined by the --entrypoint flag and the arguments should be set to "hello". + pod = new(v1.Pod) + err = yaml.Unmarshal(kube.Out.Contents(), pod) + Expect(err).To(BeNil()) + + containers = pod.Spec.Containers + Expect(len(containers)).To(Equal(1)) + Expect(containers[0].Command).To(Equal([]string{"echo"})) + Expect(containers[0].Args).To(Equal([]string{"hello"})) }) It("podman generate kube - --privileged container", func() { session := podmanTest.Podman([]string{"create", "--pod", "new:testpod", "--privileged", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "testpod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // Now make sure that the capabilities aren't set. pod := new(v1.Pod) @@ -779,16 +962,16 @@ kube = podmanTest.Podman([]string{"generate", "kube", "testpod", "-f", kubeFile}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // Remove the pod so play can recreate it. kube = podmanTest.Podman([]string{"pod", "rm", "-f", "testpod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) kube = podmanTest.Podman([]string{"play", "kube", kubeFile}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman generate kube based on user in container", func() { @@ -806,20 +989,20 @@ image := "generatekube:test" session := podmanTest.Podman([]string{"build", "--pull-never", "-f", containerfilePath, "-t", image}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "--pod", "new:testpod", image, "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "testpod"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err = yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) - Expect(*pod.Spec.Containers[0].SecurityContext.RunAsUser).To(Equal(int64(10001))) + Expect(pod.Spec.Containers[0].SecurityContext.RunAsUser).To(BeNil()) }) It("podman generate kube on named volume", func() { @@ -827,11 +1010,11 @@ session := podmanTest.Podman([]string{"volume", "create", vol}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", vol}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pvc := new(v1.PersistentVolumeClaim) err := yaml.Unmarshal(kube.Out.Contents(), pvc) @@ -849,11 +1032,11 @@ session := podmanTest.Podman([]string{"volume", "create", "--opt", "device=" + volDevice, "--opt", "type=" + volType, "--opt", "o=" + volOpts, vol}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", vol}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pvc := new(v1.PersistentVolumeClaim) err := yaml.Unmarshal(kube.Out.Contents(), pvc) @@ -877,11 +1060,11 @@ It("podman generate kube on container with auto update labels", func() { top := podmanTest.Podman([]string{"run", "-dt", "--name", "top", "--label", "io.containers.autoupdate=local", ALPINE, "top"}) top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(Equal(0)) + Expect(top).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "top"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) @@ -895,23 +1078,25 @@ It("podman generate kube on pod with auto update labels in all containers", func() { pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"}) pod1.WaitWithDefaultTimeout() - Expect(pod1.ExitCode()).To(Equal(0)) + Expect(pod1).Should(Exit(0)) top1 := podmanTest.Podman([]string{"run", "-dt", "--name", "top1", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"}) top1.WaitWithDefaultTimeout() - Expect(top1.ExitCode()).To(Equal(0)) + Expect(top1).Should(Exit(0)) - top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"}) + top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--workdir", "/root", "--pod", "pod1", "--label", "io.containers.autoupdate=registry", "--label", "io.containers.autoupdate.authfile=/some/authfile.json", ALPINE, "top"}) top2.WaitWithDefaultTimeout() - Expect(top2.ExitCode()).To(Equal(0)) + Expect(top2).Should(Exit(0)) kube := podmanTest.Podman([]string{"generate", "kube", "pod1"}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) pod := new(v1.Pod) err := yaml.Unmarshal(kube.Out.Contents(), pod) Expect(err).To(BeNil()) + Expect(pod.Spec.Containers[0].WorkingDir).To(Equal("")) + Expect(pod.Spec.Containers[1].WorkingDir).To(Equal("/root")) for _, ctr := range []string{"top1", "top2"} { v, ok := pod.GetAnnotations()["io.containers.autoupdate/"+ctr] diff -Nru libpod-3.2.1+ds1/test/e2e/generate_systemd_test.go libpod-3.4.4+ds1/test/e2e/generate_systemd_test.go --- libpod-3.2.1+ds1/test/e2e/generate_systemd_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/generate_systemd_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman generate systemd", func() { @@ -54,7 +55,7 @@ It("podman generate systemd bad restart-policy value", func() { session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "--restart-policy", "bogus", "foobar"}) session.WaitWithDefaultTimeout() @@ -65,11 +66,11 @@ It("podman generate systemd with --no-header=true", func() { session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "foobar", "--no-header=true"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).NotTo(ContainSubstring("autogenerated by")) }) @@ -77,11 +78,11 @@ It("podman generate systemd with --no-header", func() { session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "foobar", "--no-header"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).NotTo(ContainSubstring("autogenerated by")) }) @@ -89,11 +90,11 @@ It("podman generate systemd with --no-header=false", func() { session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "foobar", "--no-header=false"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("autogenerated by")) }) @@ -101,11 +102,11 @@ It("podman generate systemd good timeout value", func() { session := podmanTest.Podman([]string{"create", "--name", "foobar", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "--time", "1234", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("TimeoutStopSec=1294")) Expect(session.OutputToString()).To(ContainSubstring(" stop -t 1234 ")) }) @@ -113,11 +114,11 @@ It("podman generate systemd", func() { n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "nginx"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // The podman commands in the unit should not contain the root flags Expect(session.OutputToString()).ToNot(ContainSubstring(" --runroot")) @@ -126,11 +127,11 @@ It("podman generate systemd --files --name", func() { n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--files", "--name", "nginx"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) for _, file := range session.OutputToStringArray() { os.Remove(file) @@ -141,30 +142,30 @@ It("podman generate systemd with timeout", func() { n := podmanTest.Podman([]string{"run", "--name", "nginx", "-dt", nginx}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "5", "nginx"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("podman stop -t 5")) }) It("podman generate systemd pod --name", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) @@ -185,15 +186,15 @@ It("podman generate systemd pod --name --files", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--files", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) for _, file := range session.OutputToStringArray() { os.Remove(file) @@ -206,16 +207,15 @@ It("podman generate systemd --new --name foo", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "-t", "42", "--name", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# container-foo.service")) Expect(session.OutputToString()).To(ContainSubstring(" --replace ")) - Expect(session.OutputToString()).To(ContainSubstring(" stop --ignore --cidfile %t/container-foo.ctr-id -t 42")) if !IsRemote() { // The podman commands in the unit should contain the root flags if generate systemd --new is used Expect(session.OutputToString()).To(ContainSubstring(" --runroot")) @@ -225,39 +225,38 @@ It("podman generate systemd --new --name=foo", func() { n := podmanTest.Podman([]string{"create", "--name=foo", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "-t", "42", "--name", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# container-foo.service")) Expect(session.OutputToString()).To(ContainSubstring(" --replace ")) - Expect(session.OutputToString()).To(ContainSubstring(" stop --ignore --cidfile %t/container-foo.ctr-id -t 42")) }) It("podman generate systemd --new without explicit detaching param", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) - Expect(session.OutputToString()).To(ContainSubstring("--cgroups=no-conmon -d")) + Expect(session.OutputToString()).To(ContainSubstring(" -d ")) }) It("podman generate systemd --new with explicit detaching param in middle", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("--name foo alpine top")) @@ -266,44 +265,44 @@ It("podman generate systemd --new pod", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(" pod create ")) }) It("podman generate systemd --new=false pod", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new=false", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).NotTo(ContainSubstring(" pod create ")) }) It("podman generate systemd --new=true pod", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--time", "42", "--name", "--new=true", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(" pod create ")) }) It("podman generate systemd --container-prefix con", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--container-prefix", "con", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# con-foo.service")) @@ -313,11 +312,11 @@ It("podman generate systemd --separator _", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--name", "--separator", "_", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# container_foo.service")) @@ -326,19 +325,19 @@ It("podman generate systemd pod --pod-prefix p", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--pod-prefix", "p", "--name", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# p-foo.service")) @@ -350,19 +349,19 @@ It("podman generate systemd pod --pod-prefix p --container-prefix con --separator _ change all prefixes/separator", func() { n := podmanTest.Podman([]string{"pod", "create", "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--container-prefix", "con", "--pod-prefix", "p", "--separator", "_", "--name", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# p_foo.service")) @@ -380,19 +379,19 @@ n := podmanTest.Podman([]string{"pod", "create", "--pod-id-file", tmpFile, "--name", "foo"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-1", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) n = podmanTest.Podman([]string{"create", "--pod", "foo", "--name", "foo-2", "alpine", "top"}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--new", "--name", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grepping the output (in addition to unit tests) Expect(session.OutputToString()).To(ContainSubstring("# pod-foo.service")) @@ -407,11 +406,11 @@ It("podman generate systemd --format json", func() { n := podmanTest.Podman([]string{"create", "--name", "foo", ALPINE}) n.WaitWithDefaultTimeout() - Expect(n.ExitCode()).To(Equal(0)) + Expect(n).Should(Exit(0)) session := podmanTest.Podman([]string{"generate", "systemd", "--format", "json", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) @@ -419,20 +418,20 @@ // Regression test for #9034 session := podmanTest.Podman([]string{"create", "--name", "foo", "--log-driver=journald", "--log-opt=tag={{.Name}}", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "--new", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(" --log-opt=tag={{.Name}} ")) session = podmanTest.Podman([]string{"pod", "create", "--name", "pod", "--label", "key={{someval}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"generate", "systemd", "--new", "pod"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(" --label key={{someval}}")) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/healthcheck_run_test.go libpod-3.4.4+ds1/test/e2e/healthcheck_run_test.go --- libpod-3.2.1+ds1/test/e2e/healthcheck_run_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/healthcheck_run_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,9 +5,11 @@ "os" "time" + define "github.com/containers/podman/v3/libpod/define" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman healthcheck run", func() { @@ -44,26 +46,26 @@ It("podman disable healthcheck with --no-healthcheck on valid container", func() { session := podmanTest.Podman([]string{"run", "-dt", "--no-healthcheck", "--name", "hc", healthcheck}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(125)) + Expect(hc).Should(Exit(125)) }) It("podman disable healthcheck with --health-cmd=none on valid container", func() { session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "none", "--name", "hc", healthcheck}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(125)) + Expect(hc).Should(Exit(125)) }) It("podman healthcheck on valid container", func() { Skip("Extremely consistent flake - re-enable on debugging") session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", healthcheck}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) exitCode := 999 @@ -78,42 +80,47 @@ time.Sleep(1 * time.Second) } Expect(exitCode).To(Equal(0)) + + ps := podmanTest.Podman([]string{"ps"}) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(ps.OutputToString()).To(ContainSubstring("(healthy)")) }) It("podman healthcheck that should fail", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "quay.io/libpod/badhealthcheck:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) }) It("podman healthcheck on stopped container", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", healthcheck, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(125)) + Expect(hc).Should(Exit(125)) }) It("podman healthcheck on container without healthcheck", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(125)) + Expect(hc).Should(Exit(125)) }) It("podman healthcheck should be starting", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.InspectContainer("hc") Expect(inspect[0].State.Healthcheck.Status).To(Equal("starting")) }) @@ -121,19 +128,19 @@ It("podman healthcheck failed checks in start-period should not change status", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-start-period", "2m", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) hc = podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) hc = podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) inspect := podmanTest.InspectContainer("hc") Expect(inspect[0].State.Healthcheck.Status).To(Equal("starting")) @@ -142,65 +149,82 @@ It("podman healthcheck failed checks must reach retries before unhealthy ", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) inspect := podmanTest.InspectContainer("hc") Expect(inspect[0].State.Healthcheck.Status).To(Equal("starting")) hc = podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) inspect = podmanTest.InspectContainer("hc") - Expect(inspect[0].State.Healthcheck.Status).To(Equal("unhealthy")) + Expect(inspect[0].State.Healthcheck.Status).To(Equal(define.HealthCheckUnhealthy)) }) It("podman healthcheck good check results in healthy even in start-period", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-start-period", "2m", "--health-retries", "2", "--health-cmd", "ls || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(0)) + Expect(hc).Should(Exit(0)) inspect := podmanTest.InspectContainer("hc") - Expect(inspect[0].State.Healthcheck.Status).To(Equal("healthy")) + Expect(inspect[0].State.Healthcheck.Status).To(Equal(define.HealthCheckHealthy)) + }) + + It("podman healthcheck unhealthy but valid arguments check", func() { + session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "[\"ls\", \"/foo\"]", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) + hc.WaitWithDefaultTimeout() + Expect(hc).Should(Exit(1)) }) It("podman healthcheck single healthy result changes failed to healthy", func() { session := podmanTest.Podman([]string{"run", "-dt", "--name", "hc", "--health-retries", "2", "--health-cmd", "ls /foo || exit 1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hc := podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) inspect := podmanTest.InspectContainer("hc") Expect(inspect[0].State.Healthcheck.Status).To(Equal("starting")) hc = podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(1)) + Expect(hc).Should(Exit(1)) inspect = podmanTest.InspectContainer("hc") - Expect(inspect[0].State.Healthcheck.Status).To(Equal("unhealthy")) + Expect(inspect[0].State.Healthcheck.Status).To(Equal(define.HealthCheckUnhealthy)) foo := podmanTest.Podman([]string{"exec", "hc", "touch", "/foo"}) foo.WaitWithDefaultTimeout() - Expect(foo.ExitCode()).To(BeZero()) + Expect(foo).Should(Exit(0)) hc = podmanTest.Podman([]string{"healthcheck", "run", "hc"}) hc.WaitWithDefaultTimeout() - Expect(hc.ExitCode()).To(Equal(0)) + Expect(hc).Should(Exit(0)) inspect = podmanTest.InspectContainer("hc") - Expect(inspect[0].State.Healthcheck.Status).To(Equal("healthy")) + Expect(inspect[0].State.Healthcheck.Status).To(Equal(define.HealthCheckHealthy)) + + // Test podman ps --filter heath is working (#11687) + ps := podmanTest.Podman([]string{"ps", "--filter", "health=healthy"}) + ps.WaitWithDefaultTimeout() + Expect(ps).Should(Exit(0)) + Expect(len(ps.OutputToStringArray())).To(Equal(2)) + Expect(ps.OutputToString()).To(ContainSubstring("hc")) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/history_test.go libpod-3.4.4+ds1/test/e2e/history_test.go --- libpod-3.2.1+ds1/test/e2e/history_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/history_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman history", func() { @@ -35,40 +36,40 @@ It("podman history output flag", func() { session := podmanTest.Podman([]string{"history", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) It("podman history with GO template", func() { session := podmanTest.Podman([]string{"history", "--format", "{{.ID}} {{.Created}}", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) It("podman history with human flag", func() { session := podmanTest.Podman([]string{"history", "--human=false", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) It("podman history with quiet flag", func() { session := podmanTest.Podman([]string{"history", "-qH", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) It("podman history with no-trunc flag", func() { session := podmanTest.Podman([]string{"history", "--no-trunc", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) session = podmanTest.Podman([]string{"history", "--no-trunc", "--format", "{{.ID}}", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) lines := session.OutputToStringArray() Expect(len(lines)).To(BeNumerically(">", 0)) // the image id must be 64 chars long @@ -76,7 +77,7 @@ session = podmanTest.Podman([]string{"history", "--no-trunc", "--format", "{{.CreatedBy}}", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) lines = session.OutputToStringArray() Expect(len(lines)).To(BeNumerically(">", 0)) Expect(session.OutputToString()).ToNot(ContainSubstring("...")) @@ -87,7 +88,7 @@ It("podman history with json flag", func() { session := podmanTest.Podman([]string{"history", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/image_scp_test.go libpod-3.4.4+ds1/test/e2e/image_scp_test.go --- libpod-3.2.1+ds1/test/e2e/image_scp_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/image_scp_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,104 @@ +package integration + +import ( + "io/ioutil" + "os" + + "github.com/containers/common/pkg/config" + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("podman image scp", func() { + ConfPath := struct { + Value string + IsSet bool + }{} + var ( + tempdir string + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + ConfPath.Value, ConfPath.IsSet = os.LookupEnv("CONTAINERS_CONF") + conf, err := ioutil.TempFile("", "containersconf") + if err != nil { + panic(err) + } + os.Setenv("CONTAINERS_CONF", conf.Name()) + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + }) + + AfterEach(func() { + podmanTest.Cleanup() + os.Remove(os.Getenv("CONTAINERS_CONF")) + if ConfPath.IsSet { + os.Setenv("CONTAINERS_CONF", ConfPath.Value) + } else { + os.Unsetenv("CONTAINERS_CONF") + } + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman image scp quiet flag", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + scp := podmanTest.Podman([]string{"image", "scp", "-q", ALPINE}) + scp.WaitWithDefaultTimeout() + Expect(scp).To(Exit(0)) + }) + + It("podman image scp bogus image", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + scp := podmanTest.Podman([]string{"image", "scp", "FOOBAR"}) + scp.WaitWithDefaultTimeout() + Expect(scp).To(ExitWithError()) + }) + + It("podman image scp with proper connection", func() { + if IsRemote() { + Skip("this test is only for non-remote") + } + cmd := []string{"system", "connection", "add", + "--default", + "QA", + "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + cfg, err := config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg.Engine.ActiveService).To(Equal("QA")) + Expect(cfg.Engine.ServiceDestinations["QA"]).To(Equal( + config.Destination{ + URI: "ssh://root@server.fubar.com:2222/run/podman/podman.sock", + }, + )) + + scp := podmanTest.Podman([]string{"image", "scp", ALPINE, "QA::"}) + scp.Wait(45) + // exit with error because we cannot make an actual ssh connection + // This tests that the input we are given is validated and prepared correctly + // Error: failed to connect: dial tcp: address foo: missing port in address + Expect(scp).To(ExitWithError()) + Expect(scp.ErrorToString()).To(ContainSubstring( + "Error: failed to connect: dial tcp 66.151.147.142:2222: i/o timeout", + )) + + }) + +}) diff -Nru libpod-3.2.1+ds1/test/e2e/image_sign_test.go libpod-3.4.4+ds1/test/e2e/image_sign_test.go --- libpod-3.2.1+ds1/test/e2e/image_sign_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/image_sign_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman image sign", func() { @@ -55,7 +56,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"image", "sign", "--directory", sigDir, "--sign-by", "foo@bar.com", "docker://library/alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, err = os.Stat(filepath.Join(sigDir, "library")) Expect(err).To(BeNil()) }) @@ -69,7 +70,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"image", "sign", "--all", "--directory", sigDir, "--sign-by", "foo@bar.com", "docker://library/alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fInfos, err := ioutil.ReadDir(filepath.Join(sigDir, "library")) Expect(err).To(BeNil()) Expect(len(fInfos) > 1).To(BeTrue()) diff -Nru libpod-3.2.1+ds1/test/e2e/images_test.go libpod-3.4.4+ds1/test/e2e/images_test.go --- libpod-3.2.1+ds1/test/e2e/images_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/images_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -144,7 +144,7 @@ result := podmanTest.Podman([]string{"images", "-q", "-f", "reference=quay.io*"}) result.WaitWithDefaultTimeout() Expect(result).Should(Exit(0)) - Expect(len(result.OutputToStringArray())).To(Equal(8)) + Expect(len(result.OutputToStringArray())).To(Equal(7)) retalpine := podmanTest.Podman([]string{"images", "-f", "reference=a*pine"}) retalpine.WaitWithDefaultTimeout() diff -Nru libpod-3.2.1+ds1/test/e2e/import_test.go libpod-3.4.4+ds1/test/e2e/import_test.go --- libpod-3.2.1+ds1/test/e2e/import_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/import_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman import", func() { @@ -41,15 +42,15 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", outfile, "foobar.com/imported-image:latest"}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Equal(0)) + Expect(importImage).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "--type", "image", "foobar.com/imported-image:latest"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) }) It("podman import without reference", func() { @@ -59,16 +60,16 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", outfile}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Equal(0)) + Expect(importImage).Should(Exit(0)) // tag the image which proves it is in R/W storage tag := podmanTest.Podman([]string{"tag", importImage.OutputToString(), "foo"}) tag.WaitWithDefaultTimeout() - Expect(tag.ExitCode()).To(BeZero()) + Expect(tag).Should(Exit(0)) }) It("podman import with message flag", func() { @@ -78,15 +79,15 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", "--message", "importing container test message", outfile, "imported-image"}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Equal(0)) + Expect(importImage).Should(Exit(0)) results := podmanTest.Podman([]string{"history", "imported-image", "--format", "{{.Comment}}"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) Expect(results.LineInOutputStartsWith("importing container test message")).To(BeTrue()) }) @@ -97,15 +98,15 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", "--change", "CMD=/bin/bash", outfile, "imported-image"}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Equal(0)) + Expect(importImage).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "imported-image"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) imageData := results.InspectImageJSON() Expect(imageData[0].Config.Cmd[0]).To(Equal("/bin/sh")) Expect(imageData[0].Config.Cmd[1]).To(Equal("-c")) @@ -119,15 +120,15 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", "--change", "CMD /bin/sh", outfile, "imported-image"}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Equal(0)) + Expect(importImage).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "imported-image"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) imageData := results.InspectImageJSON() Expect(imageData[0].Config.Cmd[0]).To(Equal("/bin/sh")) Expect(imageData[0].Config.Cmd[1]).To(Equal("-c")) @@ -141,15 +142,15 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", "--change", "CMD [\"/bin/bash\"]", outfile, "imported-image"}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Equal(0)) + Expect(importImage).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "imported-image"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) imageData := results.InspectImageJSON() Expect(imageData[0].Config.Cmd[0]).To(Equal("/bin/bash")) }) @@ -161,14 +162,14 @@ export := podmanTest.Podman([]string{"export", "-o", outfile, cid}) export.WaitWithDefaultTimeout() - Expect(export.ExitCode()).To(Equal(0)) + Expect(export).Should(Exit(0)) importImage := podmanTest.Podman([]string{"import", "--signature-policy", "/no/such/file", outfile}) importImage.WaitWithDefaultTimeout() - Expect(importImage.ExitCode()).To(Not(Equal(0))) + Expect(importImage).To(ExitWithError()) result := podmanTest.Podman([]string{"import", "--signature-policy", "/etc/containers/policy.json", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/info_test.go libpod-3.4.4+ds1/test/e2e/info_test.go --- libpod-3.2.1+ds1/test/e2e/info_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/info_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -67,16 +67,25 @@ It("podman info --format GO template", func() { session := podmanTest.Podman([]string{"info", "--format", "{{.Store.GraphRoot}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman info --format GO template", func() { session := podmanTest.Podman([]string{"info", "--format", "{{.Registries}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("registry")) }) + It("podman info --format GO template plugins", func() { + session := podmanTest.Podman([]string{"info", "--format", "{{.Plugins}}"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("local")) + Expect(session.OutputToString()).To(ContainSubstring("journald")) + Expect(session.OutputToString()).To(ContainSubstring("bridge")) + }) + It("podman info rootless storage path", func() { SkipIfNotRootless("test of rootless_storage_path is only meaningful as rootless") SkipIfRemote("Only tests storage on local client") @@ -101,11 +110,11 @@ u, err := user.Current() Expect(err).To(BeNil()) + // Cannot use podmanTest.Podman() and test for storage path expect := filepath.Join("/tmp", os.Getenv("HOME"), u.Username, u.Uid, "storage") podmanPath := podmanTest.PodmanTest.PodmanBinary - cmd := exec.Command(podmanPath, "info", "--format", "{{.Store.GraphRoot}}") + cmd := exec.Command(podmanPath, "info", "--format", "{{.Store.GraphRoot -}}") out, err := cmd.CombinedOutput() - fmt.Println(string(out)) Expect(err).To(BeNil()) Expect(string(out)).To(Equal(expect)) }) @@ -113,13 +122,13 @@ It("podman info check RemoteSocket", func() { session := podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Path}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(MatchRegexp("/run/.*podman.*sock")) if IsRemote() { session = podmanTest.Podman([]string{"info", "--format", "{{.Host.RemoteSocket.Exists}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("true")) } }) diff -Nru libpod-3.2.1+ds1/test/e2e/init_test.go libpod-3.4.4+ds1/test/e2e/init_test.go --- libpod-3.2.1+ds1/test/e2e/init_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/init_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman init", func() { @@ -35,26 +36,26 @@ It("podman init bogus container", func() { session := podmanTest.Podman([]string{"start", "123456"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman init with no arguments", func() { session := podmanTest.Podman([]string{"start"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman init single container by ID", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() init := podmanTest.Podman([]string{"init", cid}) init.WaitWithDefaultTimeout() - Expect(init.ExitCode()).To(Equal(0)) + Expect(init).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].State.Status).To(Equal("created")) }) @@ -63,13 +64,13 @@ name := "test1" session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) init := podmanTest.Podman([]string{"init", name}) init.WaitWithDefaultTimeout() - Expect(init.ExitCode()).To(Equal(0)) + Expect(init).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", name}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].State.Status).To(Equal("created")) }) @@ -78,13 +79,13 @@ SkipIfRemote("--latest flag n/a") session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) init := podmanTest.Podman([]string{"init", "--latest"}) init.WaitWithDefaultTimeout() - Expect(init.ExitCode()).To(Equal(0)) + Expect(init).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].State.Status).To(Equal("created")) }) @@ -92,29 +93,29 @@ It("podman init all three containers, one running", func() { session := podmanTest.Podman([]string{"create", "--name", "test1", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"create", "--name", "test2", ALPINE, "ls"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) session3 := podmanTest.Podman([]string{"run", "--name", "test3", "-d", ALPINE, "top"}) session3.WaitWithDefaultTimeout() - Expect(session3.ExitCode()).To(Equal(0)) + Expect(session3).Should(Exit(0)) init := podmanTest.Podman([]string{"init", "--all"}) init.WaitWithDefaultTimeout() - Expect(init.ExitCode()).To(Equal(0)) + Expect(init).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].State.Status).To(Equal("created")) result2 := podmanTest.Podman([]string{"inspect", "test2"}) result2.WaitWithDefaultTimeout() - Expect(result2.ExitCode()).To(Equal(0)) + Expect(result2).Should(Exit(0)) conData2 := result2.InspectContainerToJSON() Expect(conData2[0].State.Status).To(Equal("created")) result3 := podmanTest.Podman([]string{"inspect", "test3"}) result3.WaitWithDefaultTimeout() - Expect(result3.ExitCode()).To(Equal(0)) + Expect(result3).Should(Exit(0)) conData3 := result3.InspectContainerToJSON() Expect(conData3[0].State.Status).To(Equal("running")) }) @@ -122,9 +123,9 @@ It("podman init running container errors", func() { session := podmanTest.Podman([]string{"run", "--name", "init_test", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) init := podmanTest.Podman([]string{"init", "init_test"}) init.WaitWithDefaultTimeout() - Expect(init.ExitCode()).To(Equal(125)) + Expect(init).Should(Exit(125)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/inspect_test.go libpod-3.4.4+ds1/test/e2e/inspect_test.go --- libpod-3.2.1+ds1/test/e2e/inspect_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/inspect_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -38,7 +38,7 @@ It("podman inspect alpine image", func() { session := podmanTest.Podman([]string{"inspect", "--format=json", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) imageData := session.InspectImageJSON() Expect(imageData[0].RepoTags[0]).To(Equal("quay.io/libpod/alpine:latest")) @@ -50,21 +50,39 @@ Expect(session).To(ExitWithError()) }) + It("podman inspect filter should work if result contains tab", func() { + session := podmanTest.Podman([]string{"build", "--tag", "envwithtab", "build/envwithtab"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // Verify that OS and Arch are being set + inspect := podmanTest.Podman([]string{"inspect", "-f", "{{ .Config.Env }}", "envwithtab"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + // output should not be empty + // test validates fix for https://github.com/containers/podman/issues/8785 + Expect(strings.Contains(inspect.OutputToString(), "TEST")) + + session = podmanTest.Podman([]string{"rmi", "envwithtab"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + It("podman inspect with GO format", func() { session := podmanTest.Podman([]string{"inspect", "--format", "{{.ID}}", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"images", "-q", "--no-trunc", ALPINE}) result.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(strings.Contains(result.OutputToString(), session.OutputToString())) }) It("podman inspect specified type", func() { session := podmanTest.Podman([]string{"inspect", "--type", "image", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman inspect container with GO format for ConmonPidFile", func() { @@ -73,7 +91,7 @@ session = podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman inspect container with size", func() { @@ -82,7 +100,7 @@ result := podmanTest.Podman([]string{"inspect", "--size", "sizetest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].SizeRootFs).To(BeNumerically(">", 0)) Expect(*conData[0].SizeRw).To(BeNumerically(">=", 0)) @@ -95,7 +113,7 @@ result := podmanTest.Podman([]string{"inspect", "--format={{.ID}}", cid, ALPINE}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(2)) }) @@ -106,12 +124,12 @@ result := podmanTest.Podman([]string{"inspect", "--format={{.ImageID}}", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(1)) result = podmanTest.Podman([]string{"inspect", "--format={{.Image}}", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(1)) }) @@ -122,7 +140,7 @@ result := podmanTest.Podman([]string{"inspect", "--format={{.Config.CreateCommand}}", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(1)) }) @@ -130,33 +148,33 @@ SkipIfRemote("--latest flag n/a") result := podmanTest.Podman([]string{"inspect", "-l", "1234foobar"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman inspect with mount filters", func() { ctrSession := podmanTest.Podman([]string{"create", "--name", "test", "-v", "/tmp:/test1", ALPINE, "top"}) ctrSession.WaitWithDefaultTimeout() - Expect(ctrSession.ExitCode()).To(Equal(0)) + Expect(ctrSession).Should(Exit(0)) inspectSource := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Source}}"}) inspectSource.WaitWithDefaultTimeout() - Expect(inspectSource.ExitCode()).To(Equal(0)) + Expect(inspectSource).Should(Exit(0)) Expect(inspectSource.OutputToString()).To(Equal("/tmp")) inspectSrc := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Src}}"}) inspectSrc.WaitWithDefaultTimeout() - Expect(inspectSrc.ExitCode()).To(Equal(0)) + Expect(inspectSrc).Should(Exit(0)) Expect(inspectSrc.OutputToString()).To(Equal("/tmp")) inspectDestination := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Destination}}"}) inspectDestination.WaitWithDefaultTimeout() - Expect(inspectDestination.ExitCode()).To(Equal(0)) + Expect(inspectDestination).Should(Exit(0)) Expect(inspectDestination.OutputToString()).To(Equal("/test1")) inspectDst := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Dst}}"}) inspectDst.WaitWithDefaultTimeout() - Expect(inspectDst.ExitCode()).To(Equal(0)) + Expect(inspectDst).Should(Exit(0)) Expect(inspectDst.OutputToString()).To(Equal("/test1")) }) @@ -175,23 +193,23 @@ session := podmanTest.Podman([]string{"inspect", "--latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman [image,container] inspect on image", func() { baseInspect := podmanTest.Podman([]string{"inspect", ALPINE}) baseInspect.WaitWithDefaultTimeout() - Expect(baseInspect.ExitCode()).To(Equal(0)) + Expect(baseInspect).Should(Exit(0)) baseJSON := baseInspect.InspectImageJSON() Expect(len(baseJSON)).To(Equal(1)) ctrInspect := podmanTest.Podman([]string{"container", "inspect", ALPINE}) ctrInspect.WaitWithDefaultTimeout() - Expect(ctrInspect.ExitCode()).To(Not(Equal(0))) + Expect(ctrInspect).To(ExitWithError()) imageInspect := podmanTest.Podman([]string{"image", "inspect", ALPINE}) imageInspect.WaitWithDefaultTimeout() - Expect(imageInspect.ExitCode()).To(Equal(0)) + Expect(imageInspect).Should(Exit(0)) imageJSON := imageInspect.InspectImageJSON() Expect(len(imageJSON)).To(Equal(1)) @@ -202,23 +220,23 @@ ctrName := "testCtr" create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "sh"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) baseInspect := podmanTest.Podman([]string{"inspect", ctrName}) baseInspect.WaitWithDefaultTimeout() - Expect(baseInspect.ExitCode()).To(Equal(0)) + Expect(baseInspect).Should(Exit(0)) baseJSON := baseInspect.InspectContainerToJSON() Expect(len(baseJSON)).To(Equal(1)) ctrInspect := podmanTest.Podman([]string{"container", "inspect", ctrName}) ctrInspect.WaitWithDefaultTimeout() - Expect(ctrInspect.ExitCode()).To(Equal(0)) + Expect(ctrInspect).Should(Exit(0)) ctrJSON := ctrInspect.InspectContainerToJSON() Expect(len(ctrJSON)).To(Equal(1)) imageInspect := podmanTest.Podman([]string{"image", "inspect", ctrName}) imageInspect.WaitWithDefaultTimeout() - Expect(imageInspect.ExitCode()).To(Not(Equal(0))) + Expect(imageInspect).To(ExitWithError()) Expect(baseJSON[0].ID).To(Equal(ctrJSON[0].ID)) }) @@ -226,7 +244,7 @@ It("podman inspect always produces a valid array", func() { baseInspect := podmanTest.Podman([]string{"inspect", "doesNotExist"}) baseInspect.WaitWithDefaultTimeout() - Expect(baseInspect.ExitCode()).To(Not(Equal(0))) + Expect(baseInspect).To(ExitWithError()) emptyJSON := baseInspect.InspectContainerToJSON() Expect(len(emptyJSON)).To(Equal(0)) }) @@ -235,11 +253,11 @@ ctrName := "testCtr" create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "sh"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) baseInspect := podmanTest.Podman([]string{"inspect", ctrName, "doesNotExist"}) baseInspect.WaitWithDefaultTimeout() - Expect(baseInspect.ExitCode()).To(Not(Equal(0))) + Expect(baseInspect).To(ExitWithError()) baseJSON := baseInspect.InspectContainerToJSON() Expect(len(baseJSON)).To(Equal(1)) Expect(baseJSON[0].Name).To(Equal(ctrName)) @@ -250,15 +268,15 @@ ctrName := "testcontainer" create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "sh"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) tag := podmanTest.Podman([]string{"tag", ALPINE, ctrName + ":latest"}) tag.WaitWithDefaultTimeout() - Expect(tag.ExitCode()).To(Equal(0)) + Expect(tag).Should(Exit(0)) baseInspect := podmanTest.Podman([]string{"inspect", ctrName}) baseInspect.WaitWithDefaultTimeout() - Expect(baseInspect.ExitCode()).To(Equal(0)) + Expect(baseInspect).Should(Exit(0)) baseJSON := baseInspect.InspectContainerToJSON() Expect(len(baseJSON)).To(Equal(1)) Expect(baseJSON[0].Name).To(Equal(ctrName)) @@ -278,11 +296,11 @@ ALPINE, "sh"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) baseInspect := podmanTest.Podman([]string{"inspect", ctrName}) baseInspect.WaitWithDefaultTimeout() - Expect(baseInspect.ExitCode()).To(Equal(0)) + Expect(baseInspect).Should(Exit(0)) baseJSON := baseInspect.InspectContainerToJSON() Expect(len(baseJSON)).To(Equal(1)) Expect(baseJSON[0].HostConfig.SecurityOpt).To(Equal([]string{"label=type:spc_t,label=level:s0", "seccomp=unconfined"})) @@ -292,11 +310,11 @@ podName := "testpod" create := podmanTest.Podman([]string{"pod", "create", "--name", podName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", podName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) podData := inspect.InspectPodArrToJSON() Expect(podData[0].Name).To(Equal(podName)) @@ -306,11 +324,11 @@ podName := "testpod" create := podmanTest.Podman([]string{"pod", "create", "--name", podName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) podData := inspect.InspectPodArrToJSON() Expect(podData[0].Name).To(Equal(podName)) @@ -321,11 +339,11 @@ podName := "testpod" create := podmanTest.Podman([]string{"pod", "create", "--name", podName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", "--latest"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) podData := inspect.InspectPodArrToJSON() Expect(podData[0].Name).To(Equal(podName)) @@ -335,18 +353,18 @@ podName := "testpod" pod := podmanTest.Podman([]string{"pod", "create", "--name", podName}) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(Equal(0)) + Expect(pod).Should(Exit(0)) inspect1 := podmanTest.Podman([]string{"inspect", "--type", "pod", podName}) inspect1.WaitWithDefaultTimeout() - Expect(inspect1.ExitCode()).To(Equal(0)) + Expect(inspect1).Should(Exit(0)) Expect(inspect1.IsJSONOutputValid()).To(BeTrue()) podData := inspect1.InspectPodArrToJSON() infra := podData[0].Containers[0].Name inspect := podmanTest.Podman([]string{"inspect", "--latest"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) containerData := inspect.InspectContainerToJSON() Expect(containerData[0].Name).To(Equal(infra)) @@ -358,7 +376,7 @@ session := podmanTest.Podman([]string{"inspect", name, "--format", "{{.cniVersion}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("0.3.0")).To(BeTrue()) }) @@ -366,11 +384,11 @@ session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() volName := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) @@ -378,18 +396,18 @@ session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() volName := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"inspect", "--format", "{{.Name}}", volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(volName)) }) It("podman inspect --type container on a pod should fail", func() { podName := "testpod" create := podmanTest.Podman([]string{"pod", "create", "--name", podName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--type", "container", podName}) inspect.WaitWithDefaultTimeout() @@ -400,7 +418,7 @@ ctrName := "testctr" create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--type", "network", ctrName}) inspect.WaitWithDefaultTimeout() @@ -411,7 +429,7 @@ ctrName := "testctr" create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--type", "pod", ctrName}) inspect.WaitWithDefaultTimeout() @@ -422,7 +440,7 @@ ctrName := "testctr" create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "--type", "volume", ctrName}) inspect.WaitWithDefaultTimeout() @@ -433,14 +451,14 @@ It("podman inspect --format json .NetworkSettings.Ports", func() { ctnrName := "Ctnr_" + RandomString(25) - create := podmanTest.Podman([]string{"create", "--name", ctnrName, "-p", "8080:80", ALPINE}) + create := podmanTest.Podman([]string{"create", "--name", ctnrName, "-p", "8084:80", ALPINE}) create.WaitWithDefaultTimeout() Expect(create).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", `--format="{{json .NetworkSettings.Ports}}"`, ctnrName}) inspect.WaitWithDefaultTimeout() Expect(inspect).Should(Exit(0)) - Expect(inspect.OutputToString()).To(Equal(`"{"80/tcp":[{"HostIp":"","HostPort":"8080"}]}"`)) + Expect(inspect.OutputToString()).To(Equal(`"{"80/tcp":[{"HostIp":"","HostPort":"8084"}]}"`)) }) It("Verify container inspect has default network", func() { @@ -448,7 +466,7 @@ ctrName := "testctr" session := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) inspect := podmanTest.InspectContainer(ctrName) Expect(len(inspect)).To(Equal(1)) @@ -460,7 +478,7 @@ ctrName := "testctr" session := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) inspect := podmanTest.InspectContainer(ctrName) Expect(len(inspect)).To(Equal(1)) @@ -471,11 +489,11 @@ ctrName := "testctr" session := podmanTest.Podman([]string{"run", "-d", "--ulimit", "core=-1:-1", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) data := inspect.InspectContainerToJSON() ulimits := data[0].HostConfig.Ulimits @@ -495,11 +513,11 @@ ctrName := "testCtr" session := podmanTest.Podman([]string{"run", "-d", "--cap-drop", "CAP_AUDIT_WRITE", "--cap-drop", "CAP_MKNOD", "--cap-drop", "CAP_NET_RAW", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) data := inspect.InspectContainerToJSON() Expect(len(data)).To(Equal(1)) @@ -516,6 +534,6 @@ session = podmanTest.Podman([]string{"inspect", "--format", "{{.PidFile}}", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/kill_test.go libpod-3.4.4+ds1/test/e2e/kill_test.go --- libpod-3.2.1+ds1/test/e2e/kill_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/kill_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman kill", func() { @@ -41,25 +42,25 @@ It("podman container kill a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "kill", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman container kill a running container by short id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "kill", cid[:5]}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal(cid[:5])) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -67,55 +68,55 @@ It("podman kill a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"kill", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman kill a running container by id with TERM", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"kill", "-s", "9", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman kill a running container by name", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"kill", "-s", "9", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman kill a running container by id with a bogus signal", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"kill", "-s", "foobar", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) It("podman kill latest container", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := "-l" if IsRemote() { @@ -123,7 +124,7 @@ } result := podmanTest.Podman([]string{"kill", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -135,16 +136,16 @@ session := podmanTest.Podman([]string{"run", "-dt", "--cidfile", tmpFile, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToStringArray()[0] kill := podmanTest.Podman([]string{"kill", "--cidfile", tmpFile}) kill.WaitWithDefaultTimeout() - Expect(kill.ExitCode()).To(BeZero()) + Expect(kill).Should(Exit(0)) wait := podmanTest.Podman([]string{"wait", "--condition", "exited", cid}) wait.WaitWithDefaultTimeout() - Expect(wait.ExitCode()).To(BeZero()) + Expect(wait).Should(Exit(0)) }) It("podman kill multiple --cidfile", func() { @@ -160,40 +161,40 @@ session := podmanTest.Podman([]string{"run", "-dt", "--cidfile", tmpFile1, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid1 := session.OutputToStringArray()[0] session2 := podmanTest.Podman([]string{"run", "-dt", "--cidfile", tmpFile2, ALPINE, "top"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) cid2 := session2.OutputToStringArray()[0] kill := podmanTest.Podman([]string{"kill", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) kill.WaitWithDefaultTimeout() - Expect(kill.ExitCode()).To(BeZero()) + Expect(kill).Should(Exit(0)) wait := podmanTest.Podman([]string{"wait", "--condition", "exited", cid1}) wait.WaitWithDefaultTimeout() - Expect(wait.ExitCode()).To(BeZero()) + Expect(wait).Should(Exit(0)) wait = podmanTest.Podman([]string{"wait", "--condition", "exited", cid2}) wait.WaitWithDefaultTimeout() - Expect(wait.ExitCode()).To(BeZero()) + Expect(wait).Should(Exit(0)) }) It("podman stop --all", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) session = podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) session = podmanTest.Podman([]string{"kill", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/libpod_suite_remote_test.go libpod-3.4.4+ds1/test/e2e/libpod_suite_remote_test.go --- libpod-3.2.1+ds1/test/e2e/libpod_suite_remote_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/libpod_suite_remote_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -38,11 +38,25 @@ return &PodmanSessionIntegration{podmanSession} } +// PodmanSystemdScope runs the podman command in a new systemd scope +func (p *PodmanTestIntegration) PodmanSystemdScope(args []string) *PodmanSessionIntegration { + var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} + remoteArgs = append(remoteArgs, args...) + + wrapper := []string{"systemd-run", "--scope"} + if rootless.IsRootless() { + wrapper = []string{"systemd-run", "--scope", "--user"} + } + + podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, wrapper, nil) + return &PodmanSessionIntegration{podmanSession} +} + // PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration { var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} remoteArgs = append(remoteArgs, args...) - podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, extraFiles) + podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, nil, extraFiles) return &PodmanSessionIntegration{podmanSession} } diff -Nru libpod-3.2.1+ds1/test/e2e/libpod_suite_test.go libpod-3.4.4+ds1/test/e2e/libpod_suite_test.go --- libpod-3.2.1+ds1/test/e2e/libpod_suite_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/libpod_suite_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,8 @@ "os" "path/filepath" "strings" + + "github.com/containers/podman/v3/pkg/rootless" ) func IsRemote() bool { @@ -23,9 +25,19 @@ return &PodmanSessionIntegration{podmanSession} } +// PodmanSystemdScope runs the podman command in a new systemd scope +func (p *PodmanTestIntegration) PodmanSystemdScope(args []string) *PodmanSessionIntegration { + wrapper := []string{"systemd-run", "--scope"} + if rootless.IsRootless() { + wrapper = []string{"systemd-run", "--scope", "--user"} + } + podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, wrapper, nil) + return &PodmanSessionIntegration{podmanSession} +} + // PodmanExtraFiles is the exec call to podman on the filesystem and passes down extra files func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os.File) *PodmanSessionIntegration { - podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, extraFiles) + podmanSession := p.PodmanAsUserBase(args, 0, 0, "", nil, false, false, nil, extraFiles) return &PodmanSessionIntegration{podmanSession} } diff -Nru libpod-3.2.1+ds1/test/e2e/load_test.go libpod-3.4.4+ds1/test/e2e/load_test.go --- libpod-3.2.1+ds1/test/e2e/load_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/load_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman load", func() { @@ -43,15 +44,15 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-i", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load compressed tar file", func() { @@ -59,19 +60,19 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) compress := SystemExec("gzip", []string{outfile}) - Expect(compress.ExitCode()).To(Equal(0)) + Expect(compress).Should(Exit(0)) outfile = outfile + ".gz" rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-i", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load oci-archive image", func() { @@ -79,15 +80,15 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-i", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load oci-archive with signature", func() { @@ -95,15 +96,15 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "--signature-policy", "/etc/containers/policy.json", "-i", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load with quiet flag", func() { @@ -111,15 +112,15 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-q", "-i", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load directory", func() { @@ -128,15 +129,15 @@ save := podmanTest.Podman([]string{"save", "--format", "oci-dir", "-o", outdir, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-i", outdir}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman-remote load directory", func() { @@ -148,7 +149,7 @@ result := podmanTest.Podman([]string{"load", "-i", podmanTest.TempDir}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) errMsg := fmt.Sprintf("remote client supports archives only but %q is a directory", podmanTest.TempDir) found, _ := result.ErrorGrepString(errMsg) @@ -170,45 +171,45 @@ pull := podmanTest.Podman([]string{"pull", alpVersion}) pull.WaitWithDefaultTimeout() - Expect(pull.ExitCode()).To(Equal(0)) + Expect(pull).Should(Exit(0)) save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE, alpVersion}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE, alpVersion}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-i", outfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ALPINE}) inspect.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) inspect = podmanTest.Podman([]string{"inspect", alpVersion}) inspect.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load localhost registry from scratch", func() { outfile := filepath.Join(podmanTest.TempDir, "load_test.tar.gz") setup := podmanTest.Podman([]string{"tag", ALPINE, "hello:world"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", "hello:world"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"rmi", "hello:world"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) load := podmanTest.Podman([]string{"load", "-i", outfile}) load.WaitWithDefaultTimeout() - Expect(load.ExitCode()).To(Equal(0)) + Expect(load).Should(Exit(0)) result := podmanTest.Podman([]string{"images", "hello:world"}) result.WaitWithDefaultTimeout() @@ -221,19 +222,19 @@ setup := podmanTest.Podman([]string{"tag", ALPINE, "hello"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", "hello"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"rmi", "hello"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) load := podmanTest.Podman([]string{"load", "-i", outfile}) load.WaitWithDefaultTimeout() - Expect(load.ExitCode()).To(Equal(0)) + Expect(load).Should(Exit(0)) result := podmanTest.Podman([]string{"images", "hello:latest"}) result.WaitWithDefaultTimeout() @@ -247,19 +248,19 @@ setup := podmanTest.Podman([]string{"tag", ALPINE, "hello:world"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-dir", "hello:world"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"rmi", "hello:world"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) load := podmanTest.Podman([]string{"load", "-i", outfile}) load.WaitWithDefaultTimeout() - Expect(load.ExitCode()).To(Equal(0)) + Expect(load).Should(Exit(0)) result := podmanTest.Podman([]string{"images", "load:latest"}) result.WaitWithDefaultTimeout() @@ -272,23 +273,23 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) session := SystemExec("xz", []string{outfile}) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) result := podmanTest.Podman([]string{"load", "-i", outfile + ".xz"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman load multi-image archive", func() { result := podmanTest.Podman([]string{"load", "-i", "./testdata/docker-two-images.tar.xz"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.LineInOutputContains("example.com/empty:latest")).To(BeTrue()) Expect(result.LineInOutputContains("example.com/empty/but:different")).To(BeTrue()) }) diff -Nru libpod-3.2.1+ds1/test/e2e/login_logout_test.go libpod-3.4.4+ds1/test/e2e/login_logout_test.go --- libpod-3.2.1+ds1/test/e2e/login_logout_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/login_logout_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,6 +13,7 @@ . "github.com/onsi/ginkgo" "github.com/onsi/ginkgo/config" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman login and logout", func() { @@ -54,7 +55,7 @@ session := podmanTest.Podman([]string{"run", "--entrypoint", "htpasswd", "registry:2.6", "-Bbn", "podmantest", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) f, _ := os.Create(filepath.Join(authPath, "htpasswd")) defer f.Close() @@ -78,12 +79,12 @@ session = podmanTest.Podman([]string{"run", "-d", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), "-e", strings.Join([]string{"REGISTRY_HTTP_ADDR=0.0.0.0", strconv.Itoa(port)}, ":"), "--name", "registry", "-v", - strings.Join([]string{authPath, "/auth"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", + strings.Join([]string{authPath, "/auth:Z"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", - "-v", strings.Join([]string{certPath, "/certs"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", + "-v", strings.Join([]string{certPath, "/certs:Z"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", "registry:2.6"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -96,18 +97,36 @@ os.RemoveAll(certDirPath) }) + readAuthInfo := func(filePath string) map[string]interface{} { + authBytes, err := ioutil.ReadFile(filePath) + Expect(err).To(BeNil()) + + var authInfo map[string]interface{} + err = json.Unmarshal(authBytes, &authInfo) + Expect(err).To(BeNil()) + fmt.Println(authInfo) + + const authsKey = "auths" + Expect(authInfo).To(HaveKey(authsKey)) + + auths, ok := authInfo[authsKey].(map[string]interface{}) + Expect(ok).To(BeTrue()) + + return auths + } + It("podman login and logout", func() { session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logout", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() @@ -137,41 +156,38 @@ session := podmanTest.Podman([]string{"login", "-u", "podmantest", "-p", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logout"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman login and logout with flag --authfile", func() { authFile := filepath.Join(podmanTest.TempDir, "auth.json") session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--authfile", authFile, server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) - authInfo, _ := ioutil.ReadFile(authFile) - var info map[string]interface{} - json.Unmarshal(authInfo, &info) - fmt.Println(info) + readAuthInfo(authFile) // push should fail with nonexistent authfile session = podmanTest.Podman([]string{"push", "--authfile", "/tmp/nonexistent", ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"push", "--authfile", authFile, ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--authfile", authFile, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // logout should fail with nonexistent authfile session = podmanTest.Podman([]string{"logout", "--authfile", "/tmp/nonexistent", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"logout", "--authfile", authFile, server}) }) @@ -179,15 +195,15 @@ It("podman login and logout with --tls-verify", func() { session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--tls-verify=false", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logout", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman login and logout with --cert-dir", func() { certDir := filepath.Join(podmanTest.TempDir, "certs") @@ -198,15 +214,15 @@ session := podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "--cert-dir", certDir, server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", "--cert-dir", certDir, ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logout", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman login and logout with multi registry", func() { certDir := filepath.Join(os.Getenv("HOME"), ".config/containers/certs.d", "localhost:9001") @@ -219,13 +235,16 @@ setup.WaitWithDefaultTimeout() defer os.RemoveAll(certDir) + // N/B: This second registry container shares the same auth and cert dirs + // as the registry started from BeforeEach(). Since this one starts + // second, re-labeling the volumes should keep SELinux happy. session := podmanTest.Podman([]string{"run", "-d", "-p", "9001:9001", "-e", "REGISTRY_HTTP_ADDR=0.0.0.0:9001", "--name", "registry1", "-v", - strings.Join([]string{authPath, "/auth"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", + strings.Join([]string{authPath, "/auth:z"}, ":"), "-e", "REGISTRY_AUTH=htpasswd", "-e", "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm", "-e", "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd", - "-v", strings.Join([]string{certPath, "/certs"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", + "-v", strings.Join([]string{certPath, "/certs:z"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", "registry:2.6"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry1", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -233,11 +252,11 @@ session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, "localhost:9001/test-alpine"}) session.WaitWithDefaultTimeout() @@ -245,19 +264,19 @@ session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "localhost:9001"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, "localhost:9001/test-alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logout", server}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() @@ -265,15 +284,15 @@ session = podmanTest.Podman([]string{"push", ALPINE, "localhost:9001/test-alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"login", "--username", "podmantest", "--password", "test", "localhost:9001"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"logout", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", ALPINE, testImg}) session.WaitWithDefaultTimeout() @@ -283,4 +302,204 @@ session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) }) + + It("podman login and logout with repository", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testRepository := server + "/podmantest" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testRepository, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testRepository)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepository, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo = readAuthInfo(authFile) + Expect(authInfo).NotTo(HaveKey(testRepository)) + }) + + It("podman login and logout with repository and specified image", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testTarget := server + "/podmantest/test-alpine" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testTarget)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + }) + + It("podman login and logout with repository with fallback", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testRepos := []string{ + server + "/podmantest", + server, + } + for _, testRepo := range testRepos { + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testRepo, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + } + + authInfo := readAuthInfo(authFile) + Expect(authInfo).To(HaveKey(testRepos[0])) + Expect(authInfo).To(HaveKey(testRepos[1])) + + session := podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testRepos[0] + "/test-image-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepos[0], + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testRepos[0] + "/test-image-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "logout", + "--authfile", authFile, + testRepos[1], + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + authInfo = readAuthInfo(authFile) + Expect(authInfo).NotTo(HaveKey(testRepos[0])) + Expect(authInfo).NotTo(HaveKey(testRepos[1])) + }) + + It("podman login with repository invalid arguments", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + for _, invalidArg := range []string{ + "https://" + server + "/podmantest", + server + "/podmantest/image:latest", + } { + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + invalidArg, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitWithError()) + } + }) + + It("podman login and logout with repository push with invalid auth.json credentials", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + // only `server` contains the correct login data + err := ioutil.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": { + "%s/podmantest": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" }, + "%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" } + }}`, server, server)), 0644) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, server + "/podmantest/test-image", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, server + "/test-image", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + }) + + It("podman login and logout with repository pull with wrong auth.json credentials", func() { + authFile := filepath.Join(podmanTest.TempDir, "auth.json") + + testTarget := server + "/podmantest/test-alpine" + session := podmanTest.Podman([]string{ + "login", + "-u", "podmantest", + "-p", "test", + "--authfile", authFile, + testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{ + "push", + "--authfile", authFile, + ALPINE, testTarget, + }) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // only `server + /podmantest` and `server` have the correct login data + err := ioutil.WriteFile(authFile, []byte(fmt.Sprintf(`{"auths": { + "%s/podmantest/test-alpine": { "auth": "cG9kbWFudGVzdDp3cm9uZw==" }, + "%s/podmantest": { "auth": "cG9kbWFudGVzdDp0ZXN0" }, + "%s": { "auth": "cG9kbWFudGVzdDp0ZXN0" } + }}`, server, server, server)), 0644) + Expect(err).To(BeNil()) + + session = podmanTest.Podman([]string{ + "pull", + "--authfile", authFile, + server + "/podmantest/test-alpine", + }) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/logs_test.go libpod-3.4.4+ds1/test/e2e/logs_test.go --- libpod-3.2.1+ds1/test/e2e/logs_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/logs_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -5,6 +5,7 @@ "os" "os/exec" "strings" + "time" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" @@ -12,6 +13,19 @@ . "github.com/onsi/gomega/gexec" ) +func isEventBackendJournald(podmanTest *PodmanTestIntegration) bool { + if !podmanTest.RemoteTest { + // If not remote test, '--events-backend' is set to 'file' or 'none' + return false + } + info := podmanTest.Podman([]string{"info", "--format", "{{.Host.EventLogger}}"}) + info.WaitWithDefaultTimeout() + if info.OutputToString() == "journald" { + return true + } + return false +} + var _ = Describe("Podman logs", func() { var ( tempdir string @@ -37,8 +51,18 @@ }) for _, log := range []string{"k8s-file", "journald", "json-file"} { + // This is important to move the 'log' var to the correct scope under Ginkgo flow. + log := log + + skipIfJournaldInContainer := func() { + if log == "journald" { + SkipIfInContainer("journalctl inside a container doesn't work correctly") + } + } It("all lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -52,6 +76,8 @@ }) It("tail two lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -64,6 +90,8 @@ }) It("tail zero lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -76,6 +104,8 @@ }) It("tail 99 lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -88,6 +118,8 @@ }) It("tail 800 lines: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "i=1; while [ \"$i\" -ne 1000 ]; do echo \"line $i\"; i=$((i + 1)); done"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -100,6 +132,8 @@ }) It("tail 2 lines with timestamps: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -112,6 +146,8 @@ }) It("since time 2017-08-07: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -124,6 +160,8 @@ }) It("since duration 10m: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -135,22 +173,57 @@ Expect(len(results.OutputToStringArray())).To(Equal(3)) }) + It("until duration 10m: "+log, func() { + skipIfJournaldInContainer() + + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--until", "10m", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("until time NOW: "+log, func() { + skipIfJournaldInContainer() + + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + now := time.Now() + now = now.Add(time.Minute * 1) + nowS := now.Format(time.RFC3339) + results := podmanTest.Podman([]string{"logs", "--until", nowS, cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + It("latest and container name should fail: "+log, func() { + skipIfJournaldInContainer() + results := podmanTest.Podman([]string{"logs", "-l", "foobar"}) results.WaitWithDefaultTimeout() Expect(results).To(ExitWithError()) }) It("two containers showing short container IDs: "+log, func() { + skipIfJournaldInContainer() SkipIfRemote("FIXME: podman-remote logs does not support showing two containers at the same time") + log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) log1.WaitWithDefaultTimeout() - Expect(log1.ExitCode()).To(Equal(0)) + Expect(log1).Should(Exit(0)) cid1 := log1.OutputToString() log2 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) log2.WaitWithDefaultTimeout() - Expect(log2.ExitCode()).To(Equal(0)) + Expect(log2).Should(Exit(0)) cid2 := log2.OutputToString() results := podmanTest.Podman([]string{"logs", cid1, cid2}) @@ -163,7 +236,9 @@ }) It("podman logs on a created container should result in 0 exit code: "+log, func() { - session := podmanTest.Podman([]string{"create", "-t", "--name", "log", ALPINE}) + skipIfJournaldInContainer() + + session := podmanTest.Podman([]string{"create", "--log-driver", log, "-t", "--name", "log", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).To(Exit(0)) @@ -173,6 +248,8 @@ }) It("streaming output: "+log, func() { + skipIfJournaldInContainer() + containerName := "logs-f" logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-dt", ALPINE, "sh", "-c", "echo podman-1; sleep 1; echo podman-2"}) @@ -181,6 +258,14 @@ results := podmanTest.Podman([]string{"logs", "-f", containerName}) results.WaitWithDefaultTimeout() + + if log == "journald" && !isEventBackendJournald(podmanTest) { + // --follow + journald log-driver is only supported with journald events-backend(PR #10431) + Expect(results).To(Exit(125)) + Expect(results.ErrorToString()).To(ContainSubstring("using --follow with the journald --log-driver but without the journald --events-backend")) + return + } + Expect(results).To(Exit(0)) Expect(results.OutputToString()).To(ContainSubstring("podman-1")) @@ -204,6 +289,8 @@ }) It("follow output stopped container: "+log, func() { + skipIfJournaldInContainer() + containerName := "logs-f" logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-d", ALPINE, "true"}) @@ -212,10 +299,17 @@ results := podmanTest.Podman([]string{"logs", "-f", containerName}) results.WaitWithDefaultTimeout() + if log == "journald" && !isEventBackendJournald(podmanTest) { + // --follow + journald log-driver is only supported with journald events-backend(PR #10431) + Expect(results).To(Exit(125)) + return + } Expect(results).To(Exit(0)) }) It("using container with container log-size: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--log-opt=max-size=10k", "-d", ALPINE, "sh", "-c", "echo podman podman podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -237,6 +331,8 @@ }) It("Make sure logs match expected length: "+log, func() { + skipIfJournaldInContainer() + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-t", "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) @@ -255,6 +351,8 @@ }) It("podman logs test stdout and stderr: "+log, func() { + skipIfJournaldInContainer() + cname := "log-test" logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"}) logc.WaitWithDefaultTimeout() diff -Nru libpod-3.2.1+ds1/test/e2e/manifest_test.go libpod-3.4.4+ds1/test/e2e/manifest_test.go --- libpod-3.2.1+ds1/test/e2e/manifest_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/manifest_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -47,13 +47,19 @@ It("podman manifest create", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + }) + + It("podman manifest create", func() { + session := podmanTest.Podman([]string{"manifest", "create", "foo", imageList}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("podman manifest inspect", func() { session := podmanTest.Podman([]string{"manifest", "inspect", BB}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "quay.io/libpod/busybox"}) session.WaitWithDefaultTimeout() @@ -68,35 +74,54 @@ It("podman manifest add", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--arch=arm64", "foo", imageListInstance}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman manifest add one", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--arch=arm64", "foo", imageListInstance}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(imageListARM64InstanceDigest)) }) + It("podman manifest tag", func() { + session := podmanTest.Podman([]string{"manifest", "create", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"manifest", "add", "foobar", "quay.io/libpod/busybox"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"tag", "foobar", "foobar2"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session = podmanTest.Podman([]string{"manifest", "inspect", "foobar"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + session2 := podmanTest.Podman([]string{"manifest", "inspect", "foobar2"}) + session2.WaitWithDefaultTimeout() + Expect(session2).Should(Exit(0)) + Expect(session2.OutputToString()).To(Equal(session.OutputToString())) + }) + It("podman manifest add --all", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--all", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(imageListAMD64InstanceDigest)) Expect(session.OutputToString()).To(ContainSubstring(imageListARMInstanceDigest)) Expect(session.OutputToString()).To(ContainSubstring(imageListARM64InstanceDigest)) @@ -107,13 +132,13 @@ It("podman manifest add --os", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--os", "bar", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(`"os": "bar"`)) }) @@ -121,36 +146,36 @@ SkipIfRemote("Not supporting annotate on remote connections") session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "foo", imageListInstance}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "annotate", "--arch", "bar", "foo", imageListARM64InstanceDigest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(`"architecture": "bar"`)) }) It("podman manifest remove", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--all", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(imageListARM64InstanceDigest)) session = podmanTest.Podman([]string{"manifest", "remove", "foo", imageListARM64InstanceDigest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(imageListAMD64InstanceDigest)) Expect(session.OutputToString()).To(ContainSubstring(imageListARMInstanceDigest)) Expect(session.OutputToString()).To(ContainSubstring(imageListPPC64LEInstanceDigest)) @@ -161,23 +186,27 @@ It("podman manifest remove not-found", func() { session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "remove", "foo", "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{"manifest", "rm", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) }) It("podman manifest push", func() { SkipIfRemote("manifest push to dir not supported in remote mode") session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--all", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) dest := filepath.Join(podmanTest.TempDir, "pushed") err := os.MkdirAll(dest, os.ModePerm) Expect(err).To(BeNil()) @@ -186,12 +215,12 @@ }() session = podmanTest.Podman([]string{"manifest", "push", "--all", "foo", "dir:" + dest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) files, err := filepath.Glob(dest + string(os.PathSeparator) + "*") Expect(err).To(BeNil()) check := SystemExec("sha256sum", files) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) prefix := "sha256:" Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix))) Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix))) @@ -204,10 +233,10 @@ SkipIfRemote("manifest push to dir not supported in remote mode") session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "--all", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) dest := filepath.Join(podmanTest.TempDir, "pushed") err := os.MkdirAll(dest, os.ModePerm) Expect(err).To(BeNil()) @@ -216,12 +245,12 @@ }() session = podmanTest.Podman([]string{"push", "foo", "dir:" + dest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) files, err := filepath.Glob(dest + string(os.PathSeparator) + "*") Expect(err).To(BeNil()) check := SystemExec("sha256sum", files) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) prefix := "sha256:" Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListAMD64InstanceDigest, prefix))) Expect(check.OutputToString()).To(ContainSubstring(strings.TrimPrefix(imageListARMInstanceDigest, prefix))) @@ -234,10 +263,10 @@ SkipIfRemote("remote does not support --rm") session := podmanTest.Podman([]string{"manifest", "create", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "add", "foo", imageList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) dest := filepath.Join(podmanTest.TempDir, "pushed") err := os.MkdirAll(dest, os.ModePerm) Expect(err).To(BeNil()) @@ -246,24 +275,56 @@ }() session = podmanTest.Podman([]string{"manifest", "push", "--purge", "foo", "dir:" + dest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "inspect", "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) + + session = podmanTest.Podman([]string{"manifest", "rm", "foo1", "foo2"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) }) It("podman manifest exists", func() { manifestList := "manifest-list" session := podmanTest.Podman([]string{"manifest", "create", manifestList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "exists", manifestList}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"manifest", "exists", "no-manifest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) + + It("podman manifest rm should not remove referenced images", func() { + manifestList := "manifestlist" + imageName := "quay.io/libpod/busybox" + + session := podmanTest.Podman([]string{"pull", imageName}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"manifest", "create", manifestList}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"manifest", "add", manifestList, imageName}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"manifest", "rm", manifestList}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + //image should still show up + session = podmanTest.Podman([]string{"images"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(ContainSubstring(imageName)) + Expect(session).Should(Exit(0)) + }) + }) diff -Nru libpod-3.2.1+ds1/test/e2e/mount_rootless_test.go libpod-3.4.4+ds1/test/e2e/mount_rootless_test.go --- libpod-3.2.1+ds1/test/e2e/mount_rootless_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/mount_rootless_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman mount", func() { @@ -37,31 +38,31 @@ It("podman mount", func() { setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() mount := podmanTest.Podman([]string{"mount", cid}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).ToNot(Equal(0)) + Expect(mount).To(ExitWithError()) Expect(mount.ErrorToString()).To(ContainSubstring("podman unshare")) }) It("podman unshare podman mount", func() { setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() session := podmanTest.Podman([]string{"unshare", PODMAN_BINARY, "mount", cid}) session.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) }) It("podman image mount", func() { podmanTest.AddImageToRWStore(ALPINE) mount := podmanTest.Podman([]string{"image", "mount", ALPINE}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).ToNot(Equal(0)) + Expect(mount).To(ExitWithError()) Expect(mount.ErrorToString()).To(ContainSubstring("podman unshare")) }) @@ -69,10 +70,10 @@ podmanTest.AddImageToRWStore(ALPINE) setup := podmanTest.Podman([]string{"pull", ALPINE}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"unshare", PODMAN_BINARY, "image", "mount", ALPINE}) session.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/mount_test.go libpod-3.4.4+ds1/test/e2e/mount_test.go --- libpod-3.2.1+ds1/test/e2e/mount_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/mount_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman mount", func() { @@ -37,175 +38,175 @@ It("podman mount", func() { setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() mount := podmanTest.Podman([]string{"mount", cid}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) umount := podmanTest.Podman([]string{"umount", cid}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman container mount", func() { setup := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() mount := podmanTest.Podman([]string{"container", "mount", cid}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) umount := podmanTest.Podman([]string{"container", "umount", cid}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman mount with json format", func() { setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() mount := podmanTest.Podman([]string{"mount", cid}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) j := podmanTest.Podman([]string{"mount", "--format=json"}) j.WaitWithDefaultTimeout() - Expect(j.ExitCode()).To(Equal(0)) + Expect(j).Should(Exit(0)) Expect(j.IsJSONOutputValid()).To(BeTrue()) j = podmanTest.Podman([]string{"mount", "--format='{{.foobar}}'"}) j.WaitWithDefaultTimeout() - Expect(j.ExitCode()).ToNot(Equal(0)) + Expect(j).To(ExitWithError()) Expect(j.ErrorToString()).To(ContainSubstring("unknown --format")) umount := podmanTest.Podman([]string{"umount", cid}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman mount many", func() { setup1 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup1.WaitWithDefaultTimeout() - Expect(setup1.ExitCode()).To(Equal(0)) + Expect(setup1).Should(Exit(0)) cid1 := setup1.OutputToString() setup2 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup2.WaitWithDefaultTimeout() - Expect(setup2.ExitCode()).To(Equal(0)) + Expect(setup2).Should(Exit(0)) cid2 := setup2.OutputToString() setup3 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup3.WaitWithDefaultTimeout() - Expect(setup3.ExitCode()).To(Equal(0)) + Expect(setup3).Should(Exit(0)) cid3 := setup3.OutputToString() mount1 := podmanTest.Podman([]string{"mount", cid1, cid2, cid3}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) umount := podmanTest.Podman([]string{"umount", cid1, cid2, cid3}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman umount many", func() { setup1 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup1.WaitWithDefaultTimeout() - Expect(setup1.ExitCode()).To(Equal(0)) + Expect(setup1).Should(Exit(0)) cid1 := setup1.OutputToString() setup2 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup2.WaitWithDefaultTimeout() - Expect(setup2.ExitCode()).To(Equal(0)) + Expect(setup2).Should(Exit(0)) cid2 := setup2.OutputToString() mount1 := podmanTest.Podman([]string{"mount", cid1}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) mount2 := podmanTest.Podman([]string{"mount", cid2}) mount2.WaitWithDefaultTimeout() - Expect(mount2.ExitCode()).To(Equal(0)) + Expect(mount2).Should(Exit(0)) umount := podmanTest.Podman([]string{"umount", cid1, cid2}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman umount all", func() { setup1 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup1.WaitWithDefaultTimeout() - Expect(setup1.ExitCode()).To(Equal(0)) + Expect(setup1).Should(Exit(0)) cid1 := setup1.OutputToString() setup2 := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup2.WaitWithDefaultTimeout() - Expect(setup2.ExitCode()).To(Equal(0)) + Expect(setup2).Should(Exit(0)) cid2 := setup2.OutputToString() mount1 := podmanTest.Podman([]string{"mount", cid1}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) mount2 := podmanTest.Podman([]string{"mount", cid2}) mount2.WaitWithDefaultTimeout() - Expect(mount2.ExitCode()).To(Equal(0)) + Expect(mount2).Should(Exit(0)) umount := podmanTest.Podman([]string{"umount", "--all"}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman list mounted container", func() { setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() lmount := podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(Equal("")) mount := podmanTest.Podman([]string{"mount", cid}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) lmount = podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(ContainSubstring(cid)) umount := podmanTest.Podman([]string{"umount", cid}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman list running container", func() { setup := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() lmount := podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(ContainSubstring(cid)) stop := podmanTest.Podman([]string{"stop", cid}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) lmount = podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(Equal("")) }) @@ -213,42 +214,42 @@ setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid1 := setup.OutputToString() setup = podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid2 := setup.OutputToString() setup = podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid3 := setup.OutputToString() lmount := podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(Equal("")) mount := podmanTest.Podman([]string{"mount", cid1, cid3}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) lmount = podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(ContainSubstring(cid1)) Expect(lmount.OutputToString()).ToNot(ContainSubstring(cid2)) Expect(lmount.OutputToString()).To(ContainSubstring(cid3)) umount := podmanTest.Podman([]string{"umount", cid1, cid3}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) lmount = podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(Equal("")) }) @@ -257,95 +258,95 @@ setup := podmanTest.Podman([]string{"create", ALPINE, "ls"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) cid := setup.OutputToString() lmount := podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(Equal("")) mount := podmanTest.Podman([]string{"mount", cid}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) lmount = podmanTest.Podman([]string{"mount", "--notruncate"}) lmount.WaitWithDefaultTimeout() - Expect(lmount.ExitCode()).To(Equal(0)) + Expect(lmount).Should(Exit(0)) Expect(lmount.OutputToString()).To(ContainSubstring(cid)) umount := podmanTest.Podman([]string{"umount", cid}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman image mount", func() { images := podmanTest.Podman([]string{"images"}) images.WaitWithDefaultTimeout() - Expect(images.ExitCode()).To(Equal(0)) + Expect(images).Should(Exit(0)) mount := podmanTest.Podman([]string{"image", "mount", ALPINE}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) umount := podmanTest.Podman([]string{"image", "umount", ALPINE}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(Equal("")) // Mount multiple times mount = podmanTest.Podman([]string{"image", "mount", ALPINE}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount", ALPINE}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) // Unmount once mount = podmanTest.Podman([]string{"image", "mount", ALPINE}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(ContainSubstring(ALPINE)) mount = podmanTest.Podman([]string{"image", "umount", "--all"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) }) It("podman mount with json format", func() { podmanTest.AddImageToRWStore(fedoraMinimal) mount := podmanTest.Podman([]string{"image", "mount", fedoraMinimal}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) j := podmanTest.Podman([]string{"image", "mount", "--format=json"}) j.WaitWithDefaultTimeout() - Expect(j.ExitCode()).To(Equal(0)) + Expect(j).Should(Exit(0)) Expect(j.IsJSONOutputValid()).To(BeTrue()) umount := podmanTest.Podman([]string{"image", "umount", fedoraMinimal}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) }) It("podman umount --all", func() { podmanTest.AddImageToRWStore(fedoraMinimal) mount := podmanTest.Podman([]string{"image", "mount", fedoraMinimal}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) umount := podmanTest.Podman([]string{"image", "umount", "--all"}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) Expect(len(umount.OutputToStringArray())).To(Equal(1)) }) @@ -356,66 +357,66 @@ mount1 := podmanTest.Podman([]string{"image", "mount", fedoraMinimal, ALPINE, "busybox"}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) umount := podmanTest.Podman([]string{"image", "umount", fedoraMinimal, ALPINE}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) mount := podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(ContainSubstring("busybox")) mount1 = podmanTest.Podman([]string{"image", "unmount", "busybox"}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(Equal("")) mount1 = podmanTest.Podman([]string{"image", "mount", fedoraMinimal, ALPINE, "busybox"}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(ContainSubstring(fedoraMinimal)) Expect(mount.OutputToString()).To(ContainSubstring(ALPINE)) umount = podmanTest.Podman([]string{"image", "umount", "--all"}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(Equal("")) umount = podmanTest.Podman([]string{"image", "umount", fedoraMinimal, ALPINE}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) mount1 = podmanTest.Podman([]string{"image", "mount", "--all"}) mount1.WaitWithDefaultTimeout() - Expect(mount1.ExitCode()).To(Equal(0)) + Expect(mount1).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(ContainSubstring(fedoraMinimal)) Expect(mount.OutputToString()).To(ContainSubstring(ALPINE)) umount = podmanTest.Podman([]string{"image", "umount", "--all"}) umount.WaitWithDefaultTimeout() - Expect(umount.ExitCode()).To(Equal(0)) + Expect(umount).Should(Exit(0)) mount = podmanTest.Podman([]string{"image", "mount"}) mount.WaitWithDefaultTimeout() - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(Equal("")) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/namespace_test.go libpod-3.4.4+ds1/test/e2e/namespace_test.go --- libpod-3.2.1+ds1/test/e2e/namespace_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/namespace_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman namespaces", func() { @@ -37,15 +38,15 @@ podman1.WaitWithDefaultTimeout() if IsRemote() { // --namespace flag not supported in podman remote - Expect(podman1.ExitCode()).To(Equal(125)) + Expect(podman1).Should(Exit(125)) Expect(podman1.ErrorToString()).To(ContainSubstring("unknown flag: --namespace")) return } - Expect(podman1.ExitCode()).To(Equal(0)) + Expect(podman1).Should(Exit(0)) podman2 := podmanTest.Podman([]string{"--namespace", "test2", "ps", "-aq"}) podman2.WaitWithDefaultTimeout() - Expect(podman2.ExitCode()).To(Equal(0)) + Expect(podman2).Should(Exit(0)) output := podman2.OutputToStringArray() numCtrs := 0 for _, outputLine := range output { diff -Nru libpod-3.2.1+ds1/test/e2e/network_connect_disconnect_test.go libpod-3.4.4+ds1/test/e2e/network_connect_disconnect_test.go --- libpod-3.2.1+ds1/test/e2e/network_connect_disconnect_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/network_connect_disconnect_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman network connect and disconnect", func() { @@ -35,19 +36,19 @@ It("bad network name in disconnect should result in error", func() { dis := podmanTest.Podman([]string{"network", "disconnect", "foobar", "test"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) + Expect(dis).Should(ExitWithError()) }) It("bad container name in network disconnect should result in error", func() { netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) dis := podmanTest.Podman([]string{"network", "disconnect", netName, "foobar"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) + Expect(dis).Should(ExitWithError()) }) It("network disconnect with net mode slirp4netns should result in error", func() { @@ -55,17 +56,17 @@ netName := "slirp" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) session = podmanTest.Podman([]string{"create", "--name", "test", "--network", "slirp4netns", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) con := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) con.WaitWithDefaultTimeout() - Expect(con.ExitCode()).ToNot(BeZero()) + Expect(con).Should(ExitWithError()) Expect(con.ErrorToString()).To(ContainSubstring(`"slirp4netns" is not supported: invalid network mode`)) }) @@ -73,47 +74,48 @@ netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).To(BeZero()) + Expect(dis).Should(Exit(0)) + Expect(dis.ErrorToString()).Should(Equal("")) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("0")) exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).ToNot(BeZero()) + Expect(exec).Should(ExitWithError()) }) It("bad network name in connect should result in error", func() { dis := podmanTest.Podman([]string{"network", "connect", "foobar", "test"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) + Expect(dis).Should(ExitWithError()) }) It("bad container name in network connect should result in error", func() { netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) dis := podmanTest.Podman([]string{"network", "connect", netName, "foobar"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).ToNot(BeZero()) + Expect(dis).Should(ExitWithError()) }) It("network connect with net mode slirp4netns should result in error", func() { @@ -121,17 +123,17 @@ netName := "slirp" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) session = podmanTest.Podman([]string{"create", "--name", "test", "--network", "slirp4netns", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) con := podmanTest.Podman([]string{"network", "connect", netName, "test"}) con.WaitWithDefaultTimeout() - Expect(con.ExitCode()).ToNot(BeZero()) + Expect(con).Should(ExitWithError()) Expect(con.ErrorToString()).To(ContainSubstring(`"slirp4netns" is not supported: invalid network mode`)) }) @@ -139,16 +141,16 @@ netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) con := podmanTest.Podman([]string{"network", "connect", netName, "test"}) con.WaitWithDefaultTimeout() - Expect(con.ExitCode()).ToNot(BeZero()) + Expect(con).Should(ExitWithError()) }) It("podman network connect", func() { @@ -156,41 +158,42 @@ netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) // Create a second network newNetName := "aliasTest" + stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", newNetName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(newNetName) connect := podmanTest.Podman([]string{"network", "connect", newNetName, "test"}) connect.WaitWithDefaultTimeout() - Expect(connect.ExitCode()).To(BeZero()) + Expect(connect).Should(Exit(0)) + Expect(connect.ErrorToString()).Should(Equal("")) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("2")) exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) // make sure no logrus errors are shown https://github.com/containers/podman/issues/9602 rm := podmanTest.Podman([]string{"rm", "-f", "test"}) rm.WaitWithDefaultTimeout() - Expect(rm.ExitCode()).To(BeZero()) + Expect(rm).Should(Exit(0)) Expect(rm.ErrorToString()).To(Equal("")) }) @@ -199,162 +202,158 @@ netName1 := "connect1" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName1) netName2 := "connect2" + stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", netName2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName2) ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName1, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) dis := podmanTest.Podman([]string{"network", "connect", netName2, "test"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).To(BeZero()) + Expect(dis).Should(Exit(0)) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("2")) start := podmanTest.Podman([]string{"start", "test"}) start.WaitWithDefaultTimeout() - Expect(start.ExitCode()).To(BeZero()) + Expect(start).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) }) It("podman network connect and run with network ID", func() { - SkipIfRemote("remote flakes to much I will fix this in another PR") - SkipIfRootless("network connect and disconnect are only rootful") netName := "ID" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.ID}}", "--filter", "name=" + netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) netID := session.OutputToString() - ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netID, ALPINE, "top"}) + ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netID, "--network-alias", "somealias", ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) // Create a second network newNetName := "ID2" + stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", newNetName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(newNetName) session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.ID}}", "--filter", "name=" + newNetName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) newNetID := session.OutputToString() - connect := podmanTest.Podman([]string{"network", "connect", newNetID, "test"}) + connect := podmanTest.Podman([]string{"network", "connect", "--alias", "secondalias", newNetID, "test"}) connect.WaitWithDefaultTimeout() - Expect(connect.ExitCode()).To(BeZero()) + Expect(connect).Should(Exit(0)) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{.NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(netName)) Expect(inspect.OutputToString()).To(ContainSubstring(newNetName)) exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) }) It("podman network disconnect when not running", func() { netName1 := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName1) netName2 := "aliasTest" + stringid.GenerateNonCryptoID() session2 := podmanTest.Podman([]string{"network", "create", netName2}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(BeZero()) + Expect(session2).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName2) ctr := podmanTest.Podman([]string{"create", "--name", "test", "--network", netName1 + "," + netName2, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) dis := podmanTest.Podman([]string{"network", "disconnect", netName1, "test"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).To(BeZero()) + Expect(dis).Should(Exit(0)) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("1")) start := podmanTest.Podman([]string{"start", "test"}) start.WaitWithDefaultTimeout() - Expect(start.ExitCode()).To(BeZero()) + Expect(start).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth1"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).ToNot(BeZero()) + Expect(exec).Should(ExitWithError()) }) It("podman network disconnect and run with network ID", func() { - SkipIfRemote("remote flakes to much I will fix this in another PR") - SkipIfRootless("network connect and disconnect are only rootful") netName := "aliasTest" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.ID}}", "--filter", "name=" + netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) netID := session.OutputToString() ctr := podmanTest.Podman([]string{"run", "-dt", "--name", "test", "--network", netID, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(BeZero()) + Expect(ctr).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(BeZero()) + Expect(exec).Should(Exit(0)) dis := podmanTest.Podman([]string{"network", "disconnect", netID, "test"}) dis.WaitWithDefaultTimeout() - Expect(dis.ExitCode()).To(BeZero()) + Expect(dis).Should(Exit(0)) inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("0")) exec = podmanTest.Podman([]string{"exec", "-it", "test", "ip", "addr", "show", "eth0"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).ToNot(BeZero()) + Expect(exec).Should(ExitWithError()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/network_create_test.go libpod-3.4.4+ds1/test/e2e/network_create_test.go --- libpod-3.2.1+ds1/test/e2e/network_create_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/network_create_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,6 +13,7 @@ "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" "github.com/pkg/errors" ) @@ -89,7 +90,7 @@ nc := podmanTest.Podman([]string{"network", "create"}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) fileContent, err := ioutil.ReadFile(nc.OutputToString()) Expect(err).To(BeNil()) @@ -121,7 +122,7 @@ nc := podmanTest.Podman([]string{"network", "create", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) inspect := podmanTest.Podman([]string{"network", "inspect", netName}) inspect.WaitWithDefaultTimeout() @@ -141,7 +142,7 @@ nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/24", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) // Inspect the network configuration inspect := podmanTest.Podman([]string{"network", "inspect", netName}) @@ -184,7 +185,7 @@ nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:1:2:3:4::/64", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) // Inspect the network configuration inspect := podmanTest.Podman([]string{"network", "inspect", netName}) @@ -225,7 +226,7 @@ nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:3:2:1::/64", "--ipv6", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) // Inspect the network configuration inspect := podmanTest.Podman([]string{"network", "inspect", netName}) @@ -243,10 +244,59 @@ Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) Expect(bridgePlugin.IPAM.Routes[1].Dest).To(Equal("0.0.0.0/0")) + Expect(bridgePlugin.IPAM.Ranges).To(HaveLen(2)) + Expect(bridgePlugin.IPAM.Ranges[0]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[0][0].Subnet).ToNot(BeEmpty()) + Expect(bridgePlugin.IPAM.Ranges[1]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[1][0].Subnet).ToNot(BeEmpty()) + + _, subnet11, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[0][0].Subnet) + Expect(err).To(BeNil()) + _, subnet12, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[1][0].Subnet) + Expect(err).To(BeNil()) + // Once a container executes a new network, the nic will be created. We should clean those up // best we can defer removeNetworkDevice(bridgePlugin.BrName) + // create a second network to check the auto assigned ipv4 subnet does not overlap + // https://github.com/containers/podman/issues/11032 + netName2 := "dual-" + stringid.GenerateNonCryptoID() + nc = podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:6:3:2:1::/64", "--ipv6", netName2}) + nc.WaitWithDefaultTimeout() + defer podmanTest.removeCNINetwork(netName2) + Expect(nc).Should(Exit(0)) + + // Inspect the network configuration + inspect = podmanTest.Podman([]string{"network", "inspect", netName2}) + inspect.WaitWithDefaultTimeout() + + // JSON the network configuration into something usable + err = json.Unmarshal([]byte(inspect.OutputToString()), &results) + Expect(err).To(BeNil()) + result = results[0] + Expect(result["name"]).To(Equal(netName2)) + + // JSON the bridge info + bridgePlugin, err = genericPluginsToBridge(result["plugins"], "bridge") + Expect(err).To(BeNil()) + Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0")) + Expect(bridgePlugin.IPAM.Routes[1].Dest).To(Equal("0.0.0.0/0")) + Expect(bridgePlugin.IPAM.Ranges).To(HaveLen(2)) + Expect(bridgePlugin.IPAM.Ranges[0]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[0][0].Subnet).ToNot(BeEmpty()) + Expect(bridgePlugin.IPAM.Ranges[1]).To(HaveLen(1)) + Expect(bridgePlugin.IPAM.Ranges[1][0].Subnet).ToNot(BeEmpty()) + + _, subnet21, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[0][0].Subnet) + Expect(err).To(BeNil()) + _, subnet22, err := net.ParseCIDR(bridgePlugin.IPAM.Ranges[1][0].Subnet) + Expect(err).To(BeNil()) + + // check that the subnets do not overlap + Expect(subnet11.Contains(subnet21.IP)).To(BeFalse()) + Expect(subnet12.Contains(subnet22.IP)).To(BeFalse()) + try := podmanTest.Podman([]string{"run", "-it", "--rm", "--network", netName, ALPINE, "sh", "-c", "ip addr show eth0 | grep global | awk ' /inet6 / {print $2}'"}) try.WaitWithDefaultTimeout() @@ -299,7 +349,7 @@ nc := podmanTest.Podman([]string{"network", "create", netName}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) ncFail := podmanTest.Podman([]string{"network", "create", netName}) ncFail.WaitWithDefaultTimeout() @@ -311,7 +361,7 @@ nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.13.0/24", netName1}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName1) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) netName2 := "sub2-" + stringid.GenerateNonCryptoID() ncFail := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.13.0/24", netName2}) @@ -326,7 +376,7 @@ nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:4:4:4::/64", "--ipv6", netName1}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName1) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) netName2 := "subipv62-" + stringid.GenerateNonCryptoID() ncFail := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:4:4:4:4::/64", "--ipv6", netName2}) @@ -346,11 +396,11 @@ nc := podmanTest.Podman([]string{"network", "create", "--opt", "mtu=9000", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) nc = podmanTest.Podman([]string{"network", "inspect", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) Expect(nc.OutputToString()).To(ContainSubstring(`"mtu": 9000,`)) }) @@ -359,11 +409,11 @@ nc := podmanTest.Podman([]string{"network", "create", "--opt", "vlan=9", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) nc = podmanTest.Podman([]string{"network", "inspect", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) Expect(nc.OutputToString()).To(ContainSubstring(`"vlan": 9`)) }) @@ -380,7 +430,7 @@ nc := podmanTest.Podman([]string{"network", "create", "--internal", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) // Not performing this check on remote tests because it is a logrus error which does // not come back via stderr on the remote client. if !IsRemote() { @@ -388,7 +438,7 @@ } nc = podmanTest.Podman([]string{"network", "inspect", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(BeZero()) + Expect(nc).Should(Exit(0)) Expect(nc.OutputToString()).ToNot(ContainSubstring("dnsname")) }) diff -Nru libpod-3.2.1+ds1/test/e2e/network_test.go libpod-3.4.4+ds1/test/e2e/network_test.go --- libpod-3.2.1+ds1/test/e2e/network_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/network_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,6 +11,7 @@ "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman network", func() { @@ -42,7 +43,7 @@ session := podmanTest.Podman([]string{"network", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeTrue()) }) @@ -52,7 +53,7 @@ session := podmanTest.Podman([]string{"network", "ls", "--quiet"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeTrue()) }) @@ -62,7 +63,7 @@ session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeTrue()) }) @@ -72,7 +73,7 @@ session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=bridge", "--filter", "name=" + name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(name)) }) @@ -85,7 +86,7 @@ session := podmanTest.Podman([]string{"network", "ls", "--filter", "name=" + name1, "--filter", "name=" + name2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(name1)) Expect(session.OutputToString()).To(ContainSubstring(name2)) }) @@ -97,23 +98,23 @@ session := podmanTest.Podman([]string{"network", "create", "--label", label1, net1}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net1) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) net2 := "labelnet" + stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", "--label", label1, "--label", label2, net2}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net2) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "ls", "--filter", "label=" + label1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(net1)) Expect(session.OutputToString()).To(ContainSubstring(net2)) session = podmanTest.Podman([]string{"network", "ls", "--filter", "label=" + label1, "--filter", "label=" + label2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).ToNot(ContainSubstring(net1)) Expect(session.OutputToString()).To(ContainSubstring(net2)) }) @@ -123,7 +124,7 @@ session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "ls", "--filter", "namr=ab"}) session.WaitWithDefaultTimeout() @@ -137,7 +138,7 @@ session := podmanTest.Podman([]string{"network", "ls", "--filter", "plugin=test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeFalse()) }) @@ -148,45 +149,45 @@ session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) // Tests Default Table Output session = podmanTest.Podman([]string{"network", "ls", "--filter", "id=" + netID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) expectedTable := "NETWORK ID NAME VERSION PLUGINS" Expect(session.OutputToString()).To(ContainSubstring(expectedTable)) session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID[:12])) session = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}} {{.ID}}", "--filter", "id=" + netID[10:50], "--no-trunc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(net + " " + netID)) session = podmanTest.Podman([]string{"network", "inspect", netID[:40]}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(net)) session = podmanTest.Podman([]string{"network", "inspect", netID[1:]}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("no such network")) session = podmanTest.Podman([]string{"network", "rm", netID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) }) rm_func := func(rm string) { It(fmt.Sprintf("podman network %s no args", rm), func() { session := podmanTest.Podman([]string{"network", rm}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) }) @@ -196,16 +197,16 @@ session := podmanTest.Podman([]string{"network", "ls", "--quiet"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains(name)).To(BeTrue()) rm := podmanTest.Podman([]string{"network", rm, name}) rm.WaitWithDefaultTimeout() - Expect(rm.ExitCode()).To(BeZero()) + Expect(rm).Should(Exit(0)) results := podmanTest.Podman([]string{"network", "ls", "--quiet"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) Expect(results.LineInOutputContains(name)).To(BeFalse()) }) } @@ -216,7 +217,7 @@ It("podman network inspect no args", func() { session := podmanTest.Podman([]string{"network", "inspect"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(BeZero()) + Expect(session).Should(ExitWithError()) }) It("podman network inspect", func() { @@ -230,7 +231,7 @@ } session := podmanTest.Podman(append([]string{"network", "inspect"}, expectedNetworks...)) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) @@ -240,7 +241,7 @@ session := podmanTest.Podman([]string{"network", "inspect", name, "--format", "{{.cniVersion}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("0.3.0")).To(BeTrue()) }) @@ -249,16 +250,16 @@ network := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.50.0/24", netName}) network.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(network.ExitCode()).To(BeZero()) + Expect(network).Should(Exit(0)) ctrName := "testCtr" container := podmanTest.Podman([]string{"run", "-dt", "--network", netName, "--name", ctrName, ALPINE, "top"}) container.WaitWithDefaultTimeout() - Expect(container.ExitCode()).To(BeZero()) + Expect(container).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) conData := inspect.InspectContainerToJSON() Expect(len(conData)).To(Equal(1)) Expect(len(conData[0].NetworkSettings.Networks)).To(Equal(1)) @@ -271,7 +272,7 @@ // Necessary to ensure the CNI network is removed cleanly rmAll := podmanTest.Podman([]string{"rm", "-f", ctrName}) rmAll.WaitWithDefaultTimeout() - Expect(rmAll.ExitCode()).To(BeZero()) + Expect(rmAll).Should(Exit(0)) }) It("podman inspect container two CNI networks (container not running)", func() { @@ -279,22 +280,22 @@ network1 := podmanTest.Podman([]string{"network", "create", netName1}) network1.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName1) - Expect(network1.ExitCode()).To(BeZero()) + Expect(network1).Should(Exit(0)) netName2 := "net2-" + stringid.GenerateNonCryptoID() network2 := podmanTest.Podman([]string{"network", "create", netName2}) network2.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName2) - Expect(network2.ExitCode()).To(BeZero()) + Expect(network2).Should(Exit(0)) ctrName := "testCtr" container := podmanTest.Podman([]string{"create", "--network", fmt.Sprintf("%s,%s", netName1, netName2), "--name", ctrName, ALPINE, "top"}) container.WaitWithDefaultTimeout() - Expect(container.ExitCode()).To(BeZero()) + Expect(container).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) conData := inspect.InspectContainerToJSON() Expect(len(conData)).To(Equal(1)) Expect(len(conData[0].NetworkSettings.Networks)).To(Equal(2)) @@ -308,7 +309,7 @@ // Necessary to ensure the CNI network is removed cleanly rmAll := podmanTest.Podman([]string{"rm", "-f", ctrName}) rmAll.WaitWithDefaultTimeout() - Expect(rmAll.ExitCode()).To(BeZero()) + Expect(rmAll).Should(Exit(0)) }) It("podman inspect container two CNI networks", func() { @@ -316,22 +317,22 @@ network1 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.0/25", netName1}) network1.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName1) - Expect(network1.ExitCode()).To(BeZero()) + Expect(network1).Should(Exit(0)) netName2 := "net2-" + stringid.GenerateNonCryptoID() network2 := podmanTest.Podman([]string{"network", "create", "--subnet", "10.50.51.128/26", netName2}) network2.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName2) - Expect(network2.ExitCode()).To(BeZero()) + Expect(network2).Should(Exit(0)) ctrName := "testCtr" container := podmanTest.Podman([]string{"run", "-dt", "--network", fmt.Sprintf("%s,%s", netName1, netName2), "--name", ctrName, ALPINE, "top"}) container.WaitWithDefaultTimeout() - Expect(container.ExitCode()).To(BeZero()) + Expect(container).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", ctrName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) conData := inspect.InspectContainerToJSON() Expect(len(conData)).To(Equal(1)) Expect(len(conData[0].NetworkSettings.Networks)).To(Equal(2)) @@ -349,7 +350,7 @@ // Necessary to ensure the CNI network is removed cleanly rmAll := podmanTest.Podman([]string{"rm", "-f", ctrName}) rmAll.WaitWithDefaultTimeout() - Expect(rmAll.ExitCode()).To(BeZero()) + Expect(rmAll).Should(Exit(0)) }) It("podman network remove after disconnect when container initially created with the network", func() { @@ -360,25 +361,25 @@ session := podmanTest.Podman([]string{"network", "create", network}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--name", container, "--network", network, "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "disconnect", network, container}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "rm", network}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman network remove bogus", func() { session := podmanTest.Podman([]string{"network", "rm", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman network remove --force with pod", func() { @@ -386,34 +387,34 @@ session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "create", "--network", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"create", "--pod", podID, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "rm", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(2)) + Expect(session).Should(Exit(2)) session = podmanTest.Podman([]string{"network", "rm", "--force", netName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) // check if pod is deleted session = podmanTest.Podman([]string{"pod", "exists", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) // check if net is deleted session = podmanTest.Podman([]string{"network", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring(netName))) }) @@ -422,17 +423,17 @@ session := podmanTest.Podman([]string{"network", "create", netName1}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName1) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) netName2 := "net2-" + stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", netName2}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName2) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "rm", netName1, netName2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) lines := session.OutputToStringArray() Expect(lines[0]).To(Equal(netName1)) Expect(lines[1]).To(Equal(netName2)) @@ -444,7 +445,7 @@ session := podmanTest.Podman([]string{"network", "create", netName}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(netName) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) interval := time.Duration(250 * time.Millisecond) for i := 0; i < 6; i++ { @@ -460,7 +461,7 @@ top := podmanTest.Podman([]string{"run", "-dt", "--name=web", "--network=" + netName, "--network-alias=web1", "--network-alias=web2", nginx}) top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(BeZero()) + Expect(top).Should(Exit(0)) interval = time.Duration(250 * time.Millisecond) // Wait for the nginx service to be running for i := 0; i < 6; i++ { @@ -480,12 +481,12 @@ // Test against the first alias c2 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, nginx, "curl", "web1"}) c2.WaitWithDefaultTimeout() - Expect(c2.ExitCode()).To(BeZero()) + Expect(c2).Should(Exit(0)) // Test against the second alias c3 := podmanTest.Podman([]string{"run", "--dns-search", "dns.podman", "--network=" + netName, nginx, "curl", "web2"}) c3.WaitWithDefaultTimeout() - Expect(c3.ExitCode()).To(BeZero()) + Expect(c3).Should(Exit(0)) }) It("podman network create/remove macvlan", func() { @@ -493,11 +494,11 @@ nc := podmanTest.Podman([]string{"network", "create", "--macvlan", "lo", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) }) It("podman network create/remove macvlan as driver (-d) no device name", func() { @@ -505,11 +506,11 @@ nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) inspect := podmanTest.Podman([]string{"network", "inspect", net}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) out, err := inspect.jq(".[0].plugins[0].master") Expect(err).To(BeNil()) @@ -517,7 +518,7 @@ nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) }) It("podman network create/remove macvlan as driver (-d) with device name", func() { @@ -525,11 +526,11 @@ nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", "-o", "parent=lo", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) inspect := podmanTest.Podman([]string{"network", "inspect", net}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) out, err := inspect.jq(".[0].plugins[0].master") Expect(err).To(BeNil()) @@ -541,7 +542,7 @@ nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) }) It("podman network exists", func() { @@ -549,15 +550,15 @@ session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "exists", net}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"network", "exists", stringid.GenerateNonCryptoID()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman network create macvlan with network info and options", func() { @@ -565,11 +566,11 @@ nc := podmanTest.Podman([]string{"network", "create", "-d", "macvlan", "-o", "parent=lo", "-o", "mtu=1500", "--gateway", "192.168.1.254", "--subnet", "192.168.1.0/24", net}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) inspect := podmanTest.Podman([]string{"network", "inspect", net}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(BeZero()) + Expect(inspect).Should(Exit(0)) mtu, err := inspect.jq(".[0].plugins[0].mtu") Expect(err).To(BeNil()) @@ -601,7 +602,7 @@ nc = podmanTest.Podman([]string{"network", "rm", net}) nc.WaitWithDefaultTimeout() - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) }) It("podman network prune --filter", func() { @@ -610,11 +611,11 @@ nc := podmanTest.Podman([]string{"network", "create", net1}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net1) - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) list := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"}) list.WaitWithDefaultTimeout() - Expect(list.ExitCode()).To(BeZero()) + Expect(list).Should(Exit(0)) Expect(StringInSlice(net1, list.OutputToStringArray())).To(BeTrue()) if !isRootless() { @@ -624,11 +625,11 @@ // -f needed only to skip y/n question prune := podmanTest.Podman([]string{"network", "prune", "-f", "--filter", "until=50"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(BeZero()) + Expect(prune).Should(Exit(0)) listAgain := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"}) listAgain.WaitWithDefaultTimeout() - Expect(listAgain.ExitCode()).To(BeZero()) + Expect(listAgain).Should(Exit(0)) Expect(StringInSlice(net1, listAgain.OutputToStringArray())).To(BeTrue()) if !isRootless() { @@ -638,11 +639,11 @@ // -f needed only to skip y/n question prune = podmanTest.Podman([]string{"network", "prune", "-f", "--filter", "until=5000000000000"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(BeZero()) + Expect(prune).Should(Exit(0)) listAgain = podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"}) listAgain.WaitWithDefaultTimeout() - Expect(listAgain.ExitCode()).To(BeZero()) + Expect(listAgain).Should(Exit(0)) Expect(StringInSlice(net1, listAgain.OutputToStringArray())).To(BeFalse()) if !isRootless() { @@ -662,16 +663,16 @@ nc := podmanTest.Podman([]string{"network", "create", net1}) nc.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net1) - Expect(nc.ExitCode()).To(Equal(0)) + Expect(nc).Should(Exit(0)) nc2 := podmanTest.Podman([]string{"network", "create", net2}) nc2.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net2) - Expect(nc2.ExitCode()).To(Equal(0)) + Expect(nc2).Should(Exit(0)) list := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"}) list.WaitWithDefaultTimeout() - Expect(list.ExitCode()).To(BeZero()) + Expect(list).Should(Exit(0)) Expect(StringInSlice(net1, list.OutputToStringArray())).To(BeTrue()) Expect(StringInSlice(net2, list.OutputToStringArray())).To(BeTrue()) @@ -681,15 +682,15 @@ session := podmanTest.Podman([]string{"run", "-dt", "--net", net2, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) prune := podmanTest.Podman([]string{"network", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(BeZero()) + Expect(prune).Should(Exit(0)) listAgain := podmanTest.Podman([]string{"network", "ls", "--format", "{{.Name}}"}) listAgain.WaitWithDefaultTimeout() - Expect(listAgain.ExitCode()).To(BeZero()) + Expect(listAgain).Should(Exit(0)) Expect(StringInSlice(net1, listAgain.OutputToStringArray())).To(BeFalse()) Expect(StringInSlice(net2, listAgain.OutputToStringArray())).To(BeTrue()) diff -Nru libpod-3.2.1+ds1/test/e2e/pause_test.go libpod-3.4.4+ds1/test/e2e/pause_test.go --- libpod-3.2.1+ds1/test/e2e/pause_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pause_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pause", func() { @@ -69,7 +70,7 @@ It("podman pause a created container by id", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"pause", cid}) @@ -78,17 +79,22 @@ Expect(result).To(ExitWithError()) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(createdState)) + + // check we can read stats for a paused container + result = podmanTest.Podman([]string{"stats", "--no-stream", cid}) + result.WaitWithDefaultTimeout() + Expect(result).To(ExitWithError()) }) It("podman pause a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"pause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) @@ -99,13 +105,13 @@ It("podman container pause a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "pause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) @@ -116,13 +122,13 @@ It("podman unpause a running container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"unpause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) @@ -130,20 +136,20 @@ It("podman remove a paused container by id without force", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"pause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) result = podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(2)) + Expect(result).Should(Exit(2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) @@ -152,56 +158,56 @@ It("podman remove a paused container by id with force", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"pause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) result = podmanTest.Podman([]string{"rm", "--force", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman stop a paused container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"pause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) result = podmanTest.Podman([]string{"stop", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(ContainSubstring(pausedState)) result = podmanTest.Podman([]string{"unpause", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) result = podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(2)) + Expect(result).Should(Exit(2)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) result = podmanTest.Podman([]string{"rm", "-f", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -209,12 +215,12 @@ It("podman pause a running container by name", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pause", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(strings.ToLower(podmanTest.GetContainerStatus())).To(Equal(pausedState)) @@ -225,23 +231,23 @@ It("podman pause a running container by id and another by name", func() { session1 := podmanTest.RunTopContainer("test1") session1.WaitWithDefaultTimeout() - Expect(session1.ExitCode()).To(Equal(0)) + Expect(session1).Should(Exit(0)) session2 := podmanTest.RunTopContainer("") session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) cid2 := session2.OutputToString() result := podmanTest.Podman([]string{"pause", cid2}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) result = podmanTest.Podman([]string{"pause", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) result = podmanTest.Podman([]string{"unpause", "test1"}) @@ -253,7 +259,7 @@ It("Pause all containers (no containers exist)", func() { result := podmanTest.Podman([]string{"pause", "--all"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -261,7 +267,7 @@ It("Unpause all containers (no paused containers exist)", func() { result := podmanTest.Podman([]string{"unpause", "--all"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -270,26 +276,26 @@ name := fmt.Sprintf("test%d", i) run := podmanTest.Podman([]string{"run", "-dt", "--name", name, nginx}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) } running := podmanTest.Podman([]string{"ps", "-q"}) running.WaitWithDefaultTimeout() - Expect(running.ExitCode()).To(Equal(0)) + Expect(running).Should(Exit(0)) Expect(len(running.OutputToStringArray())).To(Equal(3)) pause := podmanTest.Podman([]string{"pause", "--all"}) pause.WaitWithDefaultTimeout() - Expect(pause.ExitCode()).To(Equal(0)) + Expect(pause).Should(Exit(0)) running = podmanTest.Podman([]string{"ps", "-q"}) running.WaitWithDefaultTimeout() - Expect(running.ExitCode()).To(Equal(0)) + Expect(running).Should(Exit(0)) Expect(len(running.OutputToStringArray())).To(Equal(0)) unpause := podmanTest.Podman([]string{"unpause", "--all"}) unpause.WaitWithDefaultTimeout() - Expect(unpause.ExitCode()).To(Equal(0)) + Expect(unpause).Should(Exit(0)) }) It("Unpause a bunch of running containers", func() { @@ -297,20 +303,20 @@ name := fmt.Sprintf("test%d", i) run := podmanTest.Podman([]string{"run", "-dt", "--name", name, nginx}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) } pause := podmanTest.Podman([]string{"pause", "--all"}) pause.WaitWithDefaultTimeout() - Expect(pause.ExitCode()).To(Equal(0)) + Expect(pause).Should(Exit(0)) unpause := podmanTest.Podman([]string{"unpause", "--all"}) unpause.WaitWithDefaultTimeout() - Expect(unpause.ExitCode()).To(Equal(0)) + Expect(unpause).Should(Exit(0)) running := podmanTest.Podman([]string{"ps", "-q"}) running.WaitWithDefaultTimeout() - Expect(running.ExitCode()).To(Equal(0)) + Expect(running).Should(Exit(0)) Expect(len(running.OutputToStringArray())).To(Equal(3)) }) diff -Nru libpod-3.2.1+ds1/test/e2e/play_build_test.go libpod-3.4.4+ds1/test/e2e/play_build_test.go --- libpod-3.2.1+ds1/test/e2e/play_build_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/play_build_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,258 @@ +// +build !remote + +// build for play kube is not supported on remote yet. + +package integration + +import ( + "os" + "path/filepath" + + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman play kube with build", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + var testYAML = ` +apiVersion: v1 +kind: Pod +metadata: + creationTimestamp: "2021-08-05T17:55:51Z" + labels: + app: foobar + name: top_pod +spec: + containers: + - command: + - top + env: + - name: PATH + value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + - name: TERM + value: xterm + - name: container + value: podman + image: foobar + name: foobar + resources: {} + securityContext: + allowPrivilegeEscalation: true + capabilities: + drop: + - CAP_MKNOD + - CAP_NET_RAW + - CAP_AUDIT_WRITE + privileged: false + readOnlyRootFilesystem: false + seLinuxOptions: {} + tty: true + workingDir: / + dnsConfig: {} +status: {} +` + + var playBuildFile = ` +FROM quay.io/libpod/alpine_nginx:latest +RUN apk update && apk add strace +LABEL homer=dad +COPY copyfile /copyfile +` + var prebuiltImage = ` +FROM quay.io/libpod/alpine_nginx:latest +RUN apk update && apk add strace +LABEL marge=mom +` + + var copyFile = `just a text file +` + + It("Check that image is built using Dockerfile", func() { + // Setup + yamlDir := filepath.Join(tempdir, RandomString(12)) + err := os.Mkdir(yamlDir, 0755) + err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) + Expect(err).To(BeNil()) + app1Dir := filepath.Join(yamlDir, "foobar") + err = os.Mkdir(app1Dir, 0755) + Expect(err).To(BeNil()) + err = writeYaml(playBuildFile, filepath.Join(app1Dir, "Dockerfile")) + Expect(err).To(BeNil()) + // Write a file to be copied + err = writeYaml(copyFile, filepath.Join(app1Dir, "copyfile")) + Expect(err).To(BeNil()) + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + Expect(os.Chdir(yamlDir)).To(BeNil()) + defer func() { (Expect(os.Chdir(cwd)).To(BeNil())) }() + + session := podmanTest.Podman([]string{"play", "kube", "top.yaml"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + exists := podmanTest.Podman([]string{"image", "exists", "foobar"}) + exists.WaitWithDefaultTimeout() + Expect(exists).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "top_pod-foobar"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + inspectData := inspect.InspectContainerToJSON() + Expect(len(inspectData)).To(BeNumerically(">", 0)) + Expect(inspectData[0].Config.Labels["homer"]).To(Equal("dad")) + }) + + It("Check that image is built using Containerfile", func() { + // Setup + yamlDir := filepath.Join(tempdir, RandomString(12)) + err := os.Mkdir(yamlDir, 0755) + err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) + Expect(err).To(BeNil()) + app1Dir := filepath.Join(yamlDir, "foobar") + err = os.Mkdir(app1Dir, 0755) + Expect(err).To(BeNil()) + err = writeYaml(playBuildFile, filepath.Join(app1Dir, "Containerfile")) + Expect(err).To(BeNil()) + // Write a file to be copied + err = writeYaml(copyFile, filepath.Join(app1Dir, "copyfile")) + Expect(err).To(BeNil()) + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + Expect(os.Chdir(yamlDir)).To(BeNil()) + defer func() { (Expect(os.Chdir(cwd)).To(BeNil())) }() + + session := podmanTest.Podman([]string{"play", "kube", "top.yaml"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + exists := podmanTest.Podman([]string{"image", "exists", "foobar"}) + exists.WaitWithDefaultTimeout() + Expect(exists).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "top_pod-foobar"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + inspectData := inspect.InspectContainerToJSON() + Expect(len(inspectData)).To(BeNumerically(">", 0)) + Expect(inspectData[0].Config.Labels["homer"]).To(Equal("dad")) + }) + + It("Do not build image if already in the local store", func() { + // Setup + yamlDir := filepath.Join(tempdir, RandomString(12)) + err := os.Mkdir(yamlDir, 0755) + err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) + Expect(err).To(BeNil()) + + // build an image called foobar but make sure it doesnt have + // the same label as the yaml buildfile, so we can check that + // the image is NOT rebuilt. + err = writeYaml(prebuiltImage, filepath.Join(yamlDir, "Containerfile")) + Expect(err).To(BeNil()) + + app1Dir := filepath.Join(yamlDir, "foobar") + err = os.Mkdir(app1Dir, 0755) + Expect(err).To(BeNil()) + err = writeYaml(playBuildFile, filepath.Join(app1Dir, "Containerfile")) + Expect(err).To(BeNil()) + // Write a file to be copied + err = writeYaml(copyFile, filepath.Join(app1Dir, "copyfile")) + Expect(err).To(BeNil()) + + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + Expect(os.Chdir(yamlDir)).To(BeNil()) + defer func() { (Expect(os.Chdir(cwd)).To(BeNil())) }() + + // Build the image into the local store + build := podmanTest.Podman([]string{"build", "-t", "foobar", "-f", "Containerfile"}) + build.WaitWithDefaultTimeout() + Expect(build).Should(Exit(0)) + + session := podmanTest.Podman([]string{"play", "kube", "top.yaml"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "top_pod-foobar"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + inspectData := inspect.InspectContainerToJSON() + Expect(len(inspectData)).To(BeNumerically(">", 0)) + Expect(inspectData[0].Config.Labels["homer"]).To(Equal("")) + Expect(inspectData[0].Config.Labels["marge"]).To(Equal("mom")) + }) + + It("--build should override image in store", func() { + // Setup + yamlDir := filepath.Join(tempdir, RandomString(12)) + err := os.Mkdir(yamlDir, 0755) + err = writeYaml(testYAML, filepath.Join(yamlDir, "top.yaml")) + Expect(err).To(BeNil()) + + // build an image called foobar but make sure it doesnt have + // the same label as the yaml buildfile, so we can check that + // the image is NOT rebuilt. + err = writeYaml(prebuiltImage, filepath.Join(yamlDir, "Containerfile")) + Expect(err).To(BeNil()) + + app1Dir := filepath.Join(yamlDir, "foobar") + err = os.Mkdir(app1Dir, 0755) + Expect(err).To(BeNil()) + err = writeYaml(playBuildFile, filepath.Join(app1Dir, "Containerfile")) + Expect(err).To(BeNil()) + // Write a file to be copied + err = writeYaml(copyFile, filepath.Join(app1Dir, "copyfile")) + Expect(err).To(BeNil()) + + // Switch to temp dir and restore it afterwards + cwd, err := os.Getwd() + Expect(err).To(BeNil()) + Expect(os.Chdir(yamlDir)).To(BeNil()) + defer func() { (Expect(os.Chdir(cwd)).To(BeNil())) }() + + // Build the image into the local store + build := podmanTest.Podman([]string{"build", "-t", "foobar", "-f", "Containerfile"}) + build.WaitWithDefaultTimeout() + Expect(build).Should(Exit(0)) + + session := podmanTest.Podman([]string{"play", "kube", "--build", "top.yaml"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "top_pod-foobar"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + inspectData := inspect.InspectContainerToJSON() + Expect(len(inspectData)).To(BeNumerically(">", 0)) + Expect(inspectData[0].Config.Labels["homer"]).To(Equal("dad")) + Expect(inspectData[0].Config.Labels["marge"]).To(Equal("")) + }) + +}) diff -Nru libpod-3.2.1+ds1/test/e2e/play_kube_test.go libpod-3.4.4+ds1/test/e2e/play_kube_test.go --- libpod-3.2.1+ds1/test/e2e/play_kube_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/play_kube_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,12 +9,17 @@ "strconv" "strings" "text/template" + "time" + "github.com/containers/common/pkg/config" + "github.com/containers/podman/v3/libpod/define" "github.com/containers/podman/v3/pkg/util" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/onsi/gomega/format" + . "github.com/onsi/gomega/gexec" "github.com/opencontainers/selinux/go-selinux" ) @@ -28,6 +33,22 @@ spec: hostname: unknown ` +var checkInfraImagePodYaml = ` +apiVersion: v1 +kind: Pod +metadata: + labels: + app: check-infra-image + name: check-infra-image +spec: + containers: + - name: alpine + image: quay.io/libpod/alpine:latest + command: + - sleep + - 24h +status: {} +` var sharedNamespacePodYaml = ` apiVersion: v1 kind: Pod @@ -43,12 +64,6 @@ - -d - "1.5" env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman - name: HOSTNAME value: label-pod image: quay.io/libpod/alpine:latest @@ -66,6 +81,75 @@ shareProcessNamespace: true status: {} ` +var livenessProbePodYaml = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: liveness-probe + labels: + app: alpine +spec: + replicas: 1 + selector: + matchLabels: + app: alpine + template: + metadata: + labels: + app: alpine + spec: + containers: + - command: + - top + - -d + - "1.5" + name: alpine + image: quay.io/libpod/alpine:latest + ports: + - containerPort: 80 + livenessProbe: + exec: + command: + - echo + - hello + initialDelaySeconds: 5 + periodSeconds: 5 +` +var livenessProbeUnhealthyPodYaml = ` +apiVersion: apps/v1 +kind: Deployment +metadata: + name: liveness-unhealthy-probe + labels: + app: alpine +spec: + replicas: 1 + selector: + matchLabels: + app: alpine + template: + metadata: + labels: + app: alpine + spec: + restartPolicy: Never + containers: + - command: + - top + - -d + - "1.5" + name: alpine + image: quay.io/libpod/alpine:latest + ports: + - containerPort: 80 + livenessProbe: + exec: + command: + - cat + - /randomfile + initialDelaySeconds: 0 + periodSeconds: 1 +` var selinuxLabelPodYaml = ` apiVersion: v1 @@ -82,12 +166,6 @@ - -d - "1.5" env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - - name: container - value: podman - name: HOSTNAME value: label-pod image: quay.io/libpod/alpine:latest @@ -175,6 +253,17 @@ {{ end }} ip: {{ .IP }} {{ end }} + initContainers: +{{ with .InitCtrs }} + {{ range . }} + - command: + {{ range .Cmd }} + - {{.}} + {{ end }} + image: {{ .Image }} + name: {{ .Name }} + {{ end }} +{{ end }} containers: {{ with .Ctrs }} {{ range . }} @@ -187,13 +276,7 @@ - {{.}} {{ end }} env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - name: HOSTNAME - - name: container - value: podman {{ range .Env }} - name: {{ .Name }} {{ if (eq .ValueFrom "configmap") }} @@ -353,13 +436,42 @@ - {{.}} {{ end }} env: - - name: PATH - value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - - name: TERM - value: xterm - name: HOSTNAME - - name: container - value: podman + {{ range .Env }} + - name: {{ .Name }} + {{ if (eq .ValueFrom "configmap") }} + valueFrom: + configMapKeyRef: + name: {{ .RefName }} + key: {{ .RefKey }} + optional: {{ .Optional }} + {{ end }} + {{ if (eq .ValueFrom "secret") }} + valueFrom: + secretKeyRef: + name: {{ .RefName }} + key: {{ .RefKey }} + optional: {{ .Optional }} + {{ end }} + {{ if (eq .ValueFrom "") }} + value: {{ .Value }} + {{ end }} + {{ end }} + {{ with .EnvFrom}} + envFrom: + {{ range . }} + {{ if (eq .From "configmap") }} + - configMapRef: + name: {{ .Name }} + optional: {{ .Optional }} + {{ end }} + {{ if (eq .From "secret") }} + - secretRef: + name: {{ .Name }} + optional: {{ .Optional }} + {{ end }} + {{ end }} + {{ end }} image: {{ .Image }} name: {{ .Name }} imagePullPolicy: {{ .PullPolicy }} @@ -441,21 +553,6 @@ defaultSecret = []byte(`{"FOO":"Zm9v","BAR":"YmFy"}`) ) -func writeYaml(content string, fileName string) error { - f, err := os.Create(fileName) - if err != nil { - return err - } - defer f.Close() - - _, err = f.WriteString(content) - if err != nil { - return err - } - - return nil -} - // getKubeYaml returns a kubernetes YAML document. func getKubeYaml(kind string, object interface{}) (string, error) { var yamlTemplate string @@ -515,7 +612,7 @@ secret := podmanTest.Podman([]string{"secret", "create", name, secretFilePath}) secret.WaitWithDefaultTimeout() - Expect(secret.ExitCode()).To(Equal(0)) + Expect(secret).Should(Exit(0)) } // ConfigMap describes the options a kube yaml can be configured at configmap level @@ -592,6 +689,7 @@ HostNetwork bool HostAliases []HostAlias Ctrs []*Ctr + InitCtrs []*Ctr Volumes []*Volume Labels map[string]string Annotations map[string]string @@ -613,6 +711,7 @@ HostNetwork: false, HostAliases: nil, Ctrs: make([]*Ctr, 0), + InitCtrs: make([]*Ctr, 0), Volumes: make([]*Volume, 0), Labels: make(map[string]string), Annotations: make(map[string]string), @@ -655,6 +754,12 @@ } } +func withPodInitCtr(ic *Ctr) podOption { + return func(pod *Pod) { + pod.InitCtrs = append(pod.InitCtrs, ic) + } +} + func withRestartPolicy(policy string) podOption { return func(pod *Pod) { pod.RestartPolicy = policy @@ -774,6 +879,7 @@ VolumeReadOnly bool Env []Env EnvFrom []EnvFrom + InitCtrType string } // getCtr takes a list of ctrOptions and returns a Ctr with sane defaults @@ -797,6 +903,7 @@ VolumeReadOnly: false, Env: []Env{}, EnvFrom: []EnvFrom{}, + InitCtrType: "", } for _, option := range options { option(&c) @@ -812,6 +919,12 @@ } } +func withInitCtr() ctrOption { + return func(c *Ctr) { + c.InitCtrType = define.AlwaysInitContainer + } +} + func withCmd(cmd []string) ctrOption { return func(c *Ctr) { c.Cmd = cmd @@ -1020,7 +1133,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) @@ -1033,7 +1146,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "label-pod-test", "--format", "'{{ .ProcessLabel }}'"}) inspect.WaitWithDefaultTimeout() @@ -1042,14 +1155,63 @@ Expect(label).To(ContainSubstring("unconfined_u:system_r:spc_t:s0")) }) + It("podman play kube should use default infra_image", func() { + err := writeYaml(checkInfraImagePodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"inspect", "check-infra-image", "--format", "{{ .InfraContainerID }}"}) + podInspect.WaitWithDefaultTimeout() + infraContainerID := podInspect.OutputToString() + + conInspect := podmanTest.Podman([]string{"inspect", infraContainerID, "--format", "{{ .ImageName }}"}) + conInspect.WaitWithDefaultTimeout() + infraContainerImage := conInspect.OutputToString() + Expect(infraContainerImage).To(Equal(config.DefaultInfraImage)) + }) + + It("podman play kube should use customized infra_image", func() { + conffile := filepath.Join(podmanTest.TempDir, "container.conf") + + infraImage := "k8s.gcr.io/pause:3.2" + err := ioutil.WriteFile(conffile, []byte(fmt.Sprintf("[engine]\ninfra_image=\"%s\"\n", infraImage)), 0644) + Expect(err).To(BeNil()) + + os.Setenv("CONTAINERS_CONF", conffile) + defer os.Unsetenv("CONTAINERS_CONF") + + if IsRemote() { + podmanTest.RestartRemoteService() + } + + err = writeYaml(checkInfraImagePodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"inspect", "check-infra-image", "--format", "{{ .InfraContainerID }}"}) + podInspect.WaitWithDefaultTimeout() + infraContainerID := podInspect.OutputToString() + + conInspect := podmanTest.Podman([]string{"inspect", infraContainerID, "--format", "{{ .ImageName }}"}) + conInspect.WaitWithDefaultTimeout() + infraContainerImage := conInspect.OutputToString() + Expect(infraContainerImage).To(Equal(infraImage)) + }) + It("podman play kube should share ipc,net,uts when shareProcessNamespace is set", func() { - SkipIfRootless("Requires root priviledges for sharing few namespaces") + SkipIfRootless("Requires root privileges for sharing few namespaces") err := writeYaml(sharedNamespacePodYaml, kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "testpod1", "--format", "'{{ .SharedNamespaces }}'"}) inspect.WaitWithDefaultTimeout() @@ -1060,13 +1222,43 @@ Expect(sharednamespaces).To(ContainSubstring("pid")) }) + It("podman play kube support container liveness probe", func() { + err := writeYaml(livenessProbePodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", "liveness-probe-pod-0-alpine", "--format", "'{{ .Config.Healthcheck }}'"}) + inspect.WaitWithDefaultTimeout() + healthcheckcmd := inspect.OutputToString() + // check if CMD-SHELL based equivalent health check is added to container + Expect(healthcheckcmd).To(ContainSubstring("CMD-SHELL")) + }) + + It("podman play kube liveness probe should fail", func() { + err := writeYaml(livenessProbeUnhealthyPodYaml, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + time.Sleep(2 * time.Second) + hc := podmanTest.Podman([]string{"healthcheck", "run", "liveness-unhealthy-probe-pod-0-alpine"}) + hc.WaitWithDefaultTimeout() + hcoutput := hc.OutputToString() + Expect(hcoutput).To(ContainSubstring(define.HealthCheckUnhealthy)) + }) + It("podman play kube fail with nonexistent authfile", func() { err := generateKubeYaml("pod", getPod(), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", "--authfile", "/tmp/nonexistent", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) @@ -1077,16 +1269,16 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) cmd := inspect.OutputToString() inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ep := inspect.OutputToString() // Use the defined command to override the image's command @@ -1102,18 +1294,18 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // this image's ENTRYPOINT is `/entrypoint.sh` inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`/entrypoint.sh`)) // and its COMMAND is `/etc/docker/registry/config.yml` inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[/etc/docker/registry/config.yml]`)) }) @@ -1126,22 +1318,49 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // Use the defined command to override the image's command, and don't set the args // so the full command in result should not contains the image's command inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`echo hello`)) inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) // an empty command is reported as '[]' Expect(inspect.OutputToString()).To(ContainSubstring(`[]`)) }) + // If you have an init container in the pod yaml, podman should create and run the init container with play kube + It("podman play kube test with init containers", func() { + pod := getPod(withPodInitCtr(getCtr(withImage(ALPINE), withCmd([]string{"echo", "hello"}), withInitCtr(), withName("init-test"))), withCtr(getCtr(withImage(ALPINE), withCmd([]string{"top"})))) + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + // Expect the number of containers created to be 3, one init, infra, and regular container + numOfCtrs := podmanTest.NumberOfContainers() + Expect(numOfCtrs).To(Equal(3)) + + // Init container should have exited after running + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.State.Status}}", "testPod-init-test"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("exited")) + + // Regular container should be in running state + inspect = podmanTest.Podman([]string{"inspect", "--format", "{{.State.Status}}", "testPod-" + defaultCtrName}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("running")) + }) + // If you supply only args for a Container, the default Entrypoint defined in the Docker image is run with the args that you supplied. It("podman play kube test correct command with only set args in yaml file", func() { pod := getPod(withCtr(getCtr(withImage(registry), withCmd(nil), withArg([]string{"echo", "hello"})))) @@ -1150,17 +1369,17 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // this image's ENTRYPOINT is `/entrypoint.sh` inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`/entrypoint.sh`)) inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[echo hello]`)) }) @@ -1174,16 +1393,16 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`echo`)) inspect = podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Cmd }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[hello]`)) }) @@ -1195,11 +1414,45 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(p)}) logs.WaitWithDefaultTimeout() - Expect(logs.ExitCode()).To(Equal(0)) + Expect(logs).Should(Exit(0)) + Expect(logs.OutputToString()).To(ContainSubstring("hello world")) + }) + + It("podman pod logs test", func() { + SkipIfRemote("podman-remote pod logs -c is mandatory for remote machine") + p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg([]string{"world"})))) + + err := generateKubeYaml("pod", p, 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", p.Name}) + logs.WaitWithDefaultTimeout() + Expect(logs).Should(Exit(0)) + Expect(logs.OutputToString()).To(ContainSubstring("hello world")) + }) + + It("podman-remote pod logs test", func() { + // -c or --container is required in podman-remote due to api limitation. + p := getPod(withCtr(getCtr(withCmd([]string{"echo", "hello"}), withArg([]string{"world"})))) + + err := generateKubeYaml("pod", p, 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", getCtrNameInPod(p), p.Name}) + logs.WaitWithDefaultTimeout() + Expect(logs).Should(Exit(0)) Expect(logs.OutputToString()).To(ContainSubstring("hello world")) }) @@ -1218,11 +1471,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{.HostConfig.RestartPolicy.Name}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(v[2])) } }) @@ -1240,11 +1493,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`)) }) @@ -1261,7 +1514,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube test required env value from missing configmap", func() { @@ -1271,7 +1524,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube test optional env value from configmap with missing key", func() { @@ -1287,11 +1540,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) }) @@ -1302,11 +1555,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) }) @@ -1323,11 +1576,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", cmYamlPathname}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO1=foo1`)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO2=foo2`)) }) @@ -1339,7 +1592,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube test get all key-value pairs from optional configmap as envs", func() { @@ -1349,7 +1602,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman play kube test env value from secret", func() { @@ -1360,11 +1613,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`)) }) @@ -1375,7 +1628,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube test required env value from secret with missing key", func() { @@ -1386,7 +1639,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube test optional env value from missing secret", func() { @@ -1396,11 +1649,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) }) @@ -1412,11 +1665,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) }) @@ -1428,11 +1681,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`)) Expect(inspect.OutputToString()).To(ContainSubstring(`BAR=bar`)) }) @@ -1444,7 +1697,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube test get all key-value pairs from optional secret as envs", func() { @@ -1454,7 +1707,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman play kube test hostname", func() { @@ -1464,11 +1717,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ .Config.Hostname }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(defaultPodName)) }) @@ -1480,11 +1733,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ .Config.Hostname }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(hostname)) }) @@ -1503,11 +1756,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", pod.Name, "--format", "{{ .InfraConfig.HostAdd}}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()). To(Equal("[test1.podman.io:192.168.1.2 test2.podman.io:192.168.1.2 test3.podman.io:192.168.1.3 test4.podman.io:192.168.1.3]")) }) @@ -1522,11 +1775,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(capAdd)) }) @@ -1540,11 +1793,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(capDrop)) }) @@ -1556,11 +1809,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) }) It("podman play kube seccomp container level", func() { @@ -1582,11 +1835,11 @@ // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)}) logs.WaitWithDefaultTimeout() - Expect(logs.ExitCode()).To(Equal(0)) + Expect(logs).Should(Exit(0)) Expect(logs.ErrorToString()).To(ContainSubstring("Operation not permitted")) }) @@ -1609,11 +1862,11 @@ // CreateSeccompJson will put the profile into podmanTest.TempDir. Use --seccomp-profile-root to tell play kube where to look kube := podmanTest.Podman([]string{"play", "kube", "--seccomp-profile-root", podmanTest.TempDir, kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) logs := podmanTest.Podman([]string{"logs", getCtrNameInPod(pod)}) logs.WaitWithDefaultTimeout() - Expect(logs.ExitCode()).To(Equal(0)) + Expect(logs).Should(Exit(0)) Expect(logs.ErrorToString()).To(ContainSubstring("Operation not permitted")) }) @@ -1624,17 +1877,17 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(125)) + Expect(kube).Should(Exit(125)) }) It("podman play kube with pull policy of missing", func() { - ctr := getCtr(withPullPolicy("missing"), withImage(BB)) + ctr := getCtr(withPullPolicy("Missing"), withImage(BB)) err := generateKubeYaml("pod", getPod(withCtr(ctr)), kubeYaml) Expect(err).To(BeNil()) kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman play kube with pull always", func() { @@ -1644,11 +1897,11 @@ tag := podmanTest.Podman([]string{"tag", oldBB, BB}) tag.WaitWithDefaultTimeout() - Expect(tag.ExitCode()).To(BeZero()) + Expect(tag).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", oldBB}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(BeZero()) + Expect(rmi).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() @@ -1660,7 +1913,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect = podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() @@ -1675,11 +1928,11 @@ tag := podmanTest.Podman([]string{"tag", oldBB, BB}) tag.WaitWithDefaultTimeout() - Expect(tag.ExitCode()).To(BeZero()) + Expect(tag).Should(Exit(0)) rmi := podmanTest.Podman([]string{"rmi", oldBB}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(BeZero()) + Expect(rmi).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() @@ -1691,7 +1944,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect = podmanTest.Podman([]string{"inspect", BB}) inspect.WaitWithDefaultTimeout() @@ -1713,11 +1966,11 @@ pull := podmanTest.Podman([]string{"create", "--workdir", "/etc", "--name", "newBB", "--label", "key1=value1", "alpine"}) pull.WaitWithDefaultTimeout() - Expect(pull.ExitCode()).To(BeZero()) + Expect(pull).Should(Exit(0)) c := podmanTest.Podman([]string{"commit", "-c", "STOPSIGNAL=51", "newBB", "demo"}) c.WaitWithDefaultTimeout() - Expect(c.ExitCode()).To(Equal(0)) + Expect(c).Should(Exit(0)) conffile := filepath.Join(podmanTest.TempDir, "kube.yaml") tempdir, err = CreateTempDirInTempDir() @@ -1728,11 +1981,11 @@ kube := podmanTest.Podman([]string{"play", "kube", conffile}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "demo_pod-demo_kube"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() Expect(ctr[0].Config.WorkingDir).To(ContainSubstring("/etc")) @@ -1749,12 +2002,12 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) podNames := getPodNamesInDeployment(deployment) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[0]), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) // yaml's command should override the image's Entrypoint Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) }) @@ -1768,13 +2021,13 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) podNames := getPodNamesInDeployment(deployment) for i = 0; i < numReplicas; i++ { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "'{{ .Config.Entrypoint }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(strings.Join(defaultCtrCmd, " "))) } }) @@ -1790,7 +2043,7 @@ session := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.31.0/24", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) ips := []string{"10.25.31.5", "10.25.31.10", "10.25.31.15"} playArgs := []string{"play", "kube", "--network", net} @@ -1804,20 +2057,20 @@ kube := podmanTest.Podman(append(playArgs, kubeYaml)) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) podNames := getPodNamesInDeployment(deployment) for i = 0; i < numReplicas; i++ { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".IPAddress }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(ips[i])) } for i = 0; i < numReplicas; i++ { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".MacAddress }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(macs[i])) } }) @@ -1833,11 +2086,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"port", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("5000/tcp -> 127.0.0.100:5000")) }) @@ -1850,7 +2103,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).NotTo(Equal(0)) + Expect(kube).To(ExitWithError()) Expect(kube.ErrorToString()).To(ContainSubstring(defaultVolName)) }) @@ -1866,7 +2119,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman play kube test with nonexistent File HostPath type volume", func() { @@ -1878,7 +2131,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).NotTo(Equal(0)) + Expect(kube).To(ExitWithError()) }) It("podman play kube test with File HostPath type volume", func() { @@ -1893,7 +2146,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) }) It("podman play kube test with FileOrCreate HostPath type volume", func() { @@ -1905,7 +2158,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // the file should have been created _, err = os.Stat(hostPathLocation) @@ -1921,7 +2174,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) // the file should have been created st, err := os.Stat(hostPathLocation) @@ -1941,7 +2194,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).NotTo(Equal(0)) + Expect(kube).To(ExitWithError()) }) It("podman play kube test with read only HostPath volume", func() { @@ -1957,11 +2210,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{.HostConfig.Binds}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) correct := fmt.Sprintf("%s:%s:%s", hostPathLocation, hostPathLocation, "ro") Expect(inspect.OutputToString()).To(ContainSubstring(correct)) @@ -1997,15 +2250,15 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) result := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "ls", hostPathDir + "/" + testfile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) // If two volumes are specified and share the same destination, // only one will be mounted. Host path volumes take precedence. @@ -2025,11 +2278,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ (index .Mounts 0).Type }}:{{ (index .Mounts 0).Name }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) correct := fmt.Sprintf("volume:%s", volumeName) Expect(inspect.OutputToString()).To(Equal(correct)) @@ -2048,13 +2301,13 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) correctLabels := expectedLabelKey + ":" + expectedLabelValue for _, pod := range getPodNamesInDeployment(deployment) { inspect := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "'{{ .Labels }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(correctLabels)) } }) @@ -2087,7 +2340,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) for _, pod := range getPodNamesInDeployment(deployment) { inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&pod), "--format", ` @@ -2096,7 +2349,7 @@ Memory: {{ .HostConfig.Memory }} MemoryReservation: {{ .HostConfig.MemoryReservation }}`}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(fmt.Sprintf("%s: %d", "CpuQuota", expectedCpuQuota))) Expect(inspect.OutputToString()).To(ContainSubstring("MemoryReservation: " + expectedMemoryRequest)) Expect(inspect.OutputToString()).To(ContainSubstring("Memory: " + expectedMemoryLimit)) @@ -2118,8 +2371,8 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(125)) - Expect(kube.ErrorToString()).To(ContainSubstring(invalidImageName)) + Expect(kube).Should(Exit(125)) + Expect(kube.ErrorToString()).To(ContainSubstring("invalid reference format")) }) It("podman play kube applies log driver to containers", func() { @@ -2129,11 +2382,11 @@ kube := podmanTest.Podman([]string{"play", "kube", "--log-driver", "journald", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .HostConfig.LogConfig.Type }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring("journald")) }) @@ -2144,11 +2397,11 @@ kube := podmanTest.Podman([]string{"play", "kube", "--start=false", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "{{ .State.Running }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("false")) }) @@ -2163,11 +2416,11 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", pod.Name, "--format", "{{ .InfraConfig.HostNetwork }}"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal("true")) }) @@ -2186,7 +2439,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", volName, "--format", ` Name: {{ .Name }} @@ -2194,7 +2447,7 @@ Type: {{ .Options.type }} o: {{ .Options.o }}`}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring("Name: " + volName)) Expect(inspect.OutputToString()).To(ContainSubstring("Device: " + volDevice)) Expect(inspect.OutputToString()).To(ContainSubstring("Type: " + volType)) @@ -2250,21 +2503,21 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) inspectVolume := podmanTest.Podman([]string{"inspect", volName, "--format", "'{{ .Name }}'"}) inspectVolume.WaitWithDefaultTimeout() - Expect(inspectVolume.ExitCode()).To(Equal(0)) + Expect(inspectVolume).Should(Exit(0)) Expect(inspectVolume.OutputToString()).To(ContainSubstring(volName)) inspectPod := podmanTest.Podman([]string{"inspect", podName + "-pod-0", "--format", "'{{ .State }}'"}) inspectPod.WaitWithDefaultTimeout() - Expect(inspectPod.ExitCode()).To(Equal(0)) + Expect(inspectPod).Should(Exit(0)) Expect(inspectPod.OutputToString()).To(ContainSubstring(`Running`)) inspectMounts := podmanTest.Podman([]string{"inspect", podName + "-pod-0-" + ctrName, "--format", "{{ (index .Mounts 0).Type }}:{{ (index .Mounts 0).Name }}"}) inspectMounts.WaitWithDefaultTimeout() - Expect(inspectMounts.ExitCode()).To(Equal(0)) + Expect(inspectMounts).Should(Exit(0)) correct := fmt.Sprintf("volume:%s", volName) Expect(inspectMounts.OutputToString()).To(Equal(correct)) @@ -2322,12 +2575,12 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) for _, n := range podNames { inspect := podmanTest.Podman([]string{"inspect", n, "--format", "'{{ .State }}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`Running`)) } }) @@ -2364,7 +2617,7 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Not(Equal(0))) + Expect(kube).To(ExitWithError()) }) It("podman play kube with auto update annotations for all containers", func() { @@ -2391,12 +2644,12 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) for _, ctr := range []string{podName + "-" + ctr01Name, podName + "-" + ctr02Name} { inspect := podmanTest.Podman([]string{"inspect", ctr, "--format", "'{{.Config.Labels}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(autoUpdateRegistry + ":" + autoUpdateRegistryValue)) Expect(inspect.OutputToString()).To(ContainSubstring(autoUpdateAuthfile + ":" + autoUpdateAuthfileValue)) @@ -2427,18 +2680,387 @@ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) kube.WaitWithDefaultTimeout() - Expect(kube.ExitCode()).To(Equal(0)) + Expect(kube).Should(Exit(0)) podName := getPodNamesInDeployment(deployment)[0].Name inspect := podmanTest.Podman([]string{"inspect", podName + "-" + ctr01Name, "--format", "'{{.Config.Labels}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(autoUpdateRegistry + ":" + autoUpdateRegistryValue)) inspect = podmanTest.Podman([]string{"inspect", podName + "-" + ctr02Name, "--format", "'{{.Config.Labels}}'"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(ContainSubstring(`map[]`)) }) + + It("podman play kube teardown", func() { + pod := getPod() + err := generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + ls := podmanTest.Podman([]string{"pod", "ps", "--format", "'{{.ID}}'"}) + ls.WaitWithDefaultTimeout() + Expect(ls).Should(Exit(0)) + Expect(len(ls.OutputToStringArray())).To(Equal(1)) + + // teardown + teardown := podmanTest.Podman([]string{"play", "kube", "--down", kubeYaml}) + teardown.WaitWithDefaultTimeout() + Expect(teardown).Should(Exit(0)) + + checkls := podmanTest.Podman([]string{"pod", "ps", "--format", "'{{.ID}}'"}) + checkls.WaitWithDefaultTimeout() + Expect(checkls).Should(Exit(0)) + Expect(len(checkls.OutputToStringArray())).To(Equal(0)) + }) + + It("podman play kube teardown pod does not exist", func() { + // teardown + teardown := podmanTest.Podman([]string{"play", "kube", "--down", kubeYaml}) + teardown.WaitWithDefaultTimeout() + Expect(teardown).Should(Exit(125)) + }) + + It("podman play kube teardown with volume", func() { + + volName := RandomString(12) + volDevice := "tmpfs" + volType := "tmpfs" + volOpts := "nodev,noexec" + + pvc := getPVC(withPVCName(volName), + withPVCAnnotations(util.VolumeDeviceAnnotation, volDevice), + withPVCAnnotations(util.VolumeTypeAnnotation, volType), + withPVCAnnotations(util.VolumeMountOptsAnnotation, volOpts)) + err = generateKubeYaml("persistentVolumeClaim", pvc, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + exists := podmanTest.Podman([]string{"volume", "exists", volName}) + exists.WaitWithDefaultTimeout() + Expect(exists).To(Exit(0)) + + teardown := podmanTest.Podman([]string{"play", "kube", "--down", kubeYaml}) + teardown.WaitWithDefaultTimeout() + Expect(teardown).To(Exit(0)) + + // volume should not be deleted on teardown + exists = podmanTest.Podman([]string{"volume", "exists", volName}) + exists.WaitWithDefaultTimeout() + Expect(exists).To(Exit(0)) + }) + + It("podman play kube use network mode from config", func() { + confPath, err := filepath.Abs("config/containers-netns2.conf") + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confPath) + defer os.Unsetenv("CONTAINERS_CONF") + if IsRemote() { + podmanTest.RestartRemoteService() + } + + pod := getPod() + err = generateKubeYaml("pod", pod, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"pod", "inspect", pod.Name, "--format", "{{.InfraContainerID}}"}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).To(Exit(0)) + infraID := podInspect.OutputToString() + + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.HostConfig.NetworkMode}}", infraID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("bridge")) + }) + + Describe("verify environment variables", func() { + var maxLength int + BeforeEach(func() { + maxLength = format.MaxLength + format.MaxLength = 0 + }) + AfterEach(func() { + format.MaxLength = maxLength + }) + + It("values containing equal sign", func() { + javaToolOptions := `-XX:+IgnoreUnrecognizedVMOptions -XX:+IdleTuningGcOnIdle -Xshareclasses:name=openj9_system_scc,cacheDir=/opt/java/.scc,readonly,nonFatal` + openj9JavaOptions := `-XX:+IgnoreUnrecognizedVMOptions -XX:+IdleTuningGcOnIdle -Xshareclasses:name=openj9_system_scc,cacheDir=/opt/java/.scc,readonly,nonFatal -Dosgi.checkConfiguration=false` + + containerfile := fmt.Sprintf(`FROM %s +ENV JAVA_TOOL_OPTIONS=%q +ENV OPENJ9_JAVA_OPTIONS=%q +`, + ALPINE, javaToolOptions, openj9JavaOptions) + + image := "podman-kube-test:env" + podmanTest.BuildImage(containerfile, image, "false") + ctnr := getCtr(withImage(image)) + pod := getPod(withCtr(ctnr)) + Expect(generateKubeYaml("pod", pod, kubeYaml)).Should(Succeed()) + + play := podmanTest.Podman([]string{"play", "kube", "--start", kubeYaml}) + play.WaitWithDefaultTimeout() + Expect(play).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "--format=json", getCtrNameInPod(pod)}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + + contents := string(inspect.Out.Contents()) + Expect(contents).To(ContainSubstring(javaToolOptions)) + Expect(contents).To(ContainSubstring(openj9JavaOptions)) + }) + }) + + Context("with configmap in multi-doc yaml", func() { + It("podman play kube uses env value", func() { + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo")) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "FOO", false)))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`)) + }) + + It("podman play kube fails for required env value with missing key", func() { + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo")) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "MISSING_KEY", false)))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).To(ExitWithError()) + }) + + It("podman play kube succeeds for optional env value with missing key", func() { + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo")) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "MISSING_KEY", true)))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ range .Config.Env }}[{{ . }}]{{end}}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`[FOO=]`)) + }) + + It("podman play kube uses all key-value pairs as envs", func() { + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO1", "foo1"), withConfigMapData("FOO2", "foo2")) + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr(withEnvFrom("foo", "configmap", false)))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`FOO1=foo1`)) + Expect(inspect.OutputToString()).To(ContainSubstring(`FOO2=foo2`)) + }) + + It("podman play kube deployment uses variable from config map", func() { + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo")) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr(withEnv("FOO", "", "configmap", "foo", "FOO", false)))) + + deployment := getDeployment(withPod(pod)) + deploymentYaml, err := getKubeYaml("deployment", deployment) + yamls := []string{cmYaml, deploymentYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", fmt.Sprintf("%s-%s-%s", deployment.Name, "pod-0", defaultCtrName), "--format", "'{{ .Config }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring(`FOO=foo`)) + + }) + }) + + Context("with configmap in multi-doc yaml and files", func() { + It("podman play kube uses env values from both sources", func() { + SkipIfRemote("--configmaps is not supported for remote") + + fsCmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml") + fsCm := getConfigMap(withConfigMapName("fooFs"), withConfigMapData("FOO_FS", "fooFS")) + err := generateKubeYaml("configmap", fsCm, fsCmYamlPathname) + Expect(err).To(BeNil()) + + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo")) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr( + withEnv("FOO_FS", "", "configmap", "fooFs", "FOO_FS", false), + withEnv("FOO", "", "configmap", "foo", "FOO", false), + ))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", fsCmYamlPathname}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(And( + ContainSubstring(`FOO=foo`), + ContainSubstring(`FOO_FS=fooFS`), + )) + }) + + It("podman play kube uses all env values from both sources", func() { + SkipIfRemote("--configmaps is not supported for remote") + + fsCmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml") + fsCm := getConfigMap(withConfigMapName("fooFs"), + withConfigMapData("FOO_FS_1", "fooFS1"), + withConfigMapData("FOO_FS_2", "fooFS2")) + err := generateKubeYaml("configmap", fsCm, fsCmYamlPathname) + Expect(err).To(BeNil()) + + cm := getConfigMap(withConfigMapName("foo"), + withConfigMapData("FOO_1", "foo1"), + withConfigMapData("FOO_2", "foo2"), + ) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr( + withEnvFrom("foo", "configmap", false), + withEnvFrom("fooFs", "configmap", false), + ))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", fsCmYamlPathname}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .Config.Env }}'"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(And( + ContainSubstring(`FOO_1=foo1`), + ContainSubstring(`FOO_2=foo2`), + ContainSubstring(`FOO_FS_1=fooFS1`), + ContainSubstring(`FOO_FS_2=fooFS2`), + )) + }) + + It("podman play kube reports error when the same configmap name is present in both sources", func() { + SkipIfRemote("--configmaps is not supported for remote") + + fsCmYamlPathname := filepath.Join(podmanTest.TempDir, "foo-cm.yaml") + fsCm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "fooFS")) + err := generateKubeYaml("configmap", fsCm, fsCmYamlPathname) + Expect(err).To(BeNil()) + + cm := getConfigMap(withConfigMapName("foo"), withConfigMapData("FOO", "foo")) + + cmYaml, err := getKubeYaml("configmap", cm) + Expect(err).To(BeNil()) + + pod := getPod(withCtr(getCtr( + withEnv("FOO", "", "configmap", "foo", "FOO", false), + ))) + + podYaml, err := getKubeYaml("pod", pod) + Expect(err).To(BeNil()) + + yamls := []string{cmYaml, podYaml} + err = generateMultiDocKubeYaml(yamls, kubeYaml) + Expect(err).To(BeNil()) + + kube := podmanTest.Podman([]string{"play", "kube", kubeYaml, "--configmap", fsCmYamlPathname}) + kube.WaitWithDefaultTimeout() + Expect(kube).Should(Exit(125)) + Expect(kube.ErrorToString()).To(ContainSubstring("ambiguous configuration: the same config map foo is present in YAML and in --configmaps")) + }) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_create_test.go libpod-3.4.4+ds1/test/e2e/pod_create_test.go --- libpod-3.2.1+ds1/test/e2e/pod_create_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_create_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,13 +4,16 @@ "fmt" "io/ioutil" "os" + "os/user" "path/filepath" + "strconv" "strings" "github.com/containers/podman/v3/pkg/rootless" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod create", func() { @@ -76,7 +79,7 @@ name := "test" session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) Expect(ec).To(Not(Equal(0))) @@ -90,52 +93,67 @@ name := "test" session := podmanTest.Podman([]string{"pod", "create", "--name", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) pod := session.OutputToString() webserver := podmanTest.Podman([]string{"run", "--pod", pod, "-dt", nginx}) webserver.WaitWithDefaultTimeout() - Expect(webserver.ExitCode()).To(Equal(0)) + Expect(webserver).Should(Exit(0)) check := SystemExec("nc", []string{"-z", "localhost", "80"}) - Expect(check.ExitCode()).To(Equal(1)) + Expect(check).Should(Exit(1)) }) It("podman create pod with network portbindings", func() { name := "test" - session := podmanTest.Podman([]string{"pod", "create", "--name", name, "-p", "8080:80"}) + session := podmanTest.Podman([]string{"pod", "create", "--name", name, "-p", "8081:80"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) pod := session.OutputToString() webserver := podmanTest.Podman([]string{"run", "--pod", pod, "-dt", nginx}) webserver.WaitWithDefaultTimeout() - Expect(webserver.ExitCode()).To(Equal(0)) + Expect(webserver).Should(Exit(0)) - check := SystemExec("nc", []string{"-z", "localhost", "8080"}) - Expect(check.ExitCode()).To(Equal(0)) + check := SystemExec("nc", []string{"-z", "localhost", "8081"}) + Expect(check).Should(Exit(0)) + }) + + It("podman create pod with id file with network portbindings", func() { + file := filepath.Join(podmanTest.TempDir, "pod.id") + name := "test" + session := podmanTest.Podman([]string{"pod", "create", "--name", name, "--pod-id-file", file, "-p", "8082:80"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + webserver := podmanTest.Podman([]string{"run", "--pod-id-file", file, "-dt", nginx}) + webserver.WaitWithDefaultTimeout() + Expect(webserver).Should(Exit(0)) + + check := SystemExec("nc", []string{"-z", "localhost", "8082"}) + Expect(check).Should(Exit(0)) }) It("podman create pod with no infra but portbindings should fail", func() { name := "test" session := podmanTest.Podman([]string{"pod", "create", "--infra=false", "--name", name, "-p", "80:80"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman create pod with --no-hosts", func() { name := "test" podCreate := podmanTest.Podman([]string{"pod", "create", "--no-hosts", "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) alpineResolvConf := podmanTest.Podman([]string{"run", "-ti", "--rm", "--no-hosts", ALPINE, "cat", "/etc/hosts"}) alpineResolvConf.WaitWithDefaultTimeout() - Expect(alpineResolvConf.ExitCode()).To(Equal(0)) + Expect(alpineResolvConf).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/hosts"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(podResolvConf.OutputToString()).To(Equal(alpineResolvConf.OutputToString())) }) @@ -143,18 +161,18 @@ name := "test" podCreate := podmanTest.Podman([]string{"pod", "create", "--no-hosts", "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod with --add-host", func() { name := "test" podCreate := podmanTest.Podman([]string{"pod", "create", "--add-host", "test.example.com:12.34.56.78", "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/hosts"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(strings.Contains(podResolvConf.OutputToString(), "12.34.56.78 test.example.com")).To(BeTrue()) }) @@ -162,7 +180,7 @@ name := "test" podCreate := podmanTest.Podman([]string{"pod", "create", "--add-host", "test.example.com:12.34.56.78", "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod with DNS server set", func() { @@ -170,11 +188,11 @@ server := "12.34.56.78" podCreate := podmanTest.Podman([]string{"pod", "create", "--dns", server, "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("nameserver %s", server))).To(BeTrue()) }) @@ -183,7 +201,7 @@ server := "12.34.56.78" podCreate := podmanTest.Podman([]string{"pod", "create", "--dns", server, "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod with DNS option set", func() { @@ -191,11 +209,11 @@ option := "attempts:5" podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-opt", option, "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("options %s", option))).To(BeTrue()) }) @@ -204,7 +222,7 @@ option := "attempts:5" podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-opt", option, "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod with DNS search domain set", func() { @@ -212,11 +230,11 @@ search := "example.com" podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-search", search, "--name", name}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("search %s", search))).To(BeTrue()) }) @@ -225,7 +243,7 @@ search := "example.com" podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-search", search, "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod with IP address", func() { @@ -235,12 +253,12 @@ podCreate.WaitWithDefaultTimeout() // Rootless should error without network if rootless.IsRootless() { - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) } else { - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue()) } }) @@ -252,13 +270,13 @@ ip := GetRandomIPAddress() podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", podName}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podCtr := podmanTest.Podman([]string{"run", "--name", ctrName, "--pod", podName, "-d", "-t", ALPINE, "top"}) podCtr.WaitWithDefaultTimeout() - Expect(podCtr.ExitCode()).To(Equal(0)) + Expect(podCtr).Should(Exit(0)) ctrInspect := podmanTest.Podman([]string{"inspect", ctrName}) ctrInspect.WaitWithDefaultTimeout() - Expect(ctrInspect.ExitCode()).To(Equal(0)) + Expect(ctrInspect).Should(Exit(0)) ctrJSON := ctrInspect.InspectContainerToJSON() Expect(ctrJSON[0].NetworkSettings.IPAddress).To(Equal(ip)) }) @@ -268,7 +286,7 @@ ip := GetRandomIPAddress() podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod with MAC address", func() { @@ -278,12 +296,12 @@ podCreate.WaitWithDefaultTimeout() // Rootless should error if rootless.IsRootless() { - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) } else { - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) podResolvConf.WaitWithDefaultTimeout() - Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf).Should(Exit(0)) Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue()) } }) @@ -293,7 +311,7 @@ mac := "92:d0:c6:0a:29:35" podCreate := podmanTest.Podman([]string{"pod", "create", "--mac-address", mac, "--name", name, "--infra=false"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) }) It("podman create pod and print id to external file", func() { @@ -311,7 +329,7 @@ session := podmanTest.Podman([]string{"pod", "create", "--name=abc", "--pod-id-file", targetFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) id, _ := ioutil.ReadFile(targetFile) check := podmanTest.Podman([]string{"pod", "inspect", "abc"}) @@ -324,14 +342,14 @@ // Make sure we error out with --name. session := podmanTest.Podman([]string{"pod", "create", "--replace", ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) // Create and replace 5 times in a row the "same" pod. podName := "testCtr" for i := 0; i < 5; i++ { session = podmanTest.Podman([]string{"pod", "create", "--replace", "--name", podName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } }) @@ -339,22 +357,22 @@ name := "test" session := podmanTest.Podman([]string{"pod", "create", "--name", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "inspect", name}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) data := check.InspectPodToJSON() check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) check1.WaitWithDefaultTimeout() - Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1).Should(Exit(0)) Expect(check1.OutputToString()).To(Equal("/pause")) // check the Path and Args check2 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Path}}:{{.Args}}", data.Containers[0].ID}) check2.WaitWithDefaultTimeout() - Expect(check2.ExitCode()).To(Equal(0)) + Expect(check2).Should(Exit(0)) Expect(check2.OutputToString()).To(Equal("/pause:[/pause]")) }) @@ -362,22 +380,22 @@ name := "test" session := podmanTest.Podman([]string{"pod", "create", "--infra-command", "/pause1", "--name", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "inspect", name}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) data := check.InspectPodToJSON() check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) check1.WaitWithDefaultTimeout() - Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1).Should(Exit(0)) Expect(check1.OutputToString()).To(Equal("/pause1")) // check the Path and Args check2 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Path}}:{{.Args}}", data.Containers[0].ID}) check2.WaitWithDefaultTimeout() - Expect(check2.ExitCode()).To(Equal(0)) + Expect(check2).Should(Exit(0)) Expect(check2.OutputToString()).To(Equal("/pause1:[/pause1]")) }) @@ -389,22 +407,22 @@ name := "test" session := podmanTest.Podman([]string{"pod", "create", "--infra-image", "localhost/infra", "--name", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "inspect", name}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) data := check.InspectPodToJSON() check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) check1.WaitWithDefaultTimeout() - Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1).Should(Exit(0)) Expect(check1.OutputToString()).To(Equal("/fromimage")) // check the Path and Args check2 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Path}}:{{.Args}}", data.Containers[0].ID}) check2.WaitWithDefaultTimeout() - Expect(check2.ExitCode()).To(Equal(0)) + Expect(check2).Should(Exit(0)) Expect(check2.OutputToString()).To(Equal("/fromimage:[/fromimage]")) }) @@ -416,22 +434,22 @@ name := "test" session := podmanTest.Podman([]string{"pod", "create", "--infra-image", "localhost/infra", "--infra-command", "/fromcommand", "--name", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "inspect", name}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) data := check.InspectPodToJSON() check1 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Entrypoint}}", data.Containers[0].ID}) check1.WaitWithDefaultTimeout() - Expect(check1.ExitCode()).To(Equal(0)) + Expect(check1).Should(Exit(0)) Expect(check1.OutputToString()).To(Equal("/fromcommand")) // check the Path and Args check2 := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Path}}:{{.Args}}", data.Containers[0].ID}) check2.WaitWithDefaultTimeout() - Expect(check2.ExitCode()).To(Equal(0)) + Expect(check2).Should(Exit(0)) Expect(check2.OutputToString()).To(Equal("/fromcommand:[/fromcommand]")) }) @@ -439,11 +457,11 @@ name := "test" session := podmanTest.Podman([]string{"pod", "create", "--name", name, "--network", "slirp4netns:port_handler=slirp4netns", "-p", "8082:8000"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{.InfraConfig.NetworkOptions.slirp4netns}}", name}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) Expect(check.OutputToString()).To(Equal("[port_handler=slirp4netns]")) }) @@ -451,41 +469,41 @@ podName := "testpod" create := podmanTest.Podman([]string{"pod", "create", "--name", podName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) status1 := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{ .State }}", podName}) status1.WaitWithDefaultTimeout() - Expect(status1.ExitCode()).To(Equal(0)) + Expect(status1).Should(Exit(0)) Expect(strings.Contains(status1.OutputToString(), "Created")).To(BeTrue()) ctr1 := podmanTest.Podman([]string{"run", "--pod", podName, "-d", ALPINE, "top"}) ctr1.WaitWithDefaultTimeout() - Expect(ctr1.ExitCode()).To(Equal(0)) + Expect(ctr1).Should(Exit(0)) status2 := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{ .State }}", podName}) status2.WaitWithDefaultTimeout() - Expect(status2.ExitCode()).To(Equal(0)) + Expect(status2).Should(Exit(0)) Expect(strings.Contains(status2.OutputToString(), "Running")).To(BeTrue()) ctr2 := podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) ctr2.WaitWithDefaultTimeout() - Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2).Should(Exit(0)) status3 := podmanTest.Podman([]string{"pod", "inspect", "--format", "{{ .State }}", podName}) status3.WaitWithDefaultTimeout() - Expect(status3.ExitCode()).To(Equal(0)) + Expect(status3).Should(Exit(0)) Expect(strings.Contains(status3.OutputToString(), "Degraded")).To(BeTrue()) }) It("podman create with unsupported network options", func() { podCreate := podmanTest.Podman([]string{"pod", "create", "--network", "container:doesnotmatter"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode container")) podCreate = podmanTest.Podman([]string{"pod", "create", "--network", "ns:/does/not/matter"}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(125)) + Expect(podCreate).Should(Exit(125)) Expect(podCreate.ErrorToString()).To(ContainSubstring("pods presently do not support network mode path")) }) @@ -493,11 +511,11 @@ podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--network", "none", "--name", podName}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "ip", "-o", "-4", "addr"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("inet 127.0.0.1/8 scope host lo")) Expect(len(session.OutputToStringArray())).To(Equal(1)) }) @@ -512,7 +530,298 @@ create := podmanTest.Podman([]string{"pod", "create", "--infra-image", iid}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(BeZero()) + Expect(create).Should(Exit(0)) + }) + + It("podman pod create --pid", func() { + podName := "pidPod" + ns := "ns:/proc/self/ns/" + podCreate := podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + podInspect := podmanTest.Podman([]string{"pod", "inspect", podName}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).Should(Exit(0)) + podJSON := podInspect.InspectPodToJSON() + Expect(podJSON.InfraConfig.PidNS).To(Equal(ns)) + + podName = "pidPod2" + ns = "pod" + + podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(ExitWithError()) + + podName = "pidPod3" + ns = "host" + + podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + podInspect = podmanTest.Podman([]string{"pod", "inspect", podName}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).Should(Exit(0)) + podJSON = podInspect.InspectPodToJSON() + Expect(podJSON.InfraConfig.PidNS).To(Equal("host")) + + podName = "pidPod4" + ns = "private" + + podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + podInspect = podmanTest.Podman([]string{"pod", "inspect", podName}) + podInspect.WaitWithDefaultTimeout() + Expect(podInspect).Should(Exit(0)) + podJSON = podInspect.InspectPodToJSON() + Expect(podJSON.InfraConfig.PidNS).To(Equal("private")) + + podName = "pidPod5" + ns = "container:randomfakeid" + + podCreate = podmanTest.Podman([]string{"pod", "create", "--pid", ns, "--name", podName, "--share", "pid"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(ExitWithError()) + }) + It("podman pod create with --userns=keep-id", func() { + if os.Geteuid() == 0 { + Skip("Test only runs without root") + } + + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--userns", "keep-id", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "id", "-u"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + uid := fmt.Sprintf("%d", os.Geteuid()) + ok, _ := session.GrepString(uid) + Expect(ok).To(BeTrue()) + + // Check passwd + session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "id", "-un"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + u, err := user.Current() + Expect(err).To(BeNil()) + ok, _ = session.GrepString(u.Name) + Expect(ok).To(BeTrue()) + + // root owns /usr + session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "stat", "-c%u", "/usr"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal("0")) + + // fail if --pod and --userns set together + session = podmanTest.Podman([]string{"run", "--pod", podName, "--userns", "keep-id", ALPINE, "id", "-u"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + }) + + It("podman pod create with --userns=keep-id can add users", func() { + if os.Geteuid() == 0 { + Skip("Test only runs without root") + } + + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--userns", "keep-id", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + ctrName := "ctr-name" + session := podmanTest.Podman([]string{"run", "--pod", podName, "-d", "--stop-signal", "9", "--name", ctrName, fedoraMinimal, "sleep", "600"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // container inside pod inherits user form infra container if --user is not set + // etc/passwd entry will look like 1000:*:1000:1000:container user:/:/bin/sh + exec1 := podmanTest.Podman([]string{"exec", ctrName, "cat", "/etc/passwd"}) + exec1.WaitWithDefaultTimeout() + Expect(exec1).Should(Exit(0)) + Expect(exec1.OutputToString()).To(ContainSubstring("container")) + + exec2 := podmanTest.Podman([]string{"exec", ctrName, "useradd", "testuser"}) + exec2.WaitWithDefaultTimeout() + Expect(exec2).Should(Exit(0)) + + exec3 := podmanTest.Podman([]string{"exec", ctrName, "cat", "/etc/passwd"}) + exec3.WaitWithDefaultTimeout() + Expect(exec3).Should(Exit(0)) + Expect(exec3.OutputToString()).To(ContainSubstring("testuser")) + }) + + It("podman pod create with --userns=auto", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + name := u.Name + if name == "root" { + name = "containers" + } + + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + + m := make(map[string]string) + for i := 0; i < 5; i++ { + podName := "testPod" + strconv.Itoa(i) + podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + + session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + l := session.OutputToString() + Expect(strings.Contains(l, "1024")).To(BeTrue()) + m[l] = l + } + // check for no duplicates + Expect(len(m)).To(Equal(5)) + }) + + It("podman pod create --userns=auto:size=%d", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + + name := u.Name + if name == "root" { + name = "containers" + } + + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=500", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + ok, _ := session.GrepString("500") + + podName = "testPod-1" + podCreate = podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=3000", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + ok, _ = session.GrepString("3000") + + Expect(ok).To(BeTrue()) + }) + + It("podman pod create --userns=auto:uidmapping=", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + + name := u.Name + if name == "root" { + name = "containers" + } + + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto:uidmapping=0:0:1", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + output := session.OutputToString() + Expect(output).To(MatchRegexp("\\s0\\s0\\s1")) + + podName = "testPod-1" + podCreate = podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=8192,uidmapping=0:0:1", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/uid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + ok, _ := session.GrepString("8191") + Expect(ok).To(BeTrue()) + }) + + It("podman pod create --userns=auto:gidmapping=", func() { + u, err := user.Current() + Expect(err).To(BeNil()) + + name := u.Name + if name == "root" { + name = "containers" + } + + content, err := ioutil.ReadFile("/etc/subuid") + if err != nil { + Skip("cannot read /etc/subuid") + } + if !strings.Contains(string(content), name) { + Skip("cannot find mappings for the current user") + } + + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--userns=auto:gidmapping=0:0:1", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + session := podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/gid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + output := session.OutputToString() + Expect(output).To(MatchRegexp("\\s0\\s0\\s1")) + + podName = "testPod-1" + podCreate = podmanTest.Podman([]string{"pod", "create", "--userns=auto:size=8192,gidmapping=0:0:1", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(Exit(0)) + session = podmanTest.Podman([]string{"run", "--pod", podName, ALPINE, "cat", "/proc/self/gid_map"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + ok, _ := session.GrepString("8191") + Expect(ok).To(BeTrue()) + }) + + It("podman pod create read network mode from config", func() { + confPath, err := filepath.Abs("config/containers-netns.conf") + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confPath) + defer os.Unsetenv("CONTAINERS_CONF") + if IsRemote() { + podmanTest.RestartRemoteService() + } + + pod := podmanTest.Podman([]string{"pod", "create", "--name", "test", "--infra-name", "test-infra"}) + pod.WaitWithDefaultTimeout() + Expect(pod).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"inspect", "--format", "{{.HostConfig.NetworkMode}}", "test-infra"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).Should(Equal("host")) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_infra_container_test.go libpod-3.4.4+ds1/test/e2e/pod_infra_container_test.go --- libpod-3.2.1+ds1/test/e2e/pod_infra_container_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_infra_container_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod create", func() { @@ -36,7 +37,7 @@ It("podman create infra container", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() check := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) @@ -53,54 +54,54 @@ It("podman start infra container", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-qa", "--no-trunc", "--filter", "status=running"}) check.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(check.OutputToStringArray())).To(Equal(1)) }) It("podman start infra container different image", func() { session := podmanTest.Podman([]string{"pod", "create", "--infra-image", BB}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() // If we use the default entry point, we should exit with no error - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman infra container namespaces", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podID) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-a", "--no-trunc", "--ns", "--format", "{{.Namespaces.IPC}} {{.Namespaces.NET}}"}) check.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(check.OutputToStringArray())).To(Equal(2)) Expect(check.OutputToStringArray()[0]).To(Equal(check.OutputToStringArray()[1])) check = podmanTest.Podman([]string{"ps", "-a", "--no-trunc", "--ns", "--format", "{{.IPC}} {{.NET}}"}) check.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(check.OutputToStringArray())).To(Equal(2)) Expect(check.OutputToStringArray()[0]).To(Equal(check.OutputToStringArray()[1])) }) @@ -108,20 +109,20 @@ It("podman pod correctly sets up NetNS", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "net"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--pod", podID, nginx}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, fedoraMinimal, "curl", "localhost:80"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", fedoraMinimal, "curl", "localhost"}) session.WaitWithDefaultTimeout() @@ -131,43 +132,43 @@ It("podman pod correctly sets up IPCNS", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "ipc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, fedoraMinimal, "/bin/sh", "-c", "'touch /dev/shm/hi'"}) session.WaitWithDefaultTimeout() if session.ExitCode() != 0 { Skip("ShmDir not initialized, skipping...") } - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, fedoraMinimal, "/bin/sh", "-c", "'ls /dev/shm'"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("hi")) }) It("podman pod correctly sets up PIDNS", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "pid", "--name", "test-pod"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("test-ctr", podID) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"top", "test-ctr", "pid"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) PIDs := check.OutputToStringArray() Expect(len(PIDs)).To(Equal(3)) @@ -179,25 +180,25 @@ It("podman pod doesn't share PIDNS if requested to not", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "net", "--name", "test-pod"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("test-ctr", podID) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"top", "test-ctr", "pid"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) ctrTop := check.OutputToStringArray() check = podmanTest.Podman([]string{"top", podID[:12] + "-infra", "pid"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) infraTop := check.OutputToStringArray() ctrPID, _ := strconv.Atoi(ctrTop[1]) @@ -208,16 +209,16 @@ It("podman pod container can override pod net NS", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "net"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--pod", podID, nginx}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, "--network", "bridge", nginx, "curl", "localhost"}) session.WaitWithDefaultTimeout() @@ -228,26 +229,26 @@ SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") session := podmanTest.Podman([]string{"pod", "create", "--share", "pid"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, "--pid", "host", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.Namespaces.PIDNS}}"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) outputArray := check.OutputToStringArray() Expect(len(outputArray)).To(Equal(2)) check = podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.PIDNS}}"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) outputArray = check.OutputToStringArray() Expect(len(outputArray)).To(Equal(2)) @@ -259,20 +260,20 @@ It("podman pod container can override pod not sharing pid", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "net"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, "--pid", "pod", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.PIDNS}}"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) outputArray := check.OutputToStringArray() Expect(len(outputArray)).To(Equal(2)) @@ -284,20 +285,20 @@ It("podman pod container can override pod ipc NS", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "ipc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, "--ipc", "host", "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.IPC}}"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) outputArray := check.OutputToStringArray() Expect(len(outputArray)).To(Equal(2)) @@ -309,12 +310,12 @@ It("podman pod infra container deletion", func() { session := podmanTest.Podman([]string{"pod", "create", "--share", "ipc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"ps", "-aq"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) infraID := session.OutputToString() session = podmanTest.Podman([]string{"rm", infraID}) @@ -323,27 +324,27 @@ session = podmanTest.Podman([]string{"pod", "rm", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run in pod starts infra", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() result := podmanTest.Podman([]string{"ps", "-aq"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) infraID := result.OutputToString() result = podmanTest.Podman([]string{"run", "--pod", podID, "-d", ALPINE, "top"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-aq"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) Expect(result.OutputToString()).To(ContainSubstring(infraID)) @@ -352,26 +353,26 @@ It("podman start in pod starts infra", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() result := podmanTest.Podman([]string{"ps", "-aq"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) infraID := result.OutputToString() result = podmanTest.Podman([]string{"create", "--pod", podID, ALPINE, "ls"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) ctrID := result.OutputToString() result = podmanTest.Podman([]string{"start", ctrID}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-aq"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) Expect(result.OutputToString()).To(ContainSubstring(infraID)) @@ -380,7 +381,7 @@ It("podman run --add-host in pod", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() // verify we can add a host to the infra's /etc/hosts @@ -388,31 +389,31 @@ // permission denied error as of Fedora 33. session = podmanTest.Podman([]string{"run", "--pod", podID, "--add-host", "foobar:127.0.0.1", ALPINE, "ping", "-c", "1", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // verify we can see the other hosts of infra's /etc/hosts session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "ping", "-c", "1", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run hostname is shared", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() // verify we can add a host to the infra's /etc/hosts session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "hostname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hostname := session.OutputToString() infraName := podID[:12] + "-infra" // verify we can see the other hosts of infra's /etc/hosts session = podmanTest.Podman([]string{"inspect", infraName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(hostname)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_initcontainers_test.go libpod-3.4.4+ds1/test/e2e/pod_initcontainers_test.go --- libpod-3.2.1+ds1/test/e2e/pod_initcontainers_test.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_initcontainers_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,171 @@ +package integration + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/containers/podman/v3/libpod/define" + . "github.com/containers/podman/v3/test/utils" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" +) + +var _ = Describe("Podman init containers", func() { + var ( + tempdir string + err error + podmanTest *PodmanTestIntegration + ) + + BeforeEach(func() { + tempdir, err = CreateTempDirInTempDir() + if err != nil { + os.Exit(1) + } + podmanTest = PodmanTestCreate(tempdir) + podmanTest.Setup() + podmanTest.SeedImages() + }) + + AfterEach(func() { + podmanTest.Cleanup() + f := CurrentGinkgoTestDescription() + processTestResult(f) + + }) + + It("podman create init container without --pod should fail", func() { + session := podmanTest.Podman([]string{"create", "--init-ctr", "always", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + }) + + It("podman create init container with bad init type should fail", func() { + session := podmanTest.Podman([]string{"create", "--init-ctr", "unknown", "--pod", "new:foobar", ALPINE, "top"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + }) + + It("podman init containers should not degrade pod status", func() { + // create a pod + topPod := podmanTest.Podman([]string{"create", "-t", "--pod", "new:foobar", ALPINE, "top"}) + topPod.WaitWithDefaultTimeout() + Expect(topPod).Should(Exit(0)) + // add an init container + session := podmanTest.Podman([]string{"create", "--init-ctr", "always", "--pod", "foobar", ALPINE, "date"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + // start a pod + start := podmanTest.Podman([]string{"pod", "start", "foobar"}) + start.WaitWithDefaultTimeout() + Expect(start).Should(Exit(0)) + + inspect := podmanTest.Podman([]string{"pod", "inspect", "foobar"}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + data := inspect.InspectPodToJSON() + Expect(data.State).To(Equal(define.PodStateRunning)) + }) + + It("podman create init container should fail in running pod", func() { + // create a running pod + topPod := podmanTest.Podman([]string{"run", "-dt", "--pod", "new:foobar", ALPINE, "top"}) + topPod.WaitWithDefaultTimeout() + Expect(topPod).Should(Exit(0)) + // adding init-ctr to running pod should fail + session := podmanTest.Podman([]string{"create", "--init-ctr", "always", "--pod", "foobar", ALPINE, "date"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + }) + + It("podman make sure init container runs before pod containers", func() { + filename := filepath.Join("/dev/shm", RandomString(12)) + content := RandomString(16) + session := podmanTest.Podman([]string{"create", "--init-ctr", "always", "--pod", "new:foobar", ALPINE, "bin/sh", "-c", fmt.Sprintf("echo %s > %s", content, filename)}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + verify := podmanTest.Podman([]string{"create", "--pod", "foobar", "-t", ALPINE, "top"}) + verify.WaitWithDefaultTimeout() + Expect(verify).Should(Exit(0)) + start := podmanTest.Podman([]string{"pod", "start", "foobar"}) + start.WaitWithDefaultTimeout() + Expect(start).Should(Exit(0)) + checkLog := podmanTest.Podman([]string{"exec", "-it", verify.OutputToString(), "cat", filename}) + checkLog.WaitWithDefaultTimeout() + Expect(checkLog).Should(Exit(0)) + Expect(checkLog.OutputToString()).To(Equal(content)) + }) + + It("podman make sure once container is removed", func() { + filename := filepath.Join("/dev/shm", RandomString(12)) + content := RandomString(16) + session := podmanTest.Podman([]string{"create", "--init-ctr", "once", "--pod", "new:foobar", ALPINE, "bin/sh", "-c", fmt.Sprintf("echo %s > %s", content, filename)}) + session.WaitWithDefaultTimeout() + initContainerID := session.OutputToString() + Expect(session).Should(Exit(0)) + verify := podmanTest.Podman([]string{"create", "--pod", "foobar", "-t", ALPINE, "top"}) + verify.WaitWithDefaultTimeout() + Expect(verify).Should(Exit(0)) + start := podmanTest.Podman([]string{"pod", "start", "foobar"}) + start.WaitWithDefaultTimeout() + Expect(start).Should(Exit(0)) + check := podmanTest.Podman([]string{"container", "exists", initContainerID}) + check.WaitWithDefaultTimeout() + // Container was rm'd + //Expect(check).Should(Exit(1)) + Expect(check.ExitCode()).To(Equal(1), "I dont understand why the other way does not work") + // Lets double check with a stop and start + stopPod := podmanTest.Podman([]string{"pod", "stop", "foobar"}) + stopPod.WaitWithDefaultTimeout() + Expect(stopPod).Should(Exit(0)) + startPod := podmanTest.Podman([]string{"pod", "start", "foobar"}) + startPod.WaitWithDefaultTimeout() + Expect(startPod).Should(Exit(0)) + + // Because no init was run, the file should not even exist + doubleCheck := podmanTest.Podman([]string{"exec", "-it", verify.OutputToString(), "cat", filename}) + doubleCheck.WaitWithDefaultTimeout() + Expect(doubleCheck).Should(Exit(1)) + + }) + + It("podman ensure always init containers always run", func() { + filename := filepath.Join("/dev/shm", RandomString(12)) + + // Write the date to a file + session := podmanTest.Podman([]string{"create", "--init-ctr", "always", "--pod", "new:foobar", ALPINE, "bin/sh", "-c", fmt.Sprintf("date > %s", filename)}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + verify := podmanTest.Podman([]string{"create", "--pod", "foobar", "-t", ALPINE, "top"}) + verify.WaitWithDefaultTimeout() + Expect(verify).Should(Exit(0)) + start := podmanTest.Podman([]string{"pod", "start", "foobar"}) + start.WaitWithDefaultTimeout() + Expect(start).Should(Exit(0)) + + // capture the date written + checkLog := podmanTest.Podman([]string{"exec", "-it", verify.OutputToString(), "cat", filename}) + checkLog.WaitWithDefaultTimeout() + firstResult := checkLog.OutputToString() + Expect(checkLog).Should(Exit(0)) + + // Stop and start the pod + stopPod := podmanTest.Podman([]string{"pod", "stop", "foobar"}) + stopPod.WaitWithDefaultTimeout() + Expect(stopPod).Should(Exit(0)) + startPod := podmanTest.Podman([]string{"pod", "start", "foobar"}) + startPod.WaitWithDefaultTimeout() + Expect(startPod).Should(Exit(0)) + + // Check the file again with exec + secondCheckLog := podmanTest.Podman([]string{"exec", "-it", verify.OutputToString(), "cat", filename}) + secondCheckLog.WaitWithDefaultTimeout() + Expect(secondCheckLog).Should(Exit(0)) + + // Dates should not match + Expect(firstResult).ToNot(Equal(secondCheckLog.OutputToString())) + }) + +}) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_inspect_test.go libpod-3.4.4+ds1/test/e2e/pod_inspect_test.go --- libpod-3.2.1+ds1/test/e2e/pod_inspect_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_inspect_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod inspect", func() { @@ -46,15 +47,15 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"pod", "inspect", podid}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) podData := inspect.InspectPodToJSON() Expect(podData.ID).To(Equal(podid)) @@ -67,13 +68,13 @@ // Create the pod. session := podmanTest.Podman(createCommand) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Inspect the pod and make sure that the create command is // exactly how we created the pod. inspect := podmanTest.Podman([]string{"pod", "inspect", podName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) podData := inspect.InspectPodToJSON() // Let's get the last len(createCommand) items in the command. @@ -84,20 +85,20 @@ It("podman pod inspect outputs port bindings", func() { podName := "testPod" - create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "8080:80"}) + create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "-p", "8383:80"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspectOut := podmanTest.Podman([]string{"pod", "inspect", podName}) inspectOut.WaitWithDefaultTimeout() - Expect(inspectOut.ExitCode()).To(Equal(0)) + Expect(inspectOut).Should(Exit(0)) inspectJSON := new(define.InspectPodData) err := json.Unmarshal(inspectOut.Out.Contents(), inspectJSON) Expect(err).To(BeNil()) Expect(inspectJSON.InfraConfig).To(Not(BeNil())) Expect(len(inspectJSON.InfraConfig.PortBindings["80/tcp"])).To(Equal(1)) - Expect(inspectJSON.InfraConfig.PortBindings["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectJSON.InfraConfig.PortBindings["80/tcp"][0].HostPort).To(Equal("8383")) }) It("podman pod inspect outputs show correct MAC", func() { @@ -106,15 +107,15 @@ macAddr := "42:43:44:00:00:01" create := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--mac-address", macAddr}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) create = podmanTest.Podman([]string{"run", "-d", "--pod", podName, ALPINE, "top"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) inspectOut := podmanTest.Podman([]string{"pod", "inspect", podName}) inspectOut.WaitWithDefaultTimeout() - Expect(inspectOut.ExitCode()).To(Equal(0)) + Expect(inspectOut).Should(Exit(0)) Expect(inspectOut.OutputToString()).To(ContainSubstring(macAddr)) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_kill_test.go libpod-3.4.4+ds1/test/e2e/pod_kill_test.go --- libpod-3.2.1+ds1/test/e2e/pod_kill_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_kill_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod kill", func() { @@ -45,15 +46,15 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "kill", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -63,11 +64,11 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "kill", "-s", "9", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -77,11 +78,11 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "kill", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -91,11 +92,11 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "kill", "-s", "bogus", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) @@ -105,24 +106,24 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, podid2 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !IsRemote() { podid2 = "-l" } result := podmanTest.Podman([]string{"pod", "kill", podid2}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) @@ -133,23 +134,23 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, podid2 := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "kill", "-a"}) result.WaitWithDefaultTimeout() fmt.Println(result.OutputToString(), result.ErrorToString()) - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_pause_test.go libpod-3.4.4+ds1/test/e2e/pod_pause_test.go --- libpod-3.2.1+ds1/test/e2e/pod_pause_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_pause_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod pause", func() { @@ -53,7 +54,7 @@ result := podmanTest.Podman([]string{"pod", "pause", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman pod pause a running pod by id", func() { @@ -62,12 +63,12 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "pause", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState)) @@ -83,12 +84,12 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "unpause", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) @@ -98,17 +99,17 @@ session := podmanTest.RunTopContainerInPod("", "test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "pause", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) Expect(podmanTest.GetContainerStatus()).To(Equal(pausedState)) result = podmanTest.Podman([]string{"pod", "unpause", "test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_pod_namespaces_test.go libpod-3.4.4+ds1/test/e2e/pod_pod_namespaces_test.go --- libpod-3.2.1+ds1/test/e2e/pod_pod_namespaces_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_pod_namespaces_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod create", func() { @@ -36,20 +37,20 @@ It("podman pod container share Namespaces", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.Namespaces.IPC}} {{.Namespaces.UTS}} {{.Namespaces.NET}}"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) outputArray := check.OutputToStringArray() Expect(len(outputArray)).To(Equal(2)) @@ -63,39 +64,39 @@ It("podman pod container share ipc && /dev/shm ", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--pod", podID, ALPINE, "touch", "/dev/shm/test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--pod", podID, ALPINE, "ls", "/dev/shm/test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod container dontshare PIDNS", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--pod", podID, "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "{{.Namespaces.PIDNS}}"}) check.WaitWithDefaultTimeout() - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) outputArray := check.OutputToStringArray() Expect(len(outputArray)).To(Equal(2)) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_prune_test.go libpod-3.4.4+ds1/test/e2e/pod_prune_test.go --- libpod-3.2.1+ds1/test/e2e/pod_prune_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_prune_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod prune", func() { @@ -38,7 +39,7 @@ result := podmanTest.Podman([]string{"pod", "prune", "--force"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman pod prune doesn't remove a pod with a running container", func() { @@ -47,11 +48,11 @@ ec2 := podmanTest.RunTopContainerInPod("", podid) ec2.WaitWithDefaultTimeout() - Expect(ec2.ExitCode()).To(Equal(0)) + Expect(ec2).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "prune", "-f"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To((Equal(0))) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-qa"}) result.WaitWithDefaultTimeout() @@ -67,7 +68,7 @@ result := podmanTest.Podman([]string{"pod", "prune", "-f"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-qa"}) result.WaitWithDefaultTimeout() diff -Nru libpod-3.2.1+ds1/test/e2e/pod_ps_test.go libpod-3.4.4+ds1/test/e2e/pod_ps_test.go --- libpod-3.2.1+ds1/test/e2e/pod_ps_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_ps_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,7 +39,7 @@ It("podman pod ps no pods", func() { session := podmanTest.Podman([]string{"pod", "ps"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod ps default", func() { @@ -48,11 +48,11 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "ps"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) @@ -79,7 +79,7 @@ result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) Expect(podid).To(Equal(result.OutputToStringArray()[0])) }) @@ -94,7 +94,7 @@ result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(podid2)) Expect(result.OutputToString()).To(Not(ContainSubstring(podid1))) }) @@ -105,7 +105,23 @@ result := podmanTest.Podman([]string{"pod", "ps", "--filter", fmt.Sprintf("id=%s", podid)}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) + }) + + It("podman pod ps --filter until", func() { + name := "mypod" + _, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}}) + Expect(ec).To(Equal(0)) + + result := podmanTest.Podman([]string{"pod", "ps", "--filter", "until=50"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Not(ContainSubstring(name))) + + result = podmanTest.Podman([]string{"pod", "ps", "--filter", "until=5000000000"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(ContainSubstring(name)) }) It("podman pod ps filter name regexp", func() { @@ -116,14 +132,14 @@ result := podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "name=mypod"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToStringArray() Expect(len(output)).To(Equal(2)) result = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "name=mypod$"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output = result.OutputToStringArray() Expect(len(output)).To(Equal(1)) @@ -150,7 +166,7 @@ session := podmanTest.Podman([]string{"pod", "ps", "--sort=name", "--format", "{{.Name}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) sortedArr := session.OutputToStringArray() @@ -164,14 +180,14 @@ session := podmanTest.RunTopContainerInPod("test1", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.RunLsContainerInPod("test2", podid) Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"pod", "ps", "--format={{.ContainerNames}}", "--ctr-names"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("test1")) Expect(session.OutputToString()).To(ContainSubstring("test2")) }) @@ -182,7 +198,7 @@ session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec2, podid2 := podmanTest.CreatePod(nil) Expect(ec2).To(Equal(0)) @@ -192,25 +208,25 @@ session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-names=test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid1)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid2))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-names=test", "--filter", "ctr-status=running"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid1)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid2))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", fmt.Sprintf("ctr-ids=%s", cid)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-ids=" + cid[:40], "--filter", "ctr-ids=" + cid + "$"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) @@ -219,42 +235,42 @@ session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-number=1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid1)) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-number=1", "--filter", "ctr-number=0"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid1)) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(ContainSubstring(podid3)) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=running"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid1)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid2))) Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=exited"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid1))) Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=exited", "--filter", "ctr-status=running"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid1)) Expect(session.OutputToString()).To(ContainSubstring(podid2)) Expect(session.OutputToString()).To(Not(ContainSubstring(podid3))) session = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc", "--filter", "ctr-status=created"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(BeEmpty()) }) @@ -282,22 +298,22 @@ net := stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(net) session = podmanTest.Podman([]string{"pod", "create", "--network", net}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) podWithNet := session.OutputToString() session = podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) podWithoutNet := session.OutputToString() session = podmanTest.Podman([]string{"pod", "ps", "--no-trunc", "--filter", "network=" + net}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podWithNet)) Expect(session.OutputToString()).To(Not(ContainSubstring(podWithoutNet))) }) @@ -305,11 +321,11 @@ It("podman pod ps --format networks", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "ps", "--format", "{{ .Networks }}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) if isRootless() { // rootless container don't have a network by default Expect(session.OutputToString()).To(Equal("")) @@ -321,22 +337,22 @@ net1 := stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", net1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(net1) net2 := stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", net2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(net2) session = podmanTest.Podman([]string{"pod", "create", "--network", net1 + "," + net2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) pid := session.OutputToString() session = podmanTest.Podman([]string{"pod", "ps", "--format", "{{ .Networks }}", "--filter", "id=" + pid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) // the output is not deterministic so check both possible orders Expect(session.OutputToString()).To(Or(Equal(net1+","+net2), Equal(net2+","+net1))) }) @@ -344,11 +360,11 @@ It("pod no infra should ps", func() { session := podmanTest.Podman([]string{"pod", "create", "--infra=false"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ps := podmanTest.Podman([]string{"pod", "ps"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) infra := podmanTest.Podman([]string{"pod", "ps", "--format", "{{.InfraId}}"}) infra.WaitWithDefaultTimeout() @@ -367,7 +383,15 @@ session := podmanTest.Podman([]string{"pod", "ps", "--format", "{{.Labels}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("value1")) }) + + It("podman pod ps headers", func() { + session := podmanTest.Podman([]string{"pod", "ps", "--ctr-ids", "--ctr-names", "--ctr-status", "--ns"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(MatchRegexp(`^POD ID\s+NAME\s+STATUS\s+CREATED\s+INFRA ID\s+IDS\s+NAMES\s+STATUS\s+CGROUP\s+NAMESPACES$`)) + }) + }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_restart_test.go libpod-3.4.4+ds1/test/e2e/pod_restart_test.go --- libpod-3.2.1+ds1/test/e2e/pod_restart_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_restart_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod restart", func() { @@ -35,7 +36,7 @@ It("podman pod restart bogus pod", func() { session := podmanTest.Podman([]string{"pod", "restart", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod restart single empty pod", func() { @@ -44,7 +45,7 @@ session := podmanTest.Podman([]string{"pod", "restart", podid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod restart single pod by name", func() { @@ -53,14 +54,14 @@ session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"}) startTime.WaitWithDefaultTimeout() session = podmanTest.Podman([]string{"pod", "restart", "foobar99"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"}) restartTime.WaitWithDefaultTimeout() @@ -73,29 +74,29 @@ session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("test3", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("test4", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2", "test3", "test4"}) startTime.WaitWithDefaultTimeout() session = podmanTest.Podman([]string{"pod", "restart", "foobar99", "foobar100"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2", "test3", "test4"}) restartTime.WaitWithDefaultTimeout() @@ -111,21 +112,21 @@ session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) startTime.WaitWithDefaultTimeout() session = podmanTest.Podman([]string{"pod", "restart", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) restartTime.WaitWithDefaultTimeout() @@ -139,14 +140,14 @@ session := podmanTest.RunTopContainerInPod("test1", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("test2", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) startTime.WaitWithDefaultTimeout() @@ -157,7 +158,7 @@ } session = podmanTest.Podman([]string{"pod", "restart", podid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) restartTime.WaitWithDefaultTimeout() @@ -171,10 +172,10 @@ session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "restart", podid1, "doesnotexist"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_rm_test.go libpod-3.4.4+ds1/test/e2e/pod_rm_test.go --- libpod-3.2.1+ds1/test/e2e/pod_rm_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_rm_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod rm", func() { @@ -42,7 +43,7 @@ result := podmanTest.Podman([]string{"pod", "rm", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Also check that we don't leak cgroups err := filepath.Walk("/sys/fs/cgroup", func(path string, info os.FileInfo, err error) error { @@ -73,11 +74,11 @@ } result := podmanTest.Podman([]string{"pod", "rm", latest}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"pod", "ps", "-q", "--no-trunc"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(podid)) Expect(result.OutputToString()).To(Not(ContainSubstring(podid2))) }) @@ -91,7 +92,7 @@ result := podmanTest.Podman([]string{"pod", "rm", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-qa"}) result.WaitWithDefaultTimeout() @@ -104,11 +105,11 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "rm", "-f", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-q"}) result.WaitWithDefaultTimeout() @@ -126,7 +127,7 @@ session := podmanTest.RunTopContainerInPod("", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podmanTest.WaitForContainer() Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) fmt.Printf("Started container running in one pod") @@ -165,22 +166,22 @@ session := podmanTest.RunTopContainerInPod("", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "--pod", podid1, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.RunLsContainerInPod("", podid2) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", podid2) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "rm", "-fa"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"ps", "-q"}) result.WaitWithDefaultTimeout() @@ -195,7 +196,7 @@ It("podman rm bogus pod", func() { session := podmanTest.Podman([]string{"pod", "rm", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman rm bogus pod and a running pod", func() { @@ -204,15 +205,15 @@ session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "rm", "bogus", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) session = podmanTest.Podman([]string{"pod", "rm", "test1", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman rm --ignore bogus pod and a running pod", func() { @@ -222,15 +223,15 @@ session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "rm", "--force", "--ignore", "bogus", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "rm", "--ignore", "test1", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod start/remove single pod via --pod-id-file", func() { @@ -244,21 +245,21 @@ // Create a pod with --pod-id-file. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create container inside the pod. session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top session = podmanTest.Podman([]string{"pod", "rm", "--pod-id-file", tmpFile, "--force"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -274,12 +275,12 @@ // Create a pod with --pod-id-file. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create container inside the pod. session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Append the id files along with the command. podIDFiles = append(podIDFiles, "--pod-id-file") @@ -290,14 +291,31 @@ cmd = append(cmd, podIDFiles...) session := podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) cmd = []string{"pod", "rm", "--force"} cmd = append(cmd, podIDFiles...) session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) + + It("podman pod rm with exited containers", func() { + _, ec, podid := podmanTest.CreatePod(nil) + Expect(ec).To(Equal(0)) + + session := podmanTest.Podman([]string{"run", "--pod", podid, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--pod", podid, ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + result := podmanTest.Podman([]string{"pod", "rm", podid}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_start_test.go libpod-3.4.4+ds1/test/e2e/pod_start_test.go --- libpod-3.2.1+ds1/test/e2e/pod_start_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_start_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -40,7 +40,7 @@ It("podman pod start bogus pod", func() { session := podmanTest.Podman([]string{"pod", "start", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod start single empty pod", func() { @@ -49,7 +49,7 @@ session := podmanTest.Podman([]string{"pod", "start", podid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod start single pod by name", func() { @@ -58,11 +58,11 @@ session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", "foobar99"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod start multiple pods", func() { @@ -71,18 +71,18 @@ session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec2, podid2 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec2).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", podid1, podid2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) }) @@ -92,7 +92,7 @@ pod, _, podid1 := podmanTest.CreatePod(map[string][]string{ "--infra": {"true"}, "--name": {podName[0]}, - "--publish": {"127.0.0.1:8080:80"}, + "--publish": {"127.0.0.1:8083:80"}, }) Expect(pod).To(Exit(0)) @@ -103,7 +103,7 @@ pod, _, podid2 := podmanTest.CreatePod(map[string][]string{ "--infra": {"true"}, "--name": {podName[1]}, - "--publish": {"127.0.0.1:8080:80"}, + "--publish": {"127.0.0.1:8083:80"}, }) Expect(pod).To(Exit(0)) @@ -122,18 +122,18 @@ session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) }) @@ -143,14 +143,14 @@ session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.Podman([]string{"create", "--pod", "foobar100", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid := "--latest" if IsRemote() { @@ -158,7 +158,7 @@ } session = podmanTest.Podman([]string{"pod", "start", podid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) @@ -168,11 +168,11 @@ session := podmanTest.Podman([]string{"create", "--pod", "foobar99", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", podid, "doesnotexist"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod start single pod via --pod-id-file", func() { @@ -186,16 +186,16 @@ // Create a pod with --pod-id-file. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create container inside the pod. session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top }) @@ -211,12 +211,12 @@ // Create a pod with --pod-id-file. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create container inside the pod. session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Append the id files along with the command. podIDFiles = append(podIDFiles, "--pod-id-file") @@ -227,7 +227,7 @@ cmd = append(cmd, podIDFiles...) session := podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) }) @@ -241,11 +241,11 @@ // Create a pod with --infra-conmon-pid. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--infra-conmon-pidfile", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", podName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) // infra readFirstLine := func(path string) string { diff -Nru libpod-3.2.1+ds1/test/e2e/pod_stats_test.go libpod-3.4.4+ds1/test/e2e/pod_stats_test.go --- libpod-3.2.1+ds1/test/e2e/pod_stats_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_stats_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,158 +37,158 @@ processTestResult(f) }) - It("podman stats should run with no pods", func() { + It("podman pod stats should run with no pods", func() { session := podmanTest.Podman([]string{"pod", "stats", "--no-stream"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) - It("podman stats with a bogus pod", func() { + It("podman pod stats with a bogus pod", func() { session := podmanTest.Podman([]string{"pod", "stats", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) - It("podman stats on a specific running pod", func() { + It("podman pod stats on a specific running pod", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--no-stream", podid}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) }) - It("podman stats on a specific running pod with shortID", func() { + It("podman pod stats on a specific running pod with shortID", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--no-stream", podid[:5]}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) }) - It("podman stats on a specific running pod with name", func() { + It("podman pod stats on a specific running pod with name", func() { _, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"test"}}) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--no-stream", "test"}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) }) - It("podman stats on running pods", func() { + It("podman pod stats on running pods", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--no-stream"}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) }) - It("podman stats on all pods", func() { + It("podman pod stats on all pods", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--no-stream", "-a"}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) }) - It("podman stats with json output", func() { + It("podman pod stats with json output", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--format", "json", "--no-stream", "-a"}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) Expect(stats.IsJSONOutputValid()).To(BeTrue()) }) - It("podman stats with GO template", func() { + It("podman pod stats with GO template", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "table {{.CID}} {{.Pod}} {{.Mem}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}} {{.Pod}}"}) stats.WaitWithDefaultTimeout() Expect(stats).To(Exit(0)) }) - It("podman stats with invalid GO template", func() { + It("podman pod stats with invalid GO template", func() { _, ec, podid := podmanTest.CreatePod(nil) Expect(ec).To(Equal(0)) session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.ID}} \""}) stats.WaitWithDefaultTimeout() Expect(stats).To(ExitWithError()) }) - It("podman stats on net=host post", func() { + It("podman pod stats on net=host post", func() { SkipIfRootless("--net=host not supported for rootless pods at present") podName := "testPod" podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) podCreate.WaitWithDefaultTimeout() - Expect(podCreate.ExitCode()).To(Equal(0)) + Expect(podCreate).Should(Exit(0)) ctrRun := podmanTest.Podman([]string{"run", "-d", "--pod", podName, ALPINE, "top"}) ctrRun.WaitWithDefaultTimeout() - Expect(ctrRun.ExitCode()).To(Equal(0)) + Expect(ctrRun).Should(Exit(0)) stats := podmanTest.Podman([]string{"pod", "stats", "--format", "json", "--no-stream", podName}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) Expect(stats.IsJSONOutputValid()).To(BeTrue()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_stop_test.go libpod-3.4.4+ds1/test/e2e/pod_stop_test.go --- libpod-3.2.1+ds1/test/e2e/pod_stop_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_stop_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pod stop", func() { @@ -36,14 +37,14 @@ It("podman pod stop bogus pod", func() { session := podmanTest.Podman([]string{"pod", "stop", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod stop --ignore bogus pod", func() { session := podmanTest.Podman([]string{"pod", "stop", "--ignore", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman stop bogus pod and a running pod", func() { @@ -52,11 +53,11 @@ session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", "bogus", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman stop --ignore bogus pod and a running pod", func() { @@ -66,15 +67,15 @@ session := podmanTest.RunTopContainerInPod("test1", podid1) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", "--ignore", "bogus", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", "--ignore", "test1", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod stop single empty pod", func() { @@ -83,7 +84,7 @@ session := podmanTest.Podman([]string{"pod", "stop", podid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod stop single pod by name", func() { @@ -92,11 +93,11 @@ session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", "foobar99"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -106,18 +107,18 @@ session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec2, podid2 := podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec2).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", podid1, podid2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -127,18 +128,18 @@ session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -148,14 +149,14 @@ session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, _ = podmanTest.CreatePod(map[string][]string{"--name": {"foobar100"}}) Expect(ec).To(Equal(0)) session = podmanTest.RunTopContainerInPod("", "foobar100") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid := "--latest" if IsRemote() { @@ -163,7 +164,7 @@ } session = podmanTest.Podman([]string{"pod", "stop", podid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) }) @@ -173,11 +174,11 @@ session := podmanTest.RunTopContainerInPod("", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", podid1, "doesnotexist"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman pod start/stop single pod via --pod-id-file", func() { @@ -191,21 +192,21 @@ // Create a pod with --pod-id-file. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create container inside the pod. session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "start", "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) // infra+top session = podmanTest.Podman([]string{"pod", "stop", "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) @@ -221,12 +222,12 @@ // Create a pod with --pod-id-file. session := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--pod-id-file", tmpFile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create container inside the pod. session = podmanTest.Podman([]string{"create", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Append the id files along with the command. podIDFiles = append(podIDFiles, "--pod-id-file") @@ -237,14 +238,14 @@ cmd = append(cmd, podIDFiles...) session := podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(20)) // 10*(infra+top) cmd = []string{"pod", "stop"} cmd = append(cmd, podIDFiles...) session = podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pod_top_test.go libpod-3.4.4+ds1/test/e2e/pod_top_test.go --- libpod-3.2.1+ds1/test/e2e/pod_top_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pod_top_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman top", func() { @@ -37,13 +38,13 @@ It("podman pod top without pod name or id", func() { result := podmanTest.Podman([]string{"pod", "top"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman pod top on bogus pod", func() { result := podmanTest.Podman([]string{"pod", "top", "1234"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman pod top on non-running pod", func() { @@ -52,7 +53,7 @@ result := podmanTest.Podman([]string{"top", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman pod top on pod", func() { @@ -61,14 +62,14 @@ session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !IsRemote() { podid = "-l" } result := podmanTest.Podman([]string{"pod", "top", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) @@ -78,11 +79,11 @@ session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "top", podid, "pid", "%C", "args"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) @@ -92,7 +93,7 @@ session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // We need to pass -eo to force executing ps in the Alpine container. // Alpines stripped down ps(1) is accepting any kind of weird input in @@ -100,7 +101,7 @@ // the wrong input and still print the -ef output instead. result := podmanTest.Podman([]string{"pod", "top", podid, "-eo", "invalid"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman pod top on pod with containers in same pid namespace", func() { @@ -109,16 +110,16 @@ session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"run", "-d", "--pod", podid, "--pid", fmt.Sprintf("container:%s", cid), ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"pod", "top", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(3)) }) @@ -128,11 +129,11 @@ session := podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--pod", podid, ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) for i := 0; i < 10; i++ { fmt.Println("Waiting for containers to be running .... ") @@ -143,7 +144,7 @@ } result := podmanTest.Podman([]string{"pod", "top", podid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(Equal(3)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/port_test.go libpod-3.4.4+ds1/test/e2e/port_test.go --- libpod-3.2.1+ds1/test/e2e/port_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/port_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman port", func() { @@ -48,7 +49,7 @@ It("podman port -l nginx", func() { session, cid := podmanTest.RunNginxWithHealthCheck("test1") - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if err := podmanTest.RunHealthCheck(cid); err != nil { Fail(err.Error()) @@ -59,14 +60,14 @@ } result := podmanTest.Podman([]string{"port", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] Expect(result.LineInOutputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) It("podman container port -l nginx", func() { session, cid := podmanTest.RunNginxWithHealthCheck("") - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if err := podmanTest.RunHealthCheck(cid); err != nil { Fail(err.Error()) @@ -77,14 +78,14 @@ } result := podmanTest.Podman([]string{"container", "port", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] Expect(result.LineInOutputStartsWith(fmt.Sprintf("80/tcp -> 0.0.0.0:%s", port))).To(BeTrue()) }) It("podman port -l port nginx", func() { session, cid := podmanTest.RunNginxWithHealthCheck("") - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if err := podmanTest.RunHealthCheck(cid); err != nil { Fail(err.Error()) @@ -95,14 +96,14 @@ } result := podmanTest.Podman([]string{"port", cid, "80"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) port := strings.Split(result.OutputToStringArray()[0], ":")[1] Expect(result.LineInOutputStartsWith(fmt.Sprintf("0.0.0.0:%s", port))).To(BeTrue()) }) It("podman port -a nginx", func() { session, cid := podmanTest.RunNginxWithHealthCheck("") - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if err := podmanTest.RunHealthCheck(cid); err != nil { Fail(err.Error()) @@ -110,12 +111,12 @@ result := podmanTest.Podman([]string{"port", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman port nginx by name", func() { session, cid := podmanTest.RunNginxWithHealthCheck("portcheck") - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if err := podmanTest.RunHealthCheck(cid); err != nil { Fail(err.Error()) @@ -123,7 +124,7 @@ result := podmanTest.Podman([]string{"port", "portcheck"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result.LineInOutputStartsWith("80/tcp -> 0.0.0.0:") }) @@ -136,18 +137,18 @@ setup := podmanTest.Podman([]string{"run", "--name", "test", "-dt", "-p", "5000:5000", "-p", "5001:5001", ALPINE, "top"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(BeZero()) + Expect(setup).Should(Exit(0)) // Check that the first port was honored result1 := podmanTest.Podman([]string{"port", "test", "5000"}) result1.WaitWithDefaultTimeout() - Expect(result1.ExitCode()).To(BeZero()) + Expect(result1).Should(Exit(0)) Expect(result1.LineInOutputStartsWith("0.0.0.0:5000")).To(BeTrue()) // Check that the second port was honored result2 := podmanTest.Podman([]string{"port", "test", "5001"}) result2.WaitWithDefaultTimeout() - Expect(result2.ExitCode()).To(BeZero()) + Expect(result2).Should(Exit(0)) Expect(result2.LineInOutputStartsWith("0.0.0.0:5001")).To(BeTrue()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/prune_test.go libpod-3.4.4+ds1/test/e2e/prune_test.go --- libpod-3.2.1+ds1/test/e2e/prune_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/prune_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var pruneImage = fmt.Sprintf(` @@ -15,6 +16,11 @@ RUN apk update RUN apk add bash`, ALPINE) +var emptyPruneImage = ` +FROM scratch +ENV test1=test1 +ENV test2=test2` + var _ = Describe("Podman prune", func() { var ( tempdir string @@ -42,20 +48,20 @@ It("podman container prune containers", func() { top := podmanTest.RunTopContainer("") top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(Equal(0)) + Expect(top).Should(Exit(0)) top = podmanTest.RunTopContainer("") top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(Equal(0)) + Expect(top).Should(Exit(0)) cid := top.OutputToString() stop := podmanTest.Podman([]string{"stop", cid}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) prune := podmanTest.Podman([]string{"container", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) }) @@ -63,11 +69,11 @@ It("podman container prune after create containers", func() { create := podmanTest.Podman([]string{"create", "--name", "test", BB}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) prune := podmanTest.Podman([]string{"container", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) }) @@ -75,15 +81,15 @@ It("podman container prune after create & init containers", func() { create := podmanTest.Podman([]string{"create", "--name", "test", BB}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) init := podmanTest.Podman([]string{"init", "test"}) init.WaitWithDefaultTimeout() - Expect(init.ExitCode()).To(Equal(0)) + Expect(init).Should(Exit(0)) prune := podmanTest.Podman([]string{"container", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) }) @@ -91,7 +97,7 @@ It("podman image prune - remove only dangling images", func() { session := podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hasNone, _ := session.GrepString("") Expect(hasNone).To(BeFalse()) numImages := len(session.OutputToStringArray()) @@ -99,22 +105,26 @@ // Since there's no dangling image, none should be removed. session = podmanTest.Podman([]string{"image", "prune", "-f"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) // Let's be extra sure that the same number of images is // reported. session = podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(numImages)) - // Now build a new image with dangling intermediate images. + // Now build an image and untag it. The (intermediate) images + // should be removed recursively during pruning. podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") + session = podmanTest.Podman([]string{"untag", "alpine_bash:latest"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) hasNone, _ = session.GrepString("") Expect(hasNone).To(BeTrue()) // ! we have dangling ones numImages = len(session.OutputToStringArray()) @@ -123,7 +133,7 @@ // remove them. session = podmanTest.Podman([]string{"image", "prune", "-f"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) numPrunedImages := len(session.OutputToStringArray()) Expect(numPrunedImages >= 1).To(BeTrue()) @@ -131,30 +141,37 @@ // been removed. session = podmanTest.Podman([]string{"images", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(numImages - numPrunedImages)) }) - It("podman image prune skip cache images", func() { - podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") + It("podman image prune - handle empty images", func() { + // As shown in #10832, empty images were not treated correctly + // in Podman. + podmanTest.BuildImage(emptyPruneImage, "empty:scratch", "true") - none := podmanTest.Podman([]string{"images", "-a"}) - none.WaitWithDefaultTimeout() - Expect(none.ExitCode()).To(Equal(0)) - hasNone, _ := none.GrepString("") + session := podmanTest.Podman([]string{"images", "-a"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + hasNone, _ := session.GrepString("") Expect(hasNone).To(BeTrue()) - prune := podmanTest.Podman([]string{"image", "prune", "-f"}) - prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + // Nothing will be pruned. + session = podmanTest.Podman([]string{"image", "prune", "-f"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) - after := podmanTest.Podman([]string{"images", "-a"}) - after.WaitWithDefaultTimeout() - Expect(none.ExitCode()).To(Equal(0)) - // Check if all "dangling" images were pruned. - hasNoneAfter, _ := after.GrepString("") - Expect(hasNoneAfter).To(BeFalse()) - Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) + // Now the image will be untagged, and its parent images will + // be removed recursively. + session = podmanTest.Podman([]string{"untag", "empty:scratch"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"image", "prune", "-f"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) }) It("podman image prune dangling images", func() { @@ -162,18 +179,18 @@ podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") none := podmanTest.Podman([]string{"images", "-a"}) none.WaitWithDefaultTimeout() - Expect(none.ExitCode()).To(Equal(0)) + Expect(none).Should(Exit(0)) hasNone, result := none.GrepString("") Expect(len(result)).To(Equal(2)) Expect(hasNone).To(BeTrue()) prune := podmanTest.Podman([]string{"image", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) after := podmanTest.Podman([]string{"images", "-a"}) after.WaitWithDefaultTimeout() - Expect(none.ExitCode()).To(Equal(0)) + Expect(none).Should(Exit(0)) hasNoneAfter, result := none.GrepString("") Expect(hasNoneAfter).To(BeTrue()) Expect(len(after.OutputToStringArray()) > 1).To(BeTrue()) @@ -186,15 +203,15 @@ images := podmanTest.Podman([]string{"images", "-a"}) images.WaitWithDefaultTimeout() - Expect(images.ExitCode()).To(Equal(0)) + Expect(images).Should(Exit(0)) prune := podmanTest.Podman([]string{"image", "prune", "-af"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) images = podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() - Expect(images.ExitCode()).To(Equal(0)) + Expect(images).Should(Exit(0)) // all images are unused, so they all should be deleted! Expect(len(images.OutputToStringArray())).To(Equal(len(CACHE_IMAGES))) }) @@ -204,7 +221,7 @@ podmanTest.BuildImage(pruneImage, "alpine_bash:latest", "true") prune := podmanTest.Podman([]string{"system", "prune", "-a", "--force"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) images := podmanTest.Podman([]string{"images", "-aq"}) images.WaitWithDefaultTimeout() @@ -215,63 +232,63 @@ It("podman system prune pods", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid1 := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) pods := podmanTest.Podman([]string{"pod", "ps"}) pods.WaitWithDefaultTimeout() - Expect(pods.ExitCode()).To(Equal(0)) + Expect(pods).Should(Exit(0)) Expect(len(pods.OutputToStringArray())).To(Equal(3)) prune := podmanTest.Podman([]string{"system", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) pods = podmanTest.Podman([]string{"pod", "ps"}) pods.WaitWithDefaultTimeout() - Expect(pods.ExitCode()).To(Equal(0)) + Expect(pods).Should(Exit(0)) Expect(len(pods.OutputToStringArray())).To(Equal(2)) }) It("podman system prune - pod,container stopped", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid1 := session.OutputToString() // Start and stop a pod to get it in exited state. session = podmanTest.Podman([]string{"pod", "start", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create a container. This container should be pruned. create := podmanTest.Podman([]string{"create", "--name", "test", BB}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) prune := podmanTest.Podman([]string{"system", "prune", "-f"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) pods := podmanTest.Podman([]string{"pod", "ps"}) pods.WaitWithDefaultTimeout() - Expect(pods.ExitCode()).To(Equal(0)) + Expect(pods).Should(Exit(0)) Expect(podmanTest.NumberOfPods()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -281,26 +298,26 @@ // Start and stop a pod to get it in exited state. session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid1 := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Start a pod and leave it running session = podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid2 := session.OutputToString() session = podmanTest.Podman([]string{"pod", "start", podid2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Number of pod should be 2. One exited one running. Expect(podmanTest.NumberOfPods()).To(Equal(2)) @@ -321,25 +338,25 @@ // Adding unused volume should be pruned session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Volumes should be pruned. session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) // One Pod should not be pruned as it was running @@ -357,35 +374,35 @@ It("podman system prune - with dangling images true", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podid1 := session.OutputToString() // Start and stop a pod to get it in exited state. session = podmanTest.Podman([]string{"pod", "start", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pod", "stop", podid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Create a container. This container should be pruned. create := podmanTest.Podman([]string{"create", "--name", "test", BB}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) // Adding unused volume should not be pruned as volumes not set session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) prune := podmanTest.Podman([]string{"system", "prune", "-f", "-a"}) prune.WaitWithDefaultTimeout() - Expect(prune.ExitCode()).To(Equal(0)) + Expect(prune).Should(Exit(0)) pods := podmanTest.Podman([]string{"pod", "ps"}) pods.WaitWithDefaultTimeout() - Expect(pods.ExitCode()).To(Equal(0)) + Expect(pods).Should(Exit(0)) Expect(podmanTest.NumberOfPods()).To(Equal(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -393,7 +410,7 @@ // Volumes should not be pruned session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) images := podmanTest.Podman([]string{"images", "-aq"}) @@ -405,58 +422,58 @@ It("podman system prune --volumes --filter", func() { session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv1", "myvol2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv2", "myvol3"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1", "myvol4"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol5:/myvol5", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol6:/myvol6", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(7)) session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=label1=value1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(6)) session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=sharedlabel1=slv1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(5)) session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes", "--filter", "label=sharedlabel1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) podmanTest.Cleanup() diff -Nru libpod-3.2.1+ds1/test/e2e/ps_test.go libpod-3.4.4+ds1/test/e2e/ps_test.go --- libpod-3.2.1+ds1/test/e2e/ps_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/ps_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,7 +6,6 @@ "regexp" "sort" "strconv" - "strings" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" @@ -43,23 +42,23 @@ It("podman ps no containers", func() { session := podmanTest.Podman([]string{"ps"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container ps no containers", func() { session := podmanTest.Podman([]string{"container", "ps"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman ps default", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"ps"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) @@ -69,7 +68,7 @@ result := podmanTest.Podman([]string{"ps", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) @@ -79,12 +78,12 @@ result := podmanTest.Podman([]string{"container", "list", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) result = podmanTest.Podman([]string{"container", "ls", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) @@ -94,7 +93,7 @@ result := podmanTest.Podman([]string{"ps", "-a", "--size"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) @@ -104,7 +103,7 @@ result := podmanTest.Podman([]string{"ps", "-a", "-q"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) Expect(fullCid).To(ContainSubstring(result.OutputToStringArray()[0])) }) @@ -118,7 +117,7 @@ result := podmanTest.Podman([]string{"ps", "-q", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(Equal(1)) }) @@ -129,11 +128,11 @@ // well. session := podmanTest.Podman([]string{"create", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"ps", "--last", "2"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(Equal(2)) // 1 container _, ec, _ := podmanTest.RunLsContainer("test1") @@ -147,17 +146,17 @@ result = podmanTest.Podman([]string{"ps", "--last", "2"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(Equal(3)) // 2 containers result = podmanTest.Podman([]string{"ps", "--last", "3"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(Equal(4)) // 3 containers result = podmanTest.Podman([]string{"ps", "--last", "100"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(Equal(5)) // 4 containers (3 running + 1 created) }) @@ -167,18 +166,60 @@ result := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) Expect(fullCid).To(Equal(result.OutputToStringArray()[0])) }) + It("podman ps --filter network=container:", func() { + ctrAlpha := "alpha" + container := podmanTest.Podman([]string{"run", "-dt", "--name", ctrAlpha, ALPINE, "top"}) + container.WaitWithDefaultTimeout() + Expect(container).Should(Exit(0)) + + ctrBravo := "bravo" + containerBravo := podmanTest.Podman([]string{"run", "-dt", "--network", "container:alpha", "--name", ctrBravo, ALPINE, "top"}) + containerBravo.WaitWithDefaultTimeout() + Expect(containerBravo).Should(Exit(0)) + + result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.Names}}", "--filter", "network=container:alpha"}) + result.WaitWithDefaultTimeout() + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + + actual := result.OutputToString() + Expect(actual).To(ContainSubstring("bravo")) + Expect(actual).To(ContainSubstring("NAMES")) + }) + + It("podman ps --filter network=container:", func() { + ctrAlpha := "first" + container := podmanTest.Podman([]string{"run", "-dt", "--name", ctrAlpha, ALPINE, "top"}) + container.WaitWithDefaultTimeout() + cid := container.OutputToString() + Expect(container).Should(Exit(0)) + + ctrBravo := "second" + containerBravo := podmanTest.Podman([]string{"run", "-dt", "--network", "container:" + cid, "--name", ctrBravo, ALPINE, "top"}) + containerBravo.WaitWithDefaultTimeout() + Expect(containerBravo).Should(Exit(0)) + + result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.Names}}", "--filter", "network=container:" + cid}) + result.WaitWithDefaultTimeout() + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + actual := result.OutputToString() + Expect(actual).To(ContainSubstring("second")) + Expect(actual).ToNot(ContainSubstring("table")) + }) + It("podman ps namespace flag", func() { _, ec, _ := podmanTest.RunLsContainer("") Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"ps", "-a", "--namespace"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).Should(BeNumerically(">", 0)) }) @@ -189,15 +230,15 @@ result := podmanTest.Podman([]string{"ps", "-a", "--namespace", "--format", "{{with .Namespaces}}{{.Cgroup}}:{{.IPC}}:{{.MNT}}:{{.NET}}:{{.PIDNS}}:{{.User}}:{{.UTS}}{{end}}"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // it must contains `::` when some ns is null. If it works normally, it should be "$num1:$num2:$num3" - Expect(result.OutputToString()).To(Not(ContainSubstring(`::`))) + Expect(result.OutputToString()).ToNot(ContainSubstring(`::`)) }) It("podman ps with no containers is valid json format", func() { result := podmanTest.Podman([]string{"ps", "--format", "json"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.IsJSONOutputValid()).To(BeTrue()) }) @@ -207,18 +248,18 @@ result := podmanTest.Podman([]string{"ps", "-a", "--ns", "--format", "json"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.IsJSONOutputValid()).To(BeTrue()) }) It("podman ps json format Created field is int64", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"ps", "--format", "json"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) // Make sure Created field is an int64 created, err := result.jq(".[0].Created") @@ -233,7 +274,7 @@ result := podmanTest.Podman([]string{"ps", "-a", "--format", "json"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.IsJSONOutputValid()).To(BeTrue()) // must contain "Status" match, StatusLine := result.GrepString(`Status`) @@ -248,11 +289,14 @@ result := podmanTest.Podman([]string{"ps", "-a", "--format", "table {{.ID}} {{.Image}} {{.ImageID}} {{.Labels}}"}) result.WaitWithDefaultTimeout() - - Expect(result.OutputToStringArray()[0]).ToNot(ContainSubstring("table")) - Expect(result.OutputToStringArray()[0]).ToNot(ContainSubstring("ImageID")) - Expect(result.OutputToStringArray()[0]).To(ContainSubstring("alpine:latest")) Expect(result).Should(Exit(0)) + + Expect(result.OutputToString()).ToNot(ContainSubstring("table")) + + actual := result.OutputToStringArray() + Expect(actual[0]).To(ContainSubstring("CONTAINER ID")) + Expect(actual[0]).ToNot(ContainSubstring("ImageID")) + Expect(actual[1]).To(ContainSubstring("alpine:latest")) }) It("podman ps ancestor filter flag", func() { @@ -261,19 +305,19 @@ result := podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "-a", "--filter", "ancestor=quay.io/libpod/alpine:latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal(cid)) // Query just by image name, without :latest tag result = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "-a", "--filter", "ancestor=quay.io/libpod/alpine"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal(cid)) // Query by truncated image name should not match ( should return empty output ) result = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "-a", "--filter", "ancestor=quay.io/libpod/alpi"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("")) }) @@ -283,34 +327,34 @@ result := podmanTest.Podman([]string{"ps", "-a", "--filter", fmt.Sprintf("id=%s", fullCid)}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman ps id filter flag", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fullCid := session.OutputToString() result := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc", "--filter", "status=running"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToStringArray()[0]).To(Equal(fullCid)) }) It("podman ps multiple filters", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", "--label", "key1=value1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fullCid := session.OutputToString() session2 := podmanTest.Podman([]string{"run", "-d", "--name", "test2", "--label", "key1=value1", ALPINE, "top"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) result := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc", "--filter", "name=test1", "--filter", "label=key1=value1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToStringArray() Expect(len(output)).To(Equal(1)) @@ -320,15 +364,15 @@ It("podman ps filter by exited does not need all", func() { ctr := podmanTest.Podman([]string{"run", "-t", "-i", ALPINE, "ls", "/"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) psAll := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc"}) psAll.WaitWithDefaultTimeout() - Expect(psAll.ExitCode()).To(Equal(0)) + Expect(psAll).Should(Exit(0)) psFilter := podmanTest.Podman([]string{"ps", "--no-trunc", "--quiet", "--filter", "status=exited"}) psFilter.WaitWithDefaultTimeout() - Expect(psFilter.ExitCode()).To(Equal(0)) + Expect(psFilter).Should(Exit(0)) Expect(psAll.OutputToString()).To(Equal(psFilter.OutputToString())) }) @@ -337,13 +381,15 @@ ctrName := "aContainerName" ctr := podmanTest.Podman([]string{"create", "--name", ctrName, "-t", "-i", ALPINE, "ls", "/"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) psFilter := podmanTest.Podman([]string{"ps", "--no-trunc", "--quiet", "--format", "{{.Names}}", "--filter", fmt.Sprintf("name=%s", ctrName)}) psFilter.WaitWithDefaultTimeout() - Expect(psFilter.ExitCode()).To(Equal(0)) + Expect(psFilter).Should(Exit(0)) - Expect(strings.Contains(psFilter.OutputToString(), ctrName)).To(BeFalse()) + actual := psFilter.OutputToString() + Expect(actual).ToNot(ContainSubstring(ctrName)) + Expect(actual).ToNot(ContainSubstring("NAMES")) }) It("podman ps mutually exclusive flags", func() { @@ -359,30 +405,30 @@ It("podman --format by size", func() { session := podmanTest.Podman([]string{"create", BB, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-t", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "-a", "--format", "{{.Size}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).To(ContainSubstring("Size format requires --size option")) }) It("podman --sort by size", func() { session := podmanTest.Podman([]string{"create", BB, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-t", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "-a", "-s", "--sort=size", "--format", "{{.Size}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) sortedArr := session.OutputToStringArray() @@ -409,21 +455,20 @@ It("podman --sort by command", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", ALPINE, "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "-a", "--sort=command", "--format", "{{.Command}}"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) - sortedArr := session.OutputToStringArray() + Expect(session.OutputToString()).ToNot(ContainSubstring("COMMAND")) + sortedArr := session.OutputToStringArray() Expect(sort.SliceIsSorted(sortedArr, func(i, j int) bool { return sortedArr[i] < sortedArr[j] })).To(BeTrue()) - }) It("podman --pod", func() { @@ -432,16 +477,16 @@ session := podmanTest.RunTopContainerInPod("", podid) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "--no-trunc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Not(ContainSubstring(podid))) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).ToNot(ContainSubstring(podid)) session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(podid)) }) @@ -452,14 +497,14 @@ session := podmanTest.RunTopContainerInPod("", podName) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // "--no-trunc" must be given. If not it will trunc the pod ID // in the output and you will have to trunc it in the test too. session = podmanTest.Podman([]string{"ps", "--pod", "--no-trunc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(ContainSubstring(podid)) @@ -469,11 +514,15 @@ It("podman ps test with single port range", func() { session := podmanTest.Podman([]string{"run", "-dt", "-p", "2000-2006:2000-2006", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) session.WaitWithDefaultTimeout() - Expect(session.OutputToString()).To(ContainSubstring("0.0.0.0:2000-2006")) + Expect(session).To(Exit(0)) + + actual := session.OutputToString() + Expect(actual).To(ContainSubstring("0.0.0.0:2000-2006")) + Expect(actual).ToNot(ContainSubstring("PORT")) }) It("podman ps test with invalid port range", func() { @@ -481,7 +530,7 @@ "run", "-p", "1000-2000:2000-3000", "-p", "1999-2999:3001-4001", ALPINE, }) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("conflicting port mappings for host port 1999")) }) @@ -496,7 +545,7 @@ ALPINE, "top"}, ) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "--format", "{{.Ports}}"}) session.WaitWithDefaultTimeout() @@ -508,35 +557,35 @@ It("podman ps sync flag", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fullCid := session.OutputToString() result := podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--sync"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToStringArray()[0]).To(Equal(fullCid)) }) It("podman ps filter name regexp", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fullCid := session.OutputToString() session2 := podmanTest.Podman([]string{"run", "-d", "--name", "test11", ALPINE, "top"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) result := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc", "--filter", "name=test1"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToStringArray() Expect(len(output)).To(Equal(2)) result = podmanTest.Podman([]string{"ps", "-aq", "--no-trunc", "--filter", "name=test1$"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output = result.OutputToStringArray() Expect(len(output)).To(Equal(1)) @@ -547,11 +596,11 @@ ctrName := "testCtr" session := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"ps", "-q", "-a", "--format", "{{ .Names }}"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToStringArray() Expect(len(output)).To(Equal(1)) @@ -560,25 +609,25 @@ It("podman ps test with port shared with pod", func() { podName := "testPod" - pod := podmanTest.Podman([]string{"pod", "create", "-p", "8080:80", "--name", podName}) + pod := podmanTest.Podman([]string{"pod", "create", "-p", "8085:80", "--name", podName}) pod.WaitWithDefaultTimeout() - Expect(pod.ExitCode()).To(Equal(0)) + Expect(pod).Should(Exit(0)) ctrName := "testCtr" session := podmanTest.Podman([]string{"run", "--name", ctrName, "-dt", "--pod", podName, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ps := podmanTest.Podman([]string{"ps", "--filter", fmt.Sprintf("name=%s", ctrName), "--format", "{{.Ports}}"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) - Expect(ps.OutputToString()).To(ContainSubstring("0.0.0.0:8080->80/tcp")) + Expect(ps).Should(Exit(0)) + Expect(ps.OutputToString()).To(ContainSubstring("0.0.0.0:8085->80/tcp")) }) It("podman ps truncate long create command", func() { session := podmanTest.Podman([]string{"run", ALPINE, "echo", "very", "long", "create", "command"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "-a"}) session.WaitWithDefaultTimeout() @@ -590,34 +639,37 @@ result := podmanTest.Podman([]string{"ps", "-a", "--format", "{{.RunningFor}}"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) - Expect(result.OutputToString()).To(ContainSubstring("ago")) + Expect(result).Should(Exit(0)) + + actual := result.OutputToString() + Expect(actual).To(ContainSubstring("ago")) + Expect(actual).ToNot(ContainSubstring("RUNNING FOR")) }) It("podman ps filter test", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", "--label", "foo=1", "--label", "bar=2", "--volume", "volume1:/test", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "--name", "test2", "--label", "foo=1", ALPINE, "ls", "/fail"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) session = podmanTest.Podman([]string{"create", "--name", "test3", ALPINE, cid1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--name", "test4", "--volume", "volume1:/test1", "--volume", "/:/test2", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "name=test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(5)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) Expect(session.LineInOutputContains("test2")).To(BeTrue()) @@ -626,7 +678,7 @@ session = podmanTest.Podman([]string{"ps", "--all", "--filter", "name=test1", "--filter", "name=test2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) Expect(session.LineInOutputContains("test2")).To(BeTrue()) @@ -634,19 +686,19 @@ // check container id matches with regex session = podmanTest.Podman([]string{"ps", "--all", "--filter", "id=" + cid1[:40], "--filter", "id=" + cid1 + "$"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--filter", "status=created"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test3")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--filter", "status=created", "--filter", "status=exited"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(4)) Expect(session.LineInOutputContains("test2")).To(BeTrue()) Expect(session.LineInOutputContains("test3")).To(BeTrue()) @@ -654,63 +706,63 @@ session = podmanTest.Podman([]string{"ps", "--all", "--filter", "label=foo=1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) Expect(session.LineInOutputContains("test2")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--filter", "label=foo=1", "--filter", "status=exited"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test2")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "label=foo=1", "--filter", "label=non=1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(1)) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "label=foo=1", "--filter", "label=bar=2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "exited=1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test2")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "exited=1", "--filter", "exited=0"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.LineInOutputContains("test2")).To(BeTrue()) Expect(session.LineInOutputContains("test4")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "volume=volume1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) Expect(session.LineInOutputContains("test4")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "volume=/:/test2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test4")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "before=test2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.LineInOutputContains("test1")).To(BeTrue()) session = podmanTest.Podman([]string{"ps", "--all", "--filter", "since=test2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.LineInOutputContains("test3")).To(BeTrue()) Expect(session.LineInOutputContains("test4")).To(BeTrue()) @@ -718,48 +770,48 @@ It("podman ps filter pod", func() { pod1 := podmanTest.Podman([]string{"pod", "create", "--name", "pod1"}) pod1.WaitWithDefaultTimeout() - Expect(pod1.ExitCode()).To(BeZero()) + Expect(pod1).Should(Exit(0)) con1 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod1", ALPINE, "top"}) con1.WaitWithDefaultTimeout() - Expect(con1.ExitCode()).To(BeZero()) + Expect(con1).Should(Exit(0)) pod2 := podmanTest.Podman([]string{"pod", "create", "--name", "pod2"}) pod2.WaitWithDefaultTimeout() - Expect(pod2.ExitCode()).To(BeZero()) + Expect(pod2).Should(Exit(0)) con2 := podmanTest.Podman([]string{"run", "-dt", "--pod", "pod2", ALPINE, "top"}) con2.WaitWithDefaultTimeout() - Expect(con2.ExitCode()).To(BeZero()) + Expect(con2).Should(Exit(0)) // bogus pod name or id should not result in error session := podmanTest.Podman([]string{"ps", "--filter", "pod=1234"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) // filter by pod name session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) // filter by full pod id session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) // filter by partial pod id session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=" + pod1.OutputToString()[0:12]}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) // filter by multiple pods is inclusive session = podmanTest.Podman([]string{"ps", "-q", "--no-trunc", "--filter", "pod=pod1", "--filter", "pod=pod2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(4)) Expect(StringInSlice(pod1.OutputToString(), session.OutputToStringArray())) Expect(StringInSlice(pod2.OutputToString(), session.OutputToStringArray())) @@ -770,61 +822,65 @@ net := stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(net) session = podmanTest.Podman([]string{"create", "--network", net, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) ctrWithNet := session.OutputToString() session = podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) ctrWithoutNet := session.OutputToString() session = podmanTest.Podman([]string{"ps", "--all", "--no-trunc", "--filter", "network=" + net}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) - Expect(session.OutputToString()).To(ContainSubstring(ctrWithNet)) - Expect(session.OutputToString()).To(Not(ContainSubstring(ctrWithoutNet))) + Expect(session).Should(Exit(0)) + actual := session.OutputToString() + Expect(actual).To(ContainSubstring(ctrWithNet)) + Expect(actual).ToNot(ContainSubstring(ctrWithoutNet)) }) It("podman ps --format networks", func() { session := podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) + + actual := session.OutputToString() + Expect(actual).ToNot(ContainSubstring("NETWORKS")) if isRootless() { // rootless container don't have a network by default - Expect(session.OutputToString()).To(Equal("")) + Expect(actual).To(BeEmpty()) } else { // default network name is podman - Expect(session.OutputToString()).To(Equal("podman")) + Expect(actual).To(Equal("podman")) } net1 := stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", net1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(net1) net2 := stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", net2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) defer podmanTest.removeCNINetwork(net2) session = podmanTest.Podman([]string{"create", "--network", net1 + "," + net2, ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"ps", "--all", "--format", "{{ .Networks }}", "--filter", "id=" + cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) // the output is not deterministic so check both possible orders Expect(session.OutputToString()).To(Or(Equal(net1+","+net2), Equal(net2+","+net1))) }) diff -Nru libpod-3.2.1+ds1/test/e2e/pull_test.go libpod-3.4.4+ds1/test/e2e/pull_test.go --- libpod-3.2.1+ds1/test/e2e/pull_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/pull_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman pull", func() { @@ -35,6 +36,23 @@ }) + It("podman pull multiple images with/without tag/digest", func() { + session := podmanTest.Podman([]string{"pull", "busybox:musl", "alpine", "alpine:latest", "quay.io/libpod/cirros", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"pull", "busybox:latest", "docker.io/library/ibetthisdoesnotexistfr:random", "alpine"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + expectedError := "initializing source docker://ibetthisdoesnotexistfr:random" + found, _ := session.ErrorGrepString(expectedError) + Expect(found).To(Equal(true)) + + session = podmanTest.Podman([]string{"rmi", "busybox", "alpine", "testdigest_v2s2", "quay.io/libpod/cirros"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + It("podman pull from docker a not existing image", func() { session := podmanTest.Podman([]string{"pull", "ibetthisdoesntexistthere:foo"}) session.WaitWithDefaultTimeout() @@ -44,188 +62,188 @@ It("podman pull from docker with tag", func() { session := podmanTest.Podman([]string{"pull", "quay.io/libpod/testdigest_v2s2:20200210"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2:20200210"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull from docker without tag", func() { session := podmanTest.Podman([]string{"pull", "quay.io/libpod/testdigest_v2s2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull from alternate registry with tag", func() { session := podmanTest.Podman([]string{"pull", cirros}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", cirros}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull from alternate registry without tag", func() { session := podmanTest.Podman([]string{"pull", "quay.io/libpod/cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "quay.io/libpod/cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull by digest", func() { session := podmanTest.Podman([]string{"pull", "quay.io/libpod/testdigest_v2s2@sha256:755f4d90b3716e2bf57060d249e2cd61c9ac089b1233465c5c2cb2d7ee550fdb"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "testdigest_v2s2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull by digest (image list)", func() { session := podmanTest.Podman([]string{"pull", "--arch=arm64", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // inspect using the digest of the list session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(HavePrefix("[]")) // inspect using the digest of the list session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // inspect using the digest of the arch-specific image's manifest session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(HavePrefix("[]")) // inspect using the digest of the arch-specific image's manifest session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // inspect using the image ID session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64ID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(HavePrefix("[]")) // inspect using the image ID session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64ID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // remove using the digest of the list session = podmanTest.Podman([]string{"rmi", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull by instance digest (image list)", func() { session := podmanTest.Podman([]string{"pull", "--arch=arm64", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // inspect using the digest of the list session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) // inspect using the digest of the list session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) // inspect using the digest of the arch-specific image's manifest session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(HavePrefix("[]")) // inspect using the digest of the arch-specific image's manifest session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(Not(ContainSubstring(ALPINELISTDIGEST))) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // inspect using the image ID session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64ID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(HavePrefix("[]")) // inspect using the image ID session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64ID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(Not(ContainSubstring(ALPINELISTDIGEST))) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // remove using the digest of the instance session = podmanTest.Podman([]string{"rmi", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull by tag (image list)", func() { session := podmanTest.Podman([]string{"pull", "--arch=arm64", ALPINELISTTAG}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // inspect using the tag we used for pulling session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTTAG}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG)) // inspect using the tag we used for pulling session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTTAG}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // inspect using the digest of the list session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG)) // inspect using the digest of the list session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // inspect using the digest of the arch-specific image's manifest session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG)) // inspect using the digest of the arch-specific image's manifest session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64DIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // inspect using the image ID session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoTags}}", ALPINEARM64ID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTTAG)) // inspect using the image ID session = podmanTest.Podman([]string{"inspect", "--format", "{{.RepoDigests}}", ALPINEARM64ID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINELISTDIGEST)) Expect(string(session.Out.Contents())).To(ContainSubstring(ALPINEARM64DIGEST)) // remove using the tag session = podmanTest.Podman([]string{"rmi", ALPINELISTTAG}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull bogus image", func() { @@ -242,23 +260,23 @@ session := podmanTest.Podman([]string{"save", "-o", tarfn, "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:%s", tarfn)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Pulling a multi-image archive without further specifying // which image _must_ error out. Pulling is restricted to one // image. session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError := "Unexpected tar manifest.json: expected 1 item, got 2" found, _ := session.ErrorGrepString(expectedError) Expect(found).To(Equal(true)) @@ -267,31 +285,31 @@ // and index syntax. session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:@0")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty:latest")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:@1")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:example.com/empty/but:different")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Now check for some errors. session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:foo.com/does/not/exist:latest")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError = "Tag \"foo.com/does/not/exist:latest\" not found" found, _ = session.ErrorGrepString(expectedError) Expect(found).To(Equal(true)) session = podmanTest.Podman([]string{"pull", fmt.Sprintf("docker-archive:./testdata/docker-two-images.tar.xz:@2")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError = "Invalid source index @2, only 2 manifest items available" found, _ = session.ErrorGrepString(expectedError) Expect(found).To(Equal(true)) @@ -305,16 +323,16 @@ session := podmanTest.Podman([]string{"save", "--format", "oci-archive", "-o", tarfn, "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", fmt.Sprintf("oci-archive:%s", tarfn)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pull from local directory", func() { @@ -327,16 +345,16 @@ session := podmanTest.Podman([]string{"push", "cirros", imgPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", imgPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) }) @@ -350,16 +368,16 @@ session := podmanTest.Podman([]string{"push", "cirros", imgPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "cirros"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", imgPath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContainsTag(filepath.Join("localhost", dirpath), "latest")).To(BeTrue()) }) @@ -367,16 +385,16 @@ podmanTest.RestoreArtifact(ALPINE) setup := podmanTest.Podman([]string{"images", ALPINE, "-q", "--no-trunc"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) shortImageId := strings.Split(setup.OutputToString(), ":")[1] rmi := podmanTest.Podman([]string{"rmi", ALPINE}) rmi.WaitWithDefaultTimeout() - Expect(rmi.ExitCode()).To(Equal(0)) + Expect(rmi).Should(Exit(0)) pull := podmanTest.Podman([]string{"pull", "-q", ALPINE}) pull.WaitWithDefaultTimeout() - Expect(pull.ExitCode()).To(Equal(0)) + Expect(pull).Should(Exit(0)) Expect(pull.OutputToString()).To(ContainSubstring(shortImageId)) }) @@ -384,19 +402,18 @@ It("podman pull check all tags", func() { session := podmanTest.Podman([]string{"pull", "--all-tags", "k8s.gcr.io/pause"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.LineInOutputStartsWith("Pulled Images:")).To(BeTrue()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 4)) }) It("podman pull from docker with nonexistent --authfile", func() { session := podmanTest.Podman([]string{"pull", "--authfile", "/tmp/nonexistent", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman pull + inspect from unqualified-search registry", func() { @@ -418,7 +435,7 @@ getID := func(image string) string { setup := podmanTest.Podman([]string{"image", "inspect", image}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := setup.InspectImageJSON() // returns []inspect.ImageData Expect(len(data)).To(Equal(1)) @@ -428,11 +445,11 @@ untag := func(image string) { setup := podmanTest.Podman([]string{"untag", image}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"image", "inspect", image}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := setup.InspectImageJSON() // returns []inspect.ImageData Expect(len(data)).To(Equal(1)) @@ -442,10 +459,10 @@ tag := func(image, tag string) { setup := podmanTest.Podman([]string{"tag", image, tag}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) setup = podmanTest.Podman([]string{"image", "exists", tag}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) } image1 := getID(ALPINE) @@ -486,7 +503,7 @@ setup := podmanTest.Podman([]string{"image", "inspect", name}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := setup.InspectImageJSON() // returns []inspect.ImageData Expect(len(data)).To(Equal(1)) @@ -499,23 +516,23 @@ It("podman pull --platform", func() { session := podmanTest.Podman([]string{"pull", "--platform=linux/bogus", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError := "no image found in manifest list for architecture bogus" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"pull", "--platform=linux/arm64", "--os", "windows", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError = "--platform option can not be specified with --arch or --os" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"pull", "-q", "--platform=linux/arm64", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) setup := podmanTest.Podman([]string{"image", "inspect", session.OutputToString()}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := setup.InspectImageJSON() // returns []inspect.ImageData Expect(len(data)).To(Equal(1)) @@ -526,23 +543,23 @@ It("podman pull --arch", func() { session := podmanTest.Podman([]string{"pull", "--arch=bogus", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError := "no image found in manifest list for architecture bogus" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"pull", "--arch=arm64", "--os", "windows", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) expectedError = "no image found in manifest list for architecture" Expect(session.ErrorToString()).To(ContainSubstring(expectedError)) session = podmanTest.Podman([]string{"pull", "-q", "--arch=arm64", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) setup := podmanTest.Podman([]string{"image", "inspect", session.OutputToString()}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) data := setup.InspectImageJSON() // returns []inspect.ImageData Expect(len(data)).To(Equal(1)) diff -Nru libpod-3.2.1+ds1/test/e2e/push_test.go libpod-3.4.4+ds1/test/e2e/push_test.go --- libpod-3.2.1+ds1/test/e2e/push_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/push_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman push", func() { @@ -40,11 +41,11 @@ SkipIfRemote("Remote push does not support containers-storage transport") session := podmanTest.Podman([]string{"push", ALPINE, "containers-storage:busybox:test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman push to dir", func() { @@ -53,13 +54,13 @@ session := podmanTest.Podman([]string{"push", "--remove-signatures", ALPINE, fmt.Sprintf("dir:%s", bbdir)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) bbdir = filepath.Join(podmanTest.TempDir, "busybox") session = podmanTest.Podman([]string{"push", "--format", "oci", ALPINE, fmt.Sprintf("dir:%s", bbdir)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman push to local registry", func() { @@ -74,7 +75,7 @@ defer lock.Unlock() session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -82,7 +83,7 @@ push := podmanTest.Podman([]string{"push", "-q", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) // Test --digestfile option push2 := podmanTest.Podman([]string{"push", "--tls-verify=false", "--digestfile=/tmp/digestfile.txt", "--remove-signatures", ALPINE, "localhost:5000/my-alpine"}) @@ -90,7 +91,7 @@ fi, err := os.Lstat("/tmp/digestfile.txt") Expect(err).To(BeNil()) Expect(fi.Name()).To(Equal("digestfile.txt")) - Expect(push2.ExitCode()).To(Equal(0)) + Expect(push2).Should(Exit(0)) }) It("podman push to local registry with authorization", func() { @@ -108,13 +109,13 @@ if IsCommandAvailable("getenforce") { ge := SystemExec("getenforce", []string{}) - Expect(ge.ExitCode()).To(Equal(0)) + Expect(ge).Should(Exit(0)) if ge.OutputToString() == "Enforcing" { se := SystemExec("setenforce", []string{"0"}) - Expect(se.ExitCode()).To(Equal(0)) + Expect(se).Should(Exit(0)) defer func() { se2 := SystemExec("setenforce", []string{"1"}) - Expect(se2.ExitCode()).To(Equal(0)) + Expect(se2).Should(Exit(0)) }() } } @@ -122,7 +123,7 @@ defer lock.Unlock() session := podmanTest.Podman([]string{"run", "--entrypoint", "htpasswd", registry, "-Bbn", "podmantest", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) f, _ := os.Create(filepath.Join(authPath, "htpasswd")) defer f.Close() @@ -136,7 +137,7 @@ "-v", strings.Join([]string{certPath, "/certs"}, ":"), "-e", "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt", "-e", "REGISTRY_HTTP_TLS_KEY=/certs/domain.key", registry}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -145,16 +146,16 @@ session = podmanTest.Podman([]string{"logs", "registry"}) session.WaitWithDefaultTimeout() - push := podmanTest.Podman([]string{"push", "--format=v2s2", "--creds=podmantest:test", ALPINE, "localhost:5000/tlstest"}) + push := podmanTest.Podman([]string{"push", "--tls-verify=true", "--format=v2s2", "--creds=podmantest:test", ALPINE, "localhost:5000/tlstest"}) push.WaitWithDefaultTimeout() Expect(push).To(ExitWithError()) push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", "--tls-verify=false", ALPINE, "localhost:5000/tlstest"}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) setup := SystemExec("cp", []string{filepath.Join(certPath, "domain.crt"), "/etc/containers/certs.d/localhost:5000/ca.crt"}) - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) push = podmanTest.Podman([]string{"push", "--creds=podmantest:wrongpasswd", ALPINE, "localhost:5000/credstest"}) push.WaitWithDefaultTimeout() @@ -162,14 +163,14 @@ if !IsRemote() { // remote does not support --cert-dir - push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", "--cert-dir=fakedir", ALPINE, "localhost:5000/certdirtest"}) + push = podmanTest.Podman([]string{"push", "--tls-verify=true", "--creds=podmantest:test", "--cert-dir=fakedir", ALPINE, "localhost:5000/certdirtest"}) push.WaitWithDefaultTimeout() Expect(push).To(ExitWithError()) } push = podmanTest.Podman([]string{"push", "--creds=podmantest:test", ALPINE, "localhost:5000/defaultflags"}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) }) It("podman push to docker-archive", func() { @@ -178,7 +179,7 @@ session := podmanTest.Podman([]string{"push", ALPINE, fmt.Sprintf("docker-archive:%s:latest", tarfn)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman push to docker daemon", func() { @@ -189,7 +190,7 @@ setup = SystemExec("systemctl", []string{"start", "docker"}) defer func() { stop := SystemExec("systemctl", []string{"stop", "docker"}) - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) }() } else if setup.ExitCode() != 0 { Skip("Docker is not available") @@ -197,14 +198,14 @@ session := podmanTest.Podman([]string{"push", ALPINE, "docker-daemon:alpine:podmantest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := SystemExec("docker", []string{"images", "--format", "{{.Repository}}:{{.Tag}}"}) - Expect(check.ExitCode()).To(Equal(0)) + Expect(check).Should(Exit(0)) Expect(check.OutputToString()).To(ContainSubstring("alpine:podmantest")) clean := SystemExec("docker", []string{"rmi", "alpine:podmantest"}) - Expect(clean.ExitCode()).To(Equal(0)) + Expect(clean).Should(Exit(0)) }) It("podman push to oci-archive", func() { @@ -213,7 +214,7 @@ session := podmanTest.Podman([]string{"push", ALPINE, fmt.Sprintf("oci-archive:%s:latest", tarfn)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman push to docker-archive no reference", func() { @@ -222,7 +223,7 @@ session := podmanTest.Podman([]string{"push", ALPINE, fmt.Sprintf("docker-archive:%s", tarfn)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman push to oci-archive no reference", func() { @@ -232,7 +233,7 @@ fmt.Sprintf("oci-archive:%s", ociarc)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/rename_test.go libpod-3.4.4+ds1/test/e2e/rename_test.go --- libpod-3.2.1+ds1/test/e2e/rename_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/rename_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("podman rename", func() { @@ -36,23 +37,23 @@ It("podman rename on non-existent container", func() { session := podmanTest.Podman([]string{"rename", "doesNotExist", "aNewName"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("Podman rename on existing container with bad name", func() { ctrName := "testCtr" ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) newName := "invalid<>:char" rename := podmanTest.Podman([]string{"rename", ctrName, newName}) rename.WaitWithDefaultTimeout() - Expect(rename.ExitCode()).To(Not(Equal(0))) + Expect(rename).To(ExitWithError()) ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", ctrName), "--format", "{{ .Names }}"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.OutputToString()).To(ContainSubstring(ctrName)) }) @@ -60,16 +61,16 @@ ctrName := "testCtr" ctr := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) newName := "aNewName" rename := podmanTest.Podman([]string{"rename", ctrName, newName}) rename.WaitWithDefaultTimeout() - Expect(rename.ExitCode()).To(Equal(0)) + Expect(rename).Should(Exit(0)) ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.OutputToString()).To(ContainSubstring(newName)) }) @@ -77,16 +78,16 @@ ctrName := "testCtr" ctr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) newName := "aNewName" rename := podmanTest.Podman([]string{"rename", ctrName, newName}) rename.WaitWithDefaultTimeout() - Expect(rename.ExitCode()).To(Equal(0)) + Expect(rename).Should(Exit(0)) ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.OutputToString()).To(ContainSubstring(newName)) }) @@ -94,20 +95,45 @@ ctrName := "testCtr" ctr := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, ALPINE, "top"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) exec := podmanTest.Podman([]string{"exec", "-d", ctrName, "top"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(Equal(0)) + Expect(exec).Should(Exit(0)) newName := "aNewName" rename := podmanTest.Podman([]string{"rename", ctrName, newName}) rename.WaitWithDefaultTimeout() - Expect(rename.ExitCode()).To(Equal(0)) + Expect(rename).Should(Exit(0)) ps := podmanTest.Podman([]string{"ps", "-aq", "--filter", fmt.Sprintf("name=%s", newName), "--format", "{{ .Names }}"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.OutputToString()).To(ContainSubstring(newName)) }) + + It("Rename a container that is part of a pod", func() { + podName := "testPod" + infraName := "infra1" + pod := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--infra-name", infraName}) + pod.WaitWithDefaultTimeout() + Expect(pod).Should(Exit(0)) + + infraName2 := "infra2" + rename := podmanTest.Podman([]string{"rename", infraName, infraName2}) + rename.WaitWithDefaultTimeout() + Expect(rename).Should(Exit(0)) + + remove := podmanTest.Podman([]string{"pod", "rm", "-f", podName}) + remove.WaitWithDefaultTimeout() + Expect(remove).Should(Exit(0)) + + create := podmanTest.Podman([]string{"create", "--name", infraName2, ALPINE, "top"}) + create.WaitWithDefaultTimeout() + Expect(create).Should(Exit(0)) + + create2 := podmanTest.Podman([]string{"create", "--name", infraName, ALPINE, "top"}) + create2.WaitWithDefaultTimeout() + Expect(create2).Should(Exit(0)) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/restart_test.go libpod-3.4.4+ds1/test/e2e/restart_test.go --- libpod-3.2.1+ds1/test/e2e/restart_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/restart_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman restart", func() { @@ -36,7 +37,7 @@ It("Podman restart bogus container", func() { session := podmanTest.Podman([]string{"start", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("Podman restart stopped container by name", func() { @@ -47,7 +48,7 @@ session := podmanTest.Podman([]string{"restart", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) @@ -56,18 +57,18 @@ It("Podman restart stopped container by ID", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", cid}) startTime.WaitWithDefaultTimeout() startSession := podmanTest.Podman([]string{"start", cid}) startSession.WaitWithDefaultTimeout() - Expect(startSession.ExitCode()).To(Equal(0)) + Expect(startSession).Should(Exit(0)) session2 := podmanTest.Podman([]string{"restart", cid}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", cid}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) @@ -82,7 +83,7 @@ session := podmanTest.Podman([]string{"restart", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) @@ -97,7 +98,7 @@ session := podmanTest.Podman([]string{"container", "restart", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"container", "inspect", "--format='{{.State.StartedAt}}'", "test1"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString()))) @@ -114,7 +115,7 @@ session := podmanTest.Podman([]string{"restart", "test1", "test2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToStringArray()[0]).To(Not(Equal(startTime.OutputToStringArray()[0]))) @@ -137,7 +138,7 @@ } session := podmanTest.Podman([]string{"restart", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0])) @@ -147,11 +148,11 @@ It("Podman restart non-stop container with short timeout", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test1", "--env", "STOPSIGNAL=SIGKILL", ALPINE, "sleep", "999"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) startTime := time.Now() session = podmanTest.Podman([]string{"restart", "-t", "2", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) timeSince := time.Since(startTime) Expect(timeSince < 10*time.Second).To(BeTrue()) Expect(timeSince > 2*time.Second).To(BeTrue()) @@ -163,14 +164,14 @@ test2 := podmanTest.RunTopContainer("test2") test2.WaitWithDefaultTimeout() - Expect(test2.ExitCode()).To(Equal(0)) + Expect(test2).Should(Exit(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) startTime.WaitWithDefaultTimeout() session := podmanTest.Podman([]string{"restart", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToStringArray()[0]).To(Not(Equal(startTime.OutputToStringArray()[0]))) @@ -183,14 +184,14 @@ test2 := podmanTest.RunTopContainer("test2") test2.WaitWithDefaultTimeout() - Expect(test2.ExitCode()).To(Equal(0)) + Expect(test2).Should(Exit(0)) startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) startTime.WaitWithDefaultTimeout() session := podmanTest.Podman([]string{"restart", "-a", "--running"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"}) restartTime.WaitWithDefaultTimeout() Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0])) @@ -205,22 +206,22 @@ session := podmanTest.RunTopContainerInPod("host-restart-test", "foobar99") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) testCmd := []string{"exec", "host-restart-test", "sh", "-c", "wc -l < /etc/hosts"} // before restart beforeRestart := podmanTest.Podman(testCmd) beforeRestart.WaitWithDefaultTimeout() - Expect(beforeRestart.ExitCode()).To(Equal(0)) + Expect(beforeRestart).Should(Exit(0)) session = podmanTest.Podman([]string{"restart", "host-restart-test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) afterRestart := podmanTest.Podman(testCmd) afterRestart.WaitWithDefaultTimeout() - Expect(afterRestart.ExitCode()).To(Equal(0)) + Expect(afterRestart).Should(Exit(0)) // line count should be equal Expect(beforeRestart.OutputToString()).To(Equal(afterRestart.OutputToString())) @@ -229,22 +230,22 @@ It("podman restart --all", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) session = podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) session = podmanTest.Podman([]string{"stop", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) session = podmanTest.Podman([]string{"restart", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/rm_test.go libpod-3.4.4+ds1/test/e2e/rm_test.go --- libpod-3.2.1+ds1/test/e2e/rm_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/rm_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman rm", func() { @@ -39,93 +40,93 @@ result := podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman rm refuse to remove a running container", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(2)) + Expect(result).Should(Exit(2)) }) It("podman rm created container", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman container rm created container", func() { session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"container", "rm", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman rm running container with -f", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() result := podmanTest.Podman([]string{"rm", "-f", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman rm all containers", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"rm", "-a"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman rm all containers with one running and short options", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"rm", "-af"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman rm the latest container", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, ec, cid := podmanTest.RunLsContainer("test1") Expect(ec).To(Equal(0)) @@ -136,7 +137,7 @@ } result := podmanTest.Podman([]string{"rm", latest}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToString() Expect(output).To(ContainSubstring(cid)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) @@ -153,13 +154,13 @@ session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToStringArray()[0] Expect(podmanTest.NumberOfContainers()).To(Equal(1)) result := podmanTest.Podman([]string{"rm", "--cidfile", tmpFile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToString() Expect(output).To(ContainSubstring(cid)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) @@ -176,19 +177,19 @@ session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile1, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid1 := session.OutputToStringArray()[0] Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session = podmanTest.Podman([]string{"create", "--cidfile", tmpFile2, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid2 := session.OutputToStringArray()[0] Expect(podmanTest.NumberOfContainers()).To(Equal(2)) result := podmanTest.Podman([]string{"rm", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToString() Expect(output).To(ContainSubstring(cid1)) Expect(output).To(ContainSubstring(cid2)) @@ -200,86 +201,86 @@ result := podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) result = podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--all"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) result = podmanTest.Podman([]string{"rm", "--cidfile", "foobar", "--all", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) result = podmanTest.Podman([]string{"rm", "--latest", "--all"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman rm --all", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session = podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(2)) session = podmanTest.Podman([]string{"rm", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) }) It("podman rm --ignore", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToStringArray()[0] Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session = podmanTest.Podman([]string{"rm", "bogus", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) session = podmanTest.Podman([]string{"rm", "--ignore", "bogus", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainers()).To(Equal(0)) }) It("podman rm bogus container", func() { session := podmanTest.Podman([]string{"rm", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman rm bogus container and a running container", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rm", "bogus", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) session = podmanTest.Podman([]string{"rm", "test1", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman rm --ignore bogus container and a running container", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rm", "--force", "--ignore", "bogus", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rm", "--ignore", "test1", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_apparmor_test.go libpod-3.4.4+ds1/test/e2e/run_apparmor_test.go --- libpod-3.2.1+ds1/test/e2e/run_apparmor_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_apparmor_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) // wip @@ -54,7 +55,7 @@ skipIfAppArmorDisabled() session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set @@ -66,7 +67,7 @@ skipIfAppArmorDisabled() session := podmanTest.Podman([]string{"create", "--privileged", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set @@ -78,7 +79,7 @@ skipIfAppArmorDisabled() session := podmanTest.Podman([]string{"create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile), "--privileged", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set @@ -105,11 +106,11 @@ aaFile := filepath.Join(os.TempDir(), "aaFile") Expect(ioutil.WriteFile(aaFile, []byte(aaProfile), 0755)).To(BeNil()) parse := SystemExec("apparmor_parser", []string{"-Kr", aaFile}) - Expect(parse.ExitCode()).To(Equal(0)) + Expect(parse).Should(Exit(0)) session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=aa-test-profile", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set @@ -121,14 +122,14 @@ skipIfAppArmorDisabled() session := podmanTest.Podman([]string{"run", "--security-opt", "apparmor=invalid", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) }) It("podman run apparmor unconfined", func() { skipIfAppArmorDisabled() session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=unconfined", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set @@ -141,7 +142,7 @@ // Should fail if user specifies apparmor on disabled system session := podmanTest.Podman([]string{"create", "--security-opt", fmt.Sprintf("apparmor=%s", apparmor.Profile), ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) }) It("podman run apparmor disabled no default", func() { @@ -149,7 +150,7 @@ // Should succeed if user specifies apparmor on disabled system session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set @@ -162,7 +163,7 @@ session := podmanTest.Podman([]string{"create", "--security-opt", "apparmor=unconfined", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() // Verify that apparmor.Profile is being set diff -Nru libpod-3.2.1+ds1/test/e2e/run_cgroup_parent_test.go libpod-3.4.4+ds1/test/e2e/run_cgroup_parent_test.go --- libpod-3.2.1+ds1/test/e2e/run_cgroup_parent_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_cgroup_parent_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -2,6 +2,7 @@ import ( "fmt" + "io/ioutil" "os" "path/filepath" "strings" @@ -9,8 +10,11 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) +const cgroupRoot = "/sys/fs/cgroup" + var _ = Describe("Podman run with --cgroup-parent", func() { var ( tempdir string @@ -43,7 +47,7 @@ cgroup := "/zzz" run := podmanTest.Podman([]string{"run", "--cgroupns=host", "--cgroup-parent", cgroup, fedoraMinimal, "cat", "/proc/self/cgroup"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) ok, _ := run.GrepString(cgroup) Expect(ok).To(BeTrue()) }) @@ -56,7 +60,7 @@ } run := podmanTest.Podman([]string{"run", "--cgroupns=host", fedoraMinimal, "cat", "/proc/self/cgroup"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) ok, _ := run.GrepString(cgroup) Expect(ok).To(BeTrue()) }) @@ -72,25 +76,34 @@ run := podmanTest.Podman([]string{"run", "-d", "--cgroupns=host", fedoraMinimal, "sleep", "100"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) cid := run.OutputToString() - exec := podmanTest.Podman([]string{"exec", cid, "cat", "/proc/self/cgroup"}) + exec := podmanTest.Podman([]string{"exec", cid, "cat", "/proc/1/cgroup"}) exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(Equal(0)) + Expect(exec).Should(Exit(0)) - cgroup := filepath.Dir(strings.TrimRight(strings.Replace(exec.OutputToString(), "0::", "", -1), "\n")) + containerCgroup := strings.TrimRight(strings.Replace(exec.OutputToString(), "0::", "", -1), "\n") - run = podmanTest.Podman([]string{"--cgroup-manager=cgroupfs", "run", "-d", fmt.Sprintf("--cgroup-parent=%s", cgroup), fedoraMinimal, "sleep", "100"}) - run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + // Move the container process to a sub cgroup + content, err := ioutil.ReadFile(filepath.Join(cgroupRoot, containerCgroup, "cgroup.procs")) + Expect(err).To(BeNil()) + oldSubCgroupPath := filepath.Join(filepath.Join(cgroupRoot, containerCgroup, "old-container")) + err = os.MkdirAll(oldSubCgroupPath, 0755) + Expect(err).To(BeNil()) + err = ioutil.WriteFile(filepath.Join(oldSubCgroupPath, "cgroup.procs"), content, 0644) + Expect(err).To(BeNil()) + + newCgroup := fmt.Sprintf("%s/new-container", containerCgroup) + err = os.MkdirAll(filepath.Join(cgroupRoot, newCgroup), 0755) + Expect(err).To(BeNil()) - exec = podmanTest.Podman([]string{"exec", cid, "cat", "/proc/self/cgroup"}) - exec.WaitWithDefaultTimeout() - Expect(exec.ExitCode()).To(Equal(0)) - cgroupEffective := filepath.Dir(strings.TrimRight(strings.Replace(exec.OutputToString(), "0::", "", -1), "\n")) + run = podmanTest.Podman([]string{"--cgroup-manager=cgroupfs", "run", "--rm", "--cgroupns=host", fmt.Sprintf("--cgroup-parent=%s", newCgroup), fedoraMinimal, "cat", "/proc/self/cgroup"}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + cgroupEffective := strings.TrimRight(strings.Replace(run.OutputToString(), "0::", "", -1), "\n") - Expect(cgroupEffective).To(Equal(cgroup)) + Expect(newCgroup).To(Equal(filepath.Dir(cgroupEffective))) }) Specify("valid --cgroup-parent using slice", func() { @@ -100,7 +113,7 @@ cgroup := "aaaa.slice" run := podmanTest.Podman([]string{"run", "--cgroupns=host", "--cgroup-parent", cgroup, fedoraMinimal, "cat", "/proc/1/cgroup"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) ok, _ := run.GrepString(cgroup) Expect(ok).To(BeTrue()) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_cleanup_test.go libpod-3.4.4+ds1/test/e2e/run_cleanup_test.go --- libpod-3.2.1+ds1/test/e2e/run_cleanup_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_cleanup_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run exit", func() { @@ -39,33 +40,33 @@ result := podmanTest.Podman([]string{"run", "-dt", ALPINE, "top"}) result.WaitWithDefaultTimeout() cid := result.OutputToString() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) mount := SystemExec("mount", nil) - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).To(ContainSubstring(cid)) pmount := podmanTest.Podman([]string{"mount", "--notruncate"}) pmount.WaitWithDefaultTimeout() - Expect(pmount.ExitCode()).To(Equal(0)) + Expect(pmount).Should(Exit(0)) Expect(pmount.OutputToString()).To(ContainSubstring(cid)) stop := podmanTest.Podman([]string{"stop", cid}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) // We have to force cleanup so the unmount happens podmanCleanupSession := podmanTest.Podman([]string{"container", "cleanup", cid}) podmanCleanupSession.WaitWithDefaultTimeout() - Expect(podmanCleanupSession.ExitCode()).To(Equal(0)) + Expect(podmanCleanupSession).Should(Exit(0)) mount = SystemExec("mount", nil) - Expect(mount.ExitCode()).To(Equal(0)) + Expect(mount).Should(Exit(0)) Expect(mount.OutputToString()).NotTo(ContainSubstring(cid)) pmount = podmanTest.Podman([]string{"mount", "--notruncate"}) pmount.WaitWithDefaultTimeout() - Expect(pmount.ExitCode()).To(Equal(0)) + Expect(pmount).Should(Exit(0)) Expect(pmount.OutputToString()).NotTo(ContainSubstring(cid)) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_cpu_test.go libpod-3.4.4+ds1/test/e2e/run_cpu_test.go --- libpod-3.2.1+ds1/test/e2e/run_cpu_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_cpu_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run cpu", func() { @@ -50,7 +51,7 @@ result = podmanTest.Podman([]string{"run", "--rm", "--cpu-period=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"}) } result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.LineInOutputContains("5000")).To(BeTrue()) }) @@ -63,7 +64,7 @@ result = podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) } result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.LineInOutputContains("5000")).To(BeTrue()) }) @@ -71,17 +72,17 @@ if CGROUPSV2 { result := podmanTest.Podman([]string{"run", "--rm", "--cpu-quota=5000", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpu.max"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("5000 100000")) } else { result := podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_period_us"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("100000")) result = podmanTest.Podman([]string{"run", "--rm", "--cpus=0.5", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.cfs_quota_us"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("50000")) } }) @@ -91,12 +92,12 @@ // [2-262144] is mapped to [1-10000] result := podmanTest.Podman([]string{"run", "--rm", "--cpu-shares=262144", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/$(sed -e 's|0::||' < /proc/self/cgroup)/cpu.weight"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("10000")) } else { result := podmanTest.Podman([]string{"run", "--rm", "--cpu-shares=2", ALPINE, "cat", "/sys/fs/cgroup/cpu/cpu.shares"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("2")) } }) @@ -110,7 +111,7 @@ result = podmanTest.Podman([]string{"run", "--rm", "--cpuset-cpus=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.cpus"}) } result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("0")) }) @@ -123,7 +124,7 @@ result = podmanTest.Podman([]string{"run", "--rm", "--cpuset-mems=0", ALPINE, "cat", "/sys/fs/cgroup/cpuset/cpuset.mems"}) } result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(Equal("0")) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_device_test.go libpod-3.4.4+ds1/test/e2e/run_device_test.go --- libpod-3.2.1+ds1/test/e2e/run_device_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_device_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run device", func() { @@ -40,36 +41,35 @@ }) It("podman run device test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", ALPINE, "ls", "--color=never", "/dev/kmsg"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg", ALPINE, "test", "-c", "/dev/kmsg"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg")) + Expect(session).Should(Exit(0)) }) It("podman run device rename test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + // TODO: Confirm absence of /dev/kmsg in container + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1", ALPINE, "test", "-c", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) + Expect(session).Should(Exit(0)) }) It("podman run device permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:r", ALPINE, "ls", "--color=never", "/dev/kmsg"}) + // TODO: Confirm write-permission failure + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:r", ALPINE, "test", "-r", "/dev/kmsg"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg")) + Expect(session).Should(Exit(0)) }) It("podman run device rename and permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + // TODO: Confirm write-permission failure + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:r", ALPINE, "test", "-r", "/dev/kmsg1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) + Expect(session).Should(Exit(0)) }) It("podman run device rename and bad permission test", func() { - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:rd", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "/dev/kmsg:/dev/kmsg1:rd", ALPINE, "true"}) session.WaitWithDefaultTimeout() - Expect(session).To(ExitWithError()) + Expect(session).Should(Exit(125)) }) It("podman run device host device and container device parameter are directories", func() { @@ -79,21 +79,22 @@ mknod := SystemExec("mknod", []string{"/dev/foodevdir/null", "c", "1", "3"}) mknod.WaitWithDefaultTimeout() - Expect(mknod.ExitCode()).To(Equal(0)) + Expect(mknod).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "-q", "--device", "/dev/foodevdir:/dev/bar", ALPINE, "stat", "-c%t:%T", "/dev/bar/null"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("1:3")) }) It("podman run device host device with --privileged", func() { - if _, err := os.Stat("/dev/kvm"); err != nil { - Skip("/dev/kvm not available") - } - session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "/dev/kvm"}) + session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "test", "-c", "/dev/kmsg"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + // verify --privileged is required + session2 := podmanTest.Podman([]string{"run", ALPINE, "test", "-c", "/dev/kmsg"}) + session2.WaitWithDefaultTimeout() + Expect(session2).Should((Exit(1))) }) It("podman run CDI device test", func() { @@ -108,9 +109,14 @@ err = cmd.Run() Expect(err).To(BeNil()) - session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "myKmsg", ALPINE, "ls", "--color=never", "/dev/kmsg1"}) + session := podmanTest.Podman([]string{"run", "-q", "--security-opt", "label=disable", "--device", "myKmsg", ALPINE, "test", "-c", "/dev/kmsg1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + + It("podman run --gpus noop", func() { + session := podmanTest.Podman([]string{"run", "--gpus", "all", ALPINE, "true"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(Equal("/dev/kmsg1")) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_dns_test.go libpod-3.4.4+ds1/test/e2e/run_dns_test.go --- libpod-3.2.1+ds1/test/e2e/run_dns_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_dns_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run dns", func() { @@ -35,14 +36,14 @@ It("podman run add search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=foobar.com", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("search foobar.com") }) It("podman run remove all search domain", func() { session := podmanTest.Podman([]string{"run", "--dns-search=.", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("search")).To(BeFalse()) }) @@ -55,14 +56,14 @@ It("podman run add dns server", func() { session := podmanTest.Podman([]string{"run", "--dns=1.2.3.4", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("server 1.2.3.4") }) It("podman run add dns option", func() { session := podmanTest.Podman([]string{"run", "--dns-opt=debug", ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("options debug") }) @@ -75,7 +76,7 @@ It("podman run add host", func() { session := podmanTest.Podman([]string{"run", "--add-host=foobar:1.1.1.1", "--add-host=foobaz:2001:db8::68", ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session.LineInOutputStartsWith("1.1.1.1 foobar") session.LineInOutputStartsWith("2001:db8::68 foobaz") }) @@ -83,19 +84,19 @@ It("podman run add hostname", func() { session := podmanTest.Podman([]string{"run", "--hostname=foobar", ALPINE, "cat", "/etc/hostname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("foobar")) session = podmanTest.Podman([]string{"run", "--hostname=foobar", ALPINE, "hostname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("foobar")) }) It("podman run add hostname sets /etc/hosts", func() { session := podmanTest.Podman([]string{"run", "-t", "-i", "--hostname=foobar", ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("foobar")).To(BeTrue()) }) @@ -114,6 +115,6 @@ session = podmanTest.Podman([]string{"run", "--dns=1.2.3.4", "--network", "host", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To((Equal(0))) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_entrypoint_test.go libpod-3.4.4+ds1/test/e2e/run_entrypoint_test.go --- libpod-3.2.1+ds1/test/e2e/run_entrypoint_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_entrypoint_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run entrypoint", func() { @@ -40,7 +41,7 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman run entrypoint == [\"\"]", func() { @@ -51,7 +52,7 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "echo", "hello"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("hello")) }) @@ -62,7 +63,7 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) }) @@ -74,7 +75,7 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(4)) }) @@ -86,7 +87,7 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "-i"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(5)) }) @@ -97,12 +98,11 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "foobar.com/entrypoint:latest", "-i"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(5)) }) It("podman run user entrypoint overrides image entrypoint and image cmd", func() { - SkipIfRemote("FIXME: podman-remote not handling passing --entrypoint=\"\" flag correctly") dockerfile := `FROM quay.io/libpod/alpine:latest CMD ["-i"] ENTRYPOINT ["grep", "Alpine", "/etc/os-release"] @@ -110,12 +110,12 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("Linux")).To(BeTrue()) session = podmanTest.Podman([]string{"run", "--entrypoint", "", "foobar.com/entrypoint:latest", "uname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("Linux")).To(BeTrue()) }) @@ -127,7 +127,7 @@ podmanTest.BuildImage(dockerfile, "foobar.com/entrypoint:latest", "false") session := podmanTest.Podman([]string{"run", "--entrypoint=uname", "foobar.com/entrypoint:latest", "-r"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputStartsWith("Linux")).To(BeFalse()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_env_test.go libpod-3.4.4+ds1/test/e2e/run_env_test.go --- libpod-3.2.1+ds1/test/e2e/run_env_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_env_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run", func() { @@ -35,38 +36,38 @@ It("podman run environment test", func() { session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "HOME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("/root") Expect(match).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "--rm", "--user", "2", ALPINE, "printenv", "HOME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("/sbin") Expect(match).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "--rm", "--env", "HOME=/foo", ALPINE, "printenv", "HOME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("/foo") Expect(match).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "--rm", "--env", "FOO=BAR,BAZ", ALPINE, "printenv", "FOO"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("BAR,BAZ") Expect(match).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "--rm", "--env", "PATH=/bin", ALPINE, "printenv", "PATH"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("/bin") Expect(match).Should(BeTrue()) os.Setenv("FOO", "BAR") session = podmanTest.Podman([]string{"run", "--rm", "--env", "FOO", ALPINE, "printenv", "FOO"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("BAR") Expect(match).Should(BeTrue()) os.Unsetenv("FOO") @@ -74,17 +75,17 @@ session = podmanTest.Podman([]string{"run", "--rm", "--env", "FOO", ALPINE, "printenv", "FOO"}) session.WaitWithDefaultTimeout() Expect(len(session.OutputToString())).To(Equal(0)) - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // This currently does not work // Re-enable when hostname is an env variable session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "printenv"}) session.Wait(10) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("HOSTNAME") Expect(match).Should(BeTrue()) }) @@ -95,17 +96,17 @@ session.WaitWithDefaultTimeout() if IsRemote() { // podman-remote does not support --env-host - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("unknown flag: --env-host")) return } - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("BAR") Expect(match).Should(BeTrue()) session = podmanTest.PodmanAsUser([]string{"run", "--rm", "--env", "FOO=BAR1", "--env-host", ALPINE, "/bin/printenv", "FOO"}, 0, 0, "", env) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("BAR1") Expect(match).Should(BeTrue()) os.Unsetenv("FOO") @@ -119,25 +120,25 @@ } session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "http_proxy"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("1.2.3.4") Expect(match).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "--http-proxy=false", ALPINE, "printenv", "http_proxy"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) Expect(session.OutputToString()).To(Equal("")) session = podmanTest.Podman([]string{"run", "--env", "http_proxy=5.6.7.8", ALPINE, "printenv", "http_proxy"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("5.6.7.8") Expect(match).Should(BeTrue()) os.Unsetenv("http_proxy") session = podmanTest.Podman([]string{"run", "--http-proxy=false", "--env", "http_proxy=5.6.7.8", ALPINE, "printenv", "http_proxy"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ = session.GrepString("5.6.7.8") Expect(match).Should(BeTrue()) os.Unsetenv("http_proxy") diff -Nru libpod-3.2.1+ds1/test/e2e/run_exit_test.go libpod-3.4.4+ds1/test/e2e/run_exit_test.go --- libpod-3.2.1+ds1/test/e2e/run_exit_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_exit_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,12 +1,14 @@ package integration import ( + "fmt" "os" "github.com/containers/podman/v3/libpod/define" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run exit", func() { @@ -36,34 +38,36 @@ It("podman run exit define.ExecErrorCodeGeneric", func() { result := podmanTest.Podman([]string{"run", "--foobar", ALPINE, "ls", "$tmp"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(define.ExecErrorCodeGeneric)) + Expect(result).Should(Exit(define.ExecErrorCodeGeneric)) }) It("podman run exit ExecErrorCodeCannotInvoke", func() { result := podmanTest.Podman([]string{"run", ALPINE, "/etc"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(define.ExecErrorCodeCannotInvoke)) + Expect(result).Should(Exit(define.ExecErrorCodeCannotInvoke)) }) It("podman run exit ExecErrorCodeNotFound", func() { result := podmanTest.Podman([]string{"run", ALPINE, "foobar"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Not(Equal(define.ExecErrorCodeGeneric))) - // TODO This is failing we believe because of a race condition - // Between conmon and podman closing the socket early. - // Test with the following, once the race condition is solved - // Expect(result.ExitCode()).To(Equal(define.ExecErrorCodeNotFound)) + Expect(result).Should(Exit(define.ExecErrorCodeNotFound)) }) It("podman run exit 0", func() { result := podmanTest.Podman([]string{"run", ALPINE, "ls"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman run exit 50", func() { result := podmanTest.Podman([]string{"run", ALPINE, "sh", "-c", "exit 50"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(50)) + Expect(result).Should(Exit(50)) + }) + + It("podman run exit 125", func() { + result := podmanTest.Podman([]string{"run", ALPINE, "sh", "-c", fmt.Sprintf("exit %d", define.ExecErrorCodeGeneric)}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(define.ExecErrorCodeGeneric)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/runlabel_test.go libpod-3.4.4+ds1/test/e2e/runlabel_test.go --- libpod-3.2.1+ds1/test/e2e/runlabel_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/runlabel_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var PodmanDockerfile = fmt.Sprintf(` @@ -21,6 +22,10 @@ FROM %s LABEL RUN echo \$GLOBAL_OPTS`, ALPINE) +var PodmanRunlabelNameDockerfile = fmt.Sprintf(` +FROM %s +LABEL RUN podman run --name NAME IMAGE`, ALPINE) + var _ = Describe("podman container runlabel", func() { var ( tempdir string @@ -52,11 +57,11 @@ result := podmanTest.Podman([]string{"container", "runlabel", "RUN", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"rmi", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman container runlabel (ls -la)", func() { @@ -65,11 +70,11 @@ result := podmanTest.Podman([]string{"container", "runlabel", "RUN", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) result = podmanTest.Podman([]string{"rmi", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman container runlabel --display", func() { image := "podman-runlabel-test:ls" @@ -77,12 +82,12 @@ result := podmanTest.Podman([]string{"container", "runlabel", "--display", "RUN", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(podmanTest.PodmanBinary + " -la")) result = podmanTest.Podman([]string{"rmi", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("podman container runlabel bogus label should result in non-zero exit code", func() { result := podmanTest.Podman([]string{"container", "runlabel", "RUN", ALPINE}) @@ -105,13 +110,13 @@ podmanTest.BuildImage(GlobalDockerfile, image, "false") result := podmanTest.Podman([]string{"--syslog", "--log-level", "debug", "container", "runlabel", "RUN", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring("--syslog true")) Expect(result.OutputToString()).To(ContainSubstring("--log-level debug")) result = podmanTest.Podman([]string{"rmi", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) It("runlabel should fail with nonexistent authfile", func() { @@ -121,10 +126,24 @@ // runlabel should fail with nonexistent authfile result := podmanTest.Podman([]string{"container", "runlabel", "--authfile", "/tmp/nonexistent", "RUN", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Not(Equal(0))) + Expect(result).To(ExitWithError()) + + result = podmanTest.Podman([]string{"rmi", image}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + }) + + It("podman container runlabel name removes tag from image", func() { + image := "podman-runlabel-name:sometag" + podmanTest.BuildImage(PodmanRunlabelNameDockerfile, image, "false") + + result := podmanTest.Podman([]string{"container", "runlabel", "--display", "RUN", image}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToString()).To(Equal("command: " + podmanTest.PodmanBinary + " run --name podman-runlabel-name localhost/" + image)) result = podmanTest.Podman([]string{"rmi", image}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_memory_test.go libpod-3.4.4+ds1/test/e2e/run_memory_test.go --- libpod-3.2.1+ds1/test/e2e/run_memory_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_memory_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run memory", func() { @@ -44,7 +45,7 @@ session = podmanTest.Podman([]string{"run", "--memory=40m", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.limit_in_bytes"}) } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("41943040")) }) @@ -62,7 +63,7 @@ } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("41943040")) }) @@ -70,7 +71,7 @@ SkipIfCgroupV2("memory-swappiness not supported on cgroupV2") session := podmanTest.Podman([]string{"run", "--memory-swappiness=15", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.swappiness"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("15")) }) @@ -88,7 +89,7 @@ } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("41943040")) }) @@ -103,7 +104,7 @@ session = podmanTest.Podman([]string{"run", "--cgroupns=private", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"}) } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(err).To(BeNil()) if CGROUPSV2 { diff -Nru libpod-3.2.1+ds1/test/e2e/run_networking_test.go libpod-3.4.4+ds1/test/e2e/run_networking_test.go --- libpod-3.2.1+ds1/test/e2e/run_networking_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_networking_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ "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" ) @@ -40,47 +41,47 @@ It("podman run network connection with default bridge", func() { session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "wget", "www.podman.io"}) session.Wait(90) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run network connection with host", func() { session := podmanTest.Podman([]string{"run", "-dt", "--network", "host", ALPINE, "wget", "www.podman.io"}) session.Wait(90) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run network connection with default", func() { session := podmanTest.Podman([]string{"run", "--network", "default", ALPINE, "wget", "www.podman.io"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run network connection with none", func() { session := podmanTest.Podman([]string{"run", "--network", "none", ALPINE, "wget", "www.podman.io"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) Expect(session.ErrorToString()).To(ContainSubstring("wget: bad address 'www.podman.io'")) }) It("podman run network connection with private", func() { session := podmanTest.Podman([]string{"run", "--network", "private", ALPINE, "wget", "www.podman.io"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run network connection with loopback", func() { session := podmanTest.Podman([]string{"run", "--network", "host", ALPINE, "wget", "www.podman.io"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run network expose port 222", func() { SkipIfRootless("iptables is not supported for rootless users") session := podmanTest.Podman([]string{"run", "-dt", "--expose", "222-223", "-P", ALPINE, "/bin/sh"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := SystemExec("iptables", []string{"-t", "nat", "-L"}) - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) Expect(results.OutputToString()).To(ContainSubstring("222")) Expect(results.OutputToString()).To(ContainSubstring("223")) }) @@ -97,9 +98,9 @@ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 80-82 -p 8080:8080", func() { + It("podman run -p 80-82 -p 8090:8090", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "80-82", "-p", "8080:8080", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "80-82", "-p", "8090:8090", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) @@ -113,14 +114,14 @@ Expect(len(inspectOut[0].NetworkSettings.Ports["82/tcp"])).To(Equal(1)) Expect(inspectOut[0].NetworkSettings.Ports["82/tcp"][0].HostPort).To(Not(Equal("82"))) Expect(inspectOut[0].NetworkSettings.Ports["82/tcp"][0].HostIP).To(Equal("")) - Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Equal("8080")) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8090/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8090/tcp"][0].HostPort).To(Equal("8090")) + Expect(inspectOut[0].NetworkSettings.Ports["8090/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 80-81 -p 8080-8081", func() { + It("podman run -p 80-81 -p 8180-8181", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "80-81", "-p", "8080-8081", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "80-81", "-p", "8180-8181", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) @@ -131,17 +132,17 @@ Expect(len(inspectOut[0].NetworkSettings.Ports["81/tcp"])).To(Equal(1)) Expect(inspectOut[0].NetworkSettings.Ports["81/tcp"][0].HostPort).To(Not(Equal("81"))) Expect(inspectOut[0].NetworkSettings.Ports["81/tcp"][0].HostIP).To(Equal("")) - Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Not(Equal("8080"))) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) - Expect(len(inspectOut[0].NetworkSettings.Ports["8081/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostPort).To(Not(Equal("8081"))) - Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8180/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8180/tcp"][0].HostPort).To(Not(Equal("8180"))) + Expect(inspectOut[0].NetworkSettings.Ports["8180/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8181/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8181/tcp"][0].HostPort).To(Not(Equal("8181"))) + Expect(inspectOut[0].NetworkSettings.Ports["8181/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 80 -p 8080-8082:8080-8082", func() { + It("podman run -p 80 -p 8280-8282:8280-8282", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "80", "-p", "8080-8082:8080-8082", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "80", "-p", "8280-8282:8280-8282", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) @@ -149,40 +150,40 @@ Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Not(Equal("80"))) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) - Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Equal("8080")) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) - Expect(len(inspectOut[0].NetworkSettings.Ports["8081/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostPort).To(Equal("8081")) - Expect(inspectOut[0].NetworkSettings.Ports["8081/tcp"][0].HostIP).To(Equal("")) - Expect(len(inspectOut[0].NetworkSettings.Ports["8082/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8082/tcp"][0].HostPort).To(Equal("8082")) - Expect(inspectOut[0].NetworkSettings.Ports["8082/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8280/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8280/tcp"][0].HostPort).To(Equal("8280")) + Expect(inspectOut[0].NetworkSettings.Ports["8280/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8281/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8281/tcp"][0].HostPort).To(Equal("8281")) + Expect(inspectOut[0].NetworkSettings.Ports["8281/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8282/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8282/tcp"][0].HostPort).To(Equal("8282")) + Expect(inspectOut[0].NetworkSettings.Ports["8282/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 8080:80", func() { + It("podman run -p 8380:80", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "8380:80", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8380")) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 8080:80/TCP", func() { + It("podman run -p 8480:80/TCP", func() { name := "testctr" // "TCP" in upper characters - session := podmanTest.Podman([]string{"create", "-t", "-p", "8080:80/TCP", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "8480:80/TCP", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) // "tcp" in lower characters Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8480")) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) @@ -198,51 +199,51 @@ Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostIP).To(Equal("")) }) - It("podman run -p 127.0.0.1:8080:80", func() { + It("podman run -p 127.0.0.1:8580:80", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1:8080:80", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1:8580:80", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8580")) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("127.0.0.1")) }) - It("podman run -p 127.0.0.1:8080:80/udp", func() { + It("podman run -p 127.0.0.1:8680:80/udp", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1:8080:80/udp", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1:8680:80/udp", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports["80/udp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostPort).To(Equal("8680")) Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostIP).To(Equal("127.0.0.1")) }) - It("podman run -p [::1]:8080:80/udp", func() { + It("podman run -p [::1]:8780:80/udp", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "[::1]:8080:80/udp", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "[::1]:8780:80/udp", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports["80/udp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostPort).To(Equal("8780")) Expect(inspectOut[0].NetworkSettings.Ports["80/udp"][0].HostIP).To(Equal("::1")) }) - It("podman run -p [::1]:8080:80/tcp", func() { + It("podman run -p [::1]:8880:80/tcp", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "[::1]:8080:80/tcp", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "[::1]:8880:80/tcp", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8880")) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("::1")) }) @@ -282,33 +283,33 @@ Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 127.0.0.1::8080/udp", func() { + It("podman run -p 127.0.0.1::8980/udp", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1::8080/udp", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1::8980/udp", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) - Expect(len(inspectOut[0].NetworkSettings.Ports["8080/udp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostPort).To(Not(Equal("8080"))) - Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostIP).To(Equal("127.0.0.1")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8980/udp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8980/udp"][0].HostPort).To(Not(Equal("8980"))) + Expect(inspectOut[0].NetworkSettings.Ports["8980/udp"][0].HostIP).To(Equal("127.0.0.1")) }) - It("podman run -p :8080", func() { + It("podman run -p :8181", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", ":8080", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", ":8181", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) - Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Not(Equal("8080"))) - Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal("")) + Expect(len(inspectOut[0].NetworkSettings.Ports["8181/tcp"])).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports["8181/tcp"][0].HostPort).To(Not(Equal("8181"))) + Expect(inspectOut[0].NetworkSettings.Ports["8181/tcp"][0].HostIP).To(Equal("")) }) - It("podman run -p 8080:8080 -p 8081:8080", func() { + It("podman run -p xxx:8080 -p yyy:8080", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "4000:8080", "-p", "8000:8080", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "4444:8080", "-p", "5555:8080", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) @@ -319,18 +320,18 @@ hp2 := inspectOut[0].NetworkSettings.Ports["8080/tcp"][1].HostPort // We can't guarantee order - Expect((hp1 == "4000" && hp2 == "8000") || (hp1 == "8000" && hp2 == "4000")).To(BeTrue()) + Expect((hp1 == "4444" && hp2 == "5555") || (hp1 == "5555" && hp2 == "4444")).To(BeTrue()) }) - It("podman run -p 0.0.0.0:8080:80", func() { + It("podman run -p 0.0.0.0:9280:80", func() { name := "testctr" - session := podmanTest.Podman([]string{"create", "-t", "-p", "0.0.0.0:8080:80", "--name", name, ALPINE, "/bin/sh"}) + session := podmanTest.Podman([]string{"create", "-t", "-p", "0.0.0.0:9280:80", "--name", name, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() inspectOut := podmanTest.InspectContainer(name) Expect(len(inspectOut)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) Expect(len(inspectOut[0].NetworkSettings.Ports["80/tcp"])).To(Equal(1)) - Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("8080")) + Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostPort).To(Equal("9280")) Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal("")) }) @@ -338,70 +339,90 @@ SkipIfRootless("iptables is not supported for rootless users") session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := SystemExec("iptables", []string{"-t", "nat", "-L"}) - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) Expect(results.OutputToString()).To(ContainSubstring("8000")) ncBusy := SystemExec("nc", []string{"-l", "-p", "80"}) Expect(ncBusy).To(ExitWithError()) }) - It("podman run network expose host port 8081 to container port 8000 using rootlesskit port handler", func() { - session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=rootlesskit", "-dt", "-p", "8081:8000", ALPINE, "/bin/sh"}) + It("podman run network expose host port 18081 to container port 8000 using rootlesskit port handler", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=rootlesskit", "-dt", "-p", "18081:8000", ALPINE, "/bin/sh"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) - ncBusy := SystemExec("nc", []string{"-l", "-p", "8081"}) + ncBusy := SystemExec("nc", []string{"-l", "-p", "18081"}) Expect(ncBusy).To(ExitWithError()) }) - It("podman run network expose host port 8082 to container port 8000 using slirp4netns port handler", func() { - session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=slirp4netns", "-dt", "-p", "8082:8000", ALPINE, "/bin/sh"}) + It("podman run slirp4netns verify net.ipv6.conf.default.accept_dad=0", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:enable_ipv6=true", ALPINE, "ip", "addr"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) - ncBusy := SystemExec("nc", []string{"-l", "-p", "8082"}) + Expect(session).Should(Exit(0)) + // check the ipv6 setup id done without delay (https://github.com/containers/podman/issues/11062) + Expect(session.OutputToString()).To(ContainSubstring("inet6 fd00::")) + + const ipv6ConfDefaultAcceptDadSysctl = "/proc/sys/net/ipv6/conf/all/accept_dad" + + cat := SystemExec("cat", []string{ipv6ConfDefaultAcceptDadSysctl}) + cat.Wait(30) + Expect(cat).Should(Exit(0)) + sysctlValue := cat.OutputToString() + + session = podmanTest.Podman([]string{"run", "--network", "slirp4netns:enable_ipv6=true", ALPINE, "cat", ipv6ConfDefaultAcceptDadSysctl}) + session.Wait(30) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(sysctlValue)) + }) + + It("podman run network expose host port 18082 to container port 8000 using slirp4netns port handler", func() { + session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=slirp4netns", "-dt", "-p", "18082:8000", ALPINE, "/bin/sh"}) + session.Wait(30) + Expect(session).Should(Exit(0)) + ncBusy := SystemExec("nc", []string{"-l", "-p", "18082"}) Expect(ncBusy).To(ExitWithError()) }) It("podman run network expose host port 8080 to container port 8000 using invalid port handler", func() { session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:port_handler=invalid", "-dt", "-p", "8080:8000", ALPINE, "/bin/sh"}) session.Wait(30) - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman run slirp4netns network with host loopback", func() { session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:allow_host_loopback=true", ALPINE, "ping", "-c1", "10.0.2.2"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run slirp4netns network with mtu", func() { session := podmanTest.Podman([]string{"run", "--network", "slirp4netns:mtu=9000", ALPINE, "ip", "addr"}) session.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("mtu 9000")) }) It("podman run slirp4netns network with different cidr", func() { slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"}) - Expect(slirp4netnsHelp.ExitCode()).To(Equal(0)) + Expect(slirp4netnsHelp).Should(Exit(0)) networkConfiguration := "slirp4netns:cidr=192.168.0.0/24,allow_host_loopback=true" session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, ALPINE, "ping", "-c1", "192.168.0.2"}) session.Wait(30) if strings.Contains(slirp4netnsHelp.OutputToString(), "cidr") { - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } else { - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("cidr not supported")) } }) It("podman run network bind to 127.0.0.1", func() { slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"}) - Expect(slirp4netnsHelp.ExitCode()).To(Equal(0)) + Expect(slirp4netnsHelp).Should(Exit(0)) networkConfiguration := "slirp4netns:outbound_addr=127.0.0.1,allow_host_loopback=true" if strings.Contains(slirp4netnsHelp.OutputToString(), "outbound-addr") { @@ -410,13 +431,13 @@ session.Wait(30) ncListener.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) - Expect(ncListener.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + Expect(ncListener).Should(Exit(0)) Expect(ncListener.ErrorToString()).To(ContainSubstring("127.0.0.1")) } else { session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", "8083"}) session.Wait(30) - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("outbound_addr not supported")) } }) @@ -426,7 +447,7 @@ Expect(err).To(BeNil()) slirp4netnsHelp := SystemExec("slirp4netns", []string{"--help"}) - Expect(slirp4netnsHelp.ExitCode()).To(Equal(0)) + Expect(slirp4netnsHelp).Should(Exit(0)) networkConfiguration := fmt.Sprintf("slirp4netns:outbound_addr=%s,allow_host_loopback=true", ip.String()) if strings.Contains(slirp4netnsHelp.OutputToString(), "outbound-addr") { @@ -435,13 +456,13 @@ session.Wait(30) ncListener.Wait(30) - Expect(session.ExitCode()).To(Equal(0)) - Expect(ncListener.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + Expect(ncListener).Should(Exit(0)) Expect(ncListener.ErrorToString()).To(ContainSubstring(ip.String())) } else { session := podmanTest.Podman([]string{"run", "--network", networkConfiguration, "-dt", ALPINE, "nc", "-w", "2", "10.0.2.2", "8084"}) session.Wait(30) - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("outbound_addr not supported")) } }) @@ -449,10 +470,10 @@ It("podman run network expose ports in image metadata", func() { session := podmanTest.Podman([]string{"create", "--name", "test", "-t", "-P", nginx}) session.Wait(90) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "test"}) results.Wait(30) - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) Expect(results.OutputToString()).To(ContainSubstring(`"80/tcp":`)) }) @@ -461,11 +482,11 @@ session := podmanTest.Podman([]string{"run", "--name", "test", "-dt", "-p", "80", ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) containerConfig := inspect.InspectContainerToJSON() Expect(containerConfig[0].NetworkSettings.Ports).To(Not(BeNil())) @@ -476,7 +497,7 @@ It("podman run hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString(hostname) Expect(match).Should(BeFalse()) }) @@ -484,21 +505,21 @@ It("podman run --net host hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", "--net", "host", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString(hostname) Expect(match).Should(BeTrue()) }) It("podman run --net host --uts host hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", "--net", "host", "--uts", "host", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString(hostname) Expect(match).Should(BeTrue()) }) It("podman run --uts host hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", "--uts", "host", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString(hostname) Expect(match).Should(BeTrue()) }) @@ -506,7 +527,7 @@ It("podman run --net host --hostname ... hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", "--net", "host", "--hostname", "foobar", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("foobar") Expect(match).Should(BeTrue()) }) @@ -514,7 +535,7 @@ It("podman run --hostname ... hostname test", func() { session := podmanTest.Podman([]string{"run", "--rm", "--hostname", "foobar", ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("foobar") Expect(match).Should(BeTrue()) }) @@ -523,59 +544,59 @@ ctrName := "ctrToJoin" ctr1 := podmanTest.RunTopContainer(ctrName) ctr1.WaitWithDefaultTimeout() - Expect(ctr1.ExitCode()).To(Equal(0)) + Expect(ctr1).Should(Exit(0)) ctr2 := podmanTest.Podman([]string{"run", "-d", "--net=container:" + ctrName, "--uts=container:" + ctrName, ALPINE, "true"}) ctr2.WaitWithDefaultTimeout() - Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2).Should(Exit(0)) }) It("podman run --net container: copies hosts and resolv", func() { ctrName := "ctr1" ctr1 := podmanTest.RunTopContainer(ctrName) ctr1.WaitWithDefaultTimeout() - Expect(ctr1.ExitCode()).To(Equal(0)) + Expect(ctr1).Should(Exit(0)) // Exec in and modify /etc/resolv.conf and /etc/hosts exec1 := podmanTest.Podman([]string{"exec", ctrName, "sh", "-c", "echo nameserver 192.0.2.1 > /etc/resolv.conf"}) exec1.WaitWithDefaultTimeout() - Expect(exec1.ExitCode()).To(Equal(0)) + Expect(exec1).Should(Exit(0)) exec2 := podmanTest.Podman([]string{"exec", ctrName, "sh", "-c", "echo 192.0.2.2 test1 > /etc/hosts"}) exec2.WaitWithDefaultTimeout() - Expect(exec2.ExitCode()).To(Equal(0)) + Expect(exec2).Should(Exit(0)) ctrName2 := "ctr2" ctr2 := podmanTest.Podman([]string{"run", "-d", "--net=container:" + ctrName, "--name", ctrName2, ALPINE, "top"}) ctr2.WaitWithDefaultTimeout() - Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2).Should(Exit(0)) exec3 := podmanTest.Podman([]string{"exec", "-i", ctrName2, "cat", "/etc/resolv.conf"}) exec3.WaitWithDefaultTimeout() - Expect(exec3.ExitCode()).To(Equal(0)) + Expect(exec3).Should(Exit(0)) Expect(exec3.OutputToString()).To(ContainSubstring("nameserver 192.0.2.1")) exec4 := podmanTest.Podman([]string{"exec", "-i", ctrName2, "cat", "/etc/hosts"}) exec4.WaitWithDefaultTimeout() - Expect(exec4.ExitCode()).To(Equal(0)) + Expect(exec4).Should(Exit(0)) Expect(exec4.OutputToString()).To(ContainSubstring("192.0.2.2 test1")) }) It("podman run /etc/hosts contains --hostname", func() { session := podmanTest.Podman([]string{"run", "--rm", "--hostname", "foohostname", ALPINE, "grep", "foohostname", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --uidmap /etc/hosts contains --hostname", func() { SkipIfRootless("uidmap population of cninetworks not supported for rootless users") session := podmanTest.Podman([]string{"run", "--uidmap", "0:100000:1000", "--rm", "--hostname", "foohostname", ALPINE, "grep", "foohostname", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--uidmap", "0:100000:1000", "--rm", "--hostname", "foohostname", "-v", "/etc/hosts:/etc/hosts", ALPINE, "grep", "foohostname", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman run network in user created network namespace", func() { @@ -584,15 +605,15 @@ Skip("Cannot be run within a container.") } addXXX := SystemExec("ip", []string{"netns", "add", "xxx"}) - Expect(addXXX.ExitCode()).To(Equal(0)) + Expect(addXXX).Should(Exit(0)) defer func() { delXXX := SystemExec("ip", []string{"netns", "delete", "xxx"}) - Expect(delXXX.ExitCode()).To(Equal(0)) + Expect(delXXX).Should(Exit(0)) }() session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxx", ALPINE, "wget", "www.podman.io"}) session.Wait(90) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run n user created network namespace with resolv.conf", func() { @@ -601,22 +622,22 @@ Skip("Cannot be run within a container.") } addXXX2 := SystemExec("ip", []string{"netns", "add", "xxx2"}) - Expect(addXXX2.ExitCode()).To(Equal(0)) + Expect(addXXX2).Should(Exit(0)) defer func() { delXXX2 := SystemExec("ip", []string{"netns", "delete", "xxx2"}) - Expect(delXXX2.ExitCode()).To(Equal(0)) + Expect(delXXX2).Should(Exit(0)) }() mdXXX2 := SystemExec("mkdir", []string{"-p", "/etc/netns/xxx2"}) - Expect(mdXXX2.ExitCode()).To(Equal(0)) + Expect(mdXXX2).Should(Exit(0)) defer os.RemoveAll("/etc/netns/xxx2") nsXXX2 := SystemExec("bash", []string{"-c", "echo nameserver 11.11.11.11 > /etc/netns/xxx2/resolv.conf"}) - Expect(nsXXX2.ExitCode()).To(Equal(0)) + Expect(nsXXX2).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--net", "ns:/run/netns/xxx2", ALPINE, "cat", "/etc/resolv.conf"}) session.Wait(90) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("11.11.11.11")) }) @@ -632,12 +653,12 @@ ipAddr := "10.25.30.128" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.30.0/24", netName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(BeZero()) + Expect(create).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) run := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "--net", netName, "--ip", ipAddr, ALPINE, "ip", "addr"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) Expect(run.OutputToString()).To(ContainSubstring(ipAddr)) }) @@ -645,23 +666,23 @@ netName := stringid.GenerateNonCryptoID() create := podmanTest.Podman([]string{"network", "create", netName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(BeZero()) + Expect(create).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) name := "nc-server" - run := podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "-d", "--name", name, "--net", netName, ALPINE, "nc", "-l", "-p", "8080"}) + run := podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "-d", "--name", name, "--net", netName, ALPINE, "nc", "-l", "-p", "9480"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) // NOTE: we force the k8s-file log driver to make sure the // tests are passing inside a container. - run = podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "sh", "-c", fmt.Sprintf("echo podman | nc -w 1 %s.dns.podman 8080", name)}) + run = podmanTest.Podman([]string{"run", "--log-driver", "k8s-file", "--rm", "--net", netName, "--uidmap", "0:1:4096", ALPINE, "sh", "-c", fmt.Sprintf("echo podman | nc -w 1 %s.dns.podman 9480", name)}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) log := podmanTest.Podman([]string{"logs", name}) log.WaitWithDefaultTimeout() - Expect(log.ExitCode()).To(Equal(0)) + Expect(log).Should(Exit(0)) Expect(log.OutputToString()).To(Equal("podman")) }) @@ -671,31 +692,24 @@ podname := "testpod" create := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.40.0/24", netName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(BeZero()) + Expect(create).Should(Exit(0)) defer podmanTest.removeCNINetwork(netName) run := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "--pod", "new:" + podname, "--net", netName, "--ip", ipAddr, ALPINE, "ip", "addr"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) Expect(run.OutputToString()).To(ContainSubstring(ipAddr)) podrm := podmanTest.Podman([]string{"pod", "rm", "-f", podname}) podrm.WaitWithDefaultTimeout() - Expect(podrm.ExitCode()).To(BeZero()) - }) - - It("podman run net=host adds entry to /etc/hosts", func() { - run := podmanTest.Podman([]string{"run", "--net=host", ALPINE, "cat", "/etc/hosts"}) - run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) - Expect(strings.Contains(run.OutputToString(), "127.0.1.1")).To(BeTrue()) + Expect(podrm).Should(Exit(0)) }) It("podman run with --net=host and --hostname sets correct hostname", func() { hostname := "testctr" run := podmanTest.Podman([]string{"run", "--net=host", "--hostname", hostname, ALPINE, "hostname"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) }) @@ -703,7 +717,7 @@ hostname := "testctr" run := podmanTest.Podman([]string{"run", "--net=none", "--hostname", hostname, ALPINE, "hostname"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) }) @@ -711,29 +725,37 @@ hostname := "testctr" run := podmanTest.Podman([]string{"run", "--net=none", "--hostname", hostname, ALPINE, "cat", "/etc/hosts"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) Expect(strings.Contains(run.OutputToString(), hostname)).To(BeTrue()) }) + It("podman run with pod does not add extra 127 entry to /etc/hosts", func() { + pod := "testpod" + hostname := "test-hostname" + run := podmanTest.Podman([]string{"pod", "create", "--hostname", hostname, "--name", pod}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + run = podmanTest.Podman([]string{"run", "--pod", pod, ALPINE, "cat", "/etc/hosts"}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + Expect(run.OutputToString()).ToNot(ContainSubstring("127.0.0.1 %s", hostname)) + }) + ping_test := func(netns string) { hostname := "testctr" run := podmanTest.Podman([]string{"run", netns, "--hostname", hostname, ALPINE, "ping", "-c", "1", hostname}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) run = podmanTest.Podman([]string{"run", netns, "--hostname", hostname, "--name", "test", ALPINE, "ping", "-c", "1", "test"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(BeZero()) + Expect(run).Should(Exit(0)) } It("podman attempt to ping container name and hostname --net=none", func() { ping_test("--net=none") }) - It("podman attempt to ping container name and hostname --net=host", func() { - ping_test("--net=host") - }) - It("podman attempt to ping container name and hostname --net=private", func() { ping_test("--net=private") }) @@ -742,48 +764,61 @@ pod := "testpod" session := podmanTest.Podman([]string{"pod", "create", "--name", pod}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) net := "IntTest" + stringid.GenerateNonCryptoID() session = podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) pod2 := "testpod2" session = podmanTest.Podman([]string{"pod", "create", "--network", net, "--name", pod2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--name", "con1", "--network", net, ALPINE, "nslookup", "con1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--name", "con2", "--pod", pod, "--network", net, ALPINE, "nslookup", "con2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--name", "con3", "--pod", pod2, ALPINE, "nslookup", "con1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) Expect(session.ErrorToString()).To(ContainSubstring("can't resolve 'con1'")) session = podmanTest.Podman([]string{"run", "--name", "con4", "--network", net, ALPINE, "nslookup", pod2 + ".dns.podman"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) }) It("podman run check dnsname adds dns search domain", func() { - Skip("needs dnsname#57") net := "dnsname" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"network", "create", net}) session.WaitWithDefaultTimeout() defer podmanTest.removeCNINetwork(net) - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--network", net, ALPINE, "cat", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("search dns.podman")) }) + + It("Rootless podman run with --net=bridge works and connects to default network", func() { + // This is harmless when run as root, so we'll just let it run. + ctrName := "testctr" + ctr := podmanTest.Podman([]string{"run", "-d", "--net=bridge", "--name", ctrName, ALPINE, "top"}) + ctr.WaitWithDefaultTimeout() + Expect(ctr).Should(Exit(0)) + + inspectOut := podmanTest.InspectContainer(ctrName) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Networks)).To(Equal(1)) + _, ok := inspectOut[0].NetworkSettings.Networks["podman"] + Expect(ok).To(BeTrue()) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_ns_test.go libpod-3.4.4+ds1/test/e2e/run_ns_test.go --- libpod-3.2.1+ds1/test/e2e/run_ns_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_ns_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run ns", func() { @@ -38,12 +39,12 @@ SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") session := podmanTest.Podman([]string{"run", fedoraMinimal, "bash", "-c", "echo $$"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("1")) session = podmanTest.Podman([]string{"run", "--pid=host", fedoraMinimal, "bash", "-c", "echo $$"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(Equal("1"))) session = podmanTest.Podman([]string{"run", "--pid=badpid", fedoraMinimal, "bash", "-c", "echo $$"}) @@ -54,7 +55,7 @@ It("podman run --cgroup private test", func() { session := podmanTest.Podman([]string{"run", "--cgroupns=private", fedoraMinimal, "cat", "/proc/self/cgroup"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).ToNot(ContainSubstring("slice")) @@ -62,41 +63,41 @@ It("podman run ipcns test", func() { setup := SystemExec("ls", []string{"--inode", "-d", "/dev/shm"}) - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) hostShm := setup.OutputToString() session := podmanTest.Podman([]string{"run", "--ipc=host", fedoraMinimal, "ls", "--inode", "-d", "/dev/shm"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(hostShm)) }) It("podman run ipcns ipcmk host test", func() { setup := SystemExec("ipcmk", []string{"-M", "1024"}) - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) output := strings.Split(setup.OutputToString(), " ") ipc := output[len(output)-1] session := podmanTest.Podman([]string{"run", "--ipc=host", fedoraMinimal, "ipcs", "-m", "-i", ipc}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) setup = SystemExec("ipcrm", []string{"-m", ipc}) - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) }) It("podman run ipcns ipcmk container test", func() { setup := podmanTest.Podman([]string{"run", "-d", "--name", "test1", fedoraMinimal, "sleep", "999"}) setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "ipcmk", "-M", "1024"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := strings.Split(session.OutputToString(), " ") ipc := output[len(output)-1] session = podmanTest.Podman([]string{"run", "--ipc=container:test1", fedoraMinimal, "ipcs", "-m", "-i", ipc}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run bad ipc pid test", func() { @@ -108,7 +109,7 @@ It("podman run mounts fresh cgroup", func() { session := podmanTest.Podman([]string{"run", fedoraMinimal, "grep", "cgroup", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).ToNot(ContainSubstring("..")) }) @@ -129,13 +130,13 @@ session := podmanTest.Podman([]string{"run", "--ipc=host", "--pid=host", ALPINE, "ls", "-l", "/proc/self/ns/pid"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fields = strings.Split(session.OutputToString(), " ") ctrPidNS := strings.TrimSuffix(fields[len(fields)-1], "\n") session = podmanTest.Podman([]string{"run", "--ipc=host", "--pid=host", ALPINE, "ls", "-l", "/proc/self/ns/ipc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) fields = strings.Split(session.OutputToString(), " ") ctrIpcNS := strings.TrimSuffix(fields[len(fields)-1], "\n") diff -Nru libpod-3.2.1+ds1/test/e2e/run_passwd_test.go libpod-3.4.4+ds1/test/e2e/run_passwd_test.go --- libpod-3.2.1+ds1/test/e2e/run_passwd_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_passwd_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run passwd", func() { @@ -36,27 +37,27 @@ It("podman run no user specified ", func() { session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("passwd")).To(BeFalse()) }) It("podman run user specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "bin", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("passwd")).To(BeFalse()) }) It("podman run UID specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "2:1", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("passwd")).To(BeFalse()) }) It("podman run UID not specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "20001:1", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("passwd")).To(BeTrue()) }) @@ -68,48 +69,48 @@ podmanTest.BuildImage(dockerfile, imgName, "false") session := podmanTest.Podman([]string{"run", "--rm", imgName, "ls", "/etc/"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("passwd"))) }) It("podman run with no user specified does not change --group specified", func() { session := podmanTest.Podman([]string{"run", "--read-only", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("/etc/group")).To(BeFalse()) }) It("podman run group specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:bin", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("/etc/group")).To(BeFalse()) }) It("podman run non-numeric group not specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:doesnotexist", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman run numeric group specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "root:11", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("/etc/group")).To(BeFalse()) }) It("podman run numeric group not specified in container", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "20001:20001", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("/etc/group")).To(BeTrue()) }) It("podman run numeric user not specified in container modifies group", func() { session := podmanTest.Podman([]string{"run", "--read-only", "-u", "20001", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("/etc/group")).To(BeTrue()) }) @@ -121,7 +122,7 @@ podmanTest.BuildImage(dockerfile, imgName, "false") session := podmanTest.Podman([]string{"run", "--rm", imgName, "ls", "/etc/"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("/etc/group"))) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_privileged_test.go libpod-3.4.4+ds1/test/e2e/run_privileged_test.go --- libpod-3.2.1+ds1/test/e2e/run_privileged_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_privileged_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" "github.com/syndtr/gocapability/capability" ) @@ -61,7 +62,7 @@ It("podman privileged make sure sys is mounted rw", func() { session := podmanTest.Podman([]string{"run", "--privileged", BB, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, lines := session.GrepString("sysfs") Expect(ok).To(BeTrue()) Expect(lines[0]).To(ContainSubstring("sysfs (rw,")) @@ -69,11 +70,11 @@ It("podman privileged CapEff", func() { hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(hostCap.ExitCode()).To(Equal(0)) + Expect(hostCap).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--privileged", BB, "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) @@ -81,11 +82,11 @@ It("podman cap-add CapEff", func() { // Get caps of current process hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(hostCap.ExitCode()).To(Equal(0)) + Expect(hostCap).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--cap-add", "all", BB, "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) @@ -93,11 +94,11 @@ It("podman cap-add CapEff with --user", func() { // Get caps of current process hostCap := SystemExec("awk", []string{"/^CapEff/ { print $2 }", "/proc/self/status"}) - Expect(hostCap.ExitCode()).To(Equal(0)) + Expect(hostCap).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--user=bin", "--cap-add", "all", BB, "awk", "/^CapEff/ { print $2 }", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) containerCapMatchesHost(session.OutputToString(), hostCap.OutputToString()) }) @@ -105,24 +106,24 @@ It("podman cap-drop CapEff", func() { session := podmanTest.Podman([]string{"run", "--cap-drop", "all", BB, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) capEff := strings.Split(session.OutputToString(), " ") Expect("0000000000000000").To(Equal(capEff[1])) }) It("podman privileged should disable seccomp by default", func() { hostSeccomp := SystemExec("grep", []string{"-Ei", "^Seccomp:\\s+0$", "/proc/self/status"}) - Expect(hostSeccomp.ExitCode()).To(Equal(0)) + Expect(hostSeccomp).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "grep", "-Ei", "^Seccomp:\\s+0$", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman non-privileged should have very few devices", func() { session := podmanTest.Podman([]string{"run", "-t", BB, "ls", "-l", "/dev"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(17)) }) @@ -130,7 +131,7 @@ SkipIfRootless("FIXME: This seems to be broken for rootless mode, /dev/ is close to the same") session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "ls", "-l", "/dev"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 20)) }) @@ -149,12 +150,12 @@ session := podmanTest.Podman([]string{"run", BB, "grep", "NoNewPrivs", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) privs := strings.Split(session.OutputToString(), ":") session = podmanTest.Podman([]string{"run", "--security-opt", "no-new-privileges", BB, "grep", "NoNewPrivs", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) noprivs := strings.Split(session.OutputToString(), ":") Expect(privs[1]).To(Not(Equal(noprivs[1]))) diff -Nru libpod-3.2.1+ds1/test/e2e/run_restart_test.go libpod-3.4.4+ds1/test/e2e/run_restart_test.go --- libpod-3.2.1+ds1/test/e2e/run_restart_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_restart_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run restart containers", func() { @@ -35,15 +36,15 @@ It("Podman start after successful run", func() { session := podmanTest.Podman([]string{"run", "--name", "test", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"wait", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"start", "--attach", "test"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) }) It("Podman start after signal kill", func() { @@ -53,10 +54,10 @@ killSession := podmanTest.Podman([]string{"kill", "-s", "9", "test1"}) killSession.WaitWithDefaultTimeout() - Expect(killSession.ExitCode()).To(Equal(0)) + Expect(killSession).Should(Exit(0)) session2 := podmanTest.Podman([]string{"start", "test1"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_seccomp_test.go libpod-3.4.4+ds1/test/e2e/run_seccomp_test.go --- libpod-3.2.1+ds1/test/e2e/run_seccomp_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_seccomp_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run", func() { @@ -35,20 +36,20 @@ It("podman run --seccomp-policy default", func() { session := podmanTest.Podman([]string{"run", "--seccomp-policy", "default", alpineSeccomp, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --seccomp-policy ''", func() { // Empty string is interpreted as "default". session := podmanTest.Podman([]string{"run", "--seccomp-policy", "", alpineSeccomp, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --seccomp-policy invalid", func() { session := podmanTest.Podman([]string{"run", "--seccomp-policy", "invalid", alpineSeccomp, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) }) It("podman run --seccomp-policy image (block all syscalls)", func() { @@ -57,12 +58,12 @@ // TODO: we're getting a "cannot start a container that has // stopped" error which seems surprising. Investigate // why that is so. - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) }) It("podman run --seccomp-policy image (bogus profile)", func() { session := podmanTest.Podman([]string{"run", "--seccomp-policy", "image", alpineBogusSeccomp, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_security_labels_test.go libpod-3.4.4+ds1/test/e2e/run_security_labels_test.go --- libpod-3.2.1+ds1/test/e2e/run_security_labels_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_security_labels_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman generate kube", func() { @@ -37,11 +38,11 @@ It("podman security labels", func() { test1 := podmanTest.Podman([]string{"create", "--label", "io.containers.capabilities=setuid,setgid", "--name", "test1", "alpine", "echo", "test1"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(BeZero()) + Expect(test1).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") @@ -51,11 +52,11 @@ It("podman bad security labels", func() { test1 := podmanTest.Podman([]string{"create", "--label", "io.containers.capabilities=sys_admin", "--name", "test1", "alpine", "echo", "test1"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(BeZero()) + Expect(test1).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") @@ -65,11 +66,11 @@ It("podman --cap-add sys_admin security labels", func() { test1 := podmanTest.Podman([]string{"create", "--cap-add", "SYS_ADMIN", "--label", "io.containers.capabilities=sys_admin", "--name", "test1", "alpine", "echo", "test1"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(BeZero()) + Expect(test1).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") @@ -79,11 +80,11 @@ It("podman --cap-drop all sys_admin security labels", func() { test1 := podmanTest.Podman([]string{"create", "--cap-drop", "all", "--label", "io.containers.capabilities=sys_admin", "--name", "test1", "alpine", "echo", "test1"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(BeZero()) + Expect(test1).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") @@ -93,19 +94,19 @@ It("podman security labels from image", func() { test1 := podmanTest.Podman([]string{"create", "--name", "test1", "alpine", "echo", "test1"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(BeZero()) + Expect(test1).Should(Exit(0)) commit := podmanTest.Podman([]string{"commit", "-c", "label=io.containers.capabilities=sys_chroot,setuid", "test1", "image1"}) commit.WaitWithDefaultTimeout() - Expect(commit.ExitCode()).To(BeZero()) + Expect(commit).Should(Exit(0)) image1 := podmanTest.Podman([]string{"create", "--name", "test2", "image1", "echo", "test1"}) image1.WaitWithDefaultTimeout() - Expect(image1.ExitCode()).To(BeZero()) + Expect(image1).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test2"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") @@ -116,11 +117,11 @@ It("podman --privileged security labels", func() { pull := podmanTest.Podman([]string{"create", "--privileged", "--label", "io.containers.capabilities=setuid,setgid", "--name", "test1", "alpine", "echo", "test"}) pull.WaitWithDefaultTimeout() - Expect(pull.ExitCode()).To(BeZero()) + Expect(pull).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") @@ -138,11 +139,11 @@ test1 := podmanTest.Podman([]string{"create", "--name", "test1", image, "echo", "test1"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(BeZero()) + Expect(test1).Should(Exit(0)) inspect := podmanTest.Podman([]string{"inspect", "test1"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) ctr := inspect.InspectContainerToJSON() caps := strings.Join(ctr[0].EffectiveCaps, ",") diff -Nru libpod-3.2.1+ds1/test/e2e/run_selinux_test.go libpod-3.4.4+ds1/test/e2e/run_selinux_test.go --- libpod-3.2.1+ds1/test/e2e/run_selinux_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_selinux_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" "github.com/opencontainers/selinux/go-selinux" ) @@ -40,7 +41,7 @@ It("podman run selinux", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_t") Expect(match).Should(BeTrue()) }) @@ -48,7 +49,7 @@ It("podman run selinux grep test", func() { session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=level:s0:c1,c2", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("s0:c1,c2") Expect(match).Should(BeTrue()) }) @@ -56,7 +57,7 @@ It("podman run selinux disable test", func() { session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=disable", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("spc_t") Expect(match).Should(BeTrue()) }) @@ -64,7 +65,7 @@ It("podman run selinux type check test", func() { session := podmanTest.Podman([]string{"run", "-it", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match1, _ := session.GrepString("container_t") match2, _ := session.GrepString("svirt_lxc_net_t") Expect(match1 || match2).Should(BeTrue()) @@ -73,7 +74,7 @@ It("podman run selinux type setup test", func() { session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=type:spc_t", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("spc_t") Expect(match).Should(BeTrue()) }) @@ -81,7 +82,7 @@ It("podman privileged selinux", func() { session := podmanTest.Podman([]string{"run", "--privileged", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("spc_t") Expect(match).Should(BeTrue()) }) @@ -89,7 +90,7 @@ It("podman test selinux label resolv.conf", func() { session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-Z", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -97,7 +98,7 @@ It("podman test selinux label hosts", func() { session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-Z", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -105,7 +106,7 @@ It("podman test selinux label hostname", func() { session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-Z", "/etc/hostname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -113,7 +114,7 @@ It("podman test selinux label /run/secrets", func() { session := podmanTest.Podman([]string{"run", fedoraMinimal, "ls", "-dZ", "/run/secrets"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -121,7 +122,7 @@ It("podman test selinux --privileged label resolv.conf", func() { session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-Z", "/etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -129,7 +130,7 @@ It("podman test selinux --privileged label hosts", func() { session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-Z", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -137,7 +138,7 @@ It("podman test selinux --privileged label hostname", func() { session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-Z", "/etc/hostname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -145,7 +146,7 @@ It("podman test selinux --privileged label /run/secrets", func() { session := podmanTest.Podman([]string{"run", "--privileged", fedoraMinimal, "ls", "-dZ", "/run/secrets"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_file_t") Expect(match).Should(BeTrue()) }) @@ -153,19 +154,19 @@ It("podman run selinux file type setup test", func() { session := podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=type:spc_t", "--security-opt", "label=filetype:container_var_lib_t", fedoraMinimal, "ls", "-Z", "/dev"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("container_var_lib_t") Expect(match).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "-it", "--security-opt", "label=type:spc_t", "--security-opt", "label=filetype:foobar", fedoraMinimal, "ls", "-Z", "/dev"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(126)) + Expect(session).Should(Exit(126)) }) It("podman exec selinux check", func() { setup := podmanTest.RunTopContainer("test1") setup.WaitWithDefaultTimeout() - Expect(setup.ExitCode()).To(Equal(0)) + Expect(setup).Should(Exit(0)) session := podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/1/attr/current"}) session.WaitWithDefaultTimeout() @@ -177,7 +178,7 @@ It("podman run --privileged and --security-opt SELinux options", func() { session := podmanTest.Podman([]string{"run", "-it", "--privileged", "--security-opt", "label=type:spc_t", "--security-opt", "label=level:s0:c1,c2", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("spc_t") Expect(match).To(BeTrue()) match2, _ := session.GrepString("s0:c1,c2") @@ -187,90 +188,90 @@ It("podman pod container share SELinux labels", func() { session := podmanTest.Podman([]string{"pod", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) label1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(label1)) session = podmanTest.Podman([]string{"pod", "rm", podID, "--force"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman pod container --infra=false doesn't share SELinux labels", func() { session := podmanTest.Podman([]string{"pod", "create", "--infra=false"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) podID := session.OutputToString() session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) label1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "--pod", podID, ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(Equal(label1))) session = podmanTest.Podman([]string{"pod", "rm", podID, "--force"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman shared IPC NS container share SELinux labels", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) label1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "--ipc", "container:test1", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(label1)) }) It("podman shared PID NS container share SELinux labels", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) label1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "--pid", "container:test1", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(label1)) }) It("podman shared NET NS container doesn't share SELinux labels", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test1", "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) label1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "--net", "container:test1", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(Equal(label1))) }) @@ -278,35 +279,35 @@ SkipIfRootlessCgroupsV1("Not supported for rootless + CGroupsV1") session := podmanTest.Podman([]string{"run", "--pid=host", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("spc_t")) }) It("podman test --ipc=host", func() { session := podmanTest.Podman([]string{"run", "--ipc=host", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("spc_t")) }) It("podman test --ipc=net", func() { session := podmanTest.Podman([]string{"run", "--net=host", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("container_t")) }) It("podman test --ipc=net", func() { session := podmanTest.Podman([]string{"run", "--net=host", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("container_t")) }) It("podman test --ipc=net", func() { session := podmanTest.Podman([]string{"run", "--net=host", ALPINE, "cat", "/proc/self/attr/current"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("container_t")) }) @@ -321,7 +322,7 @@ } session := podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"inspect", "--format", "{{ .ProcessLabel }}", cid}) session.WaitWithDefaultTimeout() @@ -337,10 +338,18 @@ It("podman test init labels", func() { session := podmanTest.Podman([]string{"create", ubi_init, "/sbin/init"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"inspect", "--format", "{{ .ProcessLabel }}", cid}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(ContainSubstring("container_init_t")) }) + + It("podman relabels named volume with :Z", func() { + session := podmanTest.Podman([]string{"run", "-v", "testvol:/test1/test:Z", fedoraMinimal, "ls", "-alZ", "/test1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + match, _ := session.GrepString(":s0:") + Expect(match).Should(BeTrue()) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_signal_test.go libpod-3.4.4+ds1/test/e2e/run_signal_test.go --- libpod-3.2.1+ds1/test/e2e/run_signal_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_signal_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,6 +13,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" "golang.org/x/sys/unix" ) @@ -129,10 +130,10 @@ // Kill with -9 to guarantee the container dies killSession := podmanTest.Podman([]string{"kill", "-s", "9", "test2"}) killSession.WaitWithDefaultTimeout() - Expect(killSession.ExitCode()).To(Equal(0)) + Expect(killSession).Should(Exit(0)) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).ToNot(Equal(0)) + Expect(session).To(ExitWithError()) ok, _ = session.GrepString("Received") Expect(ok).To(BeFalse()) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_staticip_test.go libpod-3.4.4+ds1/test/e2e/run_staticip_test.go --- libpod-3.2.1+ds1/test/e2e/run_staticip_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_staticip_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run with --ip flag", func() { @@ -60,7 +61,7 @@ ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"run", "-ti", "--ip", ip, ALPINE, "ip", "addr"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(result.OutputToString()).To(ContainSubstring(ip + "/16")) }) @@ -68,7 +69,7 @@ ip := GetRandomIPAddress() result := podmanTest.Podman([]string{"run", "-dt", "--ip", ip, nginx}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) for i := 0; i < 10; i++ { fmt.Println("Waiting for nginx", err) time.Sleep(1 * time.Second) diff -Nru libpod-3.2.1+ds1/test/e2e/run_test.go libpod-3.4.4+ds1/test/e2e/run_test.go --- libpod-3.2.1+ds1/test/e2e/run_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -11,11 +11,13 @@ "syscall" "time" + "github.com/containers/podman/v3/pkg/cgroups" . "github.com/containers/podman/v3/test/utils" "github.com/containers/storage/pkg/stringid" "github.com/mrunalp/fileutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run", func() { @@ -44,29 +46,29 @@ It("podman run a container based on local image", func() { session := podmanTest.Podman([]string{"run", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run check /run/.containerenv", func() { session := podmanTest.Podman([]string{"run", ALPINE, "cat", "/run/.containerenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("")) session = podmanTest.Podman([]string{"run", "--privileged", "--name=test1", ALPINE, "cat", "/run/.containerenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("name=\"test1\"")) Expect(session.OutputToString()).To(ContainSubstring("image=\"" + ALPINE + "\"")) session = podmanTest.Podman([]string{"run", "-v", "/:/host", ALPINE, "cat", "/run/.containerenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("graphRootMounted=1")) session = podmanTest.Podman([]string{"run", "-v", "/:/host", "--privileged", ALPINE, "cat", "/run/.containerenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("graphRootMounted=1")) }) @@ -75,40 +77,40 @@ session := podmanTest.Podman([]string{"run", imageName, "ls"}) session.WaitWithDefaultTimeout() Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull")) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --signature-policy", func() { SkipIfRemote("SigPolicy not handled by remote") session := podmanTest.Podman([]string{"run", "--pull=always", "--signature-policy", "/no/such/file", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--pull=always", "--signature-policy", "/etc/containers/policy.json", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --rm with --restart", func() { session := podmanTest.Podman([]string{"run", "--rm", "--restart", "", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--restart", "no", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--restart", "on-failure", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--restart", "always", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--rm", "--restart", "unless-stopped", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman run a container based on on a short name with localhost", func() { @@ -121,7 +123,7 @@ session := podmanTest.Podman([]string{"run", "libpod/alpine_nginx:latest", "ls"}) session.WaitWithDefaultTimeout() Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull")) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container run a container based on on a short name with localhost", func() { @@ -134,20 +136,20 @@ session := podmanTest.Podman([]string{"container", "run", "libpod/alpine_nginx:latest", "ls"}) session.WaitWithDefaultTimeout() Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull")) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run a container based on local image with short options", func() { session := podmanTest.Podman([]string{"run", "-dt", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run a container based on local image with short options and args", func() { // regression test for #714 session := podmanTest.Podman([]string{"run", ALPINE, "find", "/etc", "-name", "hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("/etc/hosts") Expect(match).Should(BeTrue()) }) @@ -157,7 +159,7 @@ hostname := "test_hostname" session := podmanTest.Podman([]string{"run", "-ti", "--rm", "--name", name, "--hostname", hostname, ALPINE, "cat", "/etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString(name) Expect(match).Should(BeTrue()) match, _ = session.GrepString(hostname) @@ -165,9 +167,31 @@ }) It("podman run a container based on remote image", func() { - session := podmanTest.Podman([]string{"run", "-dt", BB_GLIBC, "ls"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + // Changing session to rsession + rsession := podmanTest.Podman([]string{"run", "-dt", ALPINE, "ls"}) + rsession.WaitWithDefaultTimeout() + Expect(rsession).Should(Exit(0)) + + lock := GetPortLock("5000") + defer lock.Unlock() + session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", "5000:5000", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { + Skip("Cannot start docker registry.") + } + + run := podmanTest.Podman([]string{"run", "--tls-verify=false", ALPINE}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) + Expect(podmanTest.NumberOfContainers()).To(Equal(3)) + + // Now registries.conf will be consulted where localhost:5000 + // is set to be insecure. + run = podmanTest.Podman([]string{"run", ALPINE}) + run.WaitWithDefaultTimeout() + Expect(run).Should(Exit(0)) }) It("podman run a container with a --rootfs", func() { @@ -184,25 +208,25 @@ csession := podmanTest.Podman([]string{"run", "--name", uniqueString, ALPINE, "/bin/sh", "-c", fmt.Sprintf("echo %s > %s", uniqueString, testFilePath)}) csession.WaitWithDefaultTimeout() - Expect(csession.ExitCode()).To(Equal(0)) + Expect(csession).Should(Exit(0)) // Export from working container image guarantees working root esession := podmanTest.Podman([]string{"export", "--output", tarball, uniqueString}) esession.WaitWithDefaultTimeout() - Expect(esession.ExitCode()).To(Equal(0)) + Expect(esession).Should(Exit(0)) Expect(tarball).Should(BeARegularFile()) // N/B: This will loose any extended attributes like SELinux types fmt.Fprintf(os.Stderr, "Extracting container root tarball\n") tarsession := SystemExec("tar", []string{"xf", tarball, "-C", rootfs}) - Expect(tarsession.ExitCode()).To(Equal(0)) + Expect(tarsession).Should(Exit(0)) Expect(filepath.Join(rootfs, uls)).Should(BeADirectory()) // Other tests confirm SELinux types, just confirm --rootfs is working. session := podmanTest.Podman([]string{"run", "-i", "--security-opt", "label=disable", "--rootfs", rootfs, "cat", testFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Validate changes made in original container and export stdoutLines := session.OutputToStringArray() @@ -213,10 +237,10 @@ It("podman run a container with --init", func() { session := podmanTest.Podman([]string{"run", "--name", "test", "--init", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].Path).To(Equal("/dev/init")) Expect(conData[0].Config.Annotations["io.podman.annotations.init"]).To(Equal("TRUE")) @@ -225,10 +249,10 @@ It("podman run a container with --init and --init-path", func() { session := podmanTest.Podman([]string{"run", "--name", "test", "--init", "--init-path", "/usr/libexec/podman/catatonit", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].Path).To(Equal("/dev/init")) Expect(conData[0].Config.Annotations["io.podman.annotations.init"]).To(Equal("TRUE")) @@ -237,10 +261,10 @@ It("podman run a container without --init", func() { session := podmanTest.Podman([]string{"run", "--name", "test", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(conData[0].Path).To(Equal("ls")) Expect(conData[0].Config.Annotations["io.podman.annotations.init"]).To(Equal("FALSE")) @@ -259,56 +283,56 @@ It("podman run mask and unmask path test", func() { session := podmanTest.Podman([]string{"run", "-d", "--name=maskCtr1", "--security-opt", "unmask=ALL", "--security-opt", "mask=/proc/acpi", ALPINE, "sleep", "200"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/sys/firmware"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(Not(BeEmpty())) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "maskCtr1", "ls", "/proc/acpi"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(BeEmpty()) session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr2", "--security-opt", "unmask=/proc/acpi:/sys/firmware", ALPINE, "sleep", "200"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/sys/firmware"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(Not(BeEmpty())) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "maskCtr2", "ls", "/proc/acpi"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(Not(BeEmpty())) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr3", "--security-opt", "mask=/sys/power/disk", ALPINE, "sleep", "200"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "maskCtr3", "cat", "/sys/power/disk"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(BeEmpty()) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr4", "--security-opt", "systempaths=unconfined", ALPINE, "sleep", "200"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "maskCtr4", "ls", "/sys/firmware"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(Not(BeEmpty())) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--name=maskCtr5", "--security-opt", "systempaths=unconfined", ALPINE, "grep", "/proc", "/proc/self/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToStringArray()).Should(HaveLen(1)) session = podmanTest.Podman([]string{"run", "-d", "--security-opt", "unmask=/proc/*", ALPINE, "grep", "/proc", "/proc/self/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToStringArray()).Should(HaveLen(1)) session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=/proc/a*", ALPINE, "ls", "/proc/acpi"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(BeEmpty())) }) @@ -319,32 +343,32 @@ rwOnCGroups := "/sys/fs/cgroup cgroup2 rw" session := podmanTest.Podman([]string{"run", "--security-opt", "unmask=ALL", "--security-opt", "mask=/sys/fs/cgroup", ALPINE, "cat", "/proc/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(rwOnCGroups)) session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=/sys/fs/cgroup", ALPINE, "cat", "/proc/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(rwOnCGroups)) session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=/sys/fs/cgroup///", ALPINE, "cat", "/proc/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(rwOnCGroups)) session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=ALL", ALPINE, "cat", "/proc/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(rwOnCGroups)) session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=/sys/fs/cgroup", "--security-opt", "mask=/sys/fs/cgroup", ALPINE, "cat", "/proc/mounts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(rwOnCGroups)) session = podmanTest.Podman([]string{"run", "--security-opt", "unmask=/sys/fs/cgroup", ALPINE, "ls", "/sys/fs/cgroup"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).ToNot(BeEmpty()) }) @@ -368,32 +392,32 @@ session := podmanTest.Podman([]string{"run", "-it", "--privileged", ALPINE, "grep", "Seccomp", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(ContainSubstring("0")) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run seccomp test no profile should be default", func() { session := podmanTest.Podman([]string{"run", "-it", ALPINE, "grep", "Seccomp", "/proc/self/status"}) session.WaitWithDefaultTimeout() Expect(session.OutputToString()).To(ContainSubstring("2")) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run capabilities test", func() { session := podmanTest.Podman([]string{"run", "--rm", "--cap-add", "all", ALPINE, "cat", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--cap-add", "sys_admin", ALPINE, "cat", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--cap-drop", "all", ALPINE, "cat", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--rm", "--cap-drop", "setuid", ALPINE, "cat", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run user capabilities test", func() { @@ -404,67 +428,67 @@ } session := podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--rm", "--user", "root", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) session = podmanTest.Podman([]string{"run", "--user=1000:1000", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) session = podmanTest.Podman([]string{"run", "--user=0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) session = podmanTest.Podman([]string{"run", "--user=0:0", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) if os.Geteuid() > 0 { @@ -476,17 +500,17 @@ } session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapAmb", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--privileged", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--cap-add=DAC_OVERRIDE", "--rm", ALPINE, "grep", "CapInh", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000002")) } }) @@ -502,12 +526,12 @@ podmanTest.BuildImage(dockerfile, "test", "false") session := podmanTest.Podman([]string{"run", "--rm", "--user", "bin", "test", "grep", "CapBnd", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("00000000a80425fb")) session = podmanTest.Podman([]string{"run", "--rm", "--user", "bin", "test", "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0000000000000000")) }) @@ -517,29 +541,29 @@ if !isRootless() { session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "rtprio=99", "--cap-add=sys_nice", fedoraMinimal, "cat", "/proc/self/sched"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "nofile=2048:2048", fedoraMinimal, "ulimit", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("2048")) session = podmanTest.Podman([]string{"run", "--rm", "--ulimit", "nofile=1024:1028", fedoraMinimal, "ulimit", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("1024")) if !CGROUPSV2 { // --oom-kill-disable not supported on cgroups v2. session = podmanTest.Podman([]string{"run", "--rm", "--oom-kill-disable=true", fedoraMinimal, "echo", "memory-hog"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } session = podmanTest.Podman([]string{"run", "--rm", "--oom-score-adj=111", fedoraMinimal, "cat", "/proc/self/oom_score_adj"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("111")) }) @@ -553,7 +577,7 @@ session := podmanTest.Podman([]string{"run", "--rm", "--ulimit", "host", fedoraMinimal, "ulimit", "-Hn"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ulimitCtrStr := strings.TrimSpace(session.OutputToString()) ulimitCtr, err := strconv.ParseUint(ulimitCtrStr, 10, 0) @@ -565,7 +589,7 @@ It("podman run with cidfile", func() { session := podmanTest.Podman([]string{"run", "--cidfile", tempdir + "cidfile", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) err := os.Remove(tempdir + "cidfile") Expect(err).To(BeNil()) }) @@ -574,13 +598,13 @@ SkipIfRootless("Network sysctls are not available root rootless") session := podmanTest.Podman([]string{"run", "--rm", "--sysctl", "net.core.somaxconn=65535", ALPINE, "sysctl", "net.core.somaxconn"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("net.core.somaxconn = 65535")) // network sysctls should fail if --net=host is set session = podmanTest.Podman([]string{"run", "--net", "host", "--rm", "--sysctl", "net.core.somaxconn=65535", ALPINE, "sysctl", "net.core.somaxconn"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman run blkio-weight test", func() { @@ -595,7 +619,7 @@ } session := podmanTest.Podman([]string{"run", "--rm", "--blkio-weight=15", ALPINE, "sh", "-c", "cat /sys/fs/cgroup/io.bfq.weight"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // there was a documentation issue in the kernel that reported a different range [1-10000] for the io controller. // older versions of crun/runc used it. For the time being allow both versions to pass the test. // FIXME: drop "|51" once all the runtimes we test have the fix in place. @@ -606,7 +630,7 @@ } session := podmanTest.Podman([]string{"run", "--rm", "--blkio-weight=15", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.weight"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("15")) } }) @@ -624,7 +648,7 @@ } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !CGROUPSV2 { // TODO: Test Simplification. For now, we only care about exit(0) w/ cgroupsv2 Expect(session.OutputToString()).To(ContainSubstring("1048576")) } @@ -642,7 +666,7 @@ session = podmanTest.Podman([]string{"run", "--rm", "--device-write-bps=/dev/zero:1mb", ALPINE, "cat", "/sys/fs/cgroup/blkio/blkio.throttle.write_bps_device"}) } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !CGROUPSV2 { // TODO: Test Simplification. For now, we only care about exit(0) w/ cgroupsv2 Expect(session.OutputToString()).To(ContainSubstring("1048576")) } @@ -660,7 +684,7 @@ } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !CGROUPSV2 { // TODO: Test Simplification. For now, we only care about exit(0) w/ cgroupsv2 Expect(session.OutputToString()).To(ContainSubstring("100")) } @@ -678,7 +702,7 @@ } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !CGROUPSV2 { // TODO: Test Simplification. For now, we only care about exit(0) w/ cgroupsv2 Expect(session.OutputToString()).To(ContainSubstring("100")) } @@ -706,7 +730,7 @@ session := podmanTest.Podman([]string{"run", ALPINE, "printenv", "NOTIFY_SOCKET"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(BeNumerically(">", 0)) }) @@ -714,7 +738,7 @@ log := filepath.Join(podmanTest.TempDir, "/container.log") session := podmanTest.Podman([]string{"run", "--rm", "--log-driver", "k8s-file", "--log-opt", fmt.Sprintf("path=%s", log), ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, err := os.Stat(log) Expect(err).To(BeNil()) _ = os.Remove(log) @@ -724,11 +748,11 @@ podmanTest.AddImageToRWStore(BB) tag := podmanTest.Podman([]string{"tag", BB, "bb"}) tag.WaitWithDefaultTimeout() - Expect(tag.ExitCode()).To(Equal(0)) + Expect(tag).Should(Exit(0)) session := podmanTest.Podman([]string{"run", "--rm", "bb", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman test hooks", func() { @@ -741,7 +765,7 @@ session := podmanTest.Podman([]string{"run", ALPINE, "ls"}) session.Wait(10) os.Unsetenv("HOOK_OPTION") - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run with subscription secrets", func() { @@ -771,16 +795,16 @@ err = ioutil.WriteFile(keyFile, []byte(mountString), 0755) Expect(err).To(BeNil()) execSession := SystemExec("ln", []string{"-s", targetDir, filepath.Join(secretsDir, "mysymlink")}) - Expect(execSession.ExitCode()).To(Equal(0)) + Expect(execSession).Should(Exit(0)) session := podmanTest.Podman([]string{"--default-mounts-file=" + mountsFile, "run", "--rm", ALPINE, "cat", "/run/secrets/test.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) session = podmanTest.Podman([]string{"--default-mounts-file=" + mountsFile, "run", "--rm", ALPINE, "ls", "/run/secrets/mysymlink"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("key.pem")) }) @@ -792,7 +816,7 @@ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "ls", "/run/secrets"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("system-fips")) err = os.Remove(fipsFile) @@ -802,63 +826,63 @@ It("podman run without group-add", func() { session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("27(video),777,65533(nogroup)")).To(BeFalse()) }) It("podman run with group-add", func() { session := podmanTest.Podman([]string{"run", "--rm", "--group-add=audio", "--group-add=nogroup", "--group-add=777", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("777,65533(nogroup)")).To(BeTrue()) }) It("podman run with user (default)", func() { session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("uid=0(root) gid=0(root)")).To(BeTrue()) }) It("podman run with user (integer, not in /etc/passwd)", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("uid=1234(1234) gid=0(root)")) }) It("podman run with user (integer, in /etc/passwd)", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=8", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("uid=8(mail) gid=12(mail)")).To(BeTrue()) }) It("podman run with user (username)", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=mail", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.LineInOutputContains("uid=8(mail) gid=12(mail)")).To(BeTrue()) }) It("podman run with user:group (username:integer)", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=mail:21", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp)")) }) It("podman run with user:group (integer:groupname)", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=8:ftp", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("uid=8(mail) gid=21(ftp)")) }) It("podman run with user, verify caps dropped", func() { session := podmanTest.Podman([]string{"run", "--rm", "--user=1234", ALPINE, "grep", "CapEff", "/proc/self/status"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) capEff := strings.Split(session.OutputToString(), " ") Expect("0000000000000000").To(Equal(capEff[1])) }) @@ -866,10 +890,10 @@ It("podman run with attach stdin outputs container ID", func() { session := podmanTest.Podman([]string{"run", "--attach", "stdin", ALPINE, "printenv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ps := podmanTest.Podman([]string{"ps", "-aq", "--no-trunc"}) ps.WaitWithDefaultTimeout() - Expect(ps.ExitCode()).To(Equal(0)) + Expect(ps).Should(Exit(0)) Expect(ps.LineInOutputContains(session.OutputToString())).To(BeTrue()) }) @@ -882,32 +906,44 @@ It("podman run with attach stderr does not print stdout", func() { session := podmanTest.Podman([]string{"run", "--rm", "--attach", "stderr", ALPINE, "ls", "/"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("")) }) It("podman run attach nonsense errors", func() { session := podmanTest.Podman([]string{"run", "--rm", "--attach", "asdfasdf", ALPINE, "ls", "/"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman run exit code on failure to exec", func() { session := podmanTest.Podman([]string{"run", ALPINE, "/etc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(126)) + Expect(session).Should(Exit(126)) }) It("podman run error on exec", func() { session := podmanTest.Podman([]string{"run", ALPINE, "sh", "-c", "exit 100"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(100)) + Expect(session).Should(Exit(100)) + }) + + It("podman run with named volume", func() { + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "stat", "-c", "%a %Y", "/var/tmp"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + perms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--rm", "-v", "test:/var/tmp", ALPINE, "stat", "-c", "%a %Y", "/var/tmp"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(perms)) }) It("podman run with built-in volume image", func() { session := podmanTest.Podman([]string{"run", "--rm", redis, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) dockerfile := fmt.Sprintf(`FROM %s RUN mkdir -p /myvol/data && chown -R mail.0 /myvol @@ -917,21 +953,10 @@ podmanTest.BuildImage(dockerfile, "test", "false") session = podmanTest.Podman([]string{"run", "--rm", "test", "ls", "-al", "/myvol/data"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("mail root")) }) - It("podman run with incorect VOLUME", func() { - dockerfile := fmt.Sprintf(`FROM %s -VOLUME ['/etc/foo'] -WORKDIR /etc/foo`, BB) - podmanTest.BuildImage(dockerfile, "test", "false") - session := podmanTest.Podman([]string{"run", "--rm", "test", "echo", "test"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - Expect(session.OutputToString()).To(ContainSubstring("test")) - }) - It("podman run --volumes-from flag", func() { vol := filepath.Join(podmanTest.TempDir, "vol-test") err := os.MkdirAll(vol, 0755) @@ -944,23 +969,23 @@ Expect(err).To(BeNil()) mountpoint := "/myvol/" - session := podmanTest.Podman([]string{"create", "--volume", vol + ":" + mountpoint, ALPINE, "cat", mountpoint + filename}) + session := podmanTest.Podman([]string{"create", "--volume", vol + ":" + mountpoint + ":z", ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ctrID := session.OutputToString() session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID, ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(data)) session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID, ALPINE, "sh", "-c", "echo test >> " + mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "--attach", ctrID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(data + "test")) }) @@ -978,55 +1003,55 @@ session := podmanTest.Podman([]string{"create", "--volume", vol + ":" + mountpoint, ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ctrID := session.OutputToString() // check that the read only option works session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID + ":ro", ALPINE, "touch", mountpoint + "abc.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) Expect(session.ErrorToString()).To(ContainSubstring("Read-only file system")) // check that both z and ro options work session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID + ":ro,z", ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(data)) // check that multiple ro/rw are not working session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID + ":ro,rw", ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("cannot set ro or rw options more than once")) // check that multiple z options are not working session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID + ":z,z,ro", ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) Expect(session.ErrorToString()).To(ContainSubstring("cannot set :z more than once in mount options")) // create new read only volume session = podmanTest.Podman([]string{"create", "--volume", vol + ":" + mountpoint + ":ro", ALPINE, "cat", mountpoint + filename}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ctrID = session.OutputToString() // check if the original volume was mounted as read only that --volumes-from also mount it as read only session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID, ALPINE, "touch", mountpoint + "abc.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) Expect(session.ErrorToString()).To(ContainSubstring("Read-only file system")) }) It("podman run --volumes-from flag with built-in volumes", func() { session := podmanTest.Podman([]string{"create", redis, "sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ctrID := session.OutputToString() session = podmanTest.Podman([]string{"run", "--volumes-from", ctrID, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("data")) }) @@ -1040,7 +1065,7 @@ session := podmanTest.Podman([]string{"run", "--volume", vol1 + ":/myvol1:z", "--volume", vol2 + ":/myvol2:z", ALPINE, "touch", "/myvol2/foo.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --volumes flag with empty host dir", func() { @@ -1068,7 +1093,7 @@ session := podmanTest.Podman([]string{"run", "--mount", "type=bind,src=" + vol1 + ",target=/myvol1,z", "--mount", "type=bind,src=" + vol2 + ",target=/myvol2,z", ALPINE, "touch", "/myvol2/foo.txt"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run findmnt nothing shared", func() { @@ -1081,7 +1106,7 @@ session := podmanTest.Podman([]string{"run", "--volume", vol1 + ":/myvol1:z", "--volume", vol2 + ":/myvol2:z", fedoraMinimal, "findmnt", "-o", "TARGET,PROPAGATION"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, _ := session.GrepString("shared") Expect(match).Should(BeFalse()) }) @@ -1096,7 +1121,7 @@ session := podmanTest.Podman([]string{"run", "--volume", vol1 + ":/myvol1:z", "--volume", vol2 + ":/myvol2:shared,z", fedoraMinimal, "findmnt", "-o", "TARGET,PROPAGATION"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) match, shared := session.GrepString("shared") Expect(match).Should(BeTrue()) // make sure it's only shared (and not 'shared,slave') @@ -1107,7 +1132,7 @@ It("podman run --security-opts proc-opts=", func() { session := podmanTest.Podman([]string{"run", "--security-opt", "proc-opts=nosuid,exec", fedoraMinimal, "findmnt", "-noOPTIONS", "/proc"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(ContainSubstring("nosuid")) Expect(output).To(Not(ContainSubstring("exec"))) @@ -1117,25 +1142,25 @@ SkipIfRootless("FIXME: rootless users are not allowed to mount bind-nonrecursive (Could this be a Kernel bug?") session := podmanTest.Podman([]string{"run", "--mount", "type=bind,bind-nonrecursive,slave,src=/,target=/host", fedoraMinimal, "findmnt", "-nR", "/host"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(1)) }) It("podman run --mount type=devpts,target=/foo/bar", func() { session := podmanTest.Podman([]string{"run", "--mount", "type=devpts,target=/foo/bar", fedoraMinimal, "stat", "-f", "-c%T", "/foo/bar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("devpts")) }) It("podman run --pod automatically", func() { - session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:foobar", ALPINE, "nc", "-l", "-p", "8080"}) + session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:foobar", ALPINE, "nc", "-l", "-p", "8686"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) - session = podmanTest.Podman([]string{"run", "--pod", "foobar", ALPINE, "/bin/sh", "-c", "echo test | nc -w 1 127.0.0.1 8080"}) + session = podmanTest.Podman([]string{"run", "--pod", "foobar", ALPINE, "/bin/sh", "-c", "echo test | nc -w 1 127.0.0.1 8686"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"pod", "ps", "--no-trunc"}) check.WaitWithDefaultTimeout() @@ -1147,14 +1172,14 @@ hostname := "abc" session := podmanTest.Podman([]string{"run", "--pod", "new:foobar", "--hostname", hostname, ALPINE, "cat", "/etc/hostname"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(hostname)) }) It("podman run --rm should work", func() { session := podmanTest.Podman([]string{"run", "--name", "test", "--rm", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"wait", "test"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) @@ -1190,7 +1215,7 @@ It("podman run readonly container should NOT mount /dev/shm read/only", func() { session := podmanTest.Podman([]string{"run", "--read-only", ALPINE, "mount"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("/dev/shm type tmpfs (ro,"))) }) @@ -1198,7 +1223,7 @@ It("podman run readonly container should NOT mount /run noexec", func() { session := podmanTest.Podman([]string{"run", "--read-only", ALPINE, "sh", "-c", "mount | grep \"/run \""}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) }) @@ -1211,14 +1236,14 @@ }) It("podman run with bad healthcheck timeout", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-timeout", "0s", ALPINE, "top"}) + session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "foo", "--health-timeout", "0s", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-timeout must be at least 1 second")) }) It("podman run with bad healthcheck start-period", func() { - session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "[\"foo\"]", "--health-start-period", "-1s", ALPINE, "top"}) + session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "foo", "--health-start-period", "-1s", ALPINE, "top"}) session.WaitWithDefaultTimeout() Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("healthcheck-start-period must be 0 seconds or greater")) @@ -1277,64 +1302,76 @@ SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users") SkipIfRemote("--cgroups=split cannot be used in remote mode") - container := podmanTest.Podman([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) - container.WaitWithDefaultTimeout() - Expect(container.ExitCode()).To(Equal(0)) - lines := container.OutputToStringArray() - - cgroup := "" - for _, line := range lines { - parts := strings.SplitN(line, ":", 3) - if !CGROUPSV2 { - // ignore unified on cgroup v1. - // both runc and crun do not set it. - // crun does not set named hierarchies. - if parts[1] == "" || strings.Contains(parts[1], "name=") { + checkLines := func(lines []string) { + cgroup := "" + for _, line := range lines { + parts := strings.SplitN(line, ":", 3) + if len(parts) < 2 { continue } + if !CGROUPSV2 { + // ignore unified on cgroup v1. + // both runc and crun do not set it. + // crun does not set named hierarchies. + if parts[1] == "" || strings.Contains(parts[1], "name=") { + continue + } + } + if parts[2] == "/" { + continue + } + if cgroup == "" { + cgroup = parts[2] + continue + } + Expect(cgroup).To(Equal(parts[2])) } - if parts[2] == "/" { - continue - } - if cgroup == "" { - cgroup = parts[2] - continue - } - Expect(cgroup).To(Equal(parts[2])) } + + container := podmanTest.PodmanSystemdScope([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) + container.WaitWithDefaultTimeout() + Expect(container).Should(Exit(0)) + checkLines(container.OutputToStringArray()) + + // check that --cgroups=split is honored also when a container runs in a pod + container = podmanTest.PodmanSystemdScope([]string{"run", "--rm", "--pod", "new:split-test-pod", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"}) + container.WaitWithDefaultTimeout() + Expect(container).Should(Exit(0)) + checkLines(container.OutputToStringArray()) }) It("podman run with cgroups=disabled runs without cgroups", func() { - SkipIfRootless("FIXME: I believe this should work but need to fix this test") SkipIfRootlessCgroupsV1("Disable cgroups not supported on cgroupv1 for rootless users") // Only works on crun if !strings.Contains(podmanTest.OCIRuntime, "crun") { Skip("Test only works on crun") } + ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup() + Expect(err).ShouldNot(HaveOccurred()) + if !ownsCgroup { + // Podman moves itself to a new cgroup if it doesn't own the current cgroup + Skip("Test only works when Podman owns the current cgroup") + } + + trim := func(i string) string { + return strings.TrimSuffix(i, "\n") + } + curCgroupsBytes, err := ioutil.ReadFile("/proc/self/cgroup") - Expect(err).To(BeNil()) - var curCgroups string = string(curCgroupsBytes) + Expect(err).ShouldNot(HaveOccurred()) + curCgroups := trim(string(curCgroupsBytes)) fmt.Printf("Output:\n%s\n", curCgroups) - Expect(curCgroups).To(Not(Equal(""))) + Expect(curCgroups).ToNot(Equal("")) - ctrName := "testctr" - container := podmanTest.Podman([]string{"run", "--name", ctrName, "-d", "--cgroups=disabled", ALPINE, "top"}) + container := podmanTest.Podman([]string{"run", "--cgroupns=host", "--cgroups=disabled", ALPINE, "cat", "/proc/self/cgroup"}) container.WaitWithDefaultTimeout() - Expect(container.ExitCode()).To(Equal(0)) + Expect(container).Should(Exit(0)) - // Get PID and get cgroups of that PID - inspectOut := podmanTest.InspectContainer(ctrName) - Expect(len(inspectOut)).To(Equal(1)) - pid := inspectOut[0].State.Pid - Expect(pid).To(Not(Equal(0))) - Expect(inspectOut[0].HostConfig.CgroupParent).To(Equal("")) - - ctrCgroupsBytes, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/cgroup", pid)) - Expect(err).To(BeNil()) - var ctrCgroups string = string(ctrCgroupsBytes) + ctrCgroups := trim(container.OutputToString()) fmt.Printf("Output\n:%s\n", ctrCgroups) - Expect(curCgroups).To(Equal(ctrCgroups)) + + Expect(ctrCgroups).To(Equal(curCgroups)) }) It("podman run with cgroups=enabled makes cgroups", func() { @@ -1353,7 +1390,7 @@ ctrName := "testctr" container := podmanTest.Podman([]string{"run", "--name", ctrName, "-d", "--cgroups=enabled", ALPINE, "top"}) container.WaitWithDefaultTimeout() - Expect(container.ExitCode()).To(Equal(0)) + Expect(container).Should(Exit(0)) // Get PID and get cgroups of that PID inspectOut := podmanTest.InspectContainer(ctrName) @@ -1377,7 +1414,7 @@ It("podman run should fail with nonexistent authfile", func() { session := podmanTest.Podman([]string{"run", "--authfile", "/tmp/nonexistent", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman run --device-cgroup-rule", func() { @@ -1385,24 +1422,24 @@ deviceCgroupRule := "c 42:* rwm" session := podmanTest.Podman([]string{"run", "--cap-add", "mknod", "--name", "test", "-d", "--device-cgroup-rule", deviceCgroupRule, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test", "mknod", "newDev", "c", "42", "1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --replace", func() { // Make sure we error out with --name. session := podmanTest.Podman([]string{"create", "--replace", ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) // Run and replace 5 times in a row the "same" container. ctrName := "testCtr" for i := 0; i < 5; i++ { session := podmanTest.Podman([]string{"run", "--detach", "--replace", "--name", ctrName, ALPINE, "/bin/sh"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } }) @@ -1415,21 +1452,21 @@ } session := podmanTest.PodmanExtraFiles([]string{"run", "--preserve-fds", "1", ALPINE, "ls"}, files) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run --preserve-fds invalid fd", func() { session := podmanTest.Podman([]string{"run", "--preserve-fds", "2", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("file descriptor 3 is not available")) }) It("podman run --privileged and --group-add", func() { - groupName := "kvm" + groupName := "mail" session := podmanTest.Podman([]string{"run", "-t", "-i", "--group-add", groupName, "--privileged", fedoraMinimal, "groups"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(strings.Contains(session.OutputToString(), groupName)).To(BeTrue()) }) @@ -1449,7 +1486,7 @@ badTZFile := fmt.Sprintf("../../../%s", tzFile) session := podmanTest.Podman([]string{"run", "--tz", badTZFile, "--rm", ALPINE, "date"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("error finding timezone for container")) err = os.Remove(tzFile) @@ -1457,20 +1494,20 @@ session = podmanTest.Podman([]string{"run", "--tz", "foo", "--rm", ALPINE, "date"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--tz", "America", "--rm", ALPINE, "date"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--tz", "Pacific/Honolulu", "--rm", ALPINE, "date", "+'%H %Z'"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("HST")) session = podmanTest.Podman([]string{"run", "--tz", "local", "--rm", ALPINE, "date", "+'%H %Z'"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) t := time.Now() z, _ := t.Zone() h := strconv.Itoa(t.Hour()) @@ -1484,7 +1521,7 @@ limit := "4321" session := podmanTest.Podman([]string{"run", "--pids-limit", limit, "--net=none", "--rm", ALPINE, "cat", "/sys/fs/cgroup/pids.max"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(limit)) }) @@ -1495,27 +1532,27 @@ session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "sh", "-c", "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0022")) session = podmanTest.Podman([]string{"run", "--umask", "0002", "--rm", ALPINE, "sh", "-c", "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0002")) session = podmanTest.Podman([]string{"run", "--umask", "0077", "--rm", fedoraMinimal, "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0077")) session = podmanTest.Podman([]string{"run", "--umask", "22", "--rm", ALPINE, "sh", "-c", "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0022")) session = podmanTest.Podman([]string{"run", "--umask", "9999", "--rm", ALPINE, "sh", "-c", "umask"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) Expect(session.ErrorToString()).To(ContainSubstring("Invalid umask")) }) @@ -1526,14 +1563,14 @@ podmanTest.BuildImage(dockerfile, "test", "false") session := podmanTest.Podman([]string{"run", "--rm", "test", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/madethis")) }) It("podman run --entrypoint does not use image command", func() { session := podmanTest.Podman([]string{"run", "--entrypoint", "/bin/echo", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // We can't guarantee the output is completely empty, some // nonprintables seem to work their way in. Expect(session.OutputToString()).To(Not(ContainSubstring("/bin/sh"))) @@ -1542,42 +1579,42 @@ It("podman run a container with log-level (lower case)", func() { session := podmanTest.Podman([]string{"--log-level=info", "run", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run a container with log-level (upper case)", func() { session := podmanTest.Podman([]string{"--log-level=INFO", "run", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run a container with --pull never should fail if no local store", func() { session := podmanTest.Podman([]string{"run", "--pull", "never", "docker.io/library/debian:latest", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman run container with --pull missing and only pull once", func() { session := podmanTest.Podman([]string{"run", "--pull", "missing", cirros, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).To(ContainSubstring("Trying to pull")) session = podmanTest.Podman([]string{"run", "--pull", "missing", cirros, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).ToNot(ContainSubstring("Trying to pull")) }) It("podman run container with --pull missing should pull image multiple times", func() { session := podmanTest.Podman([]string{"run", "--pull", "always", cirros, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).To(ContainSubstring("Trying to pull")) session = podmanTest.Podman([]string{"run", "--pull", "always", cirros, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).To(ContainSubstring("Trying to pull")) }) @@ -1585,7 +1622,7 @@ hostnameEnv := "test123" session := podmanTest.Podman([]string{"run", "--hostname", "testctr", "--env", fmt.Sprintf("HOSTNAME=%s", hostnameEnv), ALPINE, "printenv", "HOSTNAME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(hostnameEnv)) }) @@ -1597,16 +1634,16 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--secret", "mysecret", "--name", "secr", ALPINE, "cat", "/run/secrets/mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) session = podmanTest.Podman([]string{"inspect", "secr", "--format", " {{(index .Config.Secrets 0).Name}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("mysecret")) }) @@ -1619,20 +1656,64 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=mount", "--name", "secr", ALPINE, "cat", "/run/secrets/mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) session = podmanTest.Podman([]string{"inspect", "secr", "--format", " {{(index .Config.Secrets 0).Name}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("mysecret")) }) + It("podman run --secret source=mysecret,type=mount with target", func() { + secretsString := "somesecretdata" + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "mysecret_target", secretFilePath}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret_target,type=mount,target=hello", "--name", "secr_target", ALPINE, "cat", "/run/secrets/hello"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(secretsString)) + + session = podmanTest.Podman([]string{"inspect", "secr_target", "--format", " {{(index .Config.Secrets 0).Name}}"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("mysecret_target")) + + }) + + It("podman run --secret source=mysecret,type=mount with target at /tmp", func() { + secretsString := "somesecretdata" + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "mysecret_target2", secretFilePath}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret_target2,type=mount,target=/tmp/hello", "--name", "secr_target2", ALPINE, "cat", "/tmp/hello"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(secretsString)) + + session = podmanTest.Podman([]string{"inspect", "secr_target2", "--format", " {{(index .Config.Secrets 0).Name}}"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("mysecret_target2")) + + }) + It("podman run --secret source=mysecret,type=env", func() { secretsString := "somesecretdata" secretFilePath := filepath.Join(podmanTest.TempDir, "secret") @@ -1641,11 +1722,11 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=env", "--name", "secr", ALPINE, "printenv", "mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) }) @@ -1657,15 +1738,54 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - // target with mount type should fail - session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=mount,target=anotherplace", "--name", "secr", ALPINE, "cat", "/run/secrets/mysecret"}) - session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=env,target=anotherplace", "--name", "secr", ALPINE, "printenv", "anotherplace"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(secretsString)) + }) + + It("podman run --secret mount with uid, gid, mode options", func() { + secretsString := "somesecretdata" + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + // check default permissions + session = podmanTest.Podman([]string{"run", "--secret", "mysecret", "--name", "secr", ALPINE, "ls", "-l", "/run/secrets/mysecret"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + output := session.OutputToString() + Expect(output).To(ContainSubstring("-r--r--r--")) + Expect(output).To(ContainSubstring("root")) + + session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=mount,uid=1000,gid=1001,mode=777", "--name", "secr2", ALPINE, "ls", "-ln", "/run/secrets/mysecret"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + output = session.OutputToString() + Expect(output).To(ContainSubstring("-rwxrwxrwx")) + Expect(output).To(ContainSubstring("1000")) + Expect(output).To(ContainSubstring("1001")) + }) + + It("podman run --secret with --user", func() { + secretsString := "somesecretdata" + secretFilePath := filepath.Join(podmanTest.TempDir, "secret") + err := ioutil.WriteFile(secretFilePath, []byte(secretsString), 0755) + Expect(err).To(BeNil()) + + session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--secret", "mysecret", "--name", "nonroot", "--user", "200:200", ALPINE, "cat", "/run/secrets/mysecret"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(secretsString)) }) @@ -1677,51 +1797,56 @@ session := podmanTest.Podman([]string{"secret", "create", "mysecret", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Invalid type session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=other", "--name", "secr", ALPINE, "printenv", "mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) // Invalid option session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,invalid=invalid", "--name", "secr", ALPINE, "printenv", "mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) // Option syntax not valid session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type", "--name", "secr", ALPINE, "printenv", "mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) + + // mount option with env type + session = podmanTest.Podman([]string{"run", "--secret", "source=mysecret,type=env,uid=1000", "--name", "secr", ALPINE, "printenv", "mysecret"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) // No source given session = podmanTest.Podman([]string{"run", "--secret", "type=env", "--name", "secr", ALPINE, "printenv", "mysecret"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman run --requires", func() { depName := "ctr1" depContainer := podmanTest.Podman([]string{"create", "--name", depName, ALPINE, "top"}) depContainer.WaitWithDefaultTimeout() - Expect(depContainer.ExitCode()).To(Equal(0)) + Expect(depContainer).Should(Exit(0)) mainName := "ctr2" mainContainer := podmanTest.Podman([]string{"run", "--name", mainName, "--requires", depName, "-d", ALPINE, "top"}) mainContainer.WaitWithDefaultTimeout() - Expect(mainContainer.ExitCode()).To(Equal(0)) + Expect(mainContainer).Should(Exit(0)) stop := podmanTest.Podman([]string{"stop", "--all"}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) start := podmanTest.Podman([]string{"start", mainName}) start.WaitWithDefaultTimeout() - Expect(start.ExitCode()).To(Equal(0)) + Expect(start).Should(Exit(0)) running := podmanTest.Podman([]string{"ps", "-q"}) running.WaitWithDefaultTimeout() - Expect(running.ExitCode()).To(Equal(0)) + Expect(running).Should(Exit(0)) Expect(len(running.OutputToStringArray())).To(Equal(2)) }) @@ -1730,7 +1855,7 @@ pidfile := tempdir + "pidfile" session := podmanTest.Podman([]string{"run", "--pidfile", pidfile, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) readFirstLine := func(path string) string { content, err := ioutil.ReadFile(path) Expect(err).To(BeNil()) @@ -1740,4 +1865,15 @@ _, err = strconv.Atoi(containerPID) // Make sure it's a proper integer Expect(err).To(BeNil()) }) + + It("podman run check personality support", func() { + // TODO: Remove this as soon as this is merged and made available in our CI https://github.com/opencontainers/runc/pull/3126. + if !strings.Contains(podmanTest.OCIRuntime, "crun") { + Skip("Test only works on crun") + } + session := podmanTest.Podman([]string{"run", "--personality=LINUX32", "--name=testpersonality", ALPINE, "uname", "-a"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(ContainSubstring("i686")) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_userns_test.go libpod-3.4.4+ds1/test/e2e/run_userns_test.go --- libpod-3.2.1+ds1/test/e2e/run_userns_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_userns_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -10,6 +10,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman UserNS support", func() { @@ -45,7 +46,7 @@ It("podman uidmapping and gidmapping", func() { session := podmanTest.Podman([]string{"run", "--uidmap=0:100:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("hello") Expect(ok).To(BeTrue()) }) @@ -57,7 +58,7 @@ It("podman uidmapping and gidmapping with short-opts", func() { session := podmanTest.Podman([]string{"run", "--uidmap=0:1:5000", "--gidmap=0:200:5000", "-it", "alpine", "echo", "hello"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("hello") Expect(ok).To(BeTrue()) }) @@ -65,7 +66,7 @@ It("podman uidmapping and gidmapping with a volume", func() { session := podmanTest.Podman([]string{"run", "--uidmap=0:1:500", "--gidmap=0:200:5000", "-v", "my-foo-volume:/foo:Z", "alpine", "echo", "hello"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("hello") Expect(ok).To(BeTrue()) }) @@ -73,7 +74,7 @@ It("podman uidmapping and gidmapping --net=host", func() { session := podmanTest.Podman([]string{"run", "--net=host", "--uidmap=0:1:5000", "--gidmap=0:200:5000", "alpine", "echo", "hello"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("hello") Expect(ok).To(BeTrue()) }) @@ -81,7 +82,7 @@ It("podman --userns=keep-id", func() { session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-u"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) uid := fmt.Sprintf("%d", os.Geteuid()) ok, _ := session.GrepString(uid) Expect(ok).To(BeTrue()) @@ -90,7 +91,7 @@ It("podman --userns=keep-id check passwd", func() { session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "id", "-un"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) u, err := user.Current() Expect(err).To(BeNil()) ok, _ := session.GrepString(u.Name) @@ -100,14 +101,14 @@ It("podman --userns=keep-id root owns /usr", func() { session := podmanTest.Podman([]string{"run", "--userns=keep-id", "alpine", "stat", "-c%u", "/usr"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0")) }) It("podman --userns=keep-id --user root:root", func() { session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "alpine", "id", "-u"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("0")) }) @@ -124,16 +125,16 @@ ctrName := "ctr-name" session := podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", "-d", "--stop-signal", "9", "--name", ctrName, fedoraMinimal, "sleep", "600"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) exec1 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "cat", "/etc/passwd"}) exec1.WaitWithDefaultTimeout() - Expect(exec1.ExitCode()).To(Equal(0)) + Expect(exec1).Should(Exit(0)) Expect(exec1.OutputToString()).To(ContainSubstring(userName)) exec2 := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "useradd", "testuser"}) exec2.WaitWithDefaultTimeout() - Expect(exec2.ExitCode()).To(Equal(0)) + Expect(exec2).Should(Exit(0)) }) It("podman --userns=auto", func() { @@ -156,7 +157,7 @@ for i := 0; i < 5; i++ { session := podmanTest.Podman([]string{"run", "--userns=auto", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) l := session.OutputToString() Expect(strings.Contains(l, "1024")).To(BeTrue()) m[l] = l @@ -184,22 +185,22 @@ session := podmanTest.Podman([]string{"run", "--userns=auto:size=500", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("500") session = podmanTest.Podman([]string{"run", "--userns=auto:size=3000", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ = session.GrepString("3000") session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=2000:3000", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ = session.GrepString("3001") session = podmanTest.Podman([]string{"run", "--userns=auto", "--user=4000:1000", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ = session.GrepString("4001") Expect(ok).To(BeTrue()) }) @@ -223,13 +224,13 @@ session := podmanTest.Podman([]string{"run", "--userns=auto:uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(MatchRegexp("\\s0\\s0\\s1")) session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,uidmapping=0:0:1", "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("8191") Expect(ok).To(BeTrue()) }) @@ -253,13 +254,13 @@ session := podmanTest.Podman([]string{"run", "--userns=auto:gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(MatchRegexp("\\s0\\s0\\s1")) session = podmanTest.Podman([]string{"run", "--userns=auto:size=8192,gidmapping=0:0:1", "alpine", "cat", "/proc/self/gid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("8191") Expect(ok).To(BeTrue()) }) @@ -268,19 +269,19 @@ ctrName := "userns-ctr" session := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:0:1", "--uidmap=1:1:4998", "--name", ctrName, "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // runc has an issue and we also need to join the IPC namespace. session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--ipc=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ := session.GrepString("4998") Expect(ok).To(BeTrue()) session = podmanTest.Podman([]string{"run", "--rm", "--userns=container:" + ctrName, "--net=container:" + ctrName, "alpine", "cat", "/proc/self/uid_map"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ok, _ = session.GrepString("4998") Expect(ok).To(BeTrue()) @@ -298,17 +299,17 @@ for _, tt := range tests { session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol}) inspectUID.WaitWithDefaultTimeout() - Expect(inspectUID.ExitCode()).To(Equal(0)) + Expect(inspectUID).Should(Exit(0)) Expect(inspectUID.OutputToString()).To(Equal(tt.uid)) // Make sure we're defaulting to 0. inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol}) inspectGID.WaitWithDefaultTimeout() - Expect(inspectGID.ExitCode()).To(Equal(0)) + Expect(inspectGID).Should(Exit(0)) Expect(inspectGID.OutputToString()).To(Equal(tt.gid)) } }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_volume_test.go libpod-3.4.4+ds1/test/e2e/run_volume_test.go --- libpod-3.2.1+ds1/test/e2e/run_volume_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_volume_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -13,7 +13,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/onsi/gomega/gexec" + . "github.com/onsi/gomega/gexec" ) // in-container mount point: using a path that is definitely not present @@ -50,21 +50,21 @@ session := podmanTest.Podman([]string{"run", "--rm", "-v", vol, ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches := session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("rw")) session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":ro", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches = session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("ro")) session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches = session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("rw")) @@ -73,7 +73,7 @@ // Cached is ignored session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":cached", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches = session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("rw")) @@ -82,7 +82,7 @@ // Delegated is ignored session = podmanTest.Podman([]string{"run", "--rm", "-v", vol + ":delegated", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches = session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("rw")) @@ -99,22 +99,22 @@ session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount, ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " rw")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",readonly", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",consistency=delegated,shared", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches := session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("rw")) @@ -122,29 +122,29 @@ session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=" + dest, ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " rw,nosuid,nodev,relatime - tmpfs")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup", ALPINE, "ls", "/etc/ssl"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("certs")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,tmpcopyup,notmpcopyup", ALPINE, "ls", "/etc/ssl"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=bind,src=/tmp,target=/tmp,tmpcopyup", ALPINE, "true"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=bind,src=/tmp,target=/tmp,notmpcopyup", ALPINE, "true"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) session = podmanTest.Podman([]string{"run", "--rm", "--mount", "type=tmpfs,target=/etc/ssl,notmpcopyup", ALPINE, "ls", "/etc/ssl"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("certs"))) }) @@ -153,7 +153,7 @@ os.Mkdir(mountPath, 0755) session := podmanTest.Podman([]string{"run", "-v", mountPath + ":" + dest, "-v", "/tmp" + ":" + dest, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman run with conflict between image volume and user mount succeeds", func() { @@ -167,7 +167,7 @@ Expect(err).To(BeNil()) session := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/data", mountPath), redis, "ls", "/data/test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman run with mount flag and boolean options", func() { @@ -177,12 +177,12 @@ session := podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=false", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " rw")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(dest + " ro")) session = podmanTest.Podman([]string{"run", "--rm", "--mount", mount + ",ro=true,rw=false", ALPINE, "grep", dest, "/proc/self/mountinfo"}) @@ -193,7 +193,7 @@ It("podman run with volume flag and multiple named volumes", func() { session := podmanTest.Podman([]string{"run", "--rm", "-v", "testvol1:/testvol1", "-v", "testvol2:/testvol2", ALPINE, "grep", "/testvol", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("/testvol1")) Expect(session.OutputToString()).To(ContainSubstring("/testvol2")) }) @@ -205,7 +205,7 @@ session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":" + dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches := session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(Not(ContainSubstring("noexec"))) @@ -214,7 +214,7 @@ session = podmanTest.Podman([]string{"run", "--rm", "--tmpfs", dest + ":suid,dev,exec", ALPINE, "grep", dest, "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches = session.GrepString(dest) Expect(found).Should(BeTrue()) Expect(matches[0]).To(Not(ContainSubstring("noexec"))) @@ -222,6 +222,26 @@ Expect(matches[0]).To(Not(ContainSubstring("nosuid"))) }) + // Container should start when workdir is overlayed volume + It("podman run with volume mounted as overlay and used as workdir", func() { + SkipIfRemote("Overlay volumes only work locally") + if os.Getenv("container") != "" { + Skip("Overlay mounts not supported when running in a container") + } + if rootless.IsRootless() { + if _, err := exec.LookPath("fuse-overlayfs"); err != nil { + Skip("Fuse-Overlayfs required for rootless overlay mount test") + } + } + mountPath := filepath.Join(podmanTest.TempDir, "secrets") + os.Mkdir(mountPath, 0755) + + //Container should be able to start with custom overlayed volume + session := podmanTest.Podman([]string{"run", "--rm", "-v", mountPath + ":/data:O", "--workdir=/data", ALPINE, "echo", "hello"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + }) + It("podman run with noexec can't exec", func() { session := podmanTest.Podman([]string{"run", "--rm", "-v", "/bin:/hostbin:noexec", ALPINE, "/hostbin/ls", "/"}) session.WaitWithDefaultTimeout() @@ -234,13 +254,13 @@ volName := "testvol" mkVolume := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=nodev", "testvol"}) mkVolume.WaitWithDefaultTimeout() - Expect(mkVolume.ExitCode()).To(Equal(0)) + Expect(mkVolume).Should(Exit(0)) // Volume not mounted on create - mountCmd1, err := gexec.Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) + mountCmd1, err := Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) Expect(err).To(BeNil()) mountCmd1.Wait(90) - Expect(mountCmd1.ExitCode()).To(Equal(0)) + Expect(mountCmd1).Should(Exit(0)) os.Stdout.Sync() os.Stderr.Sync() mountOut1 := strings.Join(strings.Fields(string(mountCmd1.Out.Contents())), " ") @@ -250,13 +270,13 @@ ctrName := "testctr" podmanSession := podmanTest.Podman([]string{"run", "-d", "--name", ctrName, "-v", fmt.Sprintf("%s:/testvol", volName), ALPINE, "top"}) podmanSession.WaitWithDefaultTimeout() - Expect(podmanSession.ExitCode()).To(Equal(0)) + Expect(podmanSession).Should(Exit(0)) // Volume now mounted as container is running - mountCmd2, err := gexec.Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) + mountCmd2, err := Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) Expect(err).To(BeNil()) mountCmd2.Wait(90) - Expect(mountCmd2.ExitCode()).To(Equal(0)) + Expect(mountCmd2).Should(Exit(0)) os.Stdout.Sync() os.Stderr.Sync() mountOut2 := strings.Join(strings.Fields(string(mountCmd2.Out.Contents())), " ") @@ -266,18 +286,18 @@ // Stop the container to unmount podmanStopSession := podmanTest.Podman([]string{"stop", "--time", "0", ctrName}) podmanStopSession.WaitWithDefaultTimeout() - Expect(podmanStopSession.ExitCode()).To(Equal(0)) + Expect(podmanStopSession).Should(Exit(0)) // We have to force cleanup so the unmount happens podmanCleanupSession := podmanTest.Podman([]string{"container", "cleanup", ctrName}) podmanCleanupSession.WaitWithDefaultTimeout() - Expect(podmanCleanupSession.ExitCode()).To(Equal(0)) + Expect(podmanCleanupSession).Should(Exit(0)) // Ensure volume is unmounted - mountCmd3, err := gexec.Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) + mountCmd3, err := Start(exec.Command("mount"), GinkgoWriter, GinkgoWriter) Expect(err).To(BeNil()) mountCmd3.Wait(90) - Expect(mountCmd3.ExitCode()).To(Equal(0)) + Expect(mountCmd3).Should(Exit(0)) os.Stdout.Sync() os.Stderr.Sync() mountOut3 := strings.Join(strings.Fields(string(mountCmd3.Out.Contents())), " ") @@ -288,21 +308,21 @@ It("podman named volume copyup", func() { baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", ALPINE, "ls", "/etc/apk/"}) baselineSession.WaitWithDefaultTimeout() - Expect(baselineSession.ExitCode()).To(Equal(0)) + Expect(baselineSession).Should(Exit(0)) baselineOutput := baselineSession.OutputToString() inlineVolumeSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "-v", "testvol1:/etc/apk", ALPINE, "ls", "/etc/apk/"}) inlineVolumeSession.WaitWithDefaultTimeout() - Expect(inlineVolumeSession.ExitCode()).To(Equal(0)) + Expect(inlineVolumeSession).Should(Exit(0)) Expect(inlineVolumeSession.OutputToString()).To(Equal(baselineOutput)) makeVolumeSession := podmanTest.Podman([]string{"volume", "create", "testvol2"}) makeVolumeSession.WaitWithDefaultTimeout() - Expect(makeVolumeSession.ExitCode()).To(Equal(0)) + Expect(makeVolumeSession).Should(Exit(0)) separateVolumeSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "-v", "testvol2:/etc/apk", ALPINE, "ls", "/etc/apk/"}) separateVolumeSession.WaitWithDefaultTimeout() - Expect(separateVolumeSession.ExitCode()).To(Equal(0)) + Expect(separateVolumeSession).Should(Exit(0)) Expect(separateVolumeSession.OutputToString()).To(Equal(baselineOutput)) }) @@ -315,62 +335,62 @@ baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", imgName, "ls", "/etc/apk/"}) baselineSession.WaitWithDefaultTimeout() - Expect(baselineSession.ExitCode()).To(Equal(0)) + Expect(baselineSession).Should(Exit(0)) baselineOutput := baselineSession.OutputToString() outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/etc/apk/", imgName, "ls", "/etc/apk/"}) outputSession.WaitWithDefaultTimeout() - Expect(outputSession.ExitCode()).To(Equal(0)) + Expect(outputSession).Should(Exit(0)) Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) }) It("podman named volume copyup empty directory", func() { baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", ALPINE, "ls", "/srv"}) baselineSession.WaitWithDefaultTimeout() - Expect(baselineSession.ExitCode()).To(Equal(0)) + Expect(baselineSession).Should(Exit(0)) baselineOutput := baselineSession.OutputToString() outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/srv", ALPINE, "ls", "/srv"}) outputSession.WaitWithDefaultTimeout() - Expect(outputSession.ExitCode()).To(Equal(0)) + Expect(outputSession).Should(Exit(0)) Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) }) It("podman named volume copyup of /var", func() { baselineSession := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", fedoraMinimal, "ls", "/var"}) baselineSession.WaitWithDefaultTimeout() - Expect(baselineSession.ExitCode()).To(Equal(0)) + Expect(baselineSession).Should(Exit(0)) baselineOutput := baselineSession.OutputToString() outputSession := podmanTest.Podman([]string{"run", "-t", "-i", "-v", "/var", fedoraMinimal, "ls", "/var"}) outputSession.WaitWithDefaultTimeout() - Expect(outputSession.ExitCode()).To(Equal(0)) + Expect(outputSession).Should(Exit(0)) Expect(outputSession.OutputToString()).To(Equal(baselineOutput)) }) It("podman read-only tmpfs conflict with volume", func() { session := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "-v", "tmp_volume:" + dest, ALPINE, "touch", dest + "/a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"run", "--rm", "-t", "-i", "--read-only", "--tmpfs", dest, ALPINE, "touch", dest + "/a"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) }) It("podman run with anonymous volume", func() { list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list1.WaitWithDefaultTimeout() - Expect(list1.ExitCode()).To(Equal(0)) + Expect(list1).Should(Exit(0)) Expect(list1.OutputToString()).To(Equal("")) session := podmanTest.Podman([]string{"create", "-v", "/test", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list2.WaitWithDefaultTimeout() - Expect(list2.ExitCode()).To(Equal(0)) + Expect(list2).Should(Exit(0)) arr := list2.OutputToStringArray() Expect(len(arr)).To(Equal(1)) Expect(arr[0]).To(Not(Equal(""))) @@ -379,57 +399,57 @@ It("podman rm -v removes anonymous volume", func() { list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list1.WaitWithDefaultTimeout() - Expect(list1.ExitCode()).To(Equal(0)) + Expect(list1).Should(Exit(0)) Expect(list1.OutputToString()).To(Equal("")) ctrName := "testctr" session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", "/test", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list2.WaitWithDefaultTimeout() - Expect(list2.ExitCode()).To(Equal(0)) + Expect(list2).Should(Exit(0)) arr := list2.OutputToStringArray() Expect(len(arr)).To(Equal(1)) Expect(arr[0]).To(Not(Equal(""))) remove := podmanTest.Podman([]string{"rm", "-v", ctrName}) remove.WaitWithDefaultTimeout() - Expect(remove.ExitCode()).To(Equal(0)) + Expect(remove).Should(Exit(0)) list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list3.WaitWithDefaultTimeout() - Expect(list3.ExitCode()).To(Equal(0)) + Expect(list3).Should(Exit(0)) Expect(list3.OutputToString()).To(Equal("")) }) It("podman rm -v retains named volume", func() { list1 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list1.WaitWithDefaultTimeout() - Expect(list1.ExitCode()).To(Equal(0)) + Expect(list1).Should(Exit(0)) Expect(list1.OutputToString()).To(Equal("")) ctrName := "testctr" volName := "testvol" session := podmanTest.Podman([]string{"create", "--name", ctrName, "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) list2 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list2.WaitWithDefaultTimeout() - Expect(list2.ExitCode()).To(Equal(0)) + Expect(list2).Should(Exit(0)) arr := list2.OutputToStringArray() Expect(len(arr)).To(Equal(1)) Expect(arr[0]).To(Equal(volName)) remove := podmanTest.Podman([]string{"rm", "-v", ctrName}) remove.WaitWithDefaultTimeout() - Expect(remove.ExitCode()).To(Equal(0)) + Expect(remove).Should(Exit(0)) list3 := podmanTest.Podman([]string{"volume", "list", "--quiet"}) list3.WaitWithDefaultTimeout() - Expect(list3.ExitCode()).To(Equal(0)) + Expect(list3).Should(Exit(0)) arr2 := list3.OutputToStringArray() Expect(len(arr2)).To(Equal(1)) Expect(arr2[0]).To(Equal(volName)) @@ -438,7 +458,7 @@ It("podman run image volume is not noexec", func() { session := podmanTest.Podman([]string{"run", "--rm", redis, "grep", "/data", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) }) @@ -446,22 +466,22 @@ volName := "testVol" volCreate := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", "--opt", "device=tmpfs", "--opt", "o=invalid", volName}) volCreate.WaitWithDefaultTimeout() - Expect(volCreate.ExitCode()).To(Equal(0)) + Expect(volCreate).Should(Exit(0)) volMount := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/tmp", volName), ALPINE, "ls"}) volMount.WaitWithDefaultTimeout() - Expect(volMount.ExitCode()).To(Not(Equal(0))) + Expect(volMount).To(ExitWithError()) }) It("Podman fix for CVE-2020-1726", func() { volName := "testVol" volCreate := podmanTest.Podman([]string{"volume", "create", volName}) volCreate.WaitWithDefaultTimeout() - Expect(volCreate.ExitCode()).To(Equal(0)) + Expect(volCreate).Should(Exit(0)) volPath := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Mountpoint}}", volName}) volPath.WaitWithDefaultTimeout() - Expect(volPath.ExitCode()).To(Equal(0)) + Expect(volPath).Should(Exit(0)) path := volPath.OutputToString() fileName := "thisIsATestFile" @@ -471,7 +491,7 @@ runLs := podmanTest.Podman([]string{"run", "-t", "-i", "--rm", "-v", fmt.Sprintf("%v:/etc/ssl", volName), ALPINE, "ls", "-1", "/etc/ssl"}) runLs.WaitWithDefaultTimeout() - Expect(runLs.ExitCode()).To(Equal(0)) + Expect(runLs).Should(Exit(0)) outputArr := runLs.OutputToStringArray() Expect(len(outputArr)).To(Equal(1)) Expect(strings.Contains(outputArr[0], fileName)).To(BeTrue()) @@ -486,7 +506,7 @@ ctrName := "testCtr" create := podmanTest.Podman([]string{"create", "-v", "/tmp:/test", "--name", ctrName, image, "ls"}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) data := podmanTest.InspectContainer(ctrName) Expect(len(data)).To(Equal(1)) @@ -514,7 +534,7 @@ // Make sure host directory gets mounted in to container as overlay session := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "grep", "/run/test", "/proc/self/mountinfo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, matches := session.GrepString("/run/test") Expect(found).Should(BeTrue()) Expect(matches[0]).To(ContainSubstring("overlay")) @@ -522,37 +542,37 @@ // Make sure host files show up in the container session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "ls", "/run/test/test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Make sure modifications in container do not show up on host session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "touch", "/run/test/container"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) _, err = os.Stat(filepath.Join(mountPath, "container")) Expect(err).To(Not(BeNil())) // Make sure modifications in container disappear when container is stopped session = podmanTest.Podman([]string{"create", "-v", fmt.Sprintf("%s:/run/test:O", mountPath), ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "-l"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "-l", "touch", "/run/test/container"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "-l", "ls", "/run/test/container"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stop", "-l"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "-l"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "-l", "ls", "/run/test/container"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("overlay volume conflicts with named volume and mounts", func() { @@ -570,25 +590,27 @@ session := podmanTest.Podman([]string{"volume", "create", volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // overlay and named volume destinations conflict session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "-v", fmt.Sprintf("%s:%s", volName, mountDest), ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) // overlay and bind mount destinations conflict session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=bind,src=%s,target=%s", mountSrc, mountDest), ALPINE}) - Expect(session.ExitCode()).To(Not(Equal(0))) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) // overlay and tmpfs mount destinations conflict session = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:%s:O", mountPath, mountDest), "--mount", fmt.Sprintf("type=tmpfs,target=%s", mountDest), ALPINE}) - Expect(session.ExitCode()).To(Not(Equal(0))) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) }) It("same volume in multiple places does not deadlock", func() { volName := "testVol1" session := podmanTest.Podman([]string{"run", "-t", "-i", "-v", fmt.Sprintf("%s:/test1", volName), "-v", fmt.Sprintf("%s:/test2", volName), "--rm", ALPINE, "sh", "-c", "mount | grep /test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) }) @@ -625,20 +647,20 @@ session := podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "-v", vol, ALPINE, "stat", "-c", "%u:%g", dest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, _ := session.GrepString("888:888") Expect(found).Should(BeTrue()) session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--userns", "auto", "-v", vol, ALPINE, "stat", "-c", "%u:%g", dest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, _ = session.GrepString("888:888") Expect(found).Should(BeTrue()) vol = vol + ",O" session = podmanTest.Podman([]string{"run", "--rm", "--user", "888:888", "--userns", "keep-id", "-v", vol, ALPINE, "stat", "-c", "%u:%g", dest}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) found, _ = session.GrepString("888:888") Expect(found).Should(BeTrue()) }) @@ -654,18 +676,62 @@ test1 := podmanTest.Podman([]string{"run", "-v", "testvol1:/test", imgName, "bash", "-c", "ls -al /test | grep -v root | grep -v total"}) test1.WaitWithDefaultTimeout() - Expect(test1.ExitCode()).To(Equal(0)) + Expect(test1).Should(Exit(0)) Expect(strings.Contains(test1.OutputToString(), testString)).To(BeTrue()) volName := "testvol2" vol := podmanTest.Podman([]string{"volume", "create", volName}) vol.WaitWithDefaultTimeout() - Expect(vol.ExitCode()).To(Equal(0)) + Expect(vol).Should(Exit(0)) test2 := podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:/test", volName), imgName, "bash", "-c", "ls -al /test | grep -v root | grep -v total"}) test2.WaitWithDefaultTimeout() - Expect(test2.ExitCode()).To(Equal(0)) + Expect(test2).Should(Exit(0)) Expect(strings.Contains(test2.OutputToString(), testString)).To(BeTrue()) }) + + It("podman run with named volume check if we honor permission of target dir", func() { + session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "stat", "-c", "%a %Y", "/var/tmp"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + perms := session.OutputToString() + + session = podmanTest.Podman([]string{"run", "--rm", "-v", "test:/var/tmp", ALPINE, "stat", "-c", "%a %Y", "/var/tmp"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).To(Equal(perms)) + }) + + It("podman volume with uid and gid works", func() { + volName := "testVol" + volCreate := podmanTest.Podman([]string{"volume", "create", "--opt", "o=uid=1000", volName}) + volCreate.WaitWithDefaultTimeout() + Expect(volCreate).Should(Exit(0)) + + volMount := podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "stat", "-c", "%u", "/test"}) + volMount.WaitWithDefaultTimeout() + Expect(volMount).Should(Exit(0)) + Expect(volMount.OutputToString()).To(Equal("1000")) + + volName = "testVol2" + volCreate = podmanTest.Podman([]string{"volume", "create", "--opt", "o=gid=1000", volName}) + volCreate.WaitWithDefaultTimeout() + Expect(volCreate).Should(Exit(0)) + + volMount = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "stat", "-c", "%g", "/test"}) + volMount.WaitWithDefaultTimeout() + Expect(volMount).Should(Exit(0)) + Expect(volMount.OutputToString()).To(Equal("1000")) + + volName = "testVol3" + volCreate = podmanTest.Podman([]string{"volume", "create", "--opt", "o=uid=1000,gid=1000", volName}) + volCreate.WaitWithDefaultTimeout() + Expect(volCreate).Should(Exit(0)) + + volMount = podmanTest.Podman([]string{"run", "--rm", "-v", fmt.Sprintf("%s:/test", volName), ALPINE, "stat", "-c", "%u:%g", "/test"}) + volMount.WaitWithDefaultTimeout() + Expect(volMount).Should(Exit(0)) + Expect(volMount.OutputToString()).To(Equal("1000:1000")) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/run_working_dir_test.go libpod-3.4.4+ds1/test/e2e/run_working_dir_test.go --- libpod-3.2.1+ds1/test/e2e/run_working_dir_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/run_working_dir_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman run", func() { @@ -36,14 +37,14 @@ It("podman run a container without workdir", func() { session := podmanTest.Podman([]string{"run", ALPINE, "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/")) }) It("podman run a container using non existing --workdir", func() { session := podmanTest.Podman([]string{"run", "--workdir", "/home/foobar", ALPINE, "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(126)) + Expect(session).Should(Exit(126)) }) It("podman run a container on an image with a workdir", func() { @@ -54,7 +55,7 @@ session := podmanTest.Podman([]string{"run", "test", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/etc/foobar")) session = podmanTest.Podman([]string{"run", "test", "ls", "-ld", "."}) @@ -63,7 +64,7 @@ session = podmanTest.Podman([]string{"run", "--workdir", "/home/foobar", "test", "pwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal("/home/foobar")) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/save_test.go libpod-3.4.4+ds1/test/e2e/save_test.go --- libpod-3.2.1+ds1/test/e2e/save_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/save_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman save", func() { @@ -42,7 +43,7 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save oci flag", func() { @@ -50,7 +51,7 @@ save := podmanTest.Podman([]string{"save", "-o", outfile, "--format", "oci-archive", ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save with stdout", func() { @@ -59,7 +60,7 @@ save := podmanTest.Podman([]string{"save", ALPINE, ">", outfile}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save quiet flag", func() { @@ -67,7 +68,7 @@ save := podmanTest.Podman([]string{"save", "-q", "-o", outfile, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save bogus image", func() { @@ -86,7 +87,7 @@ save := podmanTest.Podman([]string{"save", "--format", "oci-dir", "-o", outdir, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save to directory with v2s2 docker format", func() { @@ -97,7 +98,7 @@ save := podmanTest.Podman([]string{"save", "--format", "docker-dir", "-o", outdir, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save to directory with docker format and compression", func() { @@ -108,7 +109,7 @@ save := podmanTest.Podman([]string{"save", "--compress", "--format", "docker-dir", "-o", outdir, ALPINE}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save to directory with --compress but not use docker-dir and oci-dir", func() { @@ -120,12 +121,12 @@ save := podmanTest.Podman([]string{"save", "--compress", "--format", "docker-archive", "-o", outdir, ALPINE}) save.WaitWithDefaultTimeout() // should not be 0 - Expect(save.ExitCode()).ToNot(Equal(0)) + Expect(save).To(ExitWithError()) save = podmanTest.Podman([]string{"save", "--compress", "--format", "oci-archive", "-o", outdir, ALPINE}) save.WaitWithDefaultTimeout() // should not be 0 - Expect(save.ExitCode()).ToNot(Equal(0)) + Expect(save).To(ExitWithError()) }) @@ -154,7 +155,7 @@ port := 5000 session := podmanTest.Podman([]string{"run", "-d", "--name", "registry", "-p", strings.Join([]string{strconv.Itoa(port), strconv.Itoa(port)}, ":"), "quay.io/libpod/registry:2.6"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { Skip("Cannot start docker registry.") } @@ -183,19 +184,19 @@ session = podmanTest.Podman([]string{"tag", ALPINE, "localhost:5000/alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"push", "--tls-verify=false", "--sign-by", "foo@bar.com", "localhost:5000/alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", ALPINE, "localhost:5000/alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"pull", "--tls-verify=false", "--signature-policy=sign/policy.json", "localhost:5000/alpine"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) outfile := filepath.Join(podmanTest.TempDir, "temp.tar") save := podmanTest.Podman([]string{"save", "remove-signatures=true", "-o", outfile, "localhost:5000/alpine"}) @@ -207,13 +208,13 @@ // pull a digest reference session := podmanTest.Podman([]string{"pull", ALPINELISTDIGEST}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // save a digest reference should exit without error. outfile := filepath.Join(podmanTest.TempDir, "temp.tar") save := podmanTest.Podman([]string{"save", "-o", outfile, ALPINELISTDIGEST}) save.WaitWithDefaultTimeout() - Expect(save.ExitCode()).To(Equal(0)) + Expect(save).Should(Exit(0)) }) It("podman save --multi-image-archive (tagged images)", func() { @@ -224,7 +225,7 @@ // Refer to images via ID instead of tag. session := podmanTest.Podman([]string{"images", "--format", "{{.ID}}"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) ids := session.OutputToStringArray() Expect(len(RESTORE_IMAGES), len(ids)) @@ -239,17 +240,17 @@ outfile := filepath.Join(podmanTest.TempDir, "temp.tar") session := podmanTest.Podman(append([]string{"save", "-o", outfile, "--multi-image-archive"}, images...)) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Remove all images. session = podmanTest.Podman([]string{"rmi", "-af"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Now load the archive. session = podmanTest.Podman([]string{"load", "-i", outfile}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Grep for each image in the `podman load` output. for _, image := range images { found, _ := session.GrepString(image) @@ -260,6 +261,6 @@ for _, image := range images { session = podmanTest.Podman([]string{"image", "exists", image}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } } diff -Nru libpod-3.2.1+ds1/test/e2e/search_test.go libpod-3.4.4+ds1/test/e2e/search_test.go --- libpod-3.2.1+ds1/test/e2e/search_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/search_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -12,6 +12,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) type endpoint struct { @@ -87,7 +88,7 @@ It("podman search", func() { search := podmanTest.Podman([]string{"search", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) Expect(search.LineInOutputContains("docker.io/library/alpine")).To(BeTrue()) }) @@ -95,14 +96,14 @@ It("podman search single registry flag", func() { search := podmanTest.Podman([]string{"search", "quay.io/skopeo/stable:latest"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(search.LineInOutputContains("quay.io/skopeo/stable")).To(BeTrue()) }) It("podman search image with description", func() { search := podmanTest.Podman([]string{"search", "quay.io/libpod/whalesay"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) output := string(search.Out.Contents()) match, _ := regexp.MatchString(`(?m)^quay.io\s+quay.io/libpod/whalesay\s+Static image used for automated testing.+$`, output) Expect(match).To(BeTrue()) @@ -111,7 +112,7 @@ It("podman search format flag", func() { search := podmanTest.Podman([]string{"search", "--format", "table {{.Index}} {{.Name}}", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) Expect(search.LineInOutputContains("docker.io/library/alpine")).To(BeTrue()) }) @@ -119,7 +120,7 @@ It("podman search format json", func() { search := podmanTest.Podman([]string{"search", "--format", "json", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(search.IsJSONOutputValid()).To(BeTrue()) Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) }) @@ -127,7 +128,7 @@ It("podman search format json list tags", func() { search := podmanTest.Podman([]string{"search", "--list-tags", "--format", "json", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(search.IsJSONOutputValid()).To(BeTrue()) Expect(search.OutputToString()).To(ContainSubstring("docker.io/library/alpine")) Expect(search.OutputToString()).To(ContainSubstring("3.10")) @@ -137,7 +138,7 @@ It("podman search no-trunc flag", func() { search := podmanTest.Podman([]string{"search", "--no-trunc", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 1)) Expect(search.LineInOutputContains("docker.io/library/alpine")).To(BeTrue()) Expect(search.LineInOutputContains("...")).To(BeFalse()) @@ -146,24 +147,24 @@ It("podman search limit flag", func() { search := podmanTest.Podman([]string{"search", "docker.io/alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) - Expect(len(search.OutputToStringArray())).To(Equal(26)) + Expect(search).Should(Exit(0)) + Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 10)) search = podmanTest.Podman([]string{"search", "--limit", "3", "docker.io/alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(Equal(4)) search = podmanTest.Podman([]string{"search", "--limit", "30", "docker.io/alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(Equal(31)) }) It("podman search with filter stars", func() { search := podmanTest.Podman([]string{"search", "--filter", "stars=10", "--format", "{{.Stars}}", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) output := search.OutputToStringArray() for i := 0; i < len(output); i++ { Expect(strconv.Atoi(output[i])).To(BeNumerically(">=", 10)) @@ -173,7 +174,7 @@ It("podman search with filter is-official", func() { search := podmanTest.Podman([]string{"search", "--filter", "is-official", "--format", "{{.Official}}", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) output := search.OutputToStringArray() for i := 0; i < len(output); i++ { Expect(output[i]).To(Equal("[OK]")) @@ -183,7 +184,7 @@ It("podman search with filter is-automated", func() { search := podmanTest.Podman([]string{"search", "--filter", "is-automated=false", "--format", "{{.Automated}}", "alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) output := search.OutputToStringArray() for i := 0; i < len(output); i++ { Expect(output[i]).To(Equal("")) @@ -201,7 +202,7 @@ "-p", fmt.Sprintf("%s:5000", registryEndpoints[0].Port), registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) fakereg.WaitWithDefaultTimeout() - Expect(fakereg.ExitCode()).To(Equal(0)) + Expect(fakereg).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -213,7 +214,7 @@ // if this test succeeded, there will be no output (there is no entry named fake/image:andtag in an empty registry) // and the exit code will be 0 - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(search.OutputToString()).Should(BeEmpty()) Expect(search.ErrorToString()).Should(BeEmpty()) }) @@ -228,7 +229,7 @@ "-p", fmt.Sprintf("%s:5000", registryEndpoints[3].Port), registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) registry.WaitWithDefaultTimeout() - Expect(registry.ExitCode()).To(Equal(0)) + Expect(registry).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry3", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -238,17 +239,17 @@ image := fmt.Sprintf("%s/my-alpine", registryEndpoints[3].Address()) push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, image}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) search := podmanTest.Podman([]string{"search", image, "--tls-verify=false"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(search.OutputToString()).ShouldNot(BeEmpty()) // podman search v2 registry with empty query searchEmpty := podmanTest.Podman([]string{"search", fmt.Sprintf("%s/", registryEndpoints[3].Address()), "--tls-verify=false"}) searchEmpty.WaitWithDefaultTimeout() - Expect(searchEmpty.ExitCode()).To(BeZero()) + Expect(searchEmpty).Should(Exit(0)) Expect(len(searchEmpty.OutputToStringArray())).To(BeNumerically(">=", 1)) match, _ := search.GrepString("my-alpine") Expect(match).Should(BeTrue()) @@ -264,7 +265,7 @@ registry := podmanTest.Podman([]string{"run", "-d", "-p", fmt.Sprintf("%s:5000", registryEndpoints[4].Port), "--name", "registry4", registry, "/entrypoint.sh", "/etc/docker/registry/config.yml"}) registry.WaitWithDefaultTimeout() - Expect(registry.ExitCode()).To(Equal(0)) + Expect(registry).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry4", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -274,7 +275,7 @@ image := fmt.Sprintf("%s/my-alpine", registryEndpoints[4].Address()) push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, image}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) // registries.conf set up var buffer bytes.Buffer @@ -289,7 +290,7 @@ search := podmanTest.Podman([]string{"search", image}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) match, _ := search.GrepString("my-alpine") Expect(match).Should(BeTrue()) Expect(search.ErrorToString()).Should(BeEmpty()) @@ -307,7 +308,7 @@ registry := podmanTest.Podman([]string{"run", "-d", "-p", fmt.Sprintf("%s:5000", registryEndpoints[5].Port), "--name", "registry5", registry}) registry.WaitWithDefaultTimeout() - Expect(registry.ExitCode()).To(Equal(0)) + Expect(registry).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry5", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -317,7 +318,7 @@ image := fmt.Sprintf("%s/my-alpine", registryEndpoints[5].Address()) push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, image}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) var buffer bytes.Buffer registryFileTmpl.Execute(&buffer, registryEndpoints[5]) @@ -327,7 +328,7 @@ search := podmanTest.Podman([]string{"search", image, "--tls-verify=true"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(125)) + Expect(search).Should(Exit(125)) Expect(search.OutputToString()).Should(BeEmpty()) match, _ := search.ErrorGrepString("error") Expect(match).Should(BeTrue()) @@ -345,7 +346,7 @@ registry := podmanTest.Podman([]string{"run", "-d", "-p", fmt.Sprintf("%s:5000", registryEndpoints[6].Port), "--name", "registry6", registry}) registry.WaitWithDefaultTimeout() - Expect(registry.ExitCode()).To(Equal(0)) + Expect(registry).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry6", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -355,7 +356,7 @@ image := fmt.Sprintf("%s/my-alpine", registryEndpoints[6].Address()) push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, image}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) var buffer bytes.Buffer registryFileBadTmpl.Execute(&buffer, registryEndpoints[6]) @@ -370,7 +371,7 @@ search := podmanTest.Podman([]string{"search", image}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(125)) + Expect(search).Should(Exit(125)) Expect(search.OutputToString()).Should(BeEmpty()) match, _ := search.ErrorGrepString("error") Expect(match).Should(BeTrue()) @@ -391,7 +392,7 @@ registryLocal := podmanTest.Podman([]string{"run", "-d", "--net=host", "-p", fmt.Sprintf("%s:5000", registryEndpoints[7].Port), "--name", "registry7", registry}) registryLocal.WaitWithDefaultTimeout() - Expect(registryLocal.ExitCode()).To(Equal(0)) + Expect(registryLocal).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry7", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -399,7 +400,7 @@ registryLocal = podmanTest.Podman([]string{"run", "-d", "-p", "6000:5000", "--name", "registry8", registry}) registryLocal.WaitWithDefaultTimeout() - Expect(registryLocal.ExitCode()).To(Equal(0)) + Expect(registryLocal).Should(Exit(0)) if !WaitContainerReady(podmanTest, "registry8", "listening on", 20, 1) { Skip("Cannot start docker registry.") @@ -408,7 +409,7 @@ podmanTest.RestoreArtifact(ALPINE) push := podmanTest.Podman([]string{"push", "--tls-verify=false", "--remove-signatures", ALPINE, "localhost:6000/my-alpine"}) push.WaitWithDefaultTimeout() - Expect(push.ExitCode()).To(Equal(0)) + Expect(push).Should(Exit(0)) // registries.conf set up var buffer bytes.Buffer @@ -424,7 +425,7 @@ search := podmanTest.Podman([]string{"search", "my-alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(125)) + Expect(search).Should(Exit(125)) Expect(search.OutputToString()).Should(BeEmpty()) match, _ := search.ErrorGrepString("error") Expect(match).Should(BeTrue()) @@ -437,35 +438,35 @@ It("podman search fail with nonexistent --authfile", func() { search := podmanTest.Podman([]string{"search", "--authfile", "/tmp/nonexistent", ALPINE}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Not(Equal(0))) + Expect(search).To(ExitWithError()) }) It("podman search with wildcards", func() { search := podmanTest.Podman([]string{"search", "--limit", "30", "registry.redhat.io/*"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(Equal(31)) search = podmanTest.Podman([]string{"search", "registry.redhat.io/*openshift*"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray()) > 1).To(BeTrue()) }) It("podman search repository tags", func() { search := podmanTest.Podman([]string{"search", "--list-tags", "--limit", "30", "docker.io/library/alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) + Expect(search).Should(Exit(0)) Expect(len(search.OutputToStringArray())).To(Equal(31)) search = podmanTest.Podman([]string{"search", "--list-tags", "docker.io/library/alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) - Expect(len(search.OutputToStringArray()) > 2).To(BeTrue()) + Expect(search).Should(Exit(0)) + Expect(len(search.OutputToStringArray())).To(BeNumerically(">", 2)) search = podmanTest.Podman([]string{"search", "--filter=is-official", "--list-tags", "docker.io/library/alpine"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Not(Equal(0))) + Expect(search).To(ExitWithError()) search = podmanTest.Podman([]string{"search", "--list-tags", "docker.io/library/"}) search.WaitWithDefaultTimeout() @@ -475,7 +476,7 @@ It("podman search with limit over 100", func() { search := podmanTest.Podman([]string{"search", "--limit", "130", "registry.redhat.io/rhel"}) search.WaitWithDefaultTimeout() - Expect(search.ExitCode()).To(Equal(0)) - Expect(len(search.OutputToStringArray())).To(Equal(131)) + Expect(search).Should(Exit(0)) + Expect(len(search.OutputToStringArray())).To(BeNumerically("<=", 131)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/secret_test.go libpod-3.4.4+ds1/test/e2e/secret_test.go --- libpod-3.2.1+ds1/test/e2e/secret_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/secret_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman secret", func() { @@ -39,15 +40,19 @@ err := ioutil.WriteFile(secretFilePath, []byte("mysecret"), 0755) Expect(err).To(BeNil()) - session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) + session := podmanTest.Podman([]string{"secret", "create", "--driver-opts", "opt1=val", "a", secretFilePath}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.ID}}", secrID}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(secrID)) + inspect = podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.Spec.Driver.Options}}", secrID}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).Should(Exit(0)) + Expect(inspect.OutputToString()).To(ContainSubstring("opt1:val")) }) It("podman secret create bad name should fail", func() { @@ -57,7 +62,7 @@ session := podmanTest.Podman([]string{"secret", "create", "?!", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("podman secret inspect", func() { @@ -68,11 +73,11 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"secret", "inspect", secrID}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) }) @@ -84,11 +89,11 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.ID}}", secrID}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(secrID)) }) @@ -100,16 +105,16 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"secret", "create", "b", secretFilePath}) session2.WaitWithDefaultTimeout() secrID2 := session2.OutputToString() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) inspect := podmanTest.Podman([]string{"secret", "inspect", secrID, secrID2}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.IsJSONOutputValid()).To(BeTrue()) }) @@ -120,7 +125,7 @@ inspect := podmanTest.Podman([]string{"secret", "inspect", "bogus"}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Not(Equal(0))) + Expect(inspect).To(ExitWithError()) }) @@ -131,11 +136,11 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) list := podmanTest.Podman([]string{"secret", "ls"}) list.WaitWithDefaultTimeout() - Expect(list.ExitCode()).To(Equal(0)) + Expect(list).Should(Exit(0)) Expect(len(list.OutputToStringArray())).To(Equal(2)) }) @@ -147,12 +152,12 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) list := podmanTest.Podman([]string{"secret", "ls", "--format", "table {{.Name}}"}) list.WaitWithDefaultTimeout() - Expect(list.ExitCode()).To(Equal(0)) + Expect(list).Should(Exit(0)) Expect(len(list.OutputToStringArray())).To(Equal(2), list.OutputToString()) }) @@ -164,16 +169,16 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) removed := podmanTest.Podman([]string{"secret", "rm", "a"}) removed.WaitWithDefaultTimeout() - Expect(removed.ExitCode()).To(Equal(0)) + Expect(removed).Should(Exit(0)) Expect(removed.OutputToString()).To(Equal(secrID)) session = podmanTest.Podman([]string{"secret", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(1)) }) @@ -184,18 +189,18 @@ session := podmanTest.Podman([]string{"secret", "create", "a", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"secret", "create", "b", secretFilePath}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) removed := podmanTest.Podman([]string{"secret", "rm", "-a"}) removed.WaitWithDefaultTimeout() - Expect(removed.ExitCode()).To(Equal(0)) + Expect(removed).Should(Exit(0)) session = podmanTest.Podman([]string{"secret", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(1)) }) @@ -204,7 +209,7 @@ session := podmanTest.Podman([]string{"secret", "create", "--env", "a", "MYENVVAR"}) session.WaitWithDefaultTimeout() secrID := session.OutputToString() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) os.Setenv("MYENVVAR", "somedata") if IsRemote() { @@ -214,11 +219,11 @@ session = podmanTest.Podman([]string{"secret", "create", "--env", "a", "MYENVVAR"}) session.WaitWithDefaultTimeout() secrID = session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"secret", "inspect", "--format", "{{.ID}}", secrID}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(inspect.OutputToString()).To(Equal(secrID)) }) diff -Nru libpod-3.2.1+ds1/test/e2e/start_test.go libpod-3.4.4+ds1/test/e2e/start_test.go --- libpod-3.2.1+ds1/test/e2e/start_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/start_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,79 +39,81 @@ It("podman start bogus container", func() { session := podmanTest.Podman([]string{"start", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman start single container by id", func() { session := podmanTest.Podman([]string{"create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"start", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman start --rm removed on failure", func() { session := podmanTest.Podman([]string{"create", "--name=test", "--rm", ALPINE, "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) session = podmanTest.Podman([]string{"container", "exists", "test"}) - Expect(session.ExitCode()).To(Not(Equal(0))) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) }) It("podman start --rm --attach removed on failure", func() { session := podmanTest.Podman([]string{"create", "--rm", ALPINE, "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"start", "--attach", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) session = podmanTest.Podman([]string{"container", "exists", cid}) - Expect(session.ExitCode()).To(Not(Equal(0))) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) }) It("podman container start single container by id", func() { session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"container", "start", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(cid)) }) It("podman container start single container by short id", func() { session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() shortID := cid[0:10] session = podmanTest.Podman([]string{"container", "start", shortID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(shortID)) }) It("podman container start single container by short id", func() { session := podmanTest.Podman([]string{"container", "create", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() shortID := cid[0:10] session = podmanTest.Podman([]string{"container", "start", shortID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(shortID)) session = podmanTest.Podman([]string{"stop", shortID}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(shortID)) }) @@ -119,10 +121,10 @@ name := "foobar99" session := podmanTest.Podman([]string{"create", "--name", name, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", name}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if podmanTest.RemoteTest { Skip("Container-start name check doesn't work on remote client. It always returns the full ID.") } @@ -132,12 +134,12 @@ It("podman start single container with attach and test the signal", func() { session := podmanTest.Podman([]string{"create", "--entrypoint", "sh", ALPINE, "-c", "exit 1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"start", "--attach", cid}) session.WaitWithDefaultTimeout() // It should forward the signal - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman start multiple containers", func() { @@ -149,7 +151,7 @@ cid2 := session2.OutputToString() session = podmanTest.Podman([]string{"start", cid1, cid2}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman start multiple containers with bogus", func() { @@ -158,25 +160,25 @@ cid1 := session.OutputToString() session = podmanTest.Podman([]string{"start", cid1, "doesnotexist"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman multiple containers -- attach should fail", func() { session := podmanTest.Podman([]string{"create", "--name", "foobar1", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "--name", "foobar2", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "-a", "foobar1", "foobar2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman failed to start with --rm should delete the container", func() { session := podmanTest.Podman([]string{"create", "--name", "test1", "-it", "--rm", ALPINE, "foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) start := podmanTest.Podman([]string{"start", "test1"}) start.WaitWithDefaultTimeout() @@ -215,12 +217,12 @@ pidfile := tempdir + "pidfile" session := podmanTest.Podman([]string{"create", "--pidfile", pidfile, ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"start", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) readFirstLine := func(path string) string { content, err := ioutil.ReadFile(path) Expect(err).To(BeNil()) diff -Nru libpod-3.2.1+ds1/test/e2e/stats_test.go libpod-3.4.4+ds1/test/e2e/stats_test.go --- libpod-3.2.1+ds1/test/e2e/stats_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/stats_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,5 +1,3 @@ -// +build - package integration import ( @@ -11,6 +9,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) // TODO: we need to check the output. Currently, we only check the exit codes @@ -23,6 +22,9 @@ BeforeEach(func() { SkipIfRootlessCgroupsV1("stats not supported on cgroupv1 for rootless users") + if isContainerized() { + SkipIfCgroupV1("stats not supported inside cgroupv1 container environment") + } var err error tempdir, err = CreateTempDirInTempDir() if err != nil { @@ -43,51 +45,96 @@ It("podman stats with bogus container", func() { session := podmanTest.Podman([]string{"stats", "--no-stream", "123"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman stats on a running container", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"stats", "--no-stream", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman stats on all containers", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--no-stream", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman stats on all running containers", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--no-stream"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman stats only output cids", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--all", "--no-stream", "--format", "\"{{.ID}}\""}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + }) + + It("podman stats with GO template", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--format", "table {{.ID}} {{.AVGCPU}} {{.MemUsage}} {{.CPU}} {{.NetIO}} {{.BlockIO}} {{.PIDS}}"}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(Exit(0)) + }) + + It("podman stats with invalid GO template", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--format", "\"table {{.ID}} {{.NoSuchField}} \""}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(ExitWithError()) + }) + + It("podman stats with negative interval", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--interval=-1"}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(ExitWithError()) + }) + + It("podman stats with zero interval", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--interval=0"}) + stats.WaitWithDefaultTimeout() + Expect(stats).To(ExitWithError()) + }) + + It("podman stats with interval", func() { + session := podmanTest.RunTopContainer("") + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + stats := podmanTest.Podman([]string{"stats", "-a", "--no-reset", "--no-stream", "--interval=5"}) + stats.WaitWithDefaultTimeout() + Expect(stats).Should(Exit(0)) }) It("podman stats with json output", func() { var found bool session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) for i := 0; i < 5; i++ { ps := podmanTest.Podman([]string{"ps", "-q"}) ps.WaitWithDefaultTimeout() @@ -100,42 +147,42 @@ Expect(found).To(BeTrue()) stats := podmanTest.Podman([]string{"stats", "--all", "--no-stream", "--format", "json"}) stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats).Should(Exit(0)) Expect(stats.IsJSONOutputValid()).To(BeTrue()) }) It("podman stats on a container with no net ns", func() { session := podmanTest.Podman([]string{"run", "-d", "--net", "none", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--no-stream", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman stats on a container that joined another's net ns", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"run", "-d", "--net", fmt.Sprintf("container:%s", cid), ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--no-stream", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman stats on container with forced slirp4netns", func() { // This will force the slirp4netns net mode to be tested as root session := podmanTest.Podman([]string{"run", "-d", "--net", "slirp4netns", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--no-stream", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) // Regression test for #8265 @@ -149,19 +196,19 @@ session := podmanTest.Podman([]string{"run", "-d", "--name", ctrNoLimit0, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--name", ctrNoLimit1, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "-d", "--name", ctrWithLimit, "--memory", "50m", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stats", "--no-stream", "--format", "{{.MemLimit}}", ctrNoLimit0, ctrNoLimit1, ctrWithLimit}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // We have three containers. The unlimited ones need to have // the same limit, the limited one a lower one. diff -Nru libpod-3.2.1+ds1/test/e2e/stop_test.go libpod-3.4.4+ds1/test/e2e/stop_test.go --- libpod-3.2.1+ds1/test/e2e/stop_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/stop_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman stop", func() { @@ -37,18 +38,18 @@ It("podman stop bogus container", func() { session := podmanTest.Podman([]string{"stop", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman stop --ignore bogus container", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"stop", "--ignore", "foobar", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(ContainSubstring(cid)) }) @@ -56,84 +57,84 @@ It("podman stop container by id", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() session = podmanTest.Podman([]string{"stop", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop container by name", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stop", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman container stop by name", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"container", "stop", "test1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop stopped container", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"stop", "test1"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) session3 := podmanTest.Podman([]string{"stop", "test1"}) session3.WaitWithDefaultTimeout() - Expect(session3.ExitCode()).To(Equal(0)) + Expect(session3).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop all containers -t", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid1 := session.OutputToString() session = podmanTest.RunTopContainer("test2") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid2 := session.OutputToString() session = podmanTest.RunTopContainer("test3") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid3 := session.OutputToString() session = podmanTest.Podman([]string{"stop", "-a", "-t", "1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(ContainSubstring(cid1)) Expect(output).To(ContainSubstring(cid2)) @@ -141,42 +142,42 @@ finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop container --time", func() { session := podmanTest.RunTopContainer("test4") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stop", "--time", "0", "test4"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(ContainSubstring("test4")) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop container --timeout", func() { session := podmanTest.Podman([]string{"run", "-d", "--name", "test5", ALPINE, "sleep", "100"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stop", "--timeout", "0", "test5"}) // Without timeout container stops in 10 seconds // If not stopped in 5 seconds, then --timeout did not work session.Wait(5) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) output := session.OutputToString() Expect(output).To(ContainSubstring("test5")) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) @@ -184,55 +185,66 @@ SkipIfRemote("--latest flag n/a") session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"stop", "-l", "-t", "1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop all containers with one stopped", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.RunTopContainer("test2") session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) cid := "-l" if IsRemote() { cid = "test2" } session3 := podmanTest.Podman([]string{"stop", cid, "-t", "1"}) session3.WaitWithDefaultTimeout() - Expect(session3.ExitCode()).To(Equal(0)) + Expect(session3).Should(Exit(0)) session4 := podmanTest.Podman([]string{"stop", "-a", "-t", "1"}) session4.WaitWithDefaultTimeout() - Expect(session4.ExitCode()).To(Equal(0)) + Expect(session4).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) It("podman stop all containers with one created", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session2 := podmanTest.Podman([]string{"create", ALPINE, "/bin/sh"}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) session3 := podmanTest.Podman([]string{"stop", "-a", "-t", "1"}) session3.WaitWithDefaultTimeout() - Expect(session3.ExitCode()).To(Equal(0)) + Expect(session3).Should(Exit(0)) finalCtrs := podmanTest.Podman([]string{"ps", "-q"}) finalCtrs.WaitWithDefaultTimeout() - Expect(finalCtrs.ExitCode()).To(Equal(0)) + Expect(finalCtrs).Should(Exit(0)) Expect(strings.TrimSpace(finalCtrs.OutputToString())).To(Equal("")) }) + It("podman stop should return silent success on stopping configured containers", func() { + // following container is not created on OCI runtime + // so we return success and assume that is is stopped + session2 := podmanTest.Podman([]string{"create", "--name", "stopctr", ALPINE, "/bin/sh"}) + session2.WaitWithDefaultTimeout() + Expect(session2).Should(Exit(0)) + session3 := podmanTest.Podman([]string{"stop", "stopctr"}) + session3.WaitWithDefaultTimeout() + Expect(session3).Should(Exit(0)) + }) + It("podman stop --cidfile", func() { tmpDir, err := ioutil.TempDir("", "") @@ -243,16 +255,16 @@ session := podmanTest.Podman([]string{"create", "--cidfile", tmpFile, ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToStringArray()[0] session = podmanTest.Podman([]string{"start", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"stop", "--cidfile", tmpFile}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToString() Expect(output).To(ContainSubstring(cid)) }) @@ -268,19 +280,19 @@ session := podmanTest.Podman([]string{"run", "--cidfile", tmpFile1, "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid1 := session.OutputToStringArray()[0] Expect(podmanTest.NumberOfContainers()).To(Equal(1)) session = podmanTest.Podman([]string{"run", "--cidfile", tmpFile2, "-d", ALPINE, "top"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid2 := session.OutputToStringArray()[0] Expect(podmanTest.NumberOfContainers()).To(Equal(2)) result := podmanTest.Podman([]string{"stop", "--cidfile", tmpFile1, "--cidfile", tmpFile2}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) output := result.OutputToString() Expect(output).To(ContainSubstring(cid1)) Expect(output).To(ContainSubstring(cid2)) @@ -292,52 +304,52 @@ result := podmanTest.Podman([]string{"stop", "--cidfile", "foobar", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) result = podmanTest.Podman([]string{"stop", "--cidfile", "foobar", "--all"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) result = podmanTest.Podman([]string{"stop", "--cidfile", "foobar", "--all", "--latest"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) result = podmanTest.Podman([]string{"stop", "--latest", "--all"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman stop --all", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) session = podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2)) session = podmanTest.Podman([]string{"stop", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) It("podman stop --ignore", func() { session := podmanTest.RunTopContainer("") session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid := session.OutputToString() Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1)) session = podmanTest.Podman([]string{"stop", "bogus", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) session = podmanTest.Podman([]string{"stop", "--ignore", "bogus", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/system_connection_test.go libpod-3.4.4+ds1/test/e2e/system_connection_test.go --- libpod-3.2.1+ds1/test/e2e/system_connection_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/system_connection_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -53,7 +53,7 @@ GinkgoWriter.Write([]byte(timedResult)) }) - It("add", func() { + It("add ssh://", func() { cmd := []string{"system", "connection", "add", "--default", "--identity", "~/.ssh/id_rsa", @@ -94,6 +94,68 @@ )) }) + It("add UDS", func() { + cmd := []string{"system", "connection", "add", + "QA-UDS", + "unix:///run/podman/podman.sock", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out).Should(Say("")) + + cfg, err := config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg.Engine.ActiveService).To(Equal("QA-UDS")) + Expect(cfg.Engine.ServiceDestinations["QA-UDS"]).To(Equal( + config.Destination{ + URI: "unix:///run/podman/podman.sock", + Identity: "", + }, + )) + + cmd = []string{"system", "connection", "add", + "QA-UDS1", + "--socket-path", "/run/user/podman/podman.sock", + "unix:///run/podman/podman.sock", + } + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out).Should(Say("")) + + cfg, err = config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg.Engine.ActiveService).To(Equal("QA-UDS")) + Expect(cfg.Engine.ServiceDestinations["QA-UDS1"]).To(Equal( + config.Destination{ + URI: "unix:///run/user/podman/podman.sock", + Identity: "", + }, + )) + }) + + It("add tcp", func() { + cmd := []string{"system", "connection", "add", + "QA-TCP", + "tcp://localhost:8888", + } + session := podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out).Should(Say("")) + + cfg, err := config.ReadCustomConfig() + Expect(err).ShouldNot(HaveOccurred()) + Expect(cfg.Engine.ActiveService).To(Equal("QA-TCP")) + Expect(cfg.Engine.ServiceDestinations["QA-TCP"]).To(Equal( + config.Destination{ + URI: "tcp://localhost:8888", + Identity: "", + }, + )) + }) + It("remove", func() { cmd := []string{"system", "connection", "add", "--default", @@ -147,6 +209,12 @@ session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) Expect(session.Out).Should(Say("Name *Identity *URI")) + + cmd = []string{"system", "connection", "list", "--format", "{{.Name}}"} + session = podmanTest.Podman(cmd) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("devl* qe")) }) It("failed default", func() { diff -Nru libpod-3.2.1+ds1/test/e2e/system_df_test.go libpod-3.4.4+ds1/test/e2e/system_df_test.go --- libpod-3.2.1+ds1/test/e2e/system_df_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/system_df_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("podman system df", func() { @@ -38,24 +39,24 @@ It("podman system df", func() { session := podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "data"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "data:/data", "--name", "container1", BB}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images", "-q"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) totImages := strconv.Itoa(len(session.OutputToStringArray())) session = podmanTest.Podman([]string{"system", "df"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(4)) images := strings.Fields(session.OutputToStringArray()[1]) containers := strings.Fields(session.OutputToStringArray()[2]) @@ -69,14 +70,14 @@ podmanTest.AddImageToRWStore(ALPINE) session := podmanTest.Podman([]string{"create", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"image", "untag", ALPINE}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"system", "df"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/systemd_test.go libpod-3.4.4+ds1/test/e2e/systemd_test.go --- libpod-3.2.1+ds1/test/e2e/systemd_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/systemd_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,12 +4,11 @@ "io/ioutil" "os" "strings" - "time" - "github.com/containers/podman/v3/pkg/rootless" . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman systemd", func() { @@ -36,7 +35,7 @@ ExecStop=/usr/bin/podman stop -t 10 redis KillMode=process [Install] -WantedBy=multi-user.target +WantedBy=default.target ` }) @@ -57,21 +56,21 @@ stop := SystemExec("bash", []string{"-c", "systemctl stop redis"}) os.Remove("/etc/systemd/system/redis.service") SystemExec("bash", []string{"-c", "systemctl daemon-reload"}) - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) }() create := podmanTest.Podman([]string{"create", "--name", "redis", redis}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) enable := SystemExec("bash", []string{"-c", "systemctl daemon-reload"}) - Expect(enable.ExitCode()).To(Equal(0)) + Expect(enable).Should(Exit(0)) start := SystemExec("bash", []string{"-c", "systemctl start redis"}) - Expect(start.ExitCode()).To(Equal(0)) + Expect(start).Should(Exit(0)) logs := SystemExec("bash", []string{"-c", "journalctl -n 20 -u redis"}) - Expect(logs.ExitCode()).To(Equal(0)) + Expect(logs).Should(Exit(0)) status := SystemExec("bash", []string{"-c", "systemctl status redis"}) Expect(status.OutputToString()).To(ContainSubstring("active (running)")) @@ -81,58 +80,51 @@ ctrName := "testSystemd" run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", ubi_init, "/sbin/init"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) - ctrID := run.OutputToString() + Expect(run).Should(Exit(0)) logs := podmanTest.Podman([]string{"logs", ctrName}) logs.WaitWithDefaultTimeout() - Expect(logs.ExitCode()).To(Equal(0)) + Expect(logs).Should(Exit(0)) // Give container 10 seconds to start - started := false - for i := 0; i < 10; i++ { - runningCtrs := podmanTest.Podman([]string{"ps", "-q", "--no-trunc"}) - runningCtrs.WaitWithDefaultTimeout() - Expect(runningCtrs.ExitCode()).To(Equal(0)) - - if strings.Contains(runningCtrs.OutputToString(), ctrID) { - started = true - break - } - - time.Sleep(1 * time.Second) - } - + started := podmanTest.WaitContainerReady(ctrName, "Reached target Multi-User System.", 30, 1) Expect(started).To(BeTrue()) systemctl := podmanTest.Podman([]string{"exec", "-t", "-i", ctrName, "systemctl", "status", "--no-pager"}) systemctl.WaitWithDefaultTimeout() - Expect(systemctl.ExitCode()).To(Equal(0)) + Expect(systemctl).Should(Exit(0)) Expect(strings.Contains(systemctl.OutputToString(), "State:")).To(BeTrue()) result := podmanTest.Podman([]string{"inspect", ctrName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(len(conData)).To(Equal(1)) Expect(conData[0].Config.SystemdMode).To(BeTrue()) - if CGROUPSV2 || !rootless.IsRootless() { - stats := podmanTest.Podman([]string{"stats", "--no-stream", ctrName}) - stats.WaitWithDefaultTimeout() - Expect(stats.ExitCode()).To(Equal(0)) + // stats not supported w/ CGv1 rootless or containerized + if isCgroupsV1() && (isRootless() || isContainerized()) { + return } + stats := podmanTest.Podman([]string{"stats", "--no-stream", ctrName}) + stats.WaitWithDefaultTimeout() + Expect(stats).Should(Exit(0)) + + cgroupPath := podmanTest.Podman([]string{"inspect", "--format='{{.State.CgroupPath}}'", ctrName}) + cgroupPath.WaitWithDefaultTimeout() + Expect(cgroupPath).Should(Exit(0)) + Expect(result.OutputToString()).To(Not(ContainSubstring("init.scope"))) }) It("podman create container with systemd entrypoint triggers systemd mode", func() { ctrName := "testCtr" run := podmanTest.Podman([]string{"create", "--name", ctrName, "--entrypoint", "/sbin/init", ubi_init}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", ctrName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(len(conData)).To(Equal(1)) Expect(conData[0].Config.SystemdMode).To(BeTrue()) @@ -142,11 +134,11 @@ ctrName := "testCtrUidMap" run := podmanTest.Podman([]string{"run", "-d", "--uidmap=0:1:1000", "--name", ctrName, ALPINE, "top"}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) session := podmanTest.Podman([]string{"inspect", "--format", "{{.ConmonPidFile}}", ctrName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) pidFile := strings.TrimSuffix(session.OutputToString(), "\n") _, err := ioutil.ReadFile(pidFile) @@ -157,11 +149,11 @@ ctrName := "testCtr" run := podmanTest.Podman([]string{"create", "--name", ctrName, "--systemd", "always", ALPINE}) run.WaitWithDefaultTimeout() - Expect(run.ExitCode()).To(Equal(0)) + Expect(run).Should(Exit(0)) result := podmanTest.Podman([]string{"inspect", ctrName}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) conData := result.InspectContainerToJSON() Expect(len(conData)).To(Equal(1)) Expect(conData[0].Config.SystemdMode).To(BeTrue()) @@ -170,8 +162,25 @@ It("podman run --systemd container should NOT mount /run noexec", func() { session := podmanTest.Podman([]string{"run", "--systemd", "always", ALPINE, "sh", "-c", "mount | grep \"/run \""}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Not(ContainSubstring("noexec"))) }) + + It("podman run --systemd arg is case insensitive", func() { + session := podmanTest.Podman([]string{"run", "--rm", "--systemd", "Always", ALPINE, "echo", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("test")) + + session = podmanTest.Podman([]string{"run", "--rm", "--systemd", "True", ALPINE, "echo", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("test")) + + session = podmanTest.Podman([]string{"run", "--rm", "--systemd", "False", ALPINE, "echo", "test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).Should(Equal("test")) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/system_reset_test.go libpod-3.4.4+ds1/test/e2e/system_reset_test.go --- libpod-3.2.1+ds1/test/e2e/system_reset_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/system_reset_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("podman system reset", func() { @@ -39,25 +40,27 @@ session := podmanTest.Podman([]string{"rmi", "--force", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"images", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) l := len(session.OutputToStringArray()) podmanTest.AddImageToRWStore(ALPINE) session = podmanTest.Podman([]string{"volume", "create", "data"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "data:/data", ALPINE, "echo", "hello"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"system", "reset", "-f"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + + Expect(session.ErrorToString()).To(Not(ContainSubstring("Failed to add pause process"))) // If remote then the API service should have exited // On local tests this is a noop @@ -65,17 +68,17 @@ session = podmanTest.Podman([]string{"images", "-n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(l)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) session = podmanTest.Podman([]string{"container", "ls", "-q"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/tag_test.go libpod-3.4.4+ds1/test/e2e/tag_test.go --- libpod-3.2.1+ds1/test/e2e/tag_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/tag_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman tag", func() { @@ -35,11 +36,11 @@ It("podman tag shortname:latest", func() { session := podmanTest.Podman([]string{"tag", ALPINE, "foobar:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "foobar:latest"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) inspectData := results.InspectImageJSON() Expect(StringInSlice("quay.io/libpod/alpine:latest", inspectData[0].RepoTags)).To(BeTrue()) Expect(StringInSlice("localhost/foobar:latest", inspectData[0].RepoTags)).To(BeTrue()) @@ -48,11 +49,11 @@ It("podman tag shortname", func() { session := podmanTest.Podman([]string{"tag", ALPINE, "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "foobar:latest"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) inspectData := results.InspectImageJSON() Expect(StringInSlice("quay.io/libpod/alpine:latest", inspectData[0].RepoTags)).To(BeTrue()) Expect(StringInSlice("localhost/foobar:latest", inspectData[0].RepoTags)).To(BeTrue()) @@ -61,11 +62,11 @@ It("podman tag shortname:tag", func() { session := podmanTest.Podman([]string{"tag", ALPINE, "foobar:new"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"inspect", "foobar:new"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) inspectData := results.InspectImageJSON() Expect(StringInSlice("quay.io/libpod/alpine:latest", inspectData[0].RepoTags)).To(BeTrue()) Expect(StringInSlice("localhost/foobar:new", inspectData[0].RepoTags)).To(BeTrue()) @@ -74,14 +75,14 @@ It("podman tag shortname image no tag", func() { session := podmanTest.Podman([]string{"tag", ALPINE, "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) results := podmanTest.Podman([]string{"tag", "foobar", "barfoo"}) results.WaitWithDefaultTimeout() - Expect(results.ExitCode()).To(Equal(0)) + Expect(results).Should(Exit(0)) verify := podmanTest.Podman([]string{"inspect", "barfoo"}) verify.WaitWithDefaultTimeout() - Expect(verify.ExitCode()).To(Equal(0)) + Expect(verify).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/toolbox_test.go libpod-3.4.4+ds1/test/e2e/toolbox_test.go --- libpod-3.2.1+ds1/test/e2e/toolbox_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/toolbox_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -39,6 +39,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Toolbox-specific testing", func() { @@ -70,7 +71,7 @@ session = podmanTest.Podman([]string{"run", "--dns", "none", ALPINE, "sh", "-c", "rm -f /etc/resolv.conf; touch -d '1970-01-01 00:02:03' /etc/resolv.conf; stat -c %s:%Y /etc/resolv.conf"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0:123")) }) @@ -80,7 +81,7 @@ session = podmanTest.Podman([]string{"run", "--no-hosts", ALPINE, "sh", "-c", "rm -f /etc/hosts; touch -d '1970-01-01 00:02:03' /etc/hosts; stat -c %s:%Y /etc/hosts"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("0:123")) }) @@ -100,16 +101,16 @@ session = podmanTest.Podman([]string{"create", "--name", "test", "--ulimit", "host", ALPINE, "sleep", "1000"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test", "sh", "-c", "ulimit -H -n"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) containerHardLimit, err = strconv.Atoi(strings.Trim(session.OutputToString(), "\n")) Expect(err).To(BeNil()) Expect(containerHardLimit).To(BeNumerically(">=", rlimit.Max)) @@ -141,16 +142,16 @@ session = podmanTest.Podman([]string{"create", "--name", "test", "--ipc=host", "--pid=host", ALPINE, "sleep", "1000"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"exec", "test", "df", "/dev/shm"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) lines = session.OutputToStringArray() fields = strings.Fields(lines[len(lines)-1]) containerShmSize, err = strconv.Atoi(fields[1]) @@ -168,7 +169,7 @@ session = podmanTest.Podman([]string{"run", "--userns=keep-id", "--user", "root:root", ALPINE, "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("uid=0(root) gid=0(root)")) }) @@ -185,12 +186,12 @@ session = podmanTest.Podman([]string{"create", "--name", "test", "--userns=keep-id", ALPINE, "sleep", "1000"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(err).To(BeNil()) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) expectedOutput := fmt.Sprintf("uid=%s(%s) gid=%s(%s)", currentUser.Uid, currentUser.Username, @@ -199,7 +200,7 @@ session = podmanTest.Podman([]string{"exec", "test", "id"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(expectedOutput)) }) @@ -218,11 +219,11 @@ session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c", fmt.Sprintf("%s; %s; echo READY; sleep 1000", useradd, passwd)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) @@ -231,14 +232,14 @@ session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/passwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(expectedOutput)) expectedOutput = "passwd: Note: deleting a password also unlocks the password." session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.ErrorToString()).To(ContainSubstring(expectedOutput)) }) @@ -253,22 +254,22 @@ session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c", fmt.Sprintf("%s; echo READY; sleep 1000", groupadd)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/group"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(groupName)) session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("READY")) }) @@ -297,11 +298,11 @@ session = podmanTest.Podman([]string{"create", "--log-driver", "k8s-file", "--name", "test", "--userns=keep-id", "--user", "root:root", fedoraToolbox, "sh", "-c", fmt.Sprintf("%s; %s; %s; echo READY; sleep 1000", useradd, groupadd, usermod)}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) @@ -310,12 +311,12 @@ session = podmanTest.Podman([]string{"exec", "test", "cat", "/etc/passwd"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(expectedUser)) session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("READY")) }) @@ -325,12 +326,12 @@ session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE, "mount", "-t", "tmpfs", "tmpfs", "/tmp"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"run", "--privileged", "--userns=keep-id", "--user", "root:root", ALPINE, "mount", "--rbind", "/tmp", "/var/tmp"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman create + start - with all needed switches for create - sleep as entry-point", func() { @@ -355,17 +356,17 @@ "--user", "root:root", fedoraToolbox, "sh", "-c", "echo READY; sleep 1000"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"start", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(WaitContainerReady(podmanTest, "test", "READY", 5, 1)).To(BeTrue()) session = podmanTest.Podman([]string{"logs", "test"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("READY")) }) @@ -377,7 +378,7 @@ session = podmanTest.Podman([]string{"run", "-v", fmt.Sprintf("%s:%s", currentUser.HomeDir, currentUser.HomeDir), "--userns=keep-id", fedoraToolbox, "sh", "-c", "echo $HOME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir)) if rootless.IsRootless() { @@ -388,7 +389,7 @@ "--volume", volumeArg, fedoraToolbox, "sh", "-c", "echo $HOME"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring(currentUser.HomeDir)) } }) diff -Nru libpod-3.2.1+ds1/test/e2e/top_test.go libpod-3.4.4+ds1/test/e2e/top_test.go --- libpod-3.2.1+ds1/test/e2e/top_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/top_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman top", func() { @@ -35,13 +36,13 @@ It("podman top without container name or id", func() { result := podmanTest.Podman([]string{"top"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman top on bogus container", func() { result := podmanTest.Podman([]string{"top", "1234"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman top on non-running container", func() { @@ -49,68 +50,79 @@ Expect(ec).To(Equal(0)) result := podmanTest.Podman([]string{"top", cid}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) It("podman top on container", func() { session := podmanTest.Podman([]string{"run", "--name", "test", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"top", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) It("podman container top on container", func() { session := podmanTest.Podman([]string{"container", "run", "--name", "test", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"container", "top", "test"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) + Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) + + // Just a smoke test since groups may change over time. + result = podmanTest.Podman([]string{"container", "top", "test", "groups", "hgroups"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) It("podman top with options", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"top", session.OutputToString(), "pid", "%C", "args"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) It("podman top with ps(1) options", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"top", session.OutputToString(), "aux"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) + + result = podmanTest.Podman([]string{"top", session.OutputToString(), "ax -o args"}) + result.WaitWithDefaultTimeout() + Expect(result).Should(Exit(0)) + Expect(result.OutputToStringArray()).To(Equal([]string{"COMMAND", "top -d 2"})) }) It("podman top with comma-separated options", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "top", "-d", "2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) result := podmanTest.Podman([]string{"top", session.OutputToString(), "user,pid,comm"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(0)) + Expect(result).Should(Exit(0)) Expect(len(result.OutputToStringArray())).To(BeNumerically(">", 1)) }) It("podman top on container invalid options", func() { top := podmanTest.RunTopContainer("") top.WaitWithDefaultTimeout() - Expect(top.ExitCode()).To(Equal(0)) + Expect(top).Should(Exit(0)) cid := top.OutputToString() // We need to pass -eo to force executing ps in the Alpine container. @@ -119,7 +131,7 @@ // the wrong input and still print the -ef output instead. result := podmanTest.Podman([]string{"top", cid, "-eo", "invalid"}) result.WaitWithDefaultTimeout() - Expect(result.ExitCode()).To(Equal(125)) + Expect(result).Should(Exit(125)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/tree_test.go libpod-3.4.4+ds1/test/e2e/tree_test.go --- libpod-3.2.1+ds1/test/e2e/tree_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/tree_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman image tree", func() { @@ -45,16 +46,16 @@ session := podmanTest.Podman([]string{"image", "tree", "test:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"image", "tree", "--whatrequires", "quay.io/libpod/cirros:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "test:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"rmi", "quay.io/libpod/cirros:latest"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/trust_test.go libpod-3.4.4+ds1/test/e2e/trust_test.go --- libpod-3.2.1+ds1/test/e2e/trust_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/trust_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -9,11 +9,13 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman trust", func() { var ( - tempdir string + tempdir string + err error podmanTest *PodmanTestIntegration ) @@ -37,21 +39,17 @@ }) It("podman image trust show", func() { - path, err := os.Getwd() - if err != nil { - os.Exit(1) - } - session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Dir(path), "--policypath", filepath.Join(filepath.Dir(path), "policy.json")}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json")}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) outArray := session.OutputToStringArray() Expect(len(outArray)).To(Equal(3)) - // image order is not guaranteed. All we can do is check that - // these strings appear in output, we can't cross-check them. - Expect(session.OutputToString()).To(ContainSubstring("accept")) - Expect(session.OutputToString()).To(ContainSubstring("reject")) - Expect(session.OutputToString()).To(ContainSubstring("signed")) + // Repository order is not guaranteed. So, check that + // all expected lines appear in output; we also check total number of lines, so that handles all of them. + Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^default\s+accept\s*$`)) + Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^docker.io/library/hello-world\s+reject\s*$`)) + Expect(string(session.Out.Contents())).To(MatchRegexp(`(?m)^registry.access.redhat.com\s+signedBy\s+security@redhat.com, security@redhat.com\s+https://access.redhat.com/webassets/docker/content/sigstore\s*$`)) }) It("podman image trust set", func() { @@ -61,7 +59,7 @@ } session := podmanTest.Podman([]string{"image", "trust", "set", "--policypath", filepath.Join(filepath.Dir(path), "trust_set_test.json"), "-t", "accept", "default"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) var teststruct map[string][]map[string]string policyContent, err := ioutil.ReadFile(filepath.Join(filepath.Dir(path), "trust_set_test.json")) if err != nil { @@ -75,24 +73,52 @@ }) It("podman image trust show --json", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "--json"}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Join(INTEGRATION_ROOT, "test"), "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--json"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) var teststruct []map[string]string json.Unmarshal(session.Out.Contents(), &teststruct) - Expect(teststruct[0]["name"]).To(Equal("* (default)")) - Expect(teststruct[0]["repo_name"]).To(Equal("default")) - Expect(teststruct[0]["type"]).To(Equal("accept")) - Expect(teststruct[1]["type"]).To(Equal("insecureAcceptAnything")) + Expect(len(teststruct)).To(Equal(3)) + // To ease comparison, group the unordered array of repos by repo (and we expect only one entry by repo, so order within groups doesn’t matter) + repoMap := map[string][]map[string]string{} + for _, e := range teststruct { + key := e["name"] + repoMap[key] = append(repoMap[key], e) + } + Expect(repoMap).To(Equal(map[string][]map[string]string{ + "* (default)": {{ + "name": "* (default)", + "repo_name": "default", + "sigstore": "", + "transport": "", + "type": "accept", + }}, + "docker.io/library/hello-world": {{ + "name": "docker.io/library/hello-world", + "repo_name": "docker.io/library/hello-world", + "sigstore": "", + "transport": "", + "type": "reject", + }}, + "registry.access.redhat.com": {{ + "name": "registry.access.redhat.com", + "repo_name": "registry.access.redhat.com", + "sigstore": "https://access.redhat.com/webassets/docker/content/sigstore", + "transport": "", + "type": "signedBy", + "gpg_id": "security@redhat.com, security@redhat.com", + }}, + })) }) It("podman image trust show --raw", func() { - session := podmanTest.Podman([]string{"image", "trust", "show", "--raw"}) + session := podmanTest.Podman([]string{"image", "trust", "show", "--policypath", filepath.Join(INTEGRATION_ROOT, "test/policy.json"), "--raw"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + contents, err := ioutil.ReadFile(filepath.Join(INTEGRATION_ROOT, "test/policy.json")) + Expect(err).ShouldNot(HaveOccurred()) Expect(session.IsJSONOutputValid()).To(BeTrue()) - Expect(session.OutputToString()).To(ContainSubstring("default")) - Expect(session.OutputToString()).To(ContainSubstring("insecureAcceptAnything")) + Expect(string(session.Out.Contents())).To(Equal(string(contents) + "\n")) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/unshare_test.go libpod-3.4.4+ds1/test/e2e/unshare_test.go --- libpod-3.2.1+ds1/test/e2e/unshare_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/unshare_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman unshare", func() { @@ -45,15 +46,46 @@ userNS, _ := os.Readlink("/proc/self/ns/user") session := podmanTest.Podman([]string{"unshare", "readlink", "/proc/self/ns/user"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) - ok, _ := session.GrepString(userNS) - Expect(ok).To(BeFalse()) + Expect(session).Should(Exit(0)) + Expect(session.OutputToString()).ToNot(ContainSubstring(userNS)) }) It("podman unshare --rootles-cni", func() { session := podmanTest.Podman([]string{"unshare", "--rootless-cni", "ip", "addr"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(ContainSubstring("tap0")) }) + + It("podman unshare exit codes", func() { + session := podmanTest.Podman([]string{"unshare", "false"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(1)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(Equal("")) + + session = podmanTest.Podman([]string{"unshare", "/usr/bin/bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(127)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("no such file or directory")) + + session = podmanTest.Podman([]string{"unshare", "bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(127)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("executable file not found in $PATH")) + + session = podmanTest.Podman([]string{"unshare", "/usr"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(126)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("permission denied")) + + session = podmanTest.Podman([]string{"unshare", "--bogus"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(125)) + Expect(session.OutputToString()).Should(Equal("")) + Expect(session.ErrorToString()).Should(ContainSubstring("unknown flag: --bogus")) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/untag_test.go libpod-3.4.4+ds1/test/e2e/untag_test.go --- libpod-3.2.1+ds1/test/e2e/untag_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/untag_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman untag", func() { @@ -39,25 +40,25 @@ cmd = append(cmd, tags...) session := podmanTest.Podman(cmd) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Make sure that all tags exists. for _, t := range tags { session = podmanTest.Podman([]string{"image", "exists", t}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) } // No arguments -> remove all tags. session = podmanTest.Podman([]string{"untag", cirros}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) // Make sure that none of tags exists anymore. for _, t := range tags { session = podmanTest.Podman([]string{"image", "exists", t}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) } }) @@ -78,19 +79,19 @@ for _, tt := range tests { session := podmanTest.Podman([]string{"tag", cirros, tt.tag}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"image", "exists", tt.normalized}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"untag", cirros, tt.tag}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"image", "exists", tt.normalized}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) } }) diff -Nru libpod-3.2.1+ds1/test/e2e/version_test.go libpod-3.4.4+ds1/test/e2e/version_test.go --- libpod-3.2.1+ds1/test/e2e/version_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/version_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -31,7 +31,6 @@ f := CurrentGinkgoTestDescription() processTestResult(f) podmanTest.SeedImages() - }) It("podman version", func() { @@ -96,4 +95,13 @@ session.WaitWithDefaultTimeout() Expect(session).Should(Exit(0)) }) + + It("podman help", func() { + session := podmanTest.Podman([]string{"help"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(session.Out.Contents()).Should( + ContainSubstring("Display the Podman version information"), + ) + }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/volume_create_test.go libpod-3.4.4+ds1/test/e2e/volume_create_test.go --- libpod-3.2.1+ds1/test/e2e/volume_create_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_create_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman volume create", func() { @@ -37,7 +38,7 @@ session := podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() volName := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"volume", "ls", "-q"}) check.WaitWithDefaultTimeout() @@ -50,7 +51,7 @@ session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() volName := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) check := podmanTest.Podman([]string{"volume", "ls", "-q"}) check.WaitWithDefaultTimeout() @@ -59,6 +60,69 @@ Expect(len(check.OutputToStringArray())).To(Equal(1)) }) + It("podman create and export volume", func() { + if podmanTest.RemoteTest { + Skip("Volume export check does not work with a remote client") + } + + session := podmanTest.Podman([]string{"volume", "create", "myvol"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "echo hello >> " + "/data/test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + check := podmanTest.Podman([]string{"volume", "export", volName}) + check.WaitWithDefaultTimeout() + Expect(check.OutputToString()).To(ContainSubstring("hello")) + }) + + It("podman create and import volume", func() { + if podmanTest.RemoteTest { + Skip("Volume export check does not work with a remote client") + } + + session := podmanTest.Podman([]string{"volume", "create", "my_vol"}) + session.WaitWithDefaultTimeout() + volName := session.OutputToString() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--volume", volName + ":/data", ALPINE, "sh", "-c", "echo hello >> " + "/data/test"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "export", volName, "--output=hello.tar"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "create", "my_vol2"}) + session.WaitWithDefaultTimeout() + volName = session.OutputToString() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "import", "my_vol2", "hello.tar"}) + session.WaitWithDefaultTimeout() + volName = session.OutputToString() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"run", "--volume", "my_vol2:/data", ALPINE, "cat", "/data/test"}) + session.WaitWithDefaultTimeout() + Expect(session.OutputToString()).To(ContainSubstring("hello")) + }) + + It("podman import volume should fail", func() { + // try import on volume or source which does not exists + if podmanTest.RemoteTest { + Skip("Volume export check does not work with a remote client") + } + + session := podmanTest.Podman([]string{"volume", "import", "notfound", "notfound.tar"}) + session.WaitWithDefaultTimeout() + Expect(session).To(ExitWithError()) + }) + It("podman create volume with bad volume option", func() { session := podmanTest.Podman([]string{"volume", "create", "--opt", "badOpt=bad"}) session.WaitWithDefaultTimeout() @@ -71,16 +135,16 @@ gid := "4000" session := podmanTest.Podman([]string{"volume", "create", "--opt", fmt.Sprintf("o=uid=%s,gid=%s", uid, gid), volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", volName}) inspectUID.WaitWithDefaultTimeout() - Expect(inspectUID.ExitCode()).To(Equal(0)) + Expect(inspectUID).Should(Exit(0)) Expect(inspectUID.OutputToString()).To(Equal(uid)) inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", volName}) inspectGID.WaitWithDefaultTimeout() - Expect(inspectGID.ExitCode()).To(Equal(0)) + Expect(inspectGID).Should(Exit(0)) Expect(inspectGID.OutputToString()).To(Equal(gid)) // options should containt `uid=3000,gid=4000:3000:4000` @@ -88,7 +152,7 @@ optionStrFormatExpect := fmt.Sprintf(`uid=%s,gid=%s:%s:%s`, uid, gid, uid, gid) inspectOpts := podmanTest.Podman([]string{"volume", "inspect", "--format", optionFormat, volName}) inspectOpts.WaitWithDefaultTimeout() - Expect(inspectOpts.ExitCode()).To(Equal(0)) + Expect(inspectOpts).Should(Exit(0)) Expect(inspectOpts.OutputToString()).To(Equal(optionStrFormatExpect)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/volume_exists_test.go libpod-3.4.4+ds1/test/e2e/volume_exists_test.go --- libpod-3.2.1+ds1/test/e2e/volume_exists_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_exists_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ "github.com/containers/storage/pkg/stringid" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman volume exists", func() { @@ -37,14 +38,14 @@ vol := "vol" + stringid.GenerateNonCryptoID() session := podmanTest.Podman([]string{"volume", "create", vol}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(BeZero()) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "exists", vol}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "exists", stringid.GenerateNonCryptoID()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/volume_inspect_test.go libpod-3.4.4+ds1/test/e2e/volume_inspect_test.go --- libpod-3.2.1+ds1/test/e2e/volume_inspect_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_inspect_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -7,6 +7,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman volume inspect", func() { @@ -37,11 +38,11 @@ session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() volName := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "inspect", volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) @@ -49,11 +50,11 @@ session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() volName := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Name}}", volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToString()).To(Equal(volName)) }) @@ -61,16 +62,16 @@ session := podmanTest.Podman([]string{"volume", "create", "myvol1"}) session.WaitWithDefaultTimeout() volName1 := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "myvol2"}) session.WaitWithDefaultTimeout() volName2 := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "inspect", "--format", "{{.Name}}", "--all"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.OutputToStringArray()[0]).To(Equal(volName1)) Expect(session.OutputToStringArray()[1]).To(Equal(volName2)) @@ -80,11 +81,11 @@ volName := "testvol" session := podmanTest.Podman([]string{"volume", "create", "--opt", "type=tmpfs", volName}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) inspect := podmanTest.Podman([]string{"volume", "inspect", volName}) inspect.WaitWithDefaultTimeout() - Expect(inspect.ExitCode()).To(Equal(0)) + Expect(inspect).Should(Exit(0)) Expect(strings.Contains(inspect.OutputToString(), "tmpfs")).To(BeTrue()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/volume_ls_test.go libpod-3.4.4+ds1/test/e2e/volume_ls_test.go --- libpod-3.2.1+ds1/test/e2e/volume_ls_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_ls_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -37,29 +37,29 @@ It("podman ls volume", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) }) It("podman ls volume with JSON format", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls", "--format", "json"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.IsJSONOutputValid()).To(BeTrue()) }) It("podman ls volume with Go template", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls", "--format", "table {{.Name}} {{.Driver}} {{.Scope}}"}) session.WaitWithDefaultTimeout() @@ -72,32 +72,48 @@ session := podmanTest.Podman([]string{"volume", "create", "--label", "foo=bar", "myvol"}) volName := session.OutputToString() session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName)) session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo=foo"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo=bar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName)) session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo=baz"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) + }) + + It("podman ls volume with --filter until flag", func() { + session := podmanTest.Podman([]string{"volume", "create"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls", "--filter", "until=5000000000"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + session = podmanTest.Podman([]string{"volume", "ls", "--filter", "until=50000"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) }) @@ -105,52 +121,52 @@ volName1 := "volume1" session := podmanTest.Podman([]string{"volume", "create", volName1}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) volName2 := "volume2" session2 := podmanTest.Podman([]string{"volume", "create", volName2}) session2.WaitWithDefaultTimeout() - Expect(session2.ExitCode()).To(Equal(0)) + Expect(session2).Should(Exit(0)) ctr := podmanTest.Podman([]string{"create", "-v", fmt.Sprintf("%s:/test", volName2), ALPINE, "sh"}) ctr.WaitWithDefaultTimeout() - Expect(ctr.ExitCode()).To(Equal(0)) + Expect(ctr).Should(Exit(0)) lsNoDangling := podmanTest.Podman([]string{"volume", "ls", "--filter", "dangling=false", "--quiet"}) lsNoDangling.WaitWithDefaultTimeout() - Expect(lsNoDangling.ExitCode()).To(Equal(0)) + Expect(lsNoDangling).Should(Exit(0)) Expect(lsNoDangling.OutputToString()).To(ContainSubstring(volName2)) lsDangling := podmanTest.Podman([]string{"volume", "ls", "--filter", "dangling=true", "--quiet"}) lsDangling.WaitWithDefaultTimeout() - Expect(lsDangling.ExitCode()).To(Equal(0)) + Expect(lsDangling).Should(Exit(0)) Expect(lsDangling.OutputToString()).To(ContainSubstring(volName1)) }) It("podman ls volume with multiple --filter flag", func() { session := podmanTest.Podman([]string{"volume", "create", "--label", "foo=bar", "myvol"}) volName := session.OutputToString() session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "foo2=bar2", "anothervol"}) anotherVol := session.OutputToString() session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo", "--filter", "label=foo2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName)) Expect(session.OutputToStringArray()[2]).To(ContainSubstring(anotherVol)) session = podmanTest.Podman([]string{"volume", "ls", "--filter", "label=foo=bar", "--filter", "label=foo2=bar2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) Expect(session.OutputToStringArray()[1]).To(ContainSubstring(volName)) Expect(session.OutputToStringArray()[2]).To(ContainSubstring(anotherVol)) diff -Nru libpod-3.2.1+ds1/test/e2e/volume_plugin_test.go libpod-3.4.4+ds1/test/e2e/volume_plugin_test.go --- libpod-3.2.1+ds1/test/e2e/volume_plugin_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_plugin_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -8,6 +8,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman volume plugins", func() { @@ -41,13 +42,13 @@ It("volume create with nonexistent plugin errors", func() { session := podmanTest.Podman([]string{"volume", "create", "--driver", "notexist", "test_volume_name"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("volume create with not-running plugin does not error", func() { session := podmanTest.Podman([]string{"volume", "create", "--driver", "testvol0", "test_volume_name"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) It("volume create and remove with running plugin succeeds", func() { @@ -60,27 +61,27 @@ pluginName := "testvol1" plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) plugin.WaitWithDefaultTimeout() - Expect(plugin.ExitCode()).To(Equal(0)) + Expect(plugin).Should(Exit(0)) volName := "testVolume1" create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) ls1.WaitWithDefaultTimeout() - Expect(ls1.ExitCode()).To(Equal(0)) + Expect(ls1).Should(Exit(0)) arrOutput := ls1.OutputToStringArray() Expect(len(arrOutput)).To(Equal(1)) Expect(arrOutput[0]).To(ContainSubstring(volName)) remove := podmanTest.Podman([]string{"volume", "rm", volName}) remove.WaitWithDefaultTimeout() - Expect(remove.ExitCode()).To(Equal(0)) + Expect(remove).Should(Exit(0)) ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) ls2.WaitWithDefaultTimeout() - Expect(ls2.ExitCode()).To(Equal(0)) + Expect(ls2).Should(Exit(0)) Expect(len(ls2.OutputToStringArray())).To(Equal(0)) }) @@ -94,16 +95,16 @@ pluginName := "testvol2" plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) plugin.WaitWithDefaultTimeout() - Expect(plugin.ExitCode()).To(Equal(0)) + Expect(plugin).Should(Exit(0)) volName := "testVolume1" create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) volInspect := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .Driver }}", volName}) volInspect.WaitWithDefaultTimeout() - Expect(volInspect.ExitCode()).To(Equal(0)) + Expect(volInspect).Should(Exit(0)) Expect(volInspect.OutputToString()).To(ContainSubstring(pluginName)) }) @@ -118,33 +119,33 @@ ctrName := "pluginCtr" plugin := podmanTest.Podman([]string{"run", "--name", ctrName, "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) plugin.WaitWithDefaultTimeout() - Expect(plugin.ExitCode()).To(Equal(0)) + Expect(plugin).Should(Exit(0)) volName := "testVolume1" create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) ls1 := podmanTest.Podman([]string{"volume", "ls", "-q"}) ls1.WaitWithDefaultTimeout() - Expect(ls1.ExitCode()).To(Equal(0)) + Expect(ls1).Should(Exit(0)) arrOutput := ls1.OutputToStringArray() Expect(len(arrOutput)).To(Equal(1)) Expect(arrOutput[0]).To(ContainSubstring(volName)) stop := podmanTest.Podman([]string{"stop", "--timeout", "0", ctrName}) stop.WaitWithDefaultTimeout() - Expect(stop.ExitCode()).To(Equal(0)) + Expect(stop).Should(Exit(0)) // Remove should exit non-zero because missing plugin remove := podmanTest.Podman([]string{"volume", "rm", volName}) remove.WaitWithDefaultTimeout() - Expect(remove.ExitCode()).To(Not(Equal(0))) + Expect(remove).To(ExitWithError()) // But the volume should still be gone ls2 := podmanTest.Podman([]string{"volume", "ls", "-q"}) ls2.WaitWithDefaultTimeout() - Expect(ls2.ExitCode()).To(Equal(0)) + Expect(ls2).Should(Exit(0)) Expect(len(ls2.OutputToStringArray())).To(Equal(0)) }) @@ -158,20 +159,20 @@ pluginName := "testvol4" plugin := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", "/run/docker/plugins:/run/docker/plugins", "-v", fmt.Sprintf("%v:%v", pluginStatePath, pluginStatePath), "-d", volumeTest, "--sock-name", pluginName, "--path", pluginStatePath}) plugin.WaitWithDefaultTimeout() - Expect(plugin.ExitCode()).To(Equal(0)) + Expect(plugin).Should(Exit(0)) volName := "testVolume1" create := podmanTest.Podman([]string{"volume", "create", "--driver", pluginName, volName}) create.WaitWithDefaultTimeout() - Expect(create.ExitCode()).To(Equal(0)) + Expect(create).Should(Exit(0)) ctr1 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "sh", "-c", "touch /test/testfile && echo helloworld > /test/testfile"}) ctr1.WaitWithDefaultTimeout() - Expect(ctr1.ExitCode()).To(Equal(0)) + Expect(ctr1).Should(Exit(0)) ctr2 := podmanTest.Podman([]string{"run", "--security-opt", "label=disable", "-v", fmt.Sprintf("%v:/test", volName), ALPINE, "cat", "/test/testfile"}) ctr2.WaitWithDefaultTimeout() - Expect(ctr2.ExitCode()).To(Equal(0)) + Expect(ctr2).Should(Exit(0)) Expect(ctr2.OutputToString()).To(ContainSubstring("helloworld")) // HACK: `volume rm -f` is timing out trying to remove containers using the volume. @@ -179,6 +180,6 @@ // TODO: fix this when I get back rmAll := podmanTest.Podman([]string{"rm", "-af"}) rmAll.WaitWithDefaultTimeout() - Expect(rmAll.ExitCode()).To(Equal(0)) + Expect(rmAll).Should(Exit(0)) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/volume_prune_test.go libpod-3.4.4+ds1/test/e2e/volume_prune_test.go --- libpod-3.2.1+ds1/test/e2e/volume_prune_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_prune_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman volume prune", func() { @@ -35,88 +36,119 @@ It("podman prune volume", func() { session := podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(4)) session = podmanTest.Podman([]string{"volume", "prune", "--force"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(2)) podmanTest.Cleanup() }) + It("podman prune volume --filter until", func() { + session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "until=50"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) + + session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "until=5000000000"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + + session = podmanTest.Podman([]string{"volume", "ls"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(Exit(0)) + Expect(len(session.OutputToStringArray())).To(Equal(0)) + + podmanTest.Cleanup() + }) + It("podman prune volume --filter", func() { session := podmanTest.Podman([]string{"volume", "create", "--label", "label1=value1", "myvol1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv1", "myvol2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1=slv2", "myvol3"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "--label", "sharedlabel1", "myvol4"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol5:/myvol5", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol6:/myvol6", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(7)) session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=label1=value1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(6)) session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=sharedlabel1=slv1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(5)) session = podmanTest.Podman([]string{"volume", "prune", "--force", "--filter", "label=sharedlabel1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(3)) podmanTest.Cleanup() @@ -125,28 +157,28 @@ It("podman system prune --volume", func() { session := podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(4)) session = podmanTest.Podman([]string{"system", "prune", "--force", "--volumes"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) podmanTest.Cleanup() diff -Nru libpod-3.2.1+ds1/test/e2e/volume_rm_test.go libpod-3.4.4+ds1/test/e2e/volume_rm_test.go --- libpod-3.2.1+ds1/test/e2e/volume_rm_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/volume_rm_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman volume rm", func() { @@ -35,15 +36,15 @@ It("podman volume rm", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "rm", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) }) @@ -51,20 +52,20 @@ session := podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"}) cid := session.OutputToString() session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "rm", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(2)) + Expect(session).Should(Exit(2)) Expect(session.ErrorToString()).To(ContainSubstring(cid)) session = podmanTest.Podman([]string{"volume", "rm", "-f", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) podmanTest.Cleanup() @@ -73,51 +74,51 @@ It("podman volume remove bogus", func() { session := podmanTest.Podman([]string{"volume", "rm", "bogus"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(1)) + Expect(session).Should(Exit(1)) }) It("podman rm with --all flag", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "rm", "-a"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) }) It("podman volume rm by partial name", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "rm", "myv"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray())).To(Equal(0)) }) It("podman volume rm by nonunique partial name", func() { session := podmanTest.Podman([]string{"volume", "create", "myvol1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "create", "myvol2"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"volume", "rm", "myv"}) session.WaitWithDefaultTimeout() @@ -125,7 +126,7 @@ session = podmanTest.Podman([]string{"volume", "ls"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(len(session.OutputToStringArray()) >= 2).To(BeTrue()) }) }) diff -Nru libpod-3.2.1+ds1/test/e2e/wait_test.go libpod-3.4.4+ds1/test/e2e/wait_test.go --- libpod-3.2.1+ds1/test/e2e/wait_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/e2e/wait_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -6,6 +6,7 @@ . "github.com/containers/podman/v3/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + . "github.com/onsi/gomega/gexec" ) var _ = Describe("Podman wait", func() { @@ -35,7 +36,7 @@ It("podman wait on bogus container", func() { session := podmanTest.Podman([]string{"wait", "1234"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) @@ -43,91 +44,91 @@ session := podmanTest.Podman([]string{"run", "-d", ALPINE, "ls"}) session.Wait(10) cid := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"wait", cid}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman wait on a sleeping container", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) cid := session.OutputToString() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"wait", cid}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman wait on latest container", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if IsRemote() { session = podmanTest.Podman([]string{"wait", session.OutputToString()}) } else { session = podmanTest.Podman([]string{"wait", "-l"}) } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container wait on latest container", func() { session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) if IsRemote() { session = podmanTest.Podman([]string{"container", "wait", session.OutputToString()}) } else { session = podmanTest.Podman([]string{"container", "wait", "-l"}) } session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container wait on latest container with --interval flag", func() { session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"container", "wait", "-i", "5000", session.OutputToString()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container wait on latest container with --interval flag", func() { session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"container", "wait", "--interval", "1s", session.OutputToString()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) }) It("podman container wait on container with bogus --interval", func() { session := podmanTest.Podman([]string{"container", "run", "-d", ALPINE, "sleep", "1"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) session = podmanTest.Podman([]string{"container", "wait", "--interval", "100days", session.OutputToString()}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Equal(125)) + Expect(session).Should(Exit(125)) }) It("podman wait on three containers", func() { session := podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid1 := session.OutputToString() session = podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid2 := session.OutputToString() session = podmanTest.Podman([]string{"run", "-d", ALPINE, "sleep", "1"}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) cid3 := session.OutputToString() session = podmanTest.Podman([]string{"wait", cid1, cid2, cid3}) session.Wait(20) - Expect(session.ExitCode()).To(Equal(0)) + Expect(session).Should(Exit(0)) Expect(session.OutputToStringArray()).To(Equal([]string{"0", "0", "0"})) }) }) diff -Nru libpod-3.2.1+ds1/test/python/docker/compat/test_containers.py libpod-3.4.4+ds1/test/python/docker/compat/test_containers.py --- libpod-3.2.1+ds1/test/python/docker/compat/test_containers.py 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/python/docker/compat/test_containers.py 2021-12-26 00:45:51.000000000 +0000 @@ -1,13 +1,21 @@ +import io import subprocess import sys import time import unittest +from typing import IO, Optional, List from docker import DockerClient, 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 from test.python.docker.compat import common, constant +import tarfile + class TestContainers(unittest.TestCase): podman = None # initialized podman configuration for tests @@ -134,7 +142,7 @@ def test_remove_container_without_force(self): # Validate current container count - self.assertTrue(len(self.client.containers.list()), 1) + self.assertEqual(len(self.client.containers.list()), 1) # Remove running container should throw error top = self.client.containers.get(TestContainers.topContainerId) @@ -198,3 +206,97 @@ filters = {"name": "top"} ctnrs = self.client.containers.list(all=True, filters=filters) self.assertEqual(len(ctnrs), 1) + + def test_copy_to_container(self): + ctr: Optional[Container] = None + 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"]) + 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)) + + 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") + + ret, out = ctr.exec_run(["cat", "/tmp/a.txt"]) + self.assertEqual(ret, 0) + self.assertEqual(out.rstrip(), test_file_content, "Content of copied file") + + buff.seek(0) + with self.assertRaises(errors.APIError): + ctr.put_archive("/test-volume-read-only/", buff) + finally: + if ctr is not None: + ctr.stop() + ctr.remove() + if vol is not None: + 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') + 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"]) + ctr.start() + ret, 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') + 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"]) + ctr.start() + ret, out = 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) + 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') + finally: + if ctr is not None: + ctr.remove() + if vol is not None: + vol.remove(force=True) diff -Nru libpod-3.2.1+ds1/test/python/docker/__init__.py libpod-3.4.4+ds1/test/python/docker/__init__.py --- libpod-3.2.1+ds1/test/python/docker/__init__.py 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/python/docker/__init__.py 2021-12-26 00:45:51.000000000 +0000 @@ -27,7 +27,7 @@ # No support for tmpfs (/tmp) or extfs (/var/tmp) # self.cmd.append("--storage-driver=overlay") - if os.getenv("DEBUG"): + if os.getenv("PODMAN_PYTHON_TEST_DEBUG"): self.cmd.append("--log-level=debug") self.cmd.append("--syslog=true") diff -Nru libpod-3.2.1+ds1/test/python/requirements.txt libpod-3.4.4+ds1/test/python/requirements.txt --- libpod-3.2.1+ds1/test/python/requirements.txt 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/python/requirements.txt 2021-12-26 00:45:51.000000000 +0000 @@ -1,5 +1,5 @@ docker~=4.4.3 - +requests-mock~=1.9.3 requests~=2.20.0 setuptools~=50.3.2 python-dateutil~=2.8.1 diff -Nru libpod-3.2.1+ds1/test/README.md libpod-3.4.4+ds1/test/README.md --- libpod-3.2.1+ds1/test/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -84,7 +84,7 @@ It("podman inspect bogus pod", func() { session := podmanTest.Podman([]string{"pod", "inspect", "foobar"}) session.WaitWithDefaultTimeout() - Expect(session.ExitCode()).To(Not(Equal(0))) + Expect(session).To(ExitWithError()) }) ``` diff -Nru libpod-3.2.1+ds1/test/registries.conf libpod-3.4.4+ds1/test/registries.conf --- libpod-3.2.1+ds1/test/registries.conf 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/registries.conf 2021-12-26 00:45:51.000000000 +0000 @@ -15,3 +15,9 @@ [[registry]] prefix="docker.io/library" location="quay.io/libpod" + +# For testing #11933 to make sure that registries.conf is consulted unless +# --tls-verify is used during container creation. +[[registry]] +location="localhost:5000" +insecure=true diff -Nru libpod-3.2.1+ds1/test/system/001-basic.bats libpod-3.4.4+ds1/test/system/001-basic.bats --- libpod-3.2.1+ds1/test/system/001-basic.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/001-basic.bats 2021-12-26 00:45:51.000000000 +0000 @@ -49,6 +49,17 @@ @test "podman can pull an image" { run_podman pull $IMAGE + + # Also make sure that the tag@digest syntax is supported. + run_podman inspect --format "{{ .Digest }}" $IMAGE + digest=$output + run_podman pull $IMAGE@$digest + + # Now untag the digest reference again. + run_podman untag $IMAGE $IMAGE@$digest + + # Make sure the original image is still present (#11557). + run_podman image exists $IMAGE } # PR #7212: allow --remote anywhere before subcommand, not just as 1st flag diff -Nru libpod-3.2.1+ds1/test/system/005-info.bats libpod-3.4.4+ds1/test/system/005-info.bats --- libpod-3.2.1+ds1/test/system/005-info.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/005-info.bats 2021-12-26 00:45:51.000000000 +0000 @@ -9,6 +9,7 @@ buildahVersion: *[0-9.]\\\+ conmon:\\\s\\\+package: distribution: +logDriver: ociRuntime:\\\s\\\+name: os: rootless: @@ -33,16 +34,21 @@ expr_nvr="[a-z0-9-]\\\+-[a-z0-9.]\\\+-[a-z0-9]\\\+\." expr_path="/[a-z0-9\\\/.-]\\\+\\\$" + # FIXME: if we're ever able to get package versions on Debian, + # add '-[0-9]' to all '*.package' queries below. tests=" host.buildahVersion | [0-9.] host.conmon.path | $expr_path +host.conmon.package | .*conmon.* host.cgroupManager | \\\(systemd\\\|cgroupfs\\\) host.cgroupVersion | v[12] host.ociRuntime.path | $expr_path +host.ociRuntime.package | .*\\\(crun\\\|runc\\\).* store.configFile | $expr_path store.graphDriverName | [a-z0-9]\\\+\\\$ store.graphRoot | $expr_path store.imageStore.number | 1 +host.slirp4netns.executable | $expr_path " parse_table "$tests" | while read field expect; do @@ -82,4 +88,12 @@ # mounts. is "$output" ".*graphOptions: {}" "output includes graphOptions: {}" } + +@test "podman --root PATH info - basic output" { + if ! is_remote; then + run_podman --storage-driver=vfs --root ${PODMAN_TMPDIR}/nothing-here-move-along info --format '{{ .Store.GraphOptions }}' + is "$output" "map\[\]" "'podman --root should reset Graphoptions to []" + fi +} + # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/010-images.bats libpod-3.4.4+ds1/test/system/010-images.bats --- libpod-3.2.1+ds1/test/system/010-images.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/010-images.bats 2021-12-26 00:45:51.000000000 +0000 @@ -12,28 +12,29 @@ # 'podman images' should emit headings even if there are no images # (but --root only works locally) if ! is_remote; then - run_podman --root ${PODMAN_TMPDIR}/nothing-here-move-along images + run_podman --storage-driver=vfs --root ${PODMAN_TMPDIR}/nothing-here-move-along images is "$output" "$headings" "'podman images' emits headings even w/o images" fi } @test "podman images - custom formats" { tests=" ---format {{.ID}} | [0-9a-f]\\\{12\\\} ---format {{.ID}} --no-trunc | sha256:[0-9a-f]\\\{64\\\} ---format {{.Repository}}:{{.Tag}} | $PODMAN_TEST_IMAGE_FQN ---format {{.Labels.created_by}} | test/system/build-testimage ---format {{.Labels.created_at}} | 20[0-9-]\\\+T[0-9:]\\\+Z +{{.ID}} | [0-9a-f]\\\{12\\\} +{{.ID| upper}} | [0-9A-F]\\\{12\\\} +{{.Repository}}:{{.Tag}} | $PODMAN_TEST_IMAGE_FQN +{{.Labels.created_by}} | test/system/build-testimage +{{.Labels.created_at}} | 20[0-9-]\\\+T[0-9:]\\\+Z " parse_table "$tests" | while read fmt expect; do - run_podman images $fmt + run_podman images --format "$fmt" is "$output" "$expect\$" "podman images $fmt" done + run_podman images --format "{{.ID}}" --no-trunc + is "$output" "sha256:[0-9a-f]\\{64\\}\$" "podman images --no-trunc" } - @test "podman images - json" { # 'created': podman includes fractional seconds, podman-remote does not tests=" diff -Nru libpod-3.2.1+ds1/test/system/015-help.bats libpod-3.4.4+ds1/test/system/015-help.bats --- libpod-3.2.1+ds1/test/system/015-help.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/015-help.bats 2021-12-26 00:45:51.000000000 +0000 @@ -86,6 +86,12 @@ run_podman 125 "$@" $cmd -l nonexistent-container is "$output" "Error: .*--latest and \(containers\|pods\|arguments\) cannot be used together" \ "'$command_string' with both -l and container" + + # Combine -l and -a, too (but spell it as --all, because "-a" + # means "attach" in podman container start) + run_podman 125 "$@" $cmd --all --latest + is "$output" "Error: \(--all and --latest cannot be used together\|--all, --latest and containers cannot be used together\|--all, --latest and arguments cannot be used together\|unknown flag\)" \ + "'$command_string' with both --all and --latest" fi fi diff -Nru libpod-3.2.1+ds1/test/system/030-run.bats libpod-3.4.4+ds1/test/system/030-run.bats --- libpod-3.2.1+ds1/test/system/030-run.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/030-run.bats 2021-12-26 00:45:51.000000000 +0000 @@ -67,6 +67,11 @@ is "$output" ".*invalidflag" "failed when passing undefined flags to the runtime" } +@test "podman run --memory=0 runtime option" { + run_podman run --memory=0 --rm $IMAGE echo hello + is "$output" "hello" "failed to run when --memory is set to 0" +} + # 'run --preserve-fds' passes a number of additional file descriptors into the container @test "podman run --preserve-fds" { skip_if_remote "preserve-fds is meaningless over remote" @@ -690,4 +695,43 @@ run_podman rm $cid } +@test "podman run no /etc/mtab " { + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir + + cat >$tmpdir/Dockerfile </dev/null + if [[ $output != "journald" ]]; then + echo "--events-backend journald" + fi + fi +} + function _log_test_multi() { local driver=$1 @@ -39,10 +53,12 @@ etc='.*' fi + local events_backend=$(_additional_events_backend $driver) + # Simple helper to make the container starts, below, easier to read local -a cid doit() { - run_podman run --log-driver=$driver --rm -d --name "$1" $IMAGE sh -c "$2"; + run_podman ${events_backend} run --log-driver=$driver --rm -d --name "$1" $IMAGE sh -c "$2"; cid+=($(echo "${output:0:12}")) } @@ -54,7 +70,7 @@ doit c1 "echo a;sleep 10;echo d;sleep 3" doit c2 "sleep 1;echo b;sleep 2;echo c;sleep 3" - run_podman logs -f c1 c2 + run_podman ${events_backend} logs -f c1 c2 is "$output" \ "${cid[0]} a$etc ${cid[1]} b$etc @@ -73,4 +89,166 @@ _log_test_multi journald } +function _log_test_restarted() { + run_podman run --log-driver=$1 --name logtest $IMAGE sh -c 'start=0; if test -s log; then start=`tail -n 1 log`; fi; seq `expr $start + 1` `expr $start + 10` | tee -a log' + # FIXME: #9597 + # run/start is flaking for remote so let's wait for the container condition + # to stop wasting energy until the root cause gets fixed. + run_podman container wait --condition=exited logtest + run_podman start -a logtest + logfile=$(mktemp -p ${PODMAN_TMPDIR} logfileXXXXXXXX) + $PODMAN $_PODMAN_TEST_OPTS logs -f logtest > $logfile + expected=$(mktemp -p ${PODMAN_TMPDIR} expectedXXXXXXXX) + seq 1 20 > $expected + diff -u ${expected} ${logfile} +} + +@test "podman logs restarted - k8s-file" { + _log_test_restarted k8s-file +} + +@test "podman logs restarted journald" { + # We can't use journald on RHEL as rootless: rhbz#1895105 + skip_if_journald_unavailable + + _log_test_restarted journald +} + +@test "podman logs - journald log driver requires journald events backend" { + skip_if_remote "remote does not support --events-backend" + # We can't use journald on RHEL as rootless: rhbz#1895105 + skip_if_journald_unavailable + + run_podman --events-backend=file run --log-driver=journald -d --name test --replace $IMAGE ls / + run_podman --events-backend=file logs test + run_podman 125 --events-backend=file logs --follow test + is "$output" "Error: using --follow with the journald --log-driver but without the journald --events-backend (file) is not supported" "journald logger requires journald eventer" +} + +function _log_test_since() { + local driver=$1 + + s_before="before_$(random_string)_${driver}" + s_after="after_$(random_string)_${driver}" + + before=$(date --iso-8601=seconds) + run_podman run --log-driver=$driver -d --name test $IMAGE sh -c \ + "echo $s_before; trap 'echo $s_after; exit' SIGTERM; while :; do sleep 1; done" + + # sleep a second to make sure the date is after the first echo + sleep 1 + after=$(date --iso-8601=seconds) + run_podman stop test + + run_podman logs test + is "$output" \ + "$s_before +$s_after" + + run_podman logs --since $before test + is "$output" \ + "$s_before +$s_after" + + run_podman logs --since $after test + is "$output" "$s_after" + run_podman rm -f test +} + +@test "podman logs - since k8s-file" { + _log_test_since k8s-file +} + +@test "podman logs - since journald" { + # We can't use journald on RHEL as rootless: rhbz#1895105 + skip_if_journald_unavailable + + _log_test_since journald +} + +function _log_test_until() { + local driver=$1 + + s_before="before_$(random_string)_${driver}" + s_after="after_$(random_string)_${driver}" + + before=$(date --iso-8601=seconds) + sleep 1 + run_podman run --log-driver=$driver -d --name test $IMAGE sh -c \ + "echo $s_before; trap 'echo $s_after; exit' SIGTERM; while :; do sleep 1; done" + + # sleep a second to make sure the date is after the first echo + sleep 1 + run_podman stop test + run_podman wait test + + # Sigh. Stupid journald has a lag. Wait a few seconds for it to catch up. + retries=20 + s_both="$s_before +$s_after" + while [[ $retries -gt 0 ]]; do + run_podman logs test + if [[ "$output" = "$s_both" ]]; then + break + fi + retries=$((retries - 1)) + sleep 0.1 + done + if [[ $retries -eq 0 ]]; then + die "Timed out waiting for before&after in podman logs: $output" + fi + + run_podman logs --until $before test + is "$output" "" "podman logs --until before" + + after=$(date --date='+1 second' --iso-8601=seconds) + + run_podman logs --until $after test + is "$output" "$s_both" "podman logs --until after" + run_podman rm -f test +} + +@test "podman logs - until k8s-file" { + _log_test_until k8s-file +} + +@test "podman logs - until journald" { + # We can't use journald on RHEL as rootless: rhbz#1895105 + skip_if_journald_unavailable + + _log_test_until journald +} + +function _log_test_follow() { + local driver=$1 + cname=$(random_string) + contentA=$(random_string) + contentB=$(random_string) + contentC=$(random_string) + local events_backend=$(_additional_events_backend $driver) + + if [[ -n "${events_backend}" ]]; then + skip_if_remote "remote does not support --events-backend" + fi + + # Note: it seems we need at least three log lines to hit #11461. + run_podman ${events_backend} run --log-driver=$driver --name $cname $IMAGE sh -c "echo $contentA; echo $contentB; echo $contentC" + run_podman ${events_backend} logs -f $cname + is "$output" "$contentA +$contentB +$contentC" "logs -f on exitted container works" + + run_podman ${events_backend} rm -f $cname +} + +@test "podman logs - --follow k8s-file" { + _log_test_follow k8s-file +} + +@test "podman logs - --follow journald" { + # We can't use journald on RHEL as rootless: rhbz#1895105 + skip_if_journald_unavailable + + _log_test_follow journald +} # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/040-ps.bats libpod-3.4.4+ds1/test/system/040-ps.bats --- libpod-3.2.1+ds1/test/system/040-ps.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/040-ps.bats 2021-12-26 00:45:51.000000000 +0000 @@ -90,10 +90,18 @@ is "${#lines[@]}" "1" "setup check: no storage containers at start of test" # Force a buildah timeout; this leaves a buildah container behind + local t0=$SECONDS PODMAN_TIMEOUT=5 run_podman 124 build -t thiswillneverexist - < $srcdir/subdir/dotfile. - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer mkdir /srv/subdir + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -41,7 +40,6 @@ 0 | /tmp | /tmp/hostfile0 | copy to /tmp 1 | /tmp/ | /tmp/hostfile1 | copy to /tmp/ 2 | /tmp/. | /tmp/hostfile2 | copy to /tmp/. -0 | /tmp/hostfile2 | /tmp/hostfile2 | overwrite previous copy 0 | /tmp/anotherbase.txt | /tmp/anotherbase.txt | copy to /tmp, new name 0 | . | /srv/hostfile0 | copy to workdir (rel path), new name 1 | ./ | /srv/hostfile1 | copy to workdir (rel path), new name @@ -114,7 +112,7 @@ } -@test "podman cp file from host to container and check ownership" { +@test "podman cp (-a=true) file from host to container and check ownership" { srcdir=$PODMAN_TMPDIR/cp-test-file-host-to-ctr mkdir -p $srcdir content=cp-user-test-$(random_string 10) @@ -129,6 +127,25 @@ run_podman rm -f cpcontainer } +@test "podman cp (-a=false) file from host to container and check ownership" { + local tmpdir="${PODMAN_TMPDIR}/cp-test-file-host-to-ctr" + mkdir -p "${tmpdir}" + + pushd "${tmpdir}" + touch a.txt + tar --owner=1042 --group=1043 -cf a.tar a.txt + popd + + userid=$(id -u) + + run_podman run --user="$userid" --userns=keep-id -d --name cpcontainer $IMAGE sleep infinity + run_podman cp -a=false - cpcontainer:/tmp/ < "${tmpdir}/a.tar" + run_podman exec cpcontainer stat -c "%u:%g" /tmp/a.txt + is "$output" "1042:1043" "copied file retains uid/gid from the tar" + run_podman kill cpcontainer + run_podman rm -f cpcontainer +} + @test "podman cp file from/to host while --pid=host" { if is_rootless && ! is_cgroupsv2; then @@ -156,11 +173,12 @@ random-1-$(random_string 15) random-2-$(random_string 20) ) - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile" - run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/dotfile." - run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1" - run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2" + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /tmp/containerfile; + echo ${randomcontent[0]} > /tmp/dotfile.; + echo ${randomcontent[1]} > /srv/containerfile1; + echo ${randomcontent[2]} > /srv/subdir/containerfile2; + sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -207,6 +225,96 @@ } +@test "podman cp file from container to container" { + # Create 3 files with random content in the container. + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + random-2-$(random_string 20) + ) + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /tmp/containerfile; + echo ${randomcontent[0]} > /tmp/dotfile.; + echo ${randomcontent[1]} > /srv/containerfile1; + echo ${randomcontent[2]} > /srv/subdir/containerfile2; + sleep infinity" + + # Commit the image for testing non-running containers + run_podman commit -q cpcontainer + cpimage="$output" + + # format is: | | | | + tests=" +0 | /tmp/containerfile | | /containerfile | / +0 | /tmp/dotfile. | | /dotfile. | / +0 | /tmp/containerfile | / | /containerfile | / +0 | /tmp/containerfile | /. | /containerfile | /. +0 | /tmp/containerfile | /newfile | /newfile | /newfile +1 | containerfile1 | / | /containerfile1 | copy from workdir (rel path) to / +2 | subdir/containerfile2 | / | /containerfile2 | copy from workdir/subdir (rel path) to / +" + + # From RUNNING container + local -a destcontainers=() + while read id src dest dest_fullname description; do + # dest may be "''" for empty table cells + if [[ $dest == "''" ]];then + unset dest + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman start $destcontainer + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + done < <(parse_table "$tests") + run_podman kill cpcontainer ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} + + # From CREATED container + destcontainers=() + run_podman create --name cpcontainer --workdir=/srv $cpimage + while read id src dest dest_fullname description; do + # dest may be "''" for empty table cells + if [[ $dest == "''" ]];then + unset dest + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman start $destcontainer + run_podman exec $destcontainer cat "/$dest_fullname" + is "$output" "${randomcontent[$id]}" "$description (cp ctr:$src to /$dest)" + done < <(parse_table "$tests") + run_podman kill ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} + + run_podman rmi -f $cpimage +} + + @test "podman cp dir from host to container" { srcdir=$PODMAN_TMPDIR mkdir -p $srcdir/dir/sub @@ -222,8 +330,7 @@ mkdir -p $srcdir/dir. cp -r $srcdir/dir/* $srcdir/dir. - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer mkdir /srv/subdir + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -272,6 +379,11 @@ run_podman rm -f cpcontainer done < <(parse_table "$tests") + run_podman create --name cpcontainer --workdir=/srv $cpimage sleep infinity + run_podman 125 cp $srcdir cpcontainer:/etc/os-release + is "$output" "Error: destination must be a directory when copying a directory" "cannot copy directory to file" + run_podman rm -f cpcontainer + run_podman rmi -f $cpimage } @@ -285,12 +397,12 @@ random-0-$(random_string 10) random-1-$(random_string 15) ) - run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity - run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[0]} > /srv/subdir/containerfile0" - run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/subdir/containerfile1" - # "." and "dir/." will copy the contents, so make sure that a dir ending - # with dot is treated correctly. - run_podman exec cpcontainer sh -c 'mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./' + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /srv/subdir/containerfile0; \ + echo ${randomcontent[1]} > /srv/subdir/containerfile1; \ + mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./; \ + sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer @@ -343,12 +455,119 @@ is "$(< $destdir$dest_fullname/containerfile1)" "${randomcontent[1]}" "$description" rm -rf $destdir/* done < <(parse_table "$tests") + + touch $destdir/testfile + run_podman 125 cp cpcontainer:/etc/ $destdir/testfile + is "$output" "Error: destination must be a directory when copying a directory" "cannot copy directory to file" run_podman rm -f cpcontainer run_podman rmi -f $cpimage } +@test "podman cp dir from container to container" { + # Create 2 files with random content in the container. + local -a randomcontent=( + random-0-$(random_string 10) + random-1-$(random_string 15) + ) + + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sh -c "mkdir /srv/subdir; + echo ${randomcontent[0]} > /srv/subdir/containerfile0; \ + echo ${randomcontent[1]} > /srv/subdir/containerfile1; \ + mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./; \ + sleep infinity" + + # Commit the image for testing non-running containers + run_podman commit -q cpcontainer + cpimage="$output" + + # format is: | | | + tests=" +/srv | | /srv/subdir | copy /srv +/srv | /newdir | /newdir/subdir | copy /srv to /newdir +/srv/ | | /srv/subdir | copy /srv/ +/srv/. | | /subdir | copy /srv/. +/srv/. | /newdir | /newdir/subdir | copy /srv/. to /newdir +/srv/subdir/. | | | copy /srv/subdir/. +/tmp/subdir. | | /subdir. | copy /tmp/subdir. +" + + # From RUNNING container + local -a destcontainers=() + while read src dest dest_fullname description; do + if [[ $src == "''" ]];then + unset src + fi + if [[ $dest == "''" ]];then + unset dest + fi + if [[ $dest_fullname == "''" ]];then + unset dest_fullname + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman start $destcontainer + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + done < <(parse_table "$tests") + run_podman kill cpcontainer ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} + + # From CREATED container + destcontainers=() + run_podman create --name cpcontainer --workdir=/srv $cpimage + while read src dest dest_fullname description; do + if [[ $src == "''" ]];then + unset src + fi + if [[ $dest == "''" ]];then + unset dest + fi + if [[ $dest_fullname == "''" ]];then + unset dest_fullname + fi + + # To RUNNING container + run_podman run -d $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + + # To CREATED container + run_podman create $IMAGE sleep infinity + destcontainer="$output" + destcontainers+=($destcontainer) + run_podman start $destcontainer + run_podman cp cpcontainer:$src $destcontainer:"/$dest" + run_podman exec $destcontainer cat "/$dest_fullname/containerfile0" "/$dest_fullname/containerfile1" + is "$output" "${randomcontent[0]} +${randomcontent[1]}" "$description" + done < <(parse_table "$tests") + + run_podman kill ${destcontainers[@]} + run_podman rm -f cpcontainer ${destcontainers[@]} + run_podman rmi -f $cpimage +} + + @test "podman cp symlinked directory from container" { destdir=$PODMAN_TMPDIR/cp-weird-symlink mkdir -p $destdir @@ -359,10 +578,10 @@ random-1-$(random_string 15) ) - run_podman run -d --name cpcontainer $IMAGE sleep infinity - run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile0" - run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /tmp/containerfile1" - run_podman exec cpcontainer sh -c "mkdir /tmp/sub && cd /tmp/sub && ln -s .. weirdlink" + run_podman run -d --name cpcontainer $IMAGE sh -c "echo ${randomcontent[0]} > /tmp/containerfile0; \ + echo ${randomcontent[1]} > /tmp/containerfile1; \ + mkdir /tmp/sub && cd /tmp/sub && ln -s .. weirdlink; \ + sleep infinity" # Commit the image for testing non-running containers run_podman commit -q cpcontainer diff -Nru libpod-3.2.1+ds1/test/system/070-build.bats libpod-3.4.4+ds1/test/system/070-build.bats --- libpod-3.2.1+ds1/test/system/070-build.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/070-build.bats 2021-12-26 00:45:51.000000000 +0000 @@ -21,7 +21,60 @@ # The 'apk' command can take a long time to fetch files; bump timeout PODMAN_TIMEOUT=240 run_podman build -t build_test --format=docker $tmpdir - is "$output" ".*STEP 4: COMMIT" "COMMIT seen in log" + is "$output" ".*COMMIT" "COMMIT seen in log" + + run_podman run --rm build_test cat /$rand_filename + is "$output" "$rand_content" "reading generated file in image" + + run_podman rmi -f build_test +} + +@test "podman buildx - basic test" { + rand_filename=$(random_string 20) + rand_content=$(random_string 50) + + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir + dockerfile=$tmpdir/Dockerfile + cat >$dockerfile < /$rand_filename +VOLUME ['/etc/foo', '/etc/bar'] +EOF + + run_podman buildx build --load -t build_test --format=docker $tmpdir + is "$output" ".*COMMIT" "COMMIT seen in log" + + run_podman run --rm build_test cat /$rand_filename + is "$output" "$rand_content" "reading generated file in image" + + # Make sure the volumes are created at surprising yet Docker-compatible + # destinations (see bugzilla.redhat.com/show_bug.cgi?id=2014149). + run_podman run --rm build_test find /[ /etc/bar\] -print + is "$output" "/\[ +/\[/etc +/\[/etc/foo, +/etc/bar]" "weird VOLUME gets converted to directories with brackets and comma" + + run_podman rmi -f build_test +} + +@test "podman build test -f -" { + rand_filename=$(random_string 20) + rand_content=$(random_string 50) + + tmpdir=$PODMAN_TMPDIR/build-test + mkdir -p $tmpdir + containerfile=$PODMAN_TMPDIR/Containerfile + cat >$containerfile < /$rand_filename +EOF + + # The 'apk' command can take a long time to fetch files; bump timeout + PODMAN_TIMEOUT=240 run_podman build -t build_test -f - --format=docker $tmpdir < $containerfile + is "$output" ".*COMMIT" "COMMIT seen in log" run_podman run --rm build_test cat /$rand_filename is "$output" "$rand_content" "reading generated file in image" @@ -87,7 +140,7 @@ # One of: ADD myfile /myfile or COPY . . run_podman build -t build_test -f $tmpdir/Dockerfile $tmpdir - is "$output" ".*STEP 3: COMMIT" "COMMIT seen in log" + is "$output" ".*COMMIT" "COMMIT seen in log" if [[ "$output" =~ "Using cache" ]]; then is "$output" "[no instance of 'Using cache']" "no cache used" fi @@ -101,7 +154,7 @@ run tar -C $tmpdir -cJf $tmpdir/myfile.tar.xz subtest run_podman build -t build_test -f $tmpdir/Dockerfile $tmpdir - is "$output" ".*STEP 3: COMMIT" "COMMIT seen in log" + is "$output" ".*COMMIT" "COMMIT seen in log" # Since the tarfile is modified, podman SHOULD NOT use a cached layer. if [[ "$output" =~ "Using cache" ]]; then @@ -241,21 +294,11 @@ build_arg_implicit+="=$arg_implicit_value" fi - # FIXME FIXME FIXME: 2021-03-15: workaround for #9567 (slow ubuntu 2004): - # we're seeing lots of timeouts in CI. Until/unless #9567 gets fixed, - # let's get CI passing by extending the timeout when remote on ubuntu - local localtimeout=${PODMAN_TIMEOUT} - if is_remote; then - if grep -qi ubuntu /etc/os-release; then - localtimeout=$(( 2 * $localtimeout )) - fi - fi - # cd to the dir, so we test relative paths (important for podman-remote) cd $PODMAN_TMPDIR export arg_explicit="THIS SHOULD BE OVERRIDDEN BY COMMAND LINE!" export arg_implicit=${arg_implicit_value} - PODMAN_TIMEOUT=$localtimeout run_podman ${MOUNTS_CONF} build \ + run_podman ${MOUNTS_CONF} build \ --build-arg arg_explicit=${arg_explicit_value} \ $build_arg_implicit \ --dns-search $nosuchdomain \ @@ -412,16 +455,24 @@ @test "podman build - COPY with ignore" { local tmpdir=$PODMAN_TMPDIR/build-test-$(random_string 10) - mkdir -p $tmpdir/subdir + mkdir -p $tmpdir/subdir{1,2} # Create a bunch of files. Declare this as an array to avoid duplication # because we iterate over that list below, checking for each file. # A leading "-" indicates that the file SHOULD NOT exist in the built image + # + # Weird side effect of Buildah 3486, relating to subdirectories and + # wildcard patterns. See that PR for details, it's way too confusing + # to explain in a comment. local -a files=( -test1 -test1.txt test2 test2.txt - subdir/sub1 subdir/sub1.txt - -subdir/sub2 -subdir/sub2.txt + subdir1/sub1 subdir1/sub1.txt + -subdir1/sub2 -subdir1/sub2.txt + subdir1/sub3 subdir1/sub3.txt + -subdir2/sub1 -subdir2/sub1.txt + -subdir2/sub2 -subdir2/sub2.txt + -subdir2/sub3 -subdir2/sub3.txt this-file-does-not-match-anything-in-ignore-file comment ) @@ -448,8 +499,10 @@ # comment test* !test2* -subdir +subdir1 +subdir2 !*/sub1* +!subdir1/sub3* EOF # Build an image. For .dockerignore @@ -486,6 +539,40 @@ done } +# Regression test for #9867 +# Make sure that if you exclude everything in context dir, that +# the Containerfile/Dockerfile in the context dir are used +@test "podman build with ignore '*'" { + local tmpdir=$PODMAN_TMPDIR/build-test-$(random_string 10) + mkdir -p $tmpdir + + cat >$tmpdir/Containerfile <$tmpdir/.dockerignore <$tmpdir/.dockerignore <$tmpdir/Containerfile <$tmpdir/.containerignore <$tmpdir/.dockerignore <$tmpdir/Containerfile <$tmpdir/Dockerfile < $tmpdir/link/Dockerfile + echo RUN echo hello >> $tmpdir/link/Dockerfile + run_podman build -t build_test $tmpdir/link +} + function teardown() { # A timeout or other error in 'build' can leave behind stale images # that podman can't even see and which will cascade into subsequent @@ -827,6 +981,9 @@ run_podman '?' rm -a -f run_podman '?' rmi -f build_test + # Many of the tests above leave interim layers behind. Clean them up. + run_podman '?' image prune -f + basic_teardown } diff -Nru libpod-3.2.1+ds1/test/system/075-exec.bats libpod-3.4.4+ds1/test/system/075-exec.bats --- libpod-3.2.1+ds1/test/system/075-exec.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/075-exec.bats 2021-12-26 00:45:51.000000000 +0000 @@ -59,8 +59,6 @@ # Issue #4785 - piping to exec statement - fixed in #4818 # Issue #5046 - piping to exec truncates results (actually a conmon issue) @test "podman exec - cat from stdin" { - skip_if_remote "FIXME: pending #7360" - run_podman run -d $IMAGE sh -c 'while [ ! -e /stop ]; do sleep 0.1;done' cid="$output" @@ -103,4 +101,32 @@ run_podman rm $cid } +# #11496: podman-remote loses output +@test "podman exec/run - missing output" { + local bigfile=${PODMAN_TMPDIR}/bigfile + local newfile=${PODMAN_TMPDIR}/newfile + # create a big file, bigger than the 8K buffer size + base64 /dev/urandom | head -c 20K > $bigfile + + run_podman run --rm -v $bigfile:/tmp/test:Z $IMAGE cat /tmp/test + printf "%s" "$output" > $newfile + # use cmp to compare the files, this is very helpful since it will + # tell us the first wrong byte in case this fails + run cmp $bigfile $newfile + is "$output" "" "run output is identical with the file" + + run_podman run -d --stop-timeout 0 -v $bigfile:/tmp/test:Z $IMAGE sleep inf + cid="$output" + + run_podman exec $cid cat /tmp/test + printf "%s" "$output" > $newfile + # use cmp to compare the files, this is very helpful since it will + # tell us the first wrong byte in case this fails + run cmp $bigfile $newfile + is "$output" "" "exec output is identical with the file" + + # Clean up + run_podman rm -f $cid +} + # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/080-pause.bats libpod-3.4.4+ds1/test/system/080-pause.bats --- libpod-3.2.1+ds1/test/system/080-pause.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/080-pause.bats 2021-12-26 00:45:51.000000000 +0000 @@ -48,6 +48,7 @@ # would imply that the container never paused. is "$max_delta" "[3456]" "delta t between paused and restarted" + run_podman stop -t 0 $cname run_podman rm -f $cname # Pause/unpause on nonexistent name or id - these should all fail @@ -57,4 +58,24 @@ run_podman 125 unpause $cname } +@test "podman unpause --all" { + if is_rootless && ! is_cgroupsv2; then + skip "'podman pause' (rootless) only works with cgroups v2" + fi + + cname=$(random_string 10) + run_podman create --name notrunning $IMAGE + run_podman run -d --name $cname $IMAGE sleep 100 + cid="$output" + run_podman pause $cid + run_podman inspect --format '{{.State.Status}}' $cid + is "$output" "paused" "podman inspect .State.Status" + run_podman unpause --all + is "$output" "$cid" "podman unpause output" + run_podman ps --format '{{.ID}} {{.Names}} {{.Status}}' + is "$output" "${cid:0:12} $cname Up.*" "podman ps on resumed container" + run_podman stop -t 0 $cname + run_podman rm -f $cname + run_podman rm -f notrunning +} # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/090-events.bats libpod-3.4.4+ds1/test/system/090-events.bats --- libpod-3.2.1+ds1/test/system/090-events.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/090-events.bats 2021-12-26 00:45:51.000000000 +0000 @@ -81,9 +81,21 @@ @test "events with disjunctive filters - journald" { skip_if_remote "remote does not support --events-backend" + skip_if_journald_unavailable "system does not support journald events" _events_disjunctive_filters --events-backend=journald } +@test "events with file backend and journald logdriver with --follow failure" { + skip_if_remote "remote does not support --events-backend" + skip_if_journald_unavailable "system does not support journald events" + run_podman --events-backend=file run --log-driver=journald --name=test $IMAGE echo hi + is "$output" "hi" "Should support events-backend=file" + + run_podman 125 --events-backend=file logs --follow test + is "$output" "Error: using --follow with the journald --log-driver but without the journald --events-backend (file) is not supported" "Should fail with reasonable error message when events-backend and events-logger do not match" + +} + @test "events with disjunctive filters - default" { _events_disjunctive_filters "" } diff -Nru libpod-3.2.1+ds1/test/system/120-load.bats libpod-3.4.4+ds1/test/system/120-load.bats --- libpod-3.2.1+ds1/test/system/120-load.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/120-load.bats 2021-12-26 00:45:51.000000000 +0000 @@ -134,8 +134,16 @@ } @test "podman load - multi-image archive" { - img1="quay.io/libpod/testimage:00000000" - img2="quay.io/libpod/testimage:20200902" + # img1 & 2 should be images that are not locally present; they must also + # be usable on the host arch. The nonlocal image (:000000xx) is kept + # up-to-date for all RHEL/Fedora arches; the other image we use is + # the one tagged ':multiimage', which as of 2021-07-15 is :20210610 + # but that tag will grow stale over time. If/when this test fails, + # your first approach should be to manually update :multiimage to + # point to a more recent testimage. (Use the quay.io GUI, it's waaay + # easier than pulling/pushing the correct manifest.) + img1=${PODMAN_NONLOCAL_IMAGE_FQN} + img2="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:multiimage" archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar run_podman pull $img1 @@ -151,8 +159,9 @@ } @test "podman load - multi-image archive with redirect" { - img1="quay.io/libpod/testimage:00000000" - img2="quay.io/libpod/testimage:20200902" + # (see comments in test above re: img1 & 2) + img1=${PODMAN_NONLOCAL_IMAGE_FQN} + img2="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:multiimage" archive=$PODMAN_TMPDIR/myimage-$(random_string 8).tar run_podman pull $img1 diff -Nru libpod-3.2.1+ds1/test/system/125-import.bats libpod-3.4.4+ds1/test/system/125-import.bats --- libpod-3.2.1+ds1/test/system/125-import.bats 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/125-import.bats 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,108 @@ +#!/usr/bin/env bats -*- bats -*- +# +# tests for podman import +# + +load helpers + +@test "podman import" { + local archive=$PODMAN_TMPDIR/archive.tar + local random_content=$(random_string 12) + # Generate a random name and tag (must be lower-case) + local random_name=x0$(random_string 12 | tr A-Z a-z) + local random_tag=t0$(random_string 7 | tr A-Z a-z) + local fqin=localhost/$random_name:$random_tag + + run_podman run --name import $IMAGE sh -c "echo ${random_content} > /random.txt" + run_podman export import -o $archive + run_podman rm -f import + + # Simple import + run_podman import -q $archive + iid="$output" + run_podman run -t --rm $iid cat /random.txt + is "$output" "$random_content" "simple import" + run_podman rmi -f $iid + + # Simple import via stdin + run_podman import -q - < <(cat $archive) + iid="$output" + run_podman run -t --rm $iid cat /random.txt + is "$output" "$random_content" "simple import via stdin" + run_podman rmi -f $iid + + # Tagged import + run_podman import -q $archive $fqin + run_podman run -t --rm $fqin cat /random.txt + is "$output" "$random_content" "tagged import" + run_podman rmi -f $fqin + + # Tagged import via stdin + run_podman import -q - $fqin < <(cat $archive) + run_podman run -t --rm $fqin cat /random.txt + is "$output" "$random_content" "tagged import via stdin" + run_podman rmi -f $fqin +} + +@test "podman export, alter tarball, re-import" { + + # Create a test file following test + mkdir $PODMAN_TMPDIR/tmp + touch $PODMAN_TMPDIR/testfile1 + echo "modified tar file" >> $PODMAN_TMPDIR/tmp/testfile2 + + # Create Dockerfile for test + dockerfile=$PODMAN_TMPDIR/Dockerfile + + cat >$dockerfile < /dev/tcp/127.0.0.1/$port; } &>/dev/null; then - export PODMAN_LOGIN_REGISTRY_PORT=$port - break - fi - done + export PODMAN_LOGIN_REGISTRY_PORT=$(random_free_port) fi # Override any user-set path to an auth file @@ -62,7 +57,7 @@ # Pull registry image, but into a separate container storage mkdir -p ${PODMAN_LOGIN_WORKDIR}/root mkdir -p ${PODMAN_LOGIN_WORKDIR}/runroot - PODMAN_LOGIN_ARGS="--root ${PODMAN_LOGIN_WORKDIR}/root --runroot ${PODMAN_LOGIN_WORKDIR}/runroot" + PODMAN_LOGIN_ARGS="--storage-driver=vfs --root ${PODMAN_LOGIN_WORKDIR}/root --runroot ${PODMAN_LOGIN_WORKDIR}/runroot" # Give it three tries, to compensate for flakes run_podman ${PODMAN_LOGIN_ARGS} pull $REGISTRY_IMAGE || run_podman ${PODMAN_LOGIN_ARGS} pull $REGISTRY_IMAGE || @@ -306,10 +301,10 @@ skip "[leaving registry running by request]" fi - run_podman --root ${PODMAN_LOGIN_WORKDIR}/root \ + run_podman --storage-driver=vfs --root ${PODMAN_LOGIN_WORKDIR}/root \ --runroot ${PODMAN_LOGIN_WORKDIR}/runroot \ rm -f registry - run_podman --root ${PODMAN_LOGIN_WORKDIR}/root \ + run_podman --storage-driver=vfs --root ${PODMAN_LOGIN_WORKDIR}/root \ --runroot ${PODMAN_LOGIN_WORKDIR}/runroot \ rmi -a diff -Nru libpod-3.2.1+ds1/test/system/160-volumes.bats libpod-3.4.4+ds1/test/system/160-volumes.bats --- libpod-3.2.1+ds1/test/system/160-volumes.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/160-volumes.bats 2021-12-26 00:45:51.000000000 +0000 @@ -21,8 +21,6 @@ # Simple volume tests: share files between host and container @test "podman run --volumes : basic" { - skip_if_remote "volumes cannot be shared across hosts" - run_podman volume list --noheading is "$output" "" "baseline: empty results from list --noheading" @@ -186,6 +184,25 @@ } +# Podman volume import test +@test "podman volume import test" { + skip_if_remote "volumes import is not applicable on podman-remote" + run_podman volume create my_vol + run_podman run --rm -v my_vol:/data $IMAGE sh -c "echo hello >> /data/test" + run_podman volume create my_vol2 + + tarfile=${PODMAN_TMPDIR}/hello$(random_string | tr A-Z a-z).tar + run_podman volume export my_vol --output=$tarfile + # we want to use `run_podman volume export my_vol` but run_podman is wrapping EOF + run_podman volume import my_vol2 - < $tarfile + rm -f $tarfile + run_podman run --rm -v my_vol2:/data $IMAGE sh -c "cat /data/test" + is "$output" "hello" "output from second container" + run_podman volume rm my_vol + run_podman volume rm my_vol2 +} + + # Confirm that container sees the correct id @test "podman volume with --userns=keep-id" { is_rootless || skip "only meaningful when run rootless" diff -Nru libpod-3.2.1+ds1/test/system/200-pod.bats libpod-3.4.4+ds1/test/system/200-pod.bats --- libpod-3.2.1+ds1/test/system/200-pod.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/200-pod.bats 2021-12-26 00:45:51.000000000 +0000 @@ -76,11 +76,7 @@ fi # Randomly-assigned port in the 5xxx range - for port in $(shuf -i 5000-5999);do - if ! { exec 3<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then - break - fi - done + port=$(random_free_port) # Listener. This will exit as soon as it receives a message. run_podman run -d --pod $podname $IMAGE nc -l -p $port @@ -183,16 +179,8 @@ pod_id_file=${PODMAN_TMPDIR}/pod-id-file # Randomly-assigned ports in the 5xxx and 6xxx range - for port_in in $(shuf -i 5000-5999);do - if ! { exec 3<> /dev/tcp/127.0.0.1/$port_in; } &>/dev/null; then - break - fi - done - for port_out in $(shuf -i 6000-6999);do - if ! { exec 3<> /dev/tcp/127.0.0.1/$port_out; } &>/dev/null; then - break - fi - done + port_in=$(random_free_port 5000-5999) + port_out=$(random_free_port 6000-6999) # Create a pod with all the desired options # FIXME: --ip=$ip fails: @@ -205,6 +193,7 @@ # entrypoint to confirm that --infra-command will override. local infra_image="infra_$(random_string 10 | tr A-Z a-z)" local infra_command="/pause_$(random_string 10)" + local infra_name="infra_container_$(random_string 10 | tr A-Z a-z)" run_podman build -t $infra_image - << EOF FROM $IMAGE RUN ln /home/podman/pause $infra_command @@ -225,7 +214,8 @@ --publish "$port_out:$port_in" \ --label "${labelname}=${labelvalue}" \ --infra-image "$infra_image" \ - --infra-command "$infra_command" + --infra-command "$infra_command" \ + --infra-name "$infra_name" pod_id="$output" # Check --pod-id-file @@ -237,6 +227,9 @@ # confirm that entrypoint is what we set run_podman container inspect --format '{{.Config.Entrypoint}}' $infra_cid is "$output" "$infra_command" "infra-command took effect" + # confirm that infra container name is set + run_podman container inspect --format '{{.Name}}' $infra_cid + is "$output" "$infra_name" "infra-name took effect" # Check each of the options if [ -n "$mac_option" ]; then @@ -310,6 +303,16 @@ run_podman rm $cid run_podman pod rm -f mypod run_podman rmi $infra_image + +} + +@test "podman pod create should fail when infra-name is already in use" { + local infra_name="infra_container_$(random_string 10 | tr A-Z a-z)" + run_podman pod create --infra-name "$infra_name" + run_podman '?' pod create --infra-name "$infra_name" + if [ $status -eq 0 ]; then + die "Podman should fail when user try to create two pods with the same infra-name value" + fi } # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/220-healthcheck.bats libpod-3.4.4+ds1/test/system/220-healthcheck.bats --- libpod-3.2.1+ds1/test/system/220-healthcheck.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/220-healthcheck.bats 2021-12-26 00:45:51.000000000 +0000 @@ -74,7 +74,7 @@ # # So, just force a healthcheck run, then confirm that it's running. run_podman healthcheck run healthcheck_c - is "$output" "healthy" "output from 'podman healthcheck run'" + is "$output" "" "output from 'podman healthcheck run'" _check_health "All healthy" " Status | healthy @@ -108,6 +108,7 @@ is "$output" "unhealthy" "output from 'podman healthcheck run'" # Clean up + run_podman stop -t 0 healthcheck_c run_podman rm -f healthcheck_c run_podman rmi healthcheck_i } diff -Nru libpod-3.2.1+ds1/test/system/250-systemd.bats libpod-3.4.4+ds1/test/system/250-systemd.bats --- libpod-3.2.1+ds1/test/system/250-systemd.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/250-systemd.bats 2021-12-26 00:45:51.000000000 +0000 @@ -4,17 +4,10 @@ # load helpers +load helpers.systemd SERVICE_NAME="podman_test_$(random_string)" -SYSTEMCTL="systemctl" -UNIT_DIR="/usr/lib/systemd/system" -if is_rootless; then - UNIT_DIR="$HOME/.config/systemd/user" - mkdir -p $UNIT_DIR - - SYSTEMCTL="$SYSTEMCTL --user" -fi UNIT_FILE="$UNIT_DIR/$SERVICE_NAME.service" function setup() { @@ -24,38 +17,28 @@ } function teardown() { - run '?' $SYSTEMCTL stop "$SERVICE_NAME" + run '?' systemctl stop "$SERVICE_NAME" rm -f "$UNIT_FILE" - $SYSTEMCTL daemon-reload + systemctl daemon-reload run_podman rmi -a basic_teardown } -# Helper to setup xdg runtime for rootless -function xdg_rootless() { - # podman initializes this if unset, but systemctl doesn't - if is_rootless; then - if [ -z "$XDG_RUNTIME_DIR" ]; then - export XDG_RUNTIME_DIR=/run/user/$(id -u) - fi - fi -} - # Helper to start a systemd service running a container function service_setup() { run_podman generate systemd --new $cname echo "$output" > "$UNIT_FILE" run_podman rm $cname - $SYSTEMCTL daemon-reload + systemctl daemon-reload - run $SYSTEMCTL start "$SERVICE_NAME" + run systemctl start "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Error starting systemd unit $SERVICE_NAME, output: $output" fi - run $SYSTEMCTL status "$SERVICE_NAME" + run systemctl status "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Non-zero status of systemd unit $SERVICE_NAME, output: $output" fi @@ -63,23 +46,31 @@ # Helper to stop a systemd service running a container function service_cleanup() { - run $SYSTEMCTL stop "$SERVICE_NAME" + local status=$1 + run systemctl stop "$SERVICE_NAME" if [ $status -ne 0 ]; then die "Error stopping systemd unit $SERVICE_NAME, output: $output" fi + if [[ -z "$status" ]]; then + run systemctl is-active "$SERVICE_NAME" + if [ $status -ne 0 ]; then + die "Error checking stauts of systemd unit $SERVICE_NAME, output: $output" + fi + is "$output" "$status" "$SERVICE_NAME not in expected state" + fi + rm -f "$UNIT_FILE" - $SYSTEMCTL daemon-reload + systemctl daemon-reload } # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman generate - systemd - basic" { - xdg_rootless - cname=$(random_string) # See #7407 for --pull=always. - run_podman create --pull=always --name $cname --label "io.containers.autoupdate=registry" $IMAGE top + run_podman create --pull=always --name $cname --label "io.containers.autoupdate=registry" $IMAGE \ + sh -c "trap 'echo Received SIGTERM, finishing; exit' SIGTERM; echo WAITING; while :; do sleep 0.1; done" # Start systemd service to run this container service_setup @@ -87,7 +78,7 @@ # Give container time to start; make sure output looks top-like sleep 2 run_podman logs $cname - is "$output" ".*Load average:.*" "running container 'top'-like output" + is "$output" ".*WAITING.*" "running is waiting for signal" # Exercise `podman auto-update`. # TODO: this will at least run auto-update code but won't perform an update @@ -96,12 +87,11 @@ run_podman auto-update # All good. Stop service, clean up. - service_cleanup + # Also make sure the service is in the `inactive` state (see #11304). + service_cleanup inactive } @test "podman autoupdate local" { - xdg_rootless - cname=$(random_string) run_podman create --name $cname --label "io.containers.autoupdate=local" $IMAGE top @@ -119,7 +109,7 @@ # Run auto-update and check that it restarted the container run_podman commit --change "CMD=/bin/bash" $cname $IMAGE run_podman auto-update - is $output $SERVICE_NAME "autoupdate local restarted container" + is "$output" ".*$SERVICE_NAME.*" "autoupdate local restarted container" # All good. Stop service, clean up. service_cleanup @@ -128,8 +118,6 @@ # These tests can fail in dev. environment because of SELinux. # quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "podman generate systemd - envar" { - xdg_rootless - cname=$(random_string) FOO=value BAR=%s run_podman create --name $cname --env FOO -e BAR --env MYVAR=myval \ $IMAGE sh -c 'printenv && sleep 100' @@ -148,4 +136,69 @@ service_cleanup } +# Regression test for #11438 +@test "podman generate systemd - restart policy" { + cname=$(random_string) + run_podman create --restart=always --name $cname $IMAGE + run_podman generate systemd --new $cname + is "$output" ".*Restart=always.*" "Use container's restart policy if set" + run_podman generate systemd --new --restart-policy=on-failure $cname + is "$output" ".*Restart=on-failure.*" "Override container's restart policy" + + cname2=$(random_string) + run_podman create --restart=unless-stopped --name $cname2 $IMAGE + run_podman generate systemd --new $cname2 + is "$output" ".*Restart=always.*" "unless-stopped translated to always" + + cname3=$(random_string) + run_podman create --restart=on-failure:42 --name $cname3 $IMAGE + run_podman generate systemd --new $cname3 + is "$output" ".*Restart=on-failure.*" "on-failure:xx is parsed correclty" + is "$output" ".*StartLimitBurst=42.*" "on-failure:xx is parsed correctly" + + run_podman rm -f $cname $cname2 $cname3 +} + +function set_listen_env() { + export LISTEN_PID="100" LISTEN_FDS="1" LISTEN_FDNAMES="listen_fdnames" +} + +function unset_listen_env() { + unset LISTEN_PID LISTEN_FDS LISTEN_FDNAMES +} + +function check_listen_env() { + local stdenv="$1" + local context="$2" + if is_remote; then + is "$output" "$stdenv" "LISTEN Environment did not pass: $context" + else + is "$output" "$stdenv +LISTEN_PID=1 +LISTEN_FDS=1 +LISTEN_FDNAMES=listen_fdnames" "LISTEN Environment passed: $context" + fi +} + +@test "podman pass LISTEN environment " { + # Note that `--hostname=host1` makes sure that all containers have the same + # environment. + run_podman run --hostname=host1 --rm $IMAGE printenv + stdenv=$output + + # podman run + set_listen_env + run_podman run --hostname=host1 --rm $IMAGE printenv + unset_listen_env + check_listen_env "$stdenv" "podman run" + + # podman start + run_podman create --hostname=host1 --rm $IMAGE printenv + cid="$output" + set_listen_env + run_podman start --attach $cid + unset_listen_env + check_listen_env "$stdenv" "podman start" +} + # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/255-auto-update.bats libpod-3.4.4+ds1/test/system/255-auto-update.bats --- libpod-3.2.1+ds1/test/system/255-auto-update.bats 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/255-auto-update.bats 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,399 @@ +#!/usr/bin/env bats -*- bats -*- +# +# Tests for automatically update images for containerized services +# + +load helpers +load helpers.systemd + +SNAME_FILE=$BATS_TMPDIR/services + +function setup() { + skip_if_remote "systemd tests are meaningless over remote" + basic_setup +} + +function teardown() { + while read line; do + if [[ "$line" =~ "podman-auto-update" ]]; then + echo "Stop timer: $line.timer" + systemctl stop $line.timer + systemctl disable $line.timer + else + systemctl stop $line + fi + rm -f $UNIT_DIR/$line.{service,timer} + done < $SNAME_FILE + + rm -f $SNAME_FILE + run_podman ? rmi -f \ + quay.io/libpod/alpine:latest \ + quay.io/libpod/busybox:latest \ + quay.io/libpod/localtest:latest \ + quay.io/libpod/autoupdatebroken:latest \ + quay.io/libpod/test:latest \ + quay.io/libpod/fedora:31 + + # The rollback tests may leave some dangling images behind, so let's prune + # them to leave a clean state. + run_podman ? image prune -f + basic_teardown +} + +# This functions is used for handle the basic step in auto-update related +# tests. Including following steps: +# 1. Generate a random container name and echo it to output. +# 2. Tag the fake image before test +# 3. Start a container with io.containers.autoupdate +# 4. Generate the service file from the container +# 5. Remove the origin container +# 6. Start the container from service +function generate_service() { + local target_img_basename=$1 + local autoupdate=$2 + local command=$3 + local extraArgs=$4 + local noTag=$5 + + # Unless specified, set a default command. + if [[ -z "$command" ]]; then + command="top -d 120" + fi + + # Container name. Include the autoupdate type, to make debugging easier. + # IMPORTANT: variable 'cname' is passed (out of scope) up to caller! + cname=c_${autoupdate//\'/}_$(random_string) + target_img="quay.io/libpod/$target_img_basename:latest" + + if [[ -z "$noTag" ]]; then + run_podman tag $IMAGE $target_img + fi + + if [[ -n "$autoupdate" ]]; then + label="--label io.containers.autoupdate=$autoupdate" + else + label="" + fi + run_podman create $extraArgs --name $cname $label $target_img $command + + (cd $UNIT_DIR; run_podman generate systemd --new --files --name $cname) + echo "container-$cname" >> $SNAME_FILE + run_podman rm -f $cname + + systemctl daemon-reload + systemctl start container-$cname + systemctl status container-$cname + + # Original image ID. + # IMPORTANT: variable 'ori_image' is passed (out of scope) up to caller! + run_podman inspect --format "{{.Image}}" $cname + ori_image=$output +} + +function _wait_service_ready() { + local sname=$1 + + local timeout=6 + while [[ $timeout -gt 1 ]]; do + if systemctl -q is-active $sname; then + return + fi + sleep 1 + let timeout=$timeout-1 + done + + # Print service status as debug information before failed the case + systemctl status $sname + die "Timed out waiting for $sname to start" +} + +# Wait for container to update, as confirmed by its image ID changing +function _confirm_update() { + local cname=$1 + local old_iid=$2 + + # Image has already been pulled, so this shouldn't take too long + local timeout=5 + while [[ $timeout -gt 0 ]]; do + run_podman '?' inspect --format "{{.Image}}" $cname + if [[ $status != 0 ]]; then + if [[ $output =~ (no such object|does not exist in database): ]]; then + # this is ok, it just means the container is being restarted + : + else + die "podman inspect $cname failed unexpectedly" + fi + elif [[ $output != $old_iid ]]; then + return + fi + sleep 1 + done + + die "Timed out waiting for $cname to update; old IID=$old_iid" +} + +# This test can fail in dev. environment because of SELinux. +# quick fix: chcon -t container_runtime_exec_t ./bin/podman +@test "podman auto-update - label io.containers.autoupdate=image" { + generate_service alpine image + + _wait_service_ready container-$cname.service + run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,quay.io/libpod/alpine:latest,pending,registry.*" "Image update is pending." + + run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" "Trying to pull.*" "Image is updated." + is "$output" ".*container-$cname.service,quay.io/libpod/alpine:latest,true,registry.*" "Image is updated." + + _confirm_update $cname $ori_image +} + +@test "podman auto-update - label io.containers.autoupdate=image with rollback" { + # FIXME: this test should exercise the authfile label to have a regression + # test for #11171. + + # Note: the autoupdatebroken image is empty on purpose so it cannot be + # executed and force a rollback. The rollback test for the local policy + # is exercising the case where the container doesn't send a ready message. + image=quay.io/libpod/autoupdatebroken + + run_podman tag $IMAGE $image + generate_service autoupdatebroken image + + _wait_service_ready container-$cname.service + run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,$image:latest,pending,registry.*" "Image update is pending." + + run_podman container inspect --format "{{.Image}}" $cname + oldID="$output" + + run_podman inspect --format "{{.ID}}" $cname + containerID="$output" + + run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" "Trying to pull.*" "Image is updated." + is "$output" ".*container-$cname.service,$image:latest,rolled back,registry.*" "Image has been rolled back." + + run_podman container inspect --format "{{.Image}}" $cname + is "$output" "$oldID" "container rolled back to previous image" + + run_podman container inspect --format "{{.ID}}" $cname + if [[ $output == $containerID ]]; then + die "container has not been restarted during rollback (previous id: $containerID, current id: $output)" + fi +} + +@test "podman auto-update - label io.containers.autoupdate=disabled" { + generate_service alpine disabled + + _wait_service_ready container-$cname.service + run_podman auto-update + is "$output" "" "Image is not updated when autoupdate=disabled." + + run_podman inspect --format "{{.Image}}" $cname + is "$output" "$ori_image" "Image ID should not change" +} + +@test "podman auto-update - label io.containers.autoupdate=fakevalue" { + fakevalue=fake_$(random_string) + generate_service alpine $fakevalue + + _wait_service_ready container-$cname.service + run_podman 125 auto-update + is "$output" ".*invalid auto-update policy.*" "invalid policy setup" + + run_podman inspect --format "{{.Image}}" $cname + is "$output" "$ori_image" "Image ID should not change" +} + +@test "podman auto-update - label io.containers.autoupdate=local" { + generate_service localtest local + image=quay.io/libpod/localtest:latest + podman commit --change CMD=/bin/bash $cname $image + podman image inspect --format "{{.ID}}" $image + imageID="$output" + + _wait_service_ready container-$cname.service + run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,quay.io/libpod/localtest:latest,pending,local.*" "Image update is pending." + + run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,quay.io/libpod/localtest:latest,true,local.*" "Image is updated." + + _confirm_update $cname $ori_image +} + +@test "podman auto-update - label io.containers.autoupdate=local with rollback" { + # sdnotify fails with runc 1.0.0-3-dev2 on Ubuntu. Let's just + # assume that we work only with crun, nothing else. + # [copied from 260-sdnotify.bats] + runtime=$(podman_runtime) + if [[ "$runtime" != "crun" ]]; then + skip "this test only works with crun, not $runtime" + fi + + dockerfile1=$PODMAN_TMPDIR/Dockerfile.1 + cat >$dockerfile1 <> /runme +RUN chmod +x /runme +EOF + + dockerfile2=$PODMAN_TMPDIR/Dockerfile.2 + cat >$dockerfile2 <> /runme +RUN chmod +x /runme +EOF + image=test + + # Generate a healthy image that will run correctly. + run_podman build -t quay.io/libpod/$image -f $dockerfile1 + podman image inspect --format "{{.ID}}" $image + oldID="$output" + + generate_service $image local /runme --sdnotify=container noTag + _wait_service_ready container-$cname.service + + run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,quay.io/libpod/$image:latest,false,local.*" "No update available" + + # Generate an unhealthy image that will fail. + run_podman build -t quay.io/libpod/$image -f $dockerfile2 + podman image inspect --format "{{.ID}}" $image + newID="$output" + + run_podman auto-update --dry-run --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,quay.io/libpod/$image:latest,pending,local.*" "Image updated is pending" + + # Note: we rollback automatically by default. + run_podman auto-update --format "{{.Unit}},{{.Image}},{{.Updated}},{{.Policy}}" + is "$output" ".*container-$cname.service,quay.io/libpod/$image:latest,rolled back,local.*" "Rolled back to old image" + + # Make sure that new container is not using the new image ID anymore. + _confirm_update $cname $newID +} + +@test "podman auto-update with multiple services" { + # Preserve original image ID, to confirm that it changes (or not) + run_podman inspect --format "{{.Id}}" $IMAGE + local img_id="$output" + + local cnames=() + local -A expect_update + local -A will_update=([image]=1 [registry]=1 [local]=1) + + local fakevalue=fake_$(random_string) + for auto_update in image registry "" disabled "''" $fakevalue local + do + local img_base="alpine" + if [[ $auto_update == "registry" ]]; then + img_base="busybox" + elif [[ $auto_update == "local" ]]; then + img_base="localtest" + fi + generate_service $img_base $auto_update + cnames+=($cname) + if [[ $auto_update == "local" ]]; then + local_cname=$cname + fi + + if [[ -n "$auto_update" && -n "${will_update[$auto_update]}" ]]; then + expect_update[$cname]=1 + fi + done + + # Only check that the last service is started. Previous services should already be activated. + _wait_service_ready container-$cname.service + run_podman commit --change CMD=/bin/bash $local_cname quay.io/libpod/localtest:latest + # Exit code is expected, due to invalid 'fakevalue' + run_podman 125 auto-update + update_log=$output + is "$update_log" ".*invalid auto-update policy.*" "invalid policy setup" + is "$update_log" ".*Error: invalid auto-update policy.*" "invalid policy setup" + + local n_updated=$(grep -c 'Trying to pull' <<<"$update_log") + is "$n_updated" "2" "Number of images updated from registry." + + for cname in "${!expect_update[@]}"; do + is "$update_log" ".*$cname.*" "container with auto-update policy image updated" + # Just because podman says it fetched, doesn't mean it actually updated + _confirm_update $cname $img_id + done + + # Final confirmation that all image IDs have/haven't changed + for cname in "${cnames[@]}"; do + run_podman inspect --format "{{.Image}}" $cname + if [[ -n "${expect_update[$cname]}" ]]; then + if [[ "$output" == "$img_id" ]]; then + die "$cname: image ID ($output) did not change" + fi + else + is "$output" "$img_id" "Image should not be changed." + fi + done +} + +@test "podman auto-update using systemd" { + skip_if_journald_unavailable + + generate_service alpine image + + cat >$UNIT_DIR/podman-auto-update-$cname.timer <$UNIT_DIR/podman-auto-update-$cname.service <> $SNAME_FILE + systemctl enable --now podman-auto-update-$cname.timer + systemctl list-timers --all + + # While systemd v245 and later uses 'Finished', older versions uses 'Started' for oneshot services + local expect='(Finished|Started) Podman auto-update testing service' + local failed_start=failed + local count=0 + while [ $count -lt 120 ]; do + run journalctl -n 15 -u podman-auto-update-$cname.service + if [[ "$output" =~ $expect ]]; then + failed_start= + break + fi + ((count+=1)) + sleep 1 + done + + if [[ -n "$failed_start" ]]; then + echo "journalctl output:" + sed -e 's/^/ /' <<<"$output" + die "Did not find expected string '$expect' in journalctl output for $cname" + fi + + _confirm_update $cname $ori_image +} + +# vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/260-sdnotify.bats libpod-3.4.4+ds1/test/system/260-sdnotify.bats --- libpod-3.2.1+ds1/test/system/260-sdnotify.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/260-sdnotify.bats 2021-12-26 00:45:51.000000000 +0000 @@ -70,7 +70,7 @@ # Check that MAINPID=xxxxx points to a running conmon process function _assert_mainpid_is_conmon() { - local mainpid=$(expr "$1" : "MAINPID=\([0-9]\+\)") + local mainpid=$(expr "$1" : ".*MAINPID=\([0-9]\+\)") test -n "$mainpid" || die "Could not parse '$1' as 'MAINPID=nnnn'" test -d /proc/$mainpid || die "sdnotify MAINPID=$mainpid - but /proc/$mainpid does not exist" @@ -121,7 +121,7 @@ # we look for READY=1 _anywhere_ in the output, not just the last line. is "$output" ".*READY=1.*" "sdnotify sent READY=1" - _assert_mainpid_is_conmon "${lines[0]}" + _assert_mainpid_is_conmon "$output" # Done. Stop container, clean up. run_podman exec $cid touch /stop @@ -130,6 +130,8 @@ _stop_socat } +# These tests can fail in dev. environment because of SELinux. +# quick fix: chcon -t container_runtime_exec_t ./bin/podman @test "sdnotify : container" { # Sigh... we need to pull a humongous image because it has systemd-notify. # (IMPORTANT: fedora:32 and above silently removed systemd-notify; this @@ -150,14 +152,18 @@ wait_for_ready $cid run_podman logs $cid - is "${lines[0]}" "/.*/container\.sock/notify" "NOTIFY_SOCKET is passed to container" + is "${lines[0]}" "/run/notify/notify.sock" "NOTIFY_SOCKET is passed to container" # With container, READY=1 isn't necessarily the last message received; # just look for it anywhere in received messages run cat $_SOCAT_LOG + # The 'echo's help us debug failed runs + echo "socat log:" + echo "$output" + is "$output" ".*READY=1" "received READY=1 through notify socket" - _assert_mainpid_is_conmon "${lines[0]}" + _assert_mainpid_is_conmon "$output" # Done. Stop container, clean up. run_podman exec $cid touch /stop diff -Nru libpod-3.2.1+ds1/test/system/270-socket-activation.bats libpod-3.4.4+ds1/test/system/270-socket-activation.bats --- libpod-3.2.1+ds1/test/system/270-socket-activation.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/270-socket-activation.bats 2021-12-26 00:45:51.000000000 +0000 @@ -4,27 +4,20 @@ # load helpers +load helpers.systemd SERVICE_NAME="podman_test_$(random_string)" -SYSTEMCTL="systemctl" -UNIT_DIR="/usr/lib/systemd/system" -SERVICE_SOCK_ADDR="/run/podman/podman.sock" - +SERVICE_SOCK_ADDR="/run/podman/$SERVICE_NAME.sock" if is_rootless; then - UNIT_DIR="$HOME/.config/systemd/user" - mkdir -p $UNIT_DIR - - SYSTEMCTL="$SYSTEMCTL --user" - if [ -z "$XDG_RUNTIME_DIR" ]; then - export XDG_RUNTIME_DIR=/run/user/$(id -u) - fi - SERVICE_SOCK_ADDR="$XDG_RUNTIME_DIR/podman/podman.sock" + SERVICE_SOCK_ADDR="$XDG_RUNTIME_DIR/podman/$SERVICE_NAME.sock" fi SERVICE_FILE="$UNIT_DIR/$SERVICE_NAME.service" SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket" +# URL to use for ping +_PING=http://placeholder-hostname/libpod/_ping function setup() { skip_if_remote "systemd tests are meaningless over remote" @@ -34,8 +27,8 @@ cat > $SERVICE_FILE < /dev/null - rm -f $pause_pid + local pause_pid_file="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" + if [ -f $pause_pid_file ]; then + kill -9 $(< $pause_pid_file) 2> /dev/null + rm -f $pause_pid_file fi fi - $SYSTEMCTL start "$SERVICE_NAME.socket" + systemctl start "$SERVICE_NAME.socket" } function teardown() { - $SYSTEMCTL stop "$SERVICE_NAME.socket" + systemctl stop "$SERVICE_NAME.socket" rm -f "$SERVICE_FILE" "$SOCKET_FILE" - $SYSTEMCTL daemon-reload + systemctl daemon-reload basic_teardown } @test "podman system service - socket activation - no container" { - run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping - is "$output" "OK" "podman service responses normally" + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR $_PING + echo "curl output: $output" + is "$status" "0" "curl exit status" + is "$output" "OK" "podman service responds normally" } -@test "podman system service - socket activation - exist container " { - run_podman run $IMAGE sleep 90 - run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping - is "$output" "OK" "podman service responses normally" +@test "podman system service - socket activation - existing container" { + run_podman run -d $IMAGE sleep 90 + cid="$output" + + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR $_PING + echo "curl output: $output" + is "$status" "0" "curl exit status" + is "$output" "OK" "podman service responds normally" + + run_podman rm -f $cid } -@test "podman system service - socket activation - kill rootless pause " { +@test "podman system service - socket activation - kill rootless pause" { if ! is_rootless; then - skip "root podman no need pause process" + skip "there is no pause process when running rootful" fi - run_podman run $IMAGE sleep 90 - local pause_pid="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" - if [ -f $pause_pid ]; then - kill -9 $(cat $pause_pid) 2> /dev/null + run_podman run -d $IMAGE sleep 90 + cid="$output" + + local pause_pid_file="$XDG_RUNTIME_DIR/libpod/tmp/pause.pid" + if [ ! -f $pause_pid_file ]; then + # This seems unlikely, but not impossible + die "Pause pid file does not exist: $pause_pid_file" fi - run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR http://podman/libpod/_ping - is "$output" "OK" "podman service responses normally" + + echo "kill -9 $(< pause_pid_file)" + kill -9 $(< $pause_pid_file) + + run curl -s --max-time 3 --unix-socket $SERVICE_SOCK_ADDR $_PING + echo "curl output: $output" + is "$status" "0" "curl exit status" + is "$output" "OK" "podman service responds normally" + + run_podman rm -f $cid } # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/271-tcp-cors-server.bats libpod-3.4.4+ds1/test/system/271-tcp-cors-server.bats --- libpod-3.2.1+ds1/test/system/271-tcp-cors-server.bats 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/271-tcp-cors-server.bats 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,44 @@ +#!/usr/bin/env bats -*- bats -*- +# +# Tests podman system service CORS enabled +# + +load helpers + +SERVICE_NAME="podman_test_$(random_string)" + +SERVICE_TCP_HOST="localhost" + +SERVICE_FILE="$UNIT_DIR/$SERVICE_NAME.service" +SOCKET_FILE="$UNIT_DIR/$SERVICE_NAME.socket" + +@test "podman system service - tcp CORS" { + skip_if_remote "system service tests are meaningless over remote" + PORT=$(random_free_port 63000-64999) + run_podman system service --cors="*" tcp:$SERVICE_TCP_HOST:$PORT -t 20 & + podman_pid="$!" + sleep 5s + run curl -s --max-time 10 -vvv $SERVICE_TCP_HOST:$PORT/_ping 2>&1 + is "$output" ".*< Access-Control-Allow-Origin: \*.*" "access-control-allow-origin verifies CORS is set" + kill $podman_pid + wait $podman_pid || true +} + +@test "podman system service - tcp without CORS" { + skip_if_remote "system service tests are meaningless over remote" + PORT=$(random_free_port 63000-64999) + run_podman system service tcp:$SERVICE_TCP_HOST:$PORT -t 20 & + podman_pid="$!" + sleep 5s + (curl -s --max-time 10 -vvv $SERVICE_TCP_HOST:$PORT/_ping 2>&1 | grep -Eq "Access-Control-Allow-Origin:") && false || true + kill $podman_pid + wait $podman_pid || true +} + +@test "podman system service - CORS enabled in logs" { + skip_if_remote "system service tests are meaningless over remote" + run_podman system service --log-level="debug" --cors="*" -t 1 + is "$output" ".*CORS Headers were set to \*.*" "debug log confirms CORS headers set" +} + +# vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/330-corrupt-images.bats libpod-3.4.4+ds1/test/system/330-corrupt-images.bats --- libpod-3.2.1+ds1/test/system/330-corrupt-images.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/330-corrupt-images.bats 2021-12-26 00:45:51.000000000 +0000 @@ -13,13 +13,14 @@ export PODMAN_CORRUPT_TEST_WORKDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-${TMPDIR:-/tmp}} podman_corrupt_test.XXXXXX) fi -PODMAN_CORRUPT_TEST_IMAGE_FQIN=quay.io/libpod/alpine@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00 +PODMAN_CORRUPT_TEST_IMAGE_CANONICAL_FQIN=quay.io/libpod/alpine@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00 +PODMAN_CORRUPT_TEST_IMAGE_TAGGED_FQIN=${PODMAN_CORRUPT_TEST_IMAGE_CANONICAL_FQIN%%@sha256:*}:test PODMAN_CORRUPT_TEST_IMAGE_ID=961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4 # All tests in this file (and ONLY in this file) run with a custom rootdir function setup() { skip_if_remote "none of these tests run under podman-remote" - _PODMAN_TEST_OPTS="--root ${PODMAN_CORRUPT_TEST_WORKDIR}/root" + _PODMAN_TEST_OPTS="--storage-driver=vfs --root ${PODMAN_CORRUPT_TEST_WORKDIR}/root" } function teardown() { @@ -59,7 +60,7 @@ run_podman load -i ${PODMAN_CORRUPT_TEST_WORKDIR}/img.tar # "podman load" restores it without a tag, which (a) causes rmi-by-name # to fail, and (b) causes "podman images" to exit 0 instead of 125 - run_podman tag ${PODMAN_CORRUPT_TEST_IMAGE_ID} ${PODMAN_CORRUPT_TEST_IMAGE_FQIN} + run_podman tag ${PODMAN_CORRUPT_TEST_IMAGE_ID} ${PODMAN_CORRUPT_TEST_IMAGE_TAGGED_FQIN} # shortcut variable name local id=${PODMAN_CORRUPT_TEST_IMAGE_ID} @@ -91,9 +92,9 @@ @test "podman corrupt images - initialize" { # Pull once, save cached copy. - run_podman pull $PODMAN_CORRUPT_TEST_IMAGE_FQIN + run_podman pull $PODMAN_CORRUPT_TEST_IMAGE_CANONICAL_FQIN run_podman save -o ${PODMAN_CORRUPT_TEST_WORKDIR}/img.tar \ - $PODMAN_CORRUPT_TEST_IMAGE_FQIN + $PODMAN_CORRUPT_TEST_IMAGE_CANONICAL_FQIN } # END first "test" does a one-time pull of our desired image @@ -104,8 +105,8 @@ _corrupt_image_test "rmi -f ${PODMAN_CORRUPT_TEST_IMAGE_ID}" } -@test "podman corrupt images - rmi -f " { - _corrupt_image_test "rmi -f ${PODMAN_CORRUPT_TEST_IMAGE_FQIN}" +@test "podman corrupt images - rmi -f " { + _corrupt_image_test "rmi -f ${PODMAN_CORRUPT_TEST_IMAGE_TAGGED_FQIN}" } @test "podman corrupt images - rmi -f -a" { diff -Nru libpod-3.2.1+ds1/test/system/410-selinux.bats libpod-3.4.4+ds1/test/system/410-selinux.bats --- libpod-3.2.1+ds1/test/system/410-selinux.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/410-selinux.bats 2021-12-26 00:45:51.000000000 +0000 @@ -50,6 +50,18 @@ check_label "--systemd=always" "container_init_t" } +@test "podman selinux: init container with --security-opt type" { + check_label "--systemd=always --security-opt=label=type:spc_t" "spc_t" +} + +@test "podman selinux: init container with --security-opt level&type" { + check_label "--systemd=always --security-opt=label=level:s0:c1,c2 --security-opt=label=type:spc_t" "spc_t" "s0:c1,c2" +} + +@test "podman selinux: init container with --security-opt level" { + check_label "--systemd=always --security-opt=label=level:s0:c1,c2" "container_init_t" "s0:c1,c2" +} + @test "podman selinux: pid=host" { # FIXME this test fails when run rootless with runc: # Error: container_linux.go:367: starting container process caused: process_linux.go:495: container init caused: readonly path /proc/asound: operation not permitted: OCI permission denied diff -Nru libpod-3.2.1+ds1/test/system/450-interactive.bats libpod-3.4.4+ds1/test/system/450-interactive.bats --- libpod-3.2.1+ds1/test/system/450-interactive.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/450-interactive.bats 2021-12-26 00:45:51.000000000 +0000 @@ -49,6 +49,8 @@ # BEGIN tests @test "podman detects correct tty size" { + skip "FIXME: #10710. As of 2021-12-08, stty fails >75% of the time." + # Set the pty to a random size. Make rows/columns odd/even, to guarantee # that they can never be the same rows=$(( 15 + RANDOM % 60 | 1 )) @@ -57,7 +59,18 @@ # ...and make sure stty under podman reads that. run_podman run -it --name mystty $IMAGE stty size <$PODMAN_TEST_PTY - is "$output" "$rows $cols" "stty under podman reads the correct dimensions" + is "$output" "$rows $cols" "stty under podman run reads the correct dimensions" + + run_podman rm -f mystty + + # FIXME: the checks below are flaking a lot (see #10710). + + # check that the same works for podman exec +# run_podman run -d --name mystty $IMAGE top +# run_podman exec -it mystty stty size <$PODMAN_TEST_PTY +# is "$output" "$rows $cols" "stty under podman exec reads the correct dimensions" +# +# run_podman rm -f mystty } diff -Nru libpod-3.2.1+ds1/test/system/500-networking.bats libpod-3.4.4+ds1/test/system/500-networking.bats --- libpod-3.2.1+ds1/test/system/500-networking.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/500-networking.bats 2021-12-26 00:45:51.000000000 +0000 @@ -20,11 +20,10 @@ # Copied from tsweeney's https://github.com/containers/podman/issues/4827 @test "podman networking: port on localhost" { - skip_if_remote "FIXME: reevaluate this one after #7360 is fixed" random_1=$(random_string 30) random_2=$(random_string 30) - HOST_PORT=8080 + HOST_PORT=$(random_free_port) SERVER=http://127.0.0.1:$HOST_PORT # Create a test file with random content @@ -33,7 +32,6 @@ # Bind-mount this file with a different name to a container running httpd run_podman run -d --name myweb -p "$HOST_PORT:80" \ - --restart always \ -v $INDEX1:/var/www/index.txt:Z \ -w /var/www \ $IMAGE /bin/busybox-extras httpd -f -p 80 @@ -68,46 +66,6 @@ run_podman 125 port myweb 99/tcp is "$output" 'Error: failed to find published port "99/tcp"' - # Tests #10310: podman will restart slirp4netns on container restart - run_podman container inspect --format "{{.State.Pid}}" $cid - pid=$output - - # Kill the process; podman restart policy will bring up a new container. - # -9 is crucial: busybox httpd ignores all other signals. - kill -9 $pid - # Wait for process to exit - retries=30 - while kill -0 $pid; do - sleep 0.5 - retries=$((retries - 1)) - if [[ $retries -eq 0 ]]; then - die "Process $pid (container $cid) refused to die" - fi - done - - # Wait for container to restart - retries=20 - while :;do - run_podman container inspect --format "{{.State.Pid}}" myweb - # pid is 0 as long as the container is not running - if [[ $output -ne 0 ]]; then - if [[ $output == $pid ]]; then - die "This should never happen! Restarted container has same PID ($output) as killed one!" - fi - break - fi - sleep 0.5 - retries=$((retries - 1)) - if [[ $retries -eq 0 ]]; then - die "Timed out waiting for container to restart" - fi - done - - # Verify http contents again: curl from localhost - # Use retry since it can take a moment until the new container is ready - run curl --retry 2 -s $SERVER/index.txt - is "$output" "$random_1" "curl 127.0.0.1:/index.txt after restart" - # Clean up run_podman stop -t 1 myweb run_podman rm myweb @@ -115,11 +73,8 @@ # Issue #5466 - port-forwarding doesn't work with this option and -d @test "podman networking: port with --userns=keep-id" { - # FIXME: randomize port, and create second random host port - myport=54321 - for cidr in "" "$(random_rfc1918_subnet).0/24"; do - myport=$(( myport + 1 )) + myport=$(random_free_port 52000-52999) if [[ -z $cidr ]]; then # regex to match that we are in 10.X subnet match="10\..*" @@ -140,6 +95,8 @@ $IMAGE nc -l -n -v -p $myport cid="$output" + wait_for_output "listening on .*:$myport .*" $cid + # emit random string, and check it teststring=$(random_string 30) echo "$teststring" | nc 127.0.0.1 $myport @@ -162,29 +119,32 @@ done } -@test "podman run with slirp4ns assigns correct gateway address to host.containers.internal" { +@test "podman run with slirp4ns assigns correct addresses to /etc/hosts" { CIDR="$(random_rfc1918_subnet)" - run_podman run --network slirp4netns:cidr="${CIDR}.0/24" \ - $IMAGE grep 'host.containers.internal' /etc/hosts - is "$output" "${CIDR}.2 host.containers.internal" "host.containers.internal should be the cidr+2 address" + local conname=con-$(random_string 10) + run_podman run --rm --network slirp4netns:cidr="${CIDR}.0/24" \ + --name $conname --hostname $conname $IMAGE cat /etc/hosts + is "$output" ".*${CIDR}.2 host.containers.internal" "host.containers.internal should be the cidr+2 address" + is "$output" ".*${CIDR}.100 $conname $conname" "$conname should be the cidr+100 address" } @test "podman run with slirp4ns adds correct dns address to resolv.conf" { CIDR="$(random_rfc1918_subnet)" - run_podman run --network slirp4netns:cidr="${CIDR}.0/24" \ + run_podman run --rm --network slirp4netns:cidr="${CIDR}.0/24" \ $IMAGE grep "${CIDR}" /etc/resolv.conf is "$output" "nameserver ${CIDR}.3" "resolv.conf should have slirp4netns cidr+3 as a nameserver" } @test "podman run with slirp4ns assigns correct ip address container" { CIDR="$(random_rfc1918_subnet)" - run_podman run --network slirp4netns:cidr="${CIDR}.0/24" \ + run_podman run --rm --network slirp4netns:cidr="${CIDR}.0/24" \ $IMAGE sh -c "ip address | grep ${CIDR}" is "$output" ".*inet ${CIDR}.100/24 \+" "container should have slirp4netns cidr+100 assigned to interface" } # "network create" now works rootless, with the help of a special container @test "podman network create" { + # Deliberately use a fixed port, not random_open_port, because of #10806 myport=54322 local mynetname=testnet-$(random_string 10) @@ -207,6 +167,16 @@ $IMAGE nc -l -n -v -p $myport cid="$output" + # FIXME: debugging for #11871 + run_podman exec $cid cat /etc/resolv.conf + if is_rootless && ! is_remote; then + run_podman unshare --rootless-cni cat /etc/resolv.conf + fi + ps uxww + + # check that dns is working inside the container + run_podman exec $cid nslookup google.com + # emit random string, and check it teststring=$(random_string 30) echo "$teststring" | nc 127.0.0.1 $myport @@ -238,7 +208,7 @@ skip_if_remote "podman network reload does not have remote support" random_1=$(random_string 30) - HOST_PORT=12345 + HOST_PORT=$(random_free_port) SERVER=http://127.0.0.1:$HOST_PORT # Create a test file with random content @@ -387,4 +357,176 @@ run_podman network rm -f $netname } +# Test for https://github.com/containers/podman/issues/10052 +@test "podman network connect/disconnect with port forwarding" { + random_1=$(random_string 30) + HOST_PORT=$(random_free_port) + SERVER=http://127.0.0.1:$HOST_PORT + + # Create a test file with random content + INDEX1=$PODMAN_TMPDIR/hello.txt + echo $random_1 > $INDEX1 + + local netname=testnet-$(random_string 10) + run_podman network create $netname + is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + + local netname2=testnet2-$(random_string 10) + run_podman network create $netname2 + is "$output" ".*/cni/net.d/$netname2.conflist" "output of 'network create'" + + # First, run a container in background to ensure that the rootless cni ns + # is not destroyed after network disconnect. + run_podman run -d --network $netname $IMAGE top + background_cid=$output + + # Run a httpd container on first network with exposed port + run_podman run -d -p "$HOST_PORT:80" \ + --network $netname \ + -v $INDEX1:/var/www/index.txt:Z \ + -w /var/www \ + $IMAGE /bin/busybox-extras httpd -f -p 80 + cid=$output + + # Verify http contents: curl from localhost + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt" + + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" + ip="$output" + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" + mac="$output" + + run_podman network disconnect $netname $cid + is "$output" "" "Output should be empty (no errors)" + + # check that we cannot curl (timeout after 3 sec) + run curl --max-time 3 -s $SERVER/index.txt + if [ "$status" -eq 0 ]; then + die "curl did not fail, it should have timed out or failed with non zero exit code" + fi + + run_podman network connect $netname $cid + is "$output" "" "Output should be empty (no errors)" + + # curl should work again + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt should work again" + + # check that we have a new ip and mac + # if the ip is still the same this whole test turns into a nop + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").IPAddress}}" + if [[ "$output" == "$ip" ]]; then + die "IP address did not change after podman network disconnect/connect" + fi + run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname\").MacAddress}}" + if [[ "$output" == "$mac" ]]; then + die "MAC address did not change after podman network disconnect/connect" + fi + + # Disconnect/reconnect of a container *with no ports* should succeed quietly + run_podman network disconnect $netname $background_cid + is "$output" "" "disconnect of container with no open ports" + run_podman network connect $netname $background_cid + is "$output" "" "(re)connect of container with no open ports" + + # connect a second network + run_podman network connect $netname2 $cid + is "$output" "" "Output should be empty (no errors)" + + # curl should work + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt should work" + + # disconnect the first network + run_podman network disconnect $netname $cid + + # curl should still work + run curl --max-time 3 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt should still work" + + # cleanup + run_podman stop -t 0 $cid $background_cid + run_podman rm -f $cid $background_cid + run_podman network rm -f $netname $netname2 +} + +@test "podman network after restart" { + random_1=$(random_string 30) + + HOST_PORT=$(random_free_port) + SERVER=http://127.0.0.1:$HOST_PORT + + # Create a test file with random content + INDEX1=$PODMAN_TMPDIR/hello.txt + echo $random_1 > $INDEX1 + + local netname=testnet-$(random_string 10) + run_podman network create $netname + is "$output" ".*/cni/net.d/$netname.conflist" "output of 'network create'" + + for network in "slirp4netns" "$netname"; do + # Start container with the restart always policy + run_podman run -d --name myweb -p "$HOST_PORT:80" \ + --restart always \ + --network $network \ + -v $INDEX1:/var/www/index.txt:Z \ + -w /var/www \ + $IMAGE /bin/busybox-extras httpd -f -p 80 + cid=$output + + # Tests #10310: podman will restart slirp4netns on container restart + run_podman container inspect --format "{{.State.Pid}}" $cid + pid=$output + + # Kill the process; podman restart policy will bring up a new container. + # -9 is crucial: busybox httpd ignores all other signals. + kill -9 $pid + # Wait for process to exit + retries=30 + while kill -0 $pid; do + sleep 0.5 + retries=$((retries - 1)) + if [[ $retries -eq 0 ]]; then + die "Process $pid (container $cid) refused to die" + fi + done + + # Wait for container to restart + retries=20 + while :;do + run_podman container inspect --format "{{.State.Pid}}" $cid + # pid is 0 as long as the container is not running + if [[ $output -ne 0 ]]; then + if [[ $output == $pid ]]; then + die "This should never happen! Restarted container has same PID ($output) as killed one!" + fi + break + fi + sleep 0.5 + retries=$((retries - 1)) + if [[ $retries -eq 0 ]]; then + die "Timed out waiting for container to restart" + fi + done + + # Verify http contents again: curl from localhost + # Use retry since it can take a moment until the new container is ready + run curl --retry 2 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt after auto restart" + + run_podman restart $cid + # Verify http contents again: curl from localhost + # Use retry since it can take a moment until the new container is ready + run curl --retry 2 -s $SERVER/index.txt + is "$output" "$random_1" "curl 127.0.0.1:/index.txt after podman restart" + + run_podman stop -t 0 $cid + run_podman rm -f $cid + done + + # Cleanup network + run_podman network rm $netname +} + # vim: filetype=sh diff -Nru libpod-3.2.1+ds1/test/system/600-completion.bats libpod-3.4.4+ds1/test/system/600-completion.bats --- libpod-3.2.1+ds1/test/system/600-completion.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/600-completion.bats 2021-12-26 00:45:51.000000000 +0000 @@ -110,12 +110,10 @@ is "$output" ".*localhost/$random_image_name:$random_image_tag${nl}" \ "$* $cmd: actual image listed in suggestions" - # check that we complete the image with and without tag after at least one char is typed + # check that we complete the image with tag after at least one char is typed run_completion "$@" $cmd "${extra_args[@]}" "${random_image_name:0:1}" is "$output" ".*$random_image_name:$random_image_tag${nl}" \ "$* $cmd: image name:tag included in suggestions" - is "$output" ".*$random_image_name${nl}" \ - "$* $cmd: image name(w/o tag) included in suggestions" # check that we complete the image id after at least two chars are typed run_completion "$@" $cmd "${extra_args[@]}" "${random_image_id:0:2}" diff -Nru libpod-3.2.1+ds1/test/system/700-play.bats libpod-3.4.4+ds1/test/system/700-play.bats --- libpod-3.2.1+ds1/test/system/700-play.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/700-play.bats 2021-12-26 00:45:51.000000000 +0000 @@ -51,18 +51,121 @@ seLinuxOptions: level: "s0:c1,c2" readOnlyRootFilesystem: false + volumeMounts: + - mountPath: /testdir:z + name: home-podman-testdir workingDir: / + volumes: + - hostPath: + path: TESTDIR + type: Directory + name: home-podman-testdir status: {} " +RELABEL="system_u:object_r:container_file_t:s0" + @test "podman play with stdin" { - echo "$testYaml" > $PODMAN_TMPDIR/test.yaml + TESTDIR=$PODMAN_TMPDIR/testdir + mkdir -p $TESTDIR + echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + run_podman play kube - < $PODMAN_TMPDIR/test.yaml + if [ -e /usr/sbin/selinuxenabled -a /usr/sbin/selinuxenabled ]; then + run ls -Zd $TESTDIR + is "$output" ${RELABEL} "selinux relabel should have happened" + fi + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod } @test "podman play" { - echo "$testYaml" > $PODMAN_TMPDIR/test.yaml + TESTDIR=$PODMAN_TMPDIR/testdir + mkdir -p $TESTDIR + echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml run_podman play kube $PODMAN_TMPDIR/test.yaml + if [ -e /usr/sbin/selinuxenabled -a /usr/sbin/selinuxenabled ]; then + run ls -Zd $TESTDIR + is "$output" ${RELABEL} "selinux relabel should have happened" + fi + + run_podman stop -a -t 0 + run_podman pod stop test_pod + run_podman pod rm -f test_pod +} + +@test "podman play --network" { + TESTDIR=$PODMAN_TMPDIR/testdir + mkdir -p $TESTDIR + echo "$testYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + run_podman 125 play kube --network bridge $PODMAN_TMPDIR/test.yaml + is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail with --network host" + run_podman 125 play kube --network host $PODMAN_TMPDIR/test.yaml + is "$output" ".*invalid value passed to --network: bridge or host networking must be configured in YAML" "podman plan-network should fail with --network host" + run_podman play kube --network slirp4netns:port_handler=slirp4netns $PODMAN_TMPDIR/test.yaml + run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" + infraID="$output" + run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID + is "$output" "slirp4netns" "network mode slirp4netns is set for the container" + + run_podman stop -a -t 0 + run_podman pod stop test_pod + run_podman pod rm -f test_pod + + run_podman play kube --network none $PODMAN_TMPDIR/test.yaml + run_podman pod inspect --format {{.InfraContainerID}} "${lines[1]}" + infraID="$output" + run_podman container inspect --format "{{.HostConfig.NetworkMode}}" $infraID + is "$output" "none" "network mode none is set for the container" + + run_podman stop -a -t 0 + run_podman pod stop test_pod + run_podman pod rm -f test_pod +} + +@test "podman play with user from image" { + TESTDIR=$PODMAN_TMPDIR/testdir + mkdir -p $TESTDIR + +testUserYaml=" +apiVersion: v1 +kind: Pod +metadata: + labels: + app: test + name: test_pod +spec: + containers: + - command: + - id + env: + - name: PATH + value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + - name: TERM + value: xterm + - name: container + value: podman + image: userimage + name: test + resources: {} +status: {} +" + +cat > $PODMAN_TMPDIR/Containerfile << _EOF +from $IMAGE +USER bin +_EOF + + echo "$testUserYaml" | sed "s|TESTDIR|${TESTDIR}|g" > $PODMAN_TMPDIR/test.yaml + run_podman build -t userimage $PODMAN_TMPDIR + run_podman play kube --start=false $PODMAN_TMPDIR/test.yaml + run_podman inspect --format "{{ .Config.User }}" test_pod-test + is "$output" bin "expect container within pod to run as the bin user" + + run_podman stop -a -t 0 + run_podman pod stop test_pod run_podman pod rm -f test_pod + run_podman rmi -f userimage:latest } diff -Nru libpod-3.2.1+ds1/test/system/build-testimage libpod-3.4.4+ds1/test/system/build-testimage --- libpod-3.2.1+ds1/test/system/build-testimage 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/build-testimage 2021-12-26 00:45:51.000000000 +0000 @@ -61,8 +61,8 @@ # - check for updates @ https://hub.docker.com/_/alpine # busybox-extras provides httpd needed in 500-networking.bats cat >Containerfile < /dev/null || true +# There should always be a testimage tagged ':0000000' (eight digits, +# zero-padded sequence ID) in the same location; this is used by tests +# which need to pull a non-locally-cached image. This image will rarely +# if ever need to change, nor in fact does it even have to be a copy of +# this testimage since all we use it for is 'true'. +# However, it does need to be multiarch :-( +zerotag_latest=$(skopeo list-tags docker://quay.io/libpod/testimage |\ + jq -r '.Tags[]' |\ + sort --version-sort |\ + grep '^000' |\ + tail -n 1) +zerotag_next=$(printf "%08d" $((zerotag_latest + 1))) + +# We don't always need to push the :00xx image, but build it anyway. +zeroimg=quay.io/libpod/testimage:${zerotag_next} +buildah manifest create $zeroimg + # We need to use buildah because (as of 2021-02-23) only buildah has --manifest # and because Dan says arch emulation is not currently working on podman # (no further details). # Arch emulation on Fedora requires the qemu-user-static package. -for arch in amd64 arm64v8 ppc64le s390x;do +for arch in amd64 arm64 ppc64le s390x;do + # docker.io repo is usually the same name as the desired arch; except + # for arm64, where podman needs to have the arch be 'arm64' but the + # image lives in 'arm64v8'. + repo=$arch + if [[ $repo = "arm64" ]]; then + repo="${repo}v8" + fi + ${BUILDAH} bud \ --arch=$arch \ - --build-arg ARCH=$arch \ + --build-arg REPO=$repo \ --manifest=testimage \ --squash \ . + + # The zero-tag image + ${BUILDAH} pull --arch $arch docker.io/$repo/busybox:1.33.1 + ${BUILDAH} manifest add $zeroimg docker.io/$repo/busybox:1.33.1 done # Clean up @@ -94,23 +123,13 @@ # Tag image and push (all arches) to quay. remote_tag=quay.io/libpod/testimage:$YMD podman tag testimage ${remote_tag} -${BUILDAH} manifest push --all ${remote_tag} docker://${remote_tag} +cat <' -# (eight digits, zero-padded sequence ID) in the same location; this is -# used by tests which need to pull a non-locally-cached image. This -# image will rarely if ever need to change, nor in fact does it even -# have to be a copy of this testimage since all we use it for is 'true'. -# However, it does need to be multiarch :-( -# -# As of 2021-02-24 it is simply busybox, because it is super small, -# but it's complicated because of multiarch: -# -# img=quay.io/libpod/testimage:0000000 -# buildah manifest create $img -# for arch in amd64 arm64v8 ppc64le s390x;do -# buildah pull --arch $arch docker.io/$arch/busybox:1.32.0 -# buildah manifest add $img docker.io/$arch/busybox:1.32.0 -# done -# buildah manifest push --all $img docker://$img -# +If you're happy with these images, run: + + ${BUILDAH} manifest push --all ${remote_tag} docker://${remote_tag} + ${BUILDAH} manifest push --all ${zeroimg} docker://${zeroimg} + +(You do not always need to push the :0000 image) + +EOF diff -Nru libpod-3.2.1+ds1/test/system/helpers.bash libpod-3.4.4+ds1/test/system/helpers.bash --- libpod-3.2.1+ds1/test/system/helpers.bash 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/helpers.bash 2021-12-26 00:45:51.000000000 +0000 @@ -7,14 +7,15 @@ PODMAN_TEST_IMAGE_REGISTRY=${PODMAN_TEST_IMAGE_REGISTRY:-"quay.io"} PODMAN_TEST_IMAGE_USER=${PODMAN_TEST_IMAGE_USER:-"libpod"} PODMAN_TEST_IMAGE_NAME=${PODMAN_TEST_IMAGE_NAME:-"testimage"} -PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"20210427"} +PODMAN_TEST_IMAGE_TAG=${PODMAN_TEST_IMAGE_TAG:-"20210610"} PODMAN_TEST_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_TEST_IMAGE_TAG" PODMAN_TEST_IMAGE_ID= # Remote image that we *DO NOT* fetch or keep by default; used for testing pull -# This changed from 0 to 1 on 2021-02-24 due to multiarch considerations; it -# should change only very rarely. -PODMAN_NONLOCAL_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:00000002" +# This has changed in 2021, from 0 through 3, various iterations of getting +# multiarch to work. It should change only very rarely. +PODMAN_NONLOCAL_IMAGE_TAG=${PODMAN_NONLOCAL_IMAGE_TAG:-"00000003"} +PODMAN_NONLOCAL_IMAGE_FQN="$PODMAN_TEST_IMAGE_REGISTRY/$PODMAN_TEST_IMAGE_USER/$PODMAN_TEST_IMAGE_NAME:$PODMAN_NONLOCAL_IMAGE_TAG" # Because who wants to spell that out each time? IMAGE=$PODMAN_TEST_IMAGE_FQN @@ -277,6 +278,41 @@ wait_for_output 'READY' "$@" } +###################### +# random_free_port # Pick an available port within a specified range +###################### +function random_free_port() { + local range=${1:-5000-5999} + + local port + for port in $(shuf -i ${range}); do + if ! { exec {unused_fd}<> /dev/tcp/127.0.0.1/$port; } &>/dev/null; then + echo $port + return + fi + done + + die "Could not find open port in range $range" +} + +################### +# wait_for_port # Returns once port is available on host +################### +function wait_for_port() { + local host=$1 # Probably "localhost" + local port=$2 # Numeric port + local _timeout=${3:-5} # Optional; default to 5 seconds + + # Wait + while [ $_timeout -gt 0 ]; do + { exec {unused_fd}<> /dev/tcp/$host/$port; } &>/dev/null && return + sleep 1 + _timeout=$(( $_timeout - 1 )) + done + + die "Timed out waiting for $host:$port" +} + # END podman helpers ############################################################################### # BEGIN miscellaneous tools diff -Nru libpod-3.2.1+ds1/test/system/helpers.systemd.bash libpod-3.4.4+ds1/test/system/helpers.systemd.bash --- libpod-3.2.1+ds1/test/system/helpers.systemd.bash 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/helpers.systemd.bash 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,30 @@ +# -*- bash -*- +# +# BATS helpers for systemd-related functionality +# + +# podman initializes this if unset, but systemctl doesn't +if [ -z "$XDG_RUNTIME_DIR" ]; then + if is_rootless; then + export XDG_RUNTIME_DIR=/run/user/$(id -u) + fi +fi + +# For tests which write systemd unit files +UNIT_DIR="/run/systemd/system" +_DASHUSER= +if is_rootless; then + UNIT_DIR="${XDG_RUNTIME_DIR}/systemd/user" + # Why isn't systemd smart enough to figure this out on its own? + _DASHUSER="--user" +fi + +mkdir -p $UNIT_DIR + +systemctl() { + command systemctl $_DASHUSER "$@" +} + +journalctl() { + command journalctl $_DASHUSER "$@" +} diff -Nru libpod-3.2.1+ds1/test/system/helpers.t libpod-3.4.4+ds1/test/system/helpers.t --- libpod-3.2.1+ds1/test/system/helpers.t 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/system/helpers.t 2021-12-26 00:45:51.000000000 +0000 @@ -213,8 +213,16 @@ ) check_same_dev "zero-line output" - # END remove_same_dev_warning ############################################################################### +# BEGIN random_free_port + +# Assumes that 16700 is open +found=$(random_free_port 16700-16700) + +check_result "$found" "16700" "random_free_port" + +# END random_free_port +############################################################################### exit $rc diff -Nru libpod-3.2.1+ds1/test/testvol/main.go libpod-3.4.4+ds1/test/testvol/main.go --- libpod-3.2.1+ds1/test/testvol/main.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/testvol/main.go 2021-12-26 00:45:51.000000000 +0000 @@ -224,13 +224,13 @@ vol, exists := d.volumes[req.Name] if !exists { logrus.Debugf("Did not find volume %s", req.Name) - return errors.Errorf("no volume with name %s found") + return errors.Errorf("no volume with name %s found", req.Name) } logrus.Debugf("Found volume %s", req.Name) if len(vol.mounts) > 0 { logrus.Debugf("Cannot remove %s, is mounted", req.Name) - return errors.Errorf("volume %s is mounted and cannot be removed") + return errors.Errorf("volume %s is mounted and cannot be removed", req.Name) } delete(d.volumes, req.Name) diff -Nru libpod-3.2.1+ds1/test/upgrade/helpers.bash libpod-3.4.4+ds1/test/upgrade/helpers.bash --- libpod-3.2.1+ds1/test/upgrade/helpers.bash 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/upgrade/helpers.bash 2021-12-26 00:45:51.000000000 +0000 @@ -9,3 +9,11 @@ teardown() { : } + +# skip a test when the given version is older than the currently tested one +skip_if_version_older() { + # use ${PODMAN_UPGRADE_FROM##v} to trim the leading "v" + if printf '%s\n%s\n' "${PODMAN_UPGRADE_FROM##v}" "$1" | sort --check=quiet --version-sort; then + skip "${2-test is only meaningful when upgrading from $1 or later}" + fi +} diff -Nru libpod-3.2.1+ds1/test/upgrade/test-upgrade.bats libpod-3.4.4+ds1/test/upgrade/test-upgrade.bats --- libpod-3.2.1+ds1/test/upgrade/test-upgrade.bats 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/upgrade/test-upgrade.bats 2021-12-26 00:45:51.000000000 +0000 @@ -21,9 +21,7 @@ export LABEL_CREATED=$(random_string 16) export LABEL_FAILED=$(random_string 17) export LABEL_RUNNING=$(random_string 18) - - # FIXME: randomize this - HOST_PORT=34567 + export HOST_PORT=$(random_free_port) fi # Version string of the podman we're actually testing, e.g. '3.0.0-dev-d1a26013' @@ -44,7 +42,8 @@ false fi - export _PODMAN_TEST_OPTS="--root=$PODMAN_UPGRADE_WORKDIR/root --runroot=$PODMAN_UPGRADE_WORKDIR/runroot --tmpdir=$PODMAN_UPGRADE_WORKDIR/tmp" + # cgroup-manager=systemd does not work inside a container + export _PODMAN_TEST_OPTS="--cgroup-manager=cgroupfs --root=$PODMAN_UPGRADE_WORKDIR/root --runroot=$PODMAN_UPGRADE_WORKDIR/runroot --tmpdir=$PODMAN_UPGRADE_WORKDIR/tmp" } ############################################################################### @@ -76,8 +75,8 @@ cat >| $pmscript <" "done" is "${lines[3]}" "myfailedcontainer--Exited (17) .*----$LABEL_FAILED" "fail" - is "${lines[4]}" "myrunningcontainer--Up .*----$LABEL_RUNNING" "running" + is "${lines[4]}" "myrunningcontainer--Up .*--0.0.0.0:$HOST_PORT->80/tcp--$LABEL_RUNNING" "running" # For debugging: dump containers and IDs if [[ -n "$PODMAN_UPGRADE_TEST_DEBUG" ]]; then @@ -212,6 +220,30 @@ done < <(parse_table "$tests") } +@test "network - curl" { + run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt + is "$output" "$RANDOM_STRING_1" "curl on running container" +} + +# IMPORTANT: connect should happen before restart, we want to check +# if we can connect on an existing running container +@test "network - connect" { + skip_if_version_older 2.2.0 + run_podman network connect mynetwork myrunningcontainer + run_podman network disconnect podman myrunningcontainer + run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt + is "$output" "$RANDOM_STRING_1" "curl on container with second network connected" +} + +@test "network - restart" { + # restart the container and check if we can still use the port + run_podman stop -t0 myrunningcontainer + run_podman start myrunningcontainer + run curl --max-time 3 -s 127.0.0.1:$HOST_PORT/index.txt + is "$output" "$RANDOM_STRING_1" "curl on restarted container" +} + + @test "logs" { run_podman logs mydonecontainer is "$output" "++$RANDOM_STRING_1++" "podman logs on stopped container" @@ -235,7 +267,7 @@ run_podman pod inspect mypod is "$output" ".*mypod.*" - run_podman --cgroup-manager=cgroupfs pod start mypod + run_podman pod start mypod is "$output" "[0-9a-f]\\{64\\}" "podman pod start" run_podman pod ps @@ -245,7 +277,7 @@ run_podman pod stop mypod is "$output" "[0-9a-f]\\{64\\}" "podman pod stop" - run_podman --cgroup-manager=cgroupfs pod rm mypod + run_podman pod rm mypod # FIXME: CI runs show this (non fatal) error: # Error updating pod conmon cgroup PID limit: open /sys/fs/cgroup/libpod_parent//conmon/pids.max: no such file or directory # Investigate how to fix this (likely a race condition) @@ -257,7 +289,7 @@ @test "start" { - run_podman --cgroup-manager=cgroupfs start -a mydonecontainer + run_podman start -a mydonecontainer is "$output" "++$RANDOM_STRING_1++" "start on already-run container" } @@ -295,6 +327,8 @@ run_podman logs podman_parent run_podman rm -f podman_parent + run_podman network rm -f mynetwork + umount $PODMAN_UPGRADE_WORKDIR/root/overlay || true rm -rf $PODMAN_UPGRADE_WORKDIR diff -Nru libpod-3.2.1+ds1/test/utils/podmantest_test.go libpod-3.4.4+ds1/test/utils/podmantest_test.go --- libpod-3.2.1+ds1/test/utils/podmantest_test.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/utils/podmantest_test.go 2021-12-26 00:45:51.000000000 +0000 @@ -23,7 +23,7 @@ FakeOutputs["check"] = []string{"check"} os.Setenv("HOOK_OPTION", "hook_option") env := os.Environ() - session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env, true, false, nil) + session := podmanTest.PodmanAsUserBase([]string{"check"}, 1000, 1000, "", env, true, false, nil, nil) os.Unsetenv("HOOK_OPTION") session.WaitWithDefaultTimeout() Expect(session.Command.Process).ShouldNot(BeNil()) diff -Nru libpod-3.2.1+ds1/test/utils/utils.go libpod-3.4.4+ds1/test/utils/utils.go --- libpod-3.2.1+ds1/test/utils/utils.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/test/utils/utils.go 2021-12-26 00:45:51.000000000 +0000 @@ -19,7 +19,7 @@ ) var ( - defaultWaitTimeout = 90 + DefaultWaitTimeout = 90 OSReleasePath = "/etc/os-release" ProcessOneCgroupPath = "/proc/1/cgroup" ) @@ -66,27 +66,31 @@ // PodmanAsUserBase exec podman as user. uid and gid is set for credentials usage. env is used // to record the env for debugging -func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string, noEvents, noCache bool, extraFiles []*os.File) *PodmanSession { +func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string, env []string, noEvents, noCache bool, wrapper []string, extraFiles []*os.File) *PodmanSession { var command *exec.Cmd podmanOptions := p.MakeOptions(args, noEvents, noCache) podmanBinary := p.PodmanBinary if p.RemoteTest { podmanBinary = p.RemotePodmanBinary } + + runCmd := append(wrapper, podmanBinary) if p.RemoteTest { podmanOptions = append([]string{"--remote", "--url", p.RemoteSocket}, podmanOptions...) } if env == nil { - fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(podmanOptions, " ")) + fmt.Printf("Running: %s %s\n", strings.Join(runCmd, " "), strings.Join(podmanOptions, " ")) } else { - fmt.Printf("Running: (env: %v) %s %s\n", env, podmanBinary, strings.Join(podmanOptions, " ")) + fmt.Printf("Running: (env: %v) %s %s\n", env, strings.Join(runCmd, " "), strings.Join(podmanOptions, " ")) } if uid != 0 || gid != 0 { pythonCmd := fmt.Sprintf("import os; import sys; uid = %d; gid = %d; cwd = '%s'; os.setgid(gid); os.setuid(uid); os.chdir(cwd) if len(cwd)>0 else True; os.execv(sys.argv[1], sys.argv[1:])", gid, uid, cwd) - nsEnterOpts := append([]string{"-c", pythonCmd, podmanBinary}, podmanOptions...) + runCmd = append(runCmd, podmanOptions...) + nsEnterOpts := append([]string{"-c", pythonCmd}, runCmd...) command = exec.Command("python", nsEnterOpts...) } else { - command = exec.Command(podmanBinary, podmanOptions...) + runCmd = append(runCmd, podmanOptions...) + command = exec.Command(runCmd[0], runCmd[1:]...) } if env != nil { command.Env = env @@ -106,7 +110,7 @@ // PodmanBase exec podman with default env. func (p *PodmanTest) PodmanBase(args []string, noEvents, noCache bool) *PodmanSession { - return p.PodmanAsUserBase(args, 0, 0, "", nil, noEvents, noCache, nil) + return p.PodmanAsUserBase(args, 0, 0, "", nil, noEvents, noCache, nil, nil) } // WaitForContainer waits on a started container @@ -317,15 +321,20 @@ return true } -// WaitWithDefaultTimeout waits for process finished with defaultWaitTimeout +// WaitWithDefaultTimeout waits for process finished with DefaultWaitTimeout func (s *PodmanSession) WaitWithDefaultTimeout() { - Eventually(s, defaultWaitTimeout).Should(Exit()) + s.WaitWithTimeout(DefaultWaitTimeout) +} + +// WaitWithTimeout waits for process finished with DefaultWaitTimeout +func (s *PodmanSession) WaitWithTimeout(timeout int) { + Eventually(s, timeout).Should(Exit()) os.Stdout.Sync() os.Stderr.Sync() fmt.Println("output:", s.OutputToString()) } -// CreateTempDirinTempDir create a temp dir with prefix podman_test +// CreateTempDirInTempDir create a temp dir with prefix podman_test func CreateTempDirInTempDir() (string, error) { return ioutil.TempDir("", "podman_test") } @@ -337,7 +346,7 @@ if err != nil { Fail(fmt.Sprintf("unable to run command: %s %s", command, strings.Join(args, " "))) } - session.Wait(defaultWaitTimeout) + session.Wait(DefaultWaitTimeout) return &PodmanSession{session} } diff -Nru libpod-3.2.1+ds1/transfer.md libpod-3.4.4+ds1/transfer.md --- libpod-3.2.1+ds1/transfer.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/transfer.md 2021-12-26 00:45:51.000000000 +0000 @@ -9,7 +9,7 @@ Podman is a tool for managing Pods, Containers, and Container Images. The CLI for Podman is based on the Docker CLI, although Podman does not require a -runtime daemon to be running in order to function. +runtime daemon to be running in order to function. Podman also supports the Docker API via the Podman socket activated system service. ## System Tools @@ -21,81 +21,94 @@ For many troubleshooting and information collection steps, there may be an existing pattern. Following provides equivalent with `Podman` tools for gathering information or jumping into containers, for operational use. -| Existing Step | `Podman` (and friends) | -| :--- | :--- | -| `docker run` | [`podman run`](./docs/source/markdown/podman-run.1.md) | -| `docker exec` | [`podman exec`](./docs/source/markdown/podman-exec.1.md) | -| `docker info` | [`podman info`](./docs/source/markdown/podman-info.1.md) | -| `docker inspect` | [`podman inspect`](./docs/source/markdown/podman-inspect.1.md) | -| `docker logs` | [`podman logs`](./docs/source/markdown/podman-logs.1.md) | -| `docker ps` | [`podman ps`](./docs/source/markdown/podman-ps.1.md) | -| `docker stats`| [`podman stats`](./docs/source/markdown/podman-stats.1.md)| - ## Development Transfer There are other equivalents for these tools | Existing Step | `Podman` (and friends) | | :--- | :--- | +| `docker ` | [`podman`](./docs/source/markdown/podman.1.md) | | `docker attach` | [`podman attach`](./docs/source/markdown/podman-attach.1.md) | +| `docker auto-update`|[`podman auto-update`](./docs/source/markdown/podman-auto-update.1.md)| | `docker build` | [`podman build`](./docs/source/markdown/podman-build.1.md) | | `docker commit` | [`podman commit`](./docs/source/markdown/podman-commit.1.md) | -| `docker container`|[`podman container`](./docs/source/markdown/podman-container.1.md) | +| `docker container `|[`podman container`](./docs/source/markdown/podman-container.1.md) | +| `docker container prune`|[`podman container prune`](./docs/source/markdown/podman-container-prune.1.md) | | `docker cp` | [`podman cp`](./docs/source/markdown/podman-cp.1.md) | | `docker create` | [`podman create`](./docs/source/markdown/podman-create.1.md) | | `docker diff` | [`podman diff`](./docs/source/markdown/podman-diff.1.md) | | `docker events` | [`podman events`](./docs/source/markdown/podman-events.1.md) | +| `docker exec` | [`podman exec`](./docs/source/markdown/podman-exec.1.md) | | `docker export` | [`podman export`](./docs/source/markdown/podman-export.1.md) | | `docker history` | [`podman history`](./docs/source/markdown/podman-history.1.md) | -| `docker image` | [`podman image`](./docs/source/markdown/podman-image.1.md) | +| `docker image` | [`podman image`](./docs/source/markdown/podman-image.1.md) | | `docker images` | [`podman images`](./docs/source/markdown/podman-images.1.md) | | `docker import` | [`podman import`](./docs/source/markdown/podman-import.1.md) | +| `docker info` | [`podman info`](./docs/source/markdown/podman-info.1.md) | +| `docker inspect` | [`podman inspect`](./docs/source/markdown/podman-inspect.1.md) | | `docker kill` | [`podman kill`](./docs/source/markdown/podman-kill.1.md) | | `docker load` | [`podman load`](./docs/source/markdown/podman-load.1.md) | | `docker login` | [`podman login`](./docs/source/markdown/podman-login.1.md) | | `docker logout` | [`podman logout`](./docs/source/markdown/podman-logout.1.md) | -| `docker network create` | [`podman network create`](./docs/source/markdown/podman-network-create.1.md) | -| `docker network inspect` | [`podman network inspect`](./docs/source/markdown/podman-network-inspect.1.md) | -| `docker network ls` | [`podman network ls`](./docs/source/markdown/podman-network-ls.1.md) | -| `docker network rm` | [`podman network rm`](./docs.source/markdown/podman-network-rm.1.md) | +| `docker logs` | [`podman logs`](./docs/source/markdown/podman-logs.1.md) | +| `docker manifest `| [`podman manifest`](./docs.source/markdown/podman-manifest.1.md) | +| `docker manifest annotate` | [`podman manifest annotate`](./docs/source/markdown/podman-manifest-annotate.1.md) | +| `docker manifest create` | [`podman manifest create`](./docs/source/markdown/podman-manifest-create.1.md) | +| `docker manifest inspect`| [`podman manifest inspect`](./docs/source/markdown/podman-manifest-inspect.1.md) | +| `docker manifest push` | [`podman manifest push`](./docs/source/markdown/podman-manifest-push.1.md) | +| `docker manifest rm` | [`podman manifest rm`](./docs.source/markdown/podman-manifest-rm.1.md) | +| `docker network ` | [`podman network`](./docs.source/markdown/podman-network.1.md) | +| `docker network connect` | [`podman network connect`](./docs/source/markdown/podman-network-connect.1.md) | +| `docker network create` | [`podman network create`](./docs/source/markdown/podman-network-create.1.md) | +| `docker network disconnect`| [`podman network disconnect`](./docs/source/markdown/podman-network-disconnect.1.md)| +| `docker network inspect` | [`podman network inspect`](./docs/source/markdown/podman-network-inspect.1.md) | +| `docker network ls` | [`podman network ls`](./docs/source/markdown/podman-network-ls.1.md) | +| `docker network rm` | [`podman network rm`](./docs.source/markdown/podman-network-rm.1.md) | | `docker pause` | [`podman pause`](./docs/source/markdown/podman-pause.1.md) | | `docker port` | [`podman port`](./docs/source/markdown/podman-port.1.md) | | `docker ps` | [`podman ps`](./docs/source/markdown/podman-ps.1.md) | | `docker pull` | [`podman pull`](./docs/source/markdown/podman-pull.1.md) | | `docker push` | [`podman push`](./docs/source/markdown/podman-push.1.md) | +| `docker rename` | [`podman rename`](./docs/source/markdown/podman-rename.1.md) | | `docker restart` | [`podman restart`](./docs/source/markdown/podman-restart.1.md) | | `docker rm` | [`podman rm`](./docs/source/markdown/podman-rm.1.md) | | `docker rmi` | [`podman rmi`](./docs/source/markdown/podman-rmi.1.md) | | `docker run` | [`podman run`](./docs/source/markdown/podman-run.1.md) | | `docker save` | [`podman save`](./docs/source/markdown/podman-save.1.md) | | `docker search` | [`podman search`](./docs/source/markdown/podman-search.1.md) | +| `docker secret ` | [`podman secret`](./docs/source/markdown/podman-secret.1.md) | +| `docker secret create` | [`podman secret`](./docs/source/markdown/podman-secret-create.1.md) | +| `docker secret inspect` | [`podman secret`](./docs/source/markdown/podman-secret-inspect.1.md)| +| `docker secret ls` | [`podman secret`](./docs/source/markdown/podman-secret-ls.1.md)| +| `docker secret rm` | [`podman secret`](./docs/source/markdown/podman-secret-rm.1.md)| +| `docker service` | [`podman service`](./docs/source/markdown/podman-service.1.md) | | `docker start` | [`podman start`](./docs/source/markdown/podman-start.1.md) | +| `docker stats` | [`podman stats`](./docs/source/markdown/podman-stats.1.md) | | `docker stop` | [`podman stop`](./docs/source/markdown/podman-stop.1.md) | -| `docker system df` | [`podman system df`](./docs/source/markdown/podman-system-df.1.md) | -| `docker system info` | [`podman system info`](./docs/source/markdown/podman-system-info.1.md) | -| `docker system prune` | [`podman system prune`](./docs/source/markdown/podman-system-prune.1.md) | -| `docker system` | [`podman system`](./docs/source/markdown/podman-system.1.md) | +| `docker system ` | [`podman system`](./docs/source/markdown/podman-system.1.md) | +| `docker system df` | [`podman system df`](./docs/source/markdown/podman-system-df.1.md) | +| `docker system info` | [`podman system info`](./docs/source/markdown/podman-system-info.1.md) | +| `docker system prune` | [`podman system prune`](./docs/source/markdown/podman-system-prune.1.md)| | `docker tag` | [`podman tag`](./docs/source/markdown/podman-tag.1.md) | | `docker top` | [`podman top`](./docs/source/markdown/podman-top.1.md) | | `docker unpause` | [`podman unpause`](./docs/source/markdown/podman-unpause.1.md) | | `docker version` | [`podman version`](./docs/source/markdown/podman-version.1.md) | +| `docker volume `| [`podman volume`](./docs/source/markdown/podman-volume.1.md) | | `docker volume create` | [`podman volume create`](./docs/source/markdown/podman-volume-create.1.md) | | `docker volume inspect`| [`podman volume inspect`](./docs/source/markdown/podman-volume-inspect.1.md)| | `docker volume ls` | [`podman volume ls`](./docs/source/markdown/podman-volume-ls.1.md) | | `docker volume prune` | [`podman volume prune`](./docs/source/markdown/podman-volume-prune.1.md) | | `docker volume rm` | [`podman volume rm`](./docs/source/markdown/podman-volume-rm.1.md) | -| `docker volume` | [`podman volume`](./docs/source/markdown/podman-volume.1.md) | | `docker wait` | [`podman wait`](./docs/source/markdown/podman-wait.1.md) | -**** Use mount to take advantage of the entire linux tool chain rather then just cp. Read [`here`](./docs/podman-cp.1.md) for more information. - ## Behavioural differences and pitfalls These commands behave differently from the commands in Docker: | Command | Description | | :--- | :--- | -| `podman volume create` | While `docker volume create` is idempotent, `podman volume create` raises an error if the volume does already exist. See this [docker issue](https://github.com/moby/moby/issues/16068) for more information.| +| `podman volume create` | While `docker volume create` is idempotent, `podman volume create` raises an error if the volume does already exist. See this [docker issue](https://github.com/moby/moby/issues/16068) for more information.| +| `podman run -v /tmp/noexist:/tmp ...` | While `docker run -v /tmp/noexist:/tmp` will create non existing volumes on the host, Podman will report that the volume does not exist. The Podman team sees this as a bug in Docker.| ## Missing commands in podman @@ -103,14 +116,17 @@ | Missing command | Description| | :--- | :--- | -| `docker container rename` | podman does not support `container rename` - or the `rename` shorthand. We recommend using `podman rm` and `podman create` to create a container with a specific name.| +| `docker builder` || +| `docker buildx` || +| `docker config` || +| `docker context` || | `docker container update` | podman does not support altering running containers. We recommend recreating containers with the correct arguments.| | `docker node` || | `docker plugin` | podman does not support plugins. We recommend you use alternative OCI Runtimes or OCI Runtime Hooks to alter behavior of podman.| -| `docker secret` || -| `docker service` || | `docker stack` || | `docker swarm` | podman does not support swarm. We support Kubernetes for orchestration using [CRI-O](https://github.com/cri-o/cri-o).| +| `docker trust` |[`podman image trust`](./docs/source/markdown/podman-image-trust.1.md) | +| `docker update` || ## Missing commands in Docker @@ -122,26 +138,62 @@ * [`podman container refresh`](/docs/source/markdown/podman-container-refresh.1.md) * [`podman container restore`](/docs/source/markdown/podman-container-restore.1.md) * [`podman container runlabel`](/docs/source/markdown/podman-container-runlabel.1.md) +* [`podman generate `](./docs/source/markdown/podman-generate.1.md) * [`podman generate kube`](./docs/source/markdown/podman-generate-kube.1.md) -* [`podman generate`](./docs/source/markdown/podman-generate.1.md) +* [`podman generate systemd`](./docs/source/markdown/podman-generate-systemd.1.md) +* [`podman healthcheck `](/docs/source/markdown/podman-healthcheck.1.md) * [`podman healthcheck run`](/docs/source/markdown/podman-healthcheck-run.1.md) +* [`podman image diff`](./docs/source/markdown/podman-image-diff.1.md) * [`podman image exists`](./docs/source/markdown/podman-image-exists.1.md) +* [`podman image mount`](./docs/source/markdown/podman-image-mount.1.md) +* [`podman image prune`](./docs/source/markdown/podman-image-prune.1.md) * [`podman image sign`](./docs/source/markdown/podman-image-sign.1.md) +* [`podman image tree`](./docs/source/markdown/podman-image-tree.1.md) * [`podman image trust`](./docs/source/markdown/podman-image-trust.1.md) +* [`podman image unmount`](./docs/source/markdown/podman-image-unmount.1.md) +* [`podman init`](./docs/source/markdown/podman-init.1.md) +* [`podman machine `](./docs/source/markdown/podman-machine.1.md) +* [`podman machine init`](./docs/source/markdown/podman-machine-init.1.md) +* [`podman machine list`](./docs/source/markdown/podman-machine-list.1.md) +* [`podman machine rm`](./docs/source/markdown/podman-machine-rm.1.md) +* [`podman machine ssh`](./docs/source/markdown/podman-machine-ssh.1.md) +* [`podman machine start`](./docs/source/markdown/podman-machine-start.1.md) +* [`podman machine stop`](./docs/source/markdown/podman-machine-stop.1.md) +* [`podman manifest add`](./docs/source/markdown/podman-manifest-add.1.md) +* [`podman manifest exists`](./docs/source/markdown/podman-manifest-exists.1.md) +* [`podman manifest remove`](./docs/source/markdown/podman-manifest-remove.1.md) * [`podman mount`](./docs/source/markdown/podman-mount.1.md) +* [`podman network exists`](./docs/source/markdown/podman-network-exists.1.md) +* [`podman network prune`](./docs/source/markdown/podman-network-prune.1.md) +* [`podman network reload`](./docs/source/markdown/podman-network-reload.1.md) +* [`podman play `](./docs/source/markdown/podman-play.1.md) * [`podman play kube`](./docs/source/markdown/podman-play-kube.1.md) -* [`podman play`](./docs/source/markdown/podman-play.1.md) +* [`podman pod `](./docs/source/markdown/podman-pod.1.md) * [`podman pod create`](./docs/source/markdown/podman-pod-create.1.md) * [`podman pod exists`](./docs/source/markdown/podman-pod-exists.1.md) * [`podman pod inspect`](./docs/source/markdown/podman-pod-inspect.1.md) * [`podman pod kill`](./docs/source/markdown/podman-pod-kill.1.md) * [`podman pod pause`](./docs/source/markdown/podman-pod-pause.1.md) +* [`podman pod prune`](./docs/source/markdown/podman-pod-prune.1.md) * [`podman pod ps`](./docs/source/markdown/podman-pod-ps.1.md) * [`podman pod restart`](./docs/source/markdown/podman-pod-restart.1.md) * [`podman pod rm`](./docs/source/markdown/podman-pod-rm.1.md) * [`podman pod start`](./docs/source/markdown/podman-pod-start.1.md) +* [`podman pod stats`](./docs/source/markdown/podman-pod-stats.1.md) * [`podman pod stop`](./docs/source/markdown/podman-pod-stop.1.md) * [`podman pod top`](./docs/source/markdown/podman-pod-top.1.md) * [`podman pod unpause`](./docs/source/markdown/podman-pod-unpause.1.md) -* [`podman pod`](./docs/source/markdown/podman-pod.1.md) +* [`podman system connection `](./docs/source/markdown/podman-system-connection.1.md) +* [`podman system connection add`](./docs/source/markdown/podman-system-connection-add.1.md) +* [`podman system connection default`](./docs/source/markdown/podman-system-connection-default.1.md) +* [`podman system connection list`](./docs/source/markdown/podman-system-connection-list.1.md) +* [`podman system connection remove`](./docs/source/markdown/podman-system-connection-remove.1.md) +* [`podman system connection rename`](./docs/source/markdown/podman-system-connection-rename.1.md) +* [`podman system migrate`](./docs/source/markdown/podman-system-connection-migrate.1.md) +* [`podman system renumber`](./docs/source/markdown/podman-system-connection-renumber.1.md) +* [`podman system reset`](./docs/source/markdown/podman-system-connection-reset.1.md) +* [`podman system service`](./docs/source/markdown/podman-system-connection-service.1.md) * [`podman umount`](./docs/source/markdown/podman-umount.1.md) +* [`podman unshare`](./docs/source/markdown/podman-unshare.1.md) +* [`podman untag`](./docs/source/markdown/podman-untag.1.md) +* [`podman volume exists`](./docs/source/markdown/podman-volume-exists.1.md) diff -Nru libpod-3.2.1+ds1/troubleshooting.md libpod-3.4.4+ds1/troubleshooting.md --- libpod-3.2.1+ds1/troubleshooting.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/troubleshooting.md 2021-12-26 00:45:51.000000000 +0000 @@ -51,9 +51,9 @@ solution is to fix the user namespace. This would include container images such as the Jupyter Notebook image (which runs as "jovyan") and the Postgres image (which runs as "postgres"). In either case, use the `--userns` switch to map user namespaces, -most of the time by using keep_id option. +most of the time by using the **keep-id** option. -$ podman run -v "$PWD":/home/jovyan/work --userns=keep_id jupyter/scipy-notebook +$ podman run -v "$PWD":/home/jovyan/work --userns=keep-id jupyter/scipy-notebook --- ### 3) No such image or Bare keys cannot contain ':' @@ -156,7 +156,7 @@ #### Symptom -If you are running Podman or buildah on a home directory that is mounted noexec, +If you are running Podman or Buildah on a home directory that is mounted noexec, then they will fail. With a message like: ``` @@ -356,7 +356,7 @@ Not doing this will cause Podman in the container to detect that temporary files have been cleared, leading it to assume a system restart has taken place. This can cause Podman to reset container states and lose track of running containers. -For running containers on the host from inside a container, we also recommend the [Podman remote client](remote_client.md), which only requires a single socket to be mounted into the container. +For running containers on the host from inside a container, we also recommend the [Podman remote client](docs/tutorials/remote_client.md), which only requires a single socket to be mounted into the container. ### 14) Rootless 'podman build' fails EPERM on NFS: @@ -493,7 +493,7 @@ 1 100000 65536 ``` -Reference [subuid](http://man7.org/linux/man-pages/man5/subuid.5.html) and [subgid](http://man7.org/linux/man-pages/man5/subgid.5.html) man pages for more detail. +Reference [subuid](https://man7.org/linux/man-pages/man5/subuid.5.html) and [subgid](https://man7.org/linux/man-pages/man5/subgid.5.html) man pages for more detail. ### 20) Passed-in devices or files can't be accessed in rootless container @@ -697,3 +697,213 @@ This can happen when running a container from an image for another architecture than the one you are running on. For example, if a remote repository only has, and thus send you, a `linux/arm64` _OS/ARCH_ but you run on `linux/amd64` (as happened in https://github.com/openMF/community-app/issues/3323 due to https://github.com/timbru31/docker-ruby-node/issues/564). + +### 27) `Error: failed to create sshClient: Connection to bastion host (ssh://user@host:22/run/user/.../podman/podman.sock) failed.: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey], no supported methods remain` + +In some situations where the client is not on the same machine as where the podman daemon is running the client key could be using a cipher not supported by the host. This indicates an issue with one's SSH config. Until remedied using podman over ssh +with a pre-shared key will be impossible. + +#### Symptom + +The accepted ciphers per `/etc/crypto-policies/back-ends/openssh.config` are not one that was used to create the public/private key pair that was transferred over to the host for ssh authentication. + +You can confirm this is the case by attempting to connect to the host via `podman-remote info` from the client and simultaneously on the host running `journalctl -f` and watching for the error `userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]`. + +#### Solution + +Create a new key using a supported algorithm e.g. ecdsa: + +`ssh-keygen -t ecdsa -f ~/.ssh/podman` + +Then copy the new id over: + +`ssh-copy-id -i ~/.ssh/podman.pub user@host` + +And then re-add the connection (removing the old one if necessary): + +`podman-remote system connection add myuser --identity ~/.ssh/podman ssh://user@host/run/user/1000/podman/podman.sock` + +And now this should work: + +`podman-remote info` + +--- +### 28) Rootless CNI networking fails in RHEL with Podman v2.2.1 to v3.0.1. + +A failure is encountered when trying to use networking on a rootless +container in Podman v2.2.1 through v3.0.1 on RHEL. This error does not +occur on other Linux Distributions. + +#### Symptom + +A rootless container is created using a CNI network, but the `podman run` command +returns an error that an image must be built. + +#### Solution + +In order to use a CNI network in a rootless container on RHEL, +an Infra container image for CNI-in-slirp4netns must be created. The +instructions for building the Infra container image can be found for +v2.2.1 [here](https://github.com/containers/podman/tree/v2.2.1-rhel/contrib/rootless-cni-infra), +and for v3.0.1 [here](https://github.com/containers/podman/tree/v3.0.1-rhel/contrib/rootless-cni-infra). +### 29) Container related firewall rules are lost after reloading firewalld +Container network can't be reached after `firewall-cmd --reload` and `systemctl restart firewalld` Running `podman network reload` will fix it but it has to be done manually. + +#### Symptom +The firewall rules created by podman are lost when the firewall is reloaded. + +#### Solution +[@ranjithrajaram](https://github.com/containers/podman/issues/5431#issuecomment-847758377) has created a systemd-hook to fix this issue + +1) For "firewall-cmd --reload", create a systemd unit file with the following +``` +[Unit] +Description=firewalld reload hook - run a hook script on firewalld reload +Wants=dbus.service +After=dbus.service + +[Service] +Type=simple +ExecStart=/bin/bash -c '/bin/busctl monitor --system --match "interface=org.fedoraproject.FirewallD1,member=Reloaded" --match "interface=org.fedoraproject.FirewallD1,member=PropertiesChanged" | while read -r line ; do podman network reload --all ; done' + +[Install] +WantedBy=default.target +``` +2) For "systemctl restart firewalld", create a systemd unit file with the following +``` +[Unit] +Description=podman network reload +Wants=firewalld.service +After=firewalld.service +PartOf=firewalld.service + +[Service] +Type=simple +RemainAfterExit=yes +ExecStart=/usr/bin/podman network reload --all + +[Install] +WantedBy=default.target +``` +However, If you use busctl monitor then you can't get machine-readable output on `RHEL 8`. +Since it doesn't have `busctl -j` as mentioned here by [@yrro](https://github.com/containers/podman/issues/5431#issuecomment-896943018). + +For RHEL 8, you can use the following one-liner bash script. +``` +[Unit] +Description=Redo podman NAT rules after firewalld starts or reloads +Wants=dbus.service +After=dbus.service +Requires=firewalld.service + +[Service] +Type=simple +ExecStart=/bin/bash -c "dbus-monitor --profile --system 'type=signal,sender=org.freedesktop.DBus,path=/org/freedesktop/DBus,interface=org.freedesktop.DBus,member=NameAcquired,arg0=org.fedoraproject.FirewallD1' 'type=signal,path=/org/fedoraproject/FirewallD1,interface=org.fedoraproject.FirewallD1,member=Reloaded' | sed -u '/^#/d' | while read -r type timestamp serial sender destination path interface member _junk; do if [[ $type = '#'* ]]; then continue; elif [[ $interface = org.freedesktop.DBus && $member = NameAcquired ]]; then echo 'firewalld started'; podman network reload --all; elif [[ $interface = org.fedoraproject.FirewallD1 && $member = Reloaded ]]; then echo 'firewalld reloaded'; podman network reload --all; fi; done" +Restart=Always + +[Install] +WantedBy=default.target +``` +`busctl-monitor` is almost usable in `RHEL 8`, except that it always outputs two bogus events when it starts up, +one of which is (in its only machine-readable format) indistinguishable from the `NameOwnerChanged` that you get when firewalld starts up. +This means you would get an extra `podman network reload --all` when this unit starts. + +Apart from this, you can use the following systemd service with the python3 code. + +``` +[Unit] +Description=Redo podman NAT rules after firewalld starts or reloads +Wants=dbus.service +Requires=firewalld.service +After=dbus.service + +[Service] +Type=simple +ExecStart=/usr/bin/python /path/to/python/code/podman-redo-nat.py +Restart=always + +[Install] +WantedBy=default.target +``` +The code reloads podman network twice when you use `systemctl restart firewalld`. +``` +import dbus +from gi.repository import GLib +from dbus.mainloop.glib import DBusGMainLoop +import subprocess +import sys + +# I'm a bit confused on the return values in the code +# Not sure if they are needed. + +def reload_podman_network(): + try: + subprocess.run(["podman","network","reload","--all"],timeout=90) + # I'm not sure about this part + sys.stdout.write("podman network reload done\n") + sys.stdout.flush() + except subprocess.TimeoutExpired as t: + sys.stderr.write(f"Podman reload failed due to Timeout {t}") + except subprocess.CalledProcessError as e: + sys.stderr.write(f"Podman reload failed due to {e}") + except Exception as e: + sys.stderr.write(f"Podman reload failed with an Unhandled Exception {e}") + + return False + +def signal_handler(*args, **kwargs): + if kwargs.get('member') == "Reloaded": + reload_podman_network() + elif kwargs.get('member') == "NameOwnerChanged": + reload_podman_network() + else: + return None + return None + +def signal_listener(): + try: + DBusGMainLoop(set_as_default=True)# Define the loop. + loop = GLib.MainLoop() + system_bus = dbus.SystemBus() + # Listens to systemctl restart firewalld with a filter added, will cause podman network to be reloaded twice + system_bus.add_signal_receiver(signal_handler,dbus_interface='org.freedesktop.DBus',arg0='org.fedoraproject.FirewallD1',member_keyword='member') + # Listens to firewall-cmd --reload + system_bus.add_signal_receiver(signal_handler,dbus_interface='org.fedoraproject.FirewallD1',signal_name='Reloaded',member_keyword='member') + loop.run() + except KeyboardInterrupt: + loop.quit() + sys.exit(0) + except Exception as e: + loop.quit() + sys.stderr.write(f"Error occured {e}") + sys.exit(1) + +if __name__ == "__main__": + signal_listener() +``` +### 30) Podman run fails with `ERRO[0000] XDG_RUNTIME_DIR directory "/run/user/0" is not owned by the current user` or `Error: error creating tmpdir: mkdir /run/user/1000: permission denied`. + +A failure is encountered when performing `podman run` with a warning `XDG_RUNTIME_DIR is pointing to a path which is not writable. Most likely podman will fail.` + +#### Symptom + +A rootless container is being invoked with cgroup configuration as `cgroupv2` for user with missing or invalid **systemd session**. + +Example cases +```bash +# su user1 -c 'podman images' +ERRO[0000] XDG_RUNTIME_DIR directory "/run/user/0" is not owned by the current user +``` +```bash +# su - user1 -c 'podman images' +Error: error creating tmpdir: mkdir /run/user/1000: permission denied +``` + +#### Solution + +Podman expects a valid login session for the `rootless+cgroupv2` use-case. Podman execution is expected to fail if the login session is not present. In most cases, podman will figure out a solution on its own but if `XDG_RUNTIME_DIR` is pointing to a path that is not writable execution will most fail. Typical scenarious of such cases are seen when users are trying to use Podman with `su - -c '`, or `sudo -l` and badly configured systemd session. + +Resolution steps + +* Before invoking Podman command create a valid login session for your rootless user using `loginctl enable-linger ` +* If `loginctl` is unavailable you can also try logging in via `ssh` i.e `ssh @localhost`. diff -Nru libpod-3.2.1+ds1/.ubuntu_prepare.sh libpod-3.4.4+ds1/.ubuntu_prepare.sh --- libpod-3.2.1+ds1/.ubuntu_prepare.sh 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/.ubuntu_prepare.sh 2021-12-26 00:45:51.000000000 +0000 @@ -33,7 +33,7 @@ if [ $conmon -eq 1 ]; then # Build and install conmon from source echo "Building conmon ..." - git clone http://github.com/containers/conmon $conmon_source + git clone https://github.com/containers/conmon $conmon_source cd $conmon_source && make install PREFIX=/usr fi @@ -41,7 +41,7 @@ if [ $cni -eq 1 ]; then # Build and install containernetworking plugins from source echo "Building containernetworking-plugins..." - git clone http://github.com/containernetworking/plugins $cni_source + git clone https://github.com/containernetworking/plugins $cni_source cd $cni_source ./build.sh mkdir -p /usr/libexec/cni @@ -52,7 +52,7 @@ if [ $runc -eq 1 ]; then # Build and install runc echo "Building runc..." - git clone http://github.com/opencontainers/runc $runc_source + git clone https://github.com/opencontainers/runc $runc_source cd $runc_source make install PREFIX=/usr fi diff -Nru libpod-3.2.1+ds1/utils/utils.go libpod-3.4.4+ds1/utils/utils.go --- libpod-3.2.1+ds1/utils/utils.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/utils/utils.go 2021-12-26 00:45:51.000000000 +0000 @@ -4,13 +4,18 @@ "bytes" "fmt" "io" + "io/ioutil" + "math/rand" "os" "os/exec" "strconv" "strings" + "sync" "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/pkg/cgroups" "github.com/containers/storage/pkg/archive" + "github.com/godbus/dbus/v5" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -107,6 +112,16 @@ return archive.Untar(tarball, dest, options) } +// Creates a new tar file and wrties bytes from io.ReadCloser +func CreateTarFromSrc(source string, dest string) error { + file, err := os.Create(dest) + if err != nil { + return errors.Wrapf(err, "Could not create tarball file '%s'", dest) + } + defer file.Close() + return TarToFilesystem(source, file) +} + // TarToFilesystem creates a tarball from source and writes to an os.file // provided func TarToFilesystem(source string, tarball *os.File) error { @@ -145,3 +160,69 @@ } return result, nil } + +var ( + runsOnSystemdOnce sync.Once + runsOnSystemd bool +) + +// RunsOnSystemd returns whether the system is using systemd +func RunsOnSystemd() bool { + runsOnSystemdOnce.Do(func() { + initCommand, err := ioutil.ReadFile("/proc/1/comm") + // On errors, default to systemd + runsOnSystemd = err != nil || strings.TrimRight(string(initCommand), "\n") == "systemd" + }) + return runsOnSystemd +} + +func moveProcessToScope(pidPath, slice, scope string) error { + data, err := ioutil.ReadFile(pidPath) + if err != nil { + // do not raise an error if the file doesn't exist + if os.IsNotExist(err) { + return nil + } + return errors.Wrapf(err, "cannot read pid file %s", pidPath) + } + pid, err := strconv.ParseUint(string(data), 10, 0) + if err != nil { + return errors.Wrapf(err, "cannot parse pid file %s", pidPath) + } + err = RunUnderSystemdScope(int(pid), slice, scope) + + // If the PID is not valid anymore, do not return an error. + if dbusErr, ok := err.(dbus.Error); ok { + if dbusErr.Name == "org.freedesktop.DBus.Error.UnixProcessIdUnknown" { + return nil + } + } + + return err +} + +// MovePauseProcessToScope moves the pause process used for rootless mode to keep the namespaces alive to +// a separate scope. +func MovePauseProcessToScope(pausePidPath string) { + var err error + + for i := 0; i < 3; i++ { + r := rand.Int() + err = moveProcessToScope(pausePidPath, "user.slice", fmt.Sprintf("podman-pause-%d.scope", r)) + if err == nil { + return + } + } + + if err != nil { + unified, err2 := cgroups.IsCgroup2UnifiedMode() + if err2 != nil { + logrus.Warnf("Failed to detect if running with cgroup unified: %v", err) + } + if RunsOnSystemd() && unified { + logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err) + } else { + logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err) + } + } +} diff -Nru libpod-3.2.1+ds1/utils/utils_supported.go libpod-3.4.4+ds1/utils/utils_supported.go --- libpod-3.2.1+ds1/utils/utils_supported.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/utils/utils_supported.go 2021-12-26 00:45:51.000000000 +0000 @@ -36,6 +36,7 @@ return err } } + defer conn.Close() properties = append(properties, systemdDbus.PropSlice(slice)) properties = append(properties, newProp("PIDs", []uint32{uint32(pid)})) properties = append(properties, newProp("Delegate", true)) @@ -46,15 +47,14 @@ // On errors check if the cgroup already exists, if it does move the process there if props, err := conn.GetUnitTypeProperties(unitName, "Scope"); err == nil { if cgroup, ok := props["ControlGroup"].(string); ok && cgroup != "" { - if err := moveUnderCgroup(cgroup, "", []uint32{uint32(pid)}); err != nil { - return err + if err := moveUnderCgroup(cgroup, "", []uint32{uint32(pid)}); err == nil { + return nil } - return nil + // On errors return the original error message we got from StartTransientUnit. } } return err } - defer conn.Close() // Block until job is started <-ch @@ -172,7 +172,7 @@ if len(processes) > 0 { for _, pid := range processes { if _, err := f.Write([]byte(fmt.Sprintf("%d\n", pid))); err != nil { - logrus.Warnf("Cannot move process %d to cgroup %q", pid, newCgroup) + logrus.Debugf("Cannot move process %d to cgroup %q: %v", pid, newCgroup, err) } } } else { @@ -185,7 +185,7 @@ continue } if _, err := f.Write(pid); err != nil { - logrus.Warnf("Cannot move process %s to cgroup %q", string(pid), newCgroup) + logrus.Debugf("Cannot move process %s to cgroup %q: %v", string(pid), newCgroup, err) } } } diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/.gitignore libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/.gitignore --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/.gitignore 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -test/test -test/piggie -test/phaul -image -rpc/rpc.proto diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/LICENSE libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/LICENSE --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright {yyyy} {name of copyright owner} - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/main.go libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/main.go --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/main.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/main.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,250 +0,0 @@ -package criu - -import ( - "errors" - "fmt" - "os" - "os/exec" - "strconv" - "syscall" - - "github.com/checkpoint-restore/go-criu/rpc" - "github.com/golang/protobuf/proto" -) - -// Criu struct -type Criu struct { - swrkCmd *exec.Cmd - swrkSk *os.File -} - -// MakeCriu returns the Criu object required for most operations -func MakeCriu() *Criu { - return &Criu{} -} - -// Prepare sets up everything for the RPC communication to CRIU -func (c *Criu) Prepare() error { - fds, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_SEQPACKET, 0) - if err != nil { - return err - } - - cln := os.NewFile(uintptr(fds[0]), "criu-xprt-cln") - syscall.CloseOnExec(fds[0]) - srv := os.NewFile(uintptr(fds[1]), "criu-xprt-srv") - defer srv.Close() - - args := []string{"swrk", strconv.Itoa(fds[1])} - cmd := exec.Command("criu", args...) - - err = cmd.Start() - if err != nil { - cln.Close() - return err - } - - c.swrkCmd = cmd - c.swrkSk = cln - - return nil -} - -// Cleanup cleans up -func (c *Criu) Cleanup() { - if c.swrkCmd != nil { - c.swrkSk.Close() - c.swrkSk = nil - c.swrkCmd.Wait() - c.swrkCmd = nil - } -} - -func (c *Criu) sendAndRecv(reqB []byte) ([]byte, int, error) { - cln := c.swrkSk - _, err := cln.Write(reqB) - if err != nil { - return nil, 0, err - } - - respB := make([]byte, 2*4096) - n, err := cln.Read(respB) - if err != nil { - return nil, 0, err - } - - return respB, n, nil -} - -func (c *Criu) doSwrk(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) error { - resp, err := c.doSwrkWithResp(reqType, opts, nfy) - if err != nil { - return err - } - respType := resp.GetType() - if respType != reqType { - return errors.New("unexpected responce") - } - - return nil -} - -func (c *Criu) doSwrkWithResp(reqType rpc.CriuReqType, opts *rpc.CriuOpts, nfy Notify) (*rpc.CriuResp, error) { - var resp *rpc.CriuResp - - req := rpc.CriuReq{ - Type: &reqType, - Opts: opts, - } - - if nfy != nil { - opts.NotifyScripts = proto.Bool(true) - } - - if c.swrkCmd == nil { - err := c.Prepare() - if err != nil { - return nil, err - } - - defer c.Cleanup() - } - - for { - reqB, err := proto.Marshal(&req) - if err != nil { - return nil, err - } - - respB, respS, err := c.sendAndRecv(reqB) - if err != nil { - return nil, err - } - - resp = &rpc.CriuResp{} - err = proto.Unmarshal(respB[:respS], resp) - if err != nil { - return nil, err - } - - if !resp.GetSuccess() { - return resp, fmt.Errorf("operation failed (msg:%s err:%d)", - resp.GetCrErrmsg(), resp.GetCrErrno()) - } - - respType := resp.GetType() - if respType != rpc.CriuReqType_NOTIFY { - break - } - if nfy == nil { - return resp, errors.New("unexpected notify") - } - - notify := resp.GetNotify() - switch notify.GetScript() { - case "pre-dump": - err = nfy.PreDump() - case "post-dump": - err = nfy.PostDump() - case "pre-restore": - err = nfy.PreRestore() - case "post-restore": - err = nfy.PostRestore(notify.GetPid()) - case "network-lock": - err = nfy.NetworkLock() - case "network-unlock": - err = nfy.NetworkUnlock() - case "setup-namespaces": - err = nfy.SetupNamespaces(notify.GetPid()) - case "post-setup-namespaces": - err = nfy.PostSetupNamespaces() - case "post-resume": - err = nfy.PostResume() - default: - err = nil - } - - if err != nil { - return resp, err - } - - req = rpc.CriuReq{ - Type: &respType, - NotifySuccess: proto.Bool(true), - } - } - - return resp, nil -} - -// Dump dumps a process -func (c *Criu) Dump(opts rpc.CriuOpts, nfy Notify) error { - return c.doSwrk(rpc.CriuReqType_DUMP, &opts, nfy) -} - -// Restore restores a process -func (c *Criu) Restore(opts rpc.CriuOpts, nfy Notify) error { - return c.doSwrk(rpc.CriuReqType_RESTORE, &opts, nfy) -} - -// PreDump does a pre-dump -func (c *Criu) PreDump(opts rpc.CriuOpts, nfy Notify) error { - return c.doSwrk(rpc.CriuReqType_PRE_DUMP, &opts, nfy) -} - -// StartPageServer starts the page server -func (c *Criu) StartPageServer(opts rpc.CriuOpts) error { - return c.doSwrk(rpc.CriuReqType_PAGE_SERVER, &opts, nil) -} - -// StartPageServerChld starts the page server and returns PID and port -func (c *Criu) StartPageServerChld(opts rpc.CriuOpts) (int, int, error) { - resp, err := c.doSwrkWithResp(rpc.CriuReqType_PAGE_SERVER_CHLD, &opts, nil) - if err != nil { - return 0, 0, err - } - - return int(resp.Ps.GetPid()), int(resp.Ps.GetPort()), nil -} - -// GetCriuVersion executes the VERSION RPC call and returns the version -// as an integer. Major * 10000 + Minor * 100 + SubLevel -func (c *Criu) GetCriuVersion() (int, error) { - resp, err := c.doSwrkWithResp(rpc.CriuReqType_VERSION, nil, nil) - if err != nil { - return 0, err - } - - if resp.GetType() != rpc.CriuReqType_VERSION { - return 0, fmt.Errorf("Unexpected CRIU RPC response") - } - - version := int(*resp.GetVersion().Major) * 10000 - version += int(*resp.GetVersion().Minor) * 100 - if resp.GetVersion().Sublevel != nil { - version += int(*resp.GetVersion().Sublevel) - } - - if resp.GetVersion().Gitid != nil { - // taken from runc: if it is a git release -> increase minor by 1 - version -= (version % 100) - version += 100 - } - - return version, nil -} - -// IsCriuAtLeast checks if the version is at least the same -// as the parameter version -func (c *Criu) IsCriuAtLeast(version int) (bool, error) { - criuVersion, err := c.GetCriuVersion() - if err != nil { - return false, err - } - - if criuVersion >= version { - return true, nil - } - - return false, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/Makefile libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/Makefile --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/Makefile 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -GO ?= go -CC ?= gcc -ifeq ($(GOPATH),) -export GOPATH := $(shell $(GO) env GOPATH) -endif -FIRST_GOPATH := $(firstword $(subst :, ,$(GOPATH))) -GOBIN := $(shell $(GO) env GOBIN) -ifeq ($(GOBIN),) - GOBIN := $(FIRST_GOPATH)/bin -endif - -all: build test phaul phaul-test - -lint: - @golint . test phaul -build: - @$(GO) build -v - -test/piggie: test/piggie.c - @$(CC) $^ -o $@ - -test/test: test/main.go - @$(GO) build -v -o test/test test/main.go - -test: test/test test/piggie - mkdir -p image - test/piggie - test/test dump `pidof piggie` image - test/test restore image - pkill -9 piggie || : - -phaul: - @cd phaul; go build -v - -test/phaul: test/phaul-main.go - @$(GO) build -v -o test/phaul test/phaul-main.go - -phaul-test: test/phaul test/piggie - rm -rf image - test/piggie - test/phaul `pidof piggie` - pkill -9 piggie || : - -clean: - @rm -f test/test test/piggie test/phaul - @rm -rf image - @rm -f rpc/rpc.proto - -install.tools: - if [ ! -x "$(GOBIN)/golint" ]; then \ - $(GO) get -u golang.org/x/lint/golint; \ - fi - -rpc/rpc.proto: - curl -s https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@ - -rpc/rpc.pb.go: rpc/rpc.proto - protoc --go_out=. $^ - -.PHONY: build test clean lint phaul diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/notify.go libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/notify.go --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/notify.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/notify.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -package criu - -//Notify interface -type Notify interface { - PreDump() error - PostDump() error - PreRestore() error - PostRestore(pid int32) error - NetworkLock() error - NetworkUnlock() error - SetupNamespaces(pid int32) error - PostSetupNamespaces() error - PostResume() error -} - -// NoNotify struct -type NoNotify struct { -} - -// PreDump NoNotify -func (c NoNotify) PreDump() error { - return nil -} - -// PostDump NoNotify -func (c NoNotify) PostDump() error { - return nil -} - -// PreRestore NoNotify -func (c NoNotify) PreRestore() error { - return nil -} - -// PostRestore NoNotify -func (c NoNotify) PostRestore(pid int32) error { - return nil -} - -// NetworkLock NoNotify -func (c NoNotify) NetworkLock() error { - return nil -} - -// NetworkUnlock NoNotify -func (c NoNotify) NetworkUnlock() error { - return nil -} - -// SetupNamespaces NoNotify -func (c NoNotify) SetupNamespaces(pid int32) error { - return nil -} - -// PostSetupNamespaces NoNotify -func (c NoNotify) PostSetupNamespaces() error { - return nil -} - -// PostResume NoNotify -func (c NoNotify) PostResume() error { - return nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/README.md libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/README.md --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -[![master](https://travis-ci.org/checkpoint-restore/go-criu.svg?branch=master)](https://travis-ci.org/checkpoint-restore/go-criu) - -## go-criu -- Go bindings for [CRIU](https://criu.org/) - -This repository provides Go bindings for CRIU. The code is based on the Go based PHaul -implementation from the CRIU repository. For easier inclusion into other Go projects the -CRIU Go bindings have been moved to this repository. - -The Go bindings provide an easy way to use the CRIU RPC calls from Go without the need -to set up all the infrastructure to make the actual RPC connection to CRIU. - -The following example would print the version of CRIU: -``` - c := criu.MakeCriu() - version, err := c.GetCriuVersion() - fmt.Println(version) -``` -or to just check if at least a certain CRIU version is installed: -``` - c := criu.MakeCriu() - result, err := c.IsCriuAtLeast(31100) -``` - -## Releases - -go-criu will carry the same version number as CRIU. This implies that each -go-criu release will pull in the necessary changes from CRIU before making a -release. - -The first go-criu release was 3.11 based on CRIU 3.11. - -## How to contribute - -While bug fixes can first be identified via an "issue", that is not required. -It's ok to just open up a PR with the fix, but make sure you include the same -information you would have included in an issue - like how to reproduce it. - -PRs for new features should include some background on what use cases the -new code is trying to address. When possible and when it makes sense, try to -break-up larger PRs into smaller ones - it's easier to review smaller -code changes. But only if those smaller ones make sense as stand-alone PRs. - -Regardless of the type of PR, all PRs should include: -* well documented code changes -* additional testcases. Ideally, they should fail w/o your code change applied -* documentation changes - -Squash your commits into logical pieces of work that might want to be reviewed -separate from the rest of the PRs. Ideally, each commit should implement a -single idea, and the PR branch should pass the tests at every commit. GitHub -makes it easy to review the cumulative effect of many commits; so, when in -doubt, use smaller commits. - -PRs that fix issues should include a reference like `Closes #XXXX` in the -commit message so that github will automatically close the referenced issue -when the PR is merged. - -Contributors must assert that they are in compliance with the [Developer -Certificate of Origin 1.1](http://developercertificate.org/). This is achieved -by adding a "Signed-off-by" line containing the contributor's name and e-mail -to every commit message. Your signature certifies that you wrote the patch or -otherwise have the right to pass it on as an open-source patch. - -### License - -The license of go-criu is the Apache 2.0 license. diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/rpc/rpc.pb.go libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/rpc/rpc.pb.go --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/rpc/rpc.pb.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/rpc/rpc.pb.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,1211 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: rpc/rpc.proto - -/* -Package rpc is a generated protocol buffer package. - -It is generated from these files: - rpc/rpc.proto - -It has these top-level messages: - CriuPageServerInfo - CriuVethPair - ExtMountMap - JoinNamespace - InheritFd - CgroupRoot - UnixSk - CriuOpts - CriuDumpResp - CriuRestoreResp - CriuNotify - CriuFeatures - CriuReq - CriuResp - CriuVersion -*/ -package rpc - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type CriuCgMode int32 - -const ( - CriuCgMode_IGNORE CriuCgMode = 0 - CriuCgMode_CG_NONE CriuCgMode = 1 - CriuCgMode_PROPS CriuCgMode = 2 - CriuCgMode_SOFT CriuCgMode = 3 - CriuCgMode_FULL CriuCgMode = 4 - CriuCgMode_STRICT CriuCgMode = 5 - CriuCgMode_DEFAULT CriuCgMode = 6 -) - -var CriuCgMode_name = map[int32]string{ - 0: "IGNORE", - 1: "CG_NONE", - 2: "PROPS", - 3: "SOFT", - 4: "FULL", - 5: "STRICT", - 6: "DEFAULT", -} -var CriuCgMode_value = map[string]int32{ - "IGNORE": 0, - "CG_NONE": 1, - "PROPS": 2, - "SOFT": 3, - "FULL": 4, - "STRICT": 5, - "DEFAULT": 6, -} - -func (x CriuCgMode) Enum() *CriuCgMode { - p := new(CriuCgMode) - *p = x - return p -} -func (x CriuCgMode) String() string { - return proto.EnumName(CriuCgMode_name, int32(x)) -} -func (x *CriuCgMode) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(CriuCgMode_value, data, "CriuCgMode") - if err != nil { - return err - } - *x = CriuCgMode(value) - return nil -} -func (CriuCgMode) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -type CriuReqType int32 - -const ( - CriuReqType_EMPTY CriuReqType = 0 - CriuReqType_DUMP CriuReqType = 1 - CriuReqType_RESTORE CriuReqType = 2 - CriuReqType_CHECK CriuReqType = 3 - CriuReqType_PRE_DUMP CriuReqType = 4 - CriuReqType_PAGE_SERVER CriuReqType = 5 - CriuReqType_NOTIFY CriuReqType = 6 - CriuReqType_CPUINFO_DUMP CriuReqType = 7 - CriuReqType_CPUINFO_CHECK CriuReqType = 8 - CriuReqType_FEATURE_CHECK CriuReqType = 9 - CriuReqType_VERSION CriuReqType = 10 - CriuReqType_WAIT_PID CriuReqType = 11 - CriuReqType_PAGE_SERVER_CHLD CriuReqType = 12 -) - -var CriuReqType_name = map[int32]string{ - 0: "EMPTY", - 1: "DUMP", - 2: "RESTORE", - 3: "CHECK", - 4: "PRE_DUMP", - 5: "PAGE_SERVER", - 6: "NOTIFY", - 7: "CPUINFO_DUMP", - 8: "CPUINFO_CHECK", - 9: "FEATURE_CHECK", - 10: "VERSION", - 11: "WAIT_PID", - 12: "PAGE_SERVER_CHLD", -} -var CriuReqType_value = map[string]int32{ - "EMPTY": 0, - "DUMP": 1, - "RESTORE": 2, - "CHECK": 3, - "PRE_DUMP": 4, - "PAGE_SERVER": 5, - "NOTIFY": 6, - "CPUINFO_DUMP": 7, - "CPUINFO_CHECK": 8, - "FEATURE_CHECK": 9, - "VERSION": 10, - "WAIT_PID": 11, - "PAGE_SERVER_CHLD": 12, -} - -func (x CriuReqType) Enum() *CriuReqType { - p := new(CriuReqType) - *p = x - return p -} -func (x CriuReqType) String() string { - return proto.EnumName(CriuReqType_name, int32(x)) -} -func (x *CriuReqType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(CriuReqType_value, data, "CriuReqType") - if err != nil { - return err - } - *x = CriuReqType(value) - return nil -} -func (CriuReqType) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -type CriuPageServerInfo struct { - Address *string `protobuf:"bytes,1,opt,name=address" json:"address,omitempty"` - Port *int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` - Pid *int32 `protobuf:"varint,3,opt,name=pid" json:"pid,omitempty"` - Fd *int32 `protobuf:"varint,4,opt,name=fd" json:"fd,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuPageServerInfo) Reset() { *m = CriuPageServerInfo{} } -func (m *CriuPageServerInfo) String() string { return proto.CompactTextString(m) } -func (*CriuPageServerInfo) ProtoMessage() {} -func (*CriuPageServerInfo) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -func (m *CriuPageServerInfo) GetAddress() string { - if m != nil && m.Address != nil { - return *m.Address - } - return "" -} - -func (m *CriuPageServerInfo) GetPort() int32 { - if m != nil && m.Port != nil { - return *m.Port - } - return 0 -} - -func (m *CriuPageServerInfo) GetPid() int32 { - if m != nil && m.Pid != nil { - return *m.Pid - } - return 0 -} - -func (m *CriuPageServerInfo) GetFd() int32 { - if m != nil && m.Fd != nil { - return *m.Fd - } - return 0 -} - -type CriuVethPair struct { - IfIn *string `protobuf:"bytes,1,req,name=if_in,json=ifIn" json:"if_in,omitempty"` - IfOut *string `protobuf:"bytes,2,req,name=if_out,json=ifOut" json:"if_out,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuVethPair) Reset() { *m = CriuVethPair{} } -func (m *CriuVethPair) String() string { return proto.CompactTextString(m) } -func (*CriuVethPair) ProtoMessage() {} -func (*CriuVethPair) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *CriuVethPair) GetIfIn() string { - if m != nil && m.IfIn != nil { - return *m.IfIn - } - return "" -} - -func (m *CriuVethPair) GetIfOut() string { - if m != nil && m.IfOut != nil { - return *m.IfOut - } - return "" -} - -type ExtMountMap struct { - Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` - Val *string `protobuf:"bytes,2,req,name=val" json:"val,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ExtMountMap) Reset() { *m = ExtMountMap{} } -func (m *ExtMountMap) String() string { return proto.CompactTextString(m) } -func (*ExtMountMap) ProtoMessage() {} -func (*ExtMountMap) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } - -func (m *ExtMountMap) GetKey() string { - if m != nil && m.Key != nil { - return *m.Key - } - return "" -} - -func (m *ExtMountMap) GetVal() string { - if m != nil && m.Val != nil { - return *m.Val - } - return "" -} - -type JoinNamespace struct { - Ns *string `protobuf:"bytes,1,req,name=ns" json:"ns,omitempty"` - NsFile *string `protobuf:"bytes,2,req,name=ns_file,json=nsFile" json:"ns_file,omitempty"` - ExtraOpt *string `protobuf:"bytes,3,opt,name=extra_opt,json=extraOpt" json:"extra_opt,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *JoinNamespace) Reset() { *m = JoinNamespace{} } -func (m *JoinNamespace) String() string { return proto.CompactTextString(m) } -func (*JoinNamespace) ProtoMessage() {} -func (*JoinNamespace) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *JoinNamespace) GetNs() string { - if m != nil && m.Ns != nil { - return *m.Ns - } - return "" -} - -func (m *JoinNamespace) GetNsFile() string { - if m != nil && m.NsFile != nil { - return *m.NsFile - } - return "" -} - -func (m *JoinNamespace) GetExtraOpt() string { - if m != nil && m.ExtraOpt != nil { - return *m.ExtraOpt - } - return "" -} - -type InheritFd struct { - Key *string `protobuf:"bytes,1,req,name=key" json:"key,omitempty"` - Fd *int32 `protobuf:"varint,2,req,name=fd" json:"fd,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *InheritFd) Reset() { *m = InheritFd{} } -func (m *InheritFd) String() string { return proto.CompactTextString(m) } -func (*InheritFd) ProtoMessage() {} -func (*InheritFd) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } - -func (m *InheritFd) GetKey() string { - if m != nil && m.Key != nil { - return *m.Key - } - return "" -} - -func (m *InheritFd) GetFd() int32 { - if m != nil && m.Fd != nil { - return *m.Fd - } - return 0 -} - -type CgroupRoot struct { - Ctrl *string `protobuf:"bytes,1,opt,name=ctrl" json:"ctrl,omitempty"` - Path *string `protobuf:"bytes,2,req,name=path" json:"path,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CgroupRoot) Reset() { *m = CgroupRoot{} } -func (m *CgroupRoot) String() string { return proto.CompactTextString(m) } -func (*CgroupRoot) ProtoMessage() {} -func (*CgroupRoot) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } - -func (m *CgroupRoot) GetCtrl() string { - if m != nil && m.Ctrl != nil { - return *m.Ctrl - } - return "" -} - -func (m *CgroupRoot) GetPath() string { - if m != nil && m.Path != nil { - return *m.Path - } - return "" -} - -type UnixSk struct { - Inode *uint32 `protobuf:"varint,1,req,name=inode" json:"inode,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *UnixSk) Reset() { *m = UnixSk{} } -func (m *UnixSk) String() string { return proto.CompactTextString(m) } -func (*UnixSk) ProtoMessage() {} -func (*UnixSk) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } - -func (m *UnixSk) GetInode() uint32 { - if m != nil && m.Inode != nil { - return *m.Inode - } - return 0 -} - -type CriuOpts struct { - ImagesDirFd *int32 `protobuf:"varint,1,req,name=images_dir_fd,json=imagesDirFd" json:"images_dir_fd,omitempty"` - Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` - LeaveRunning *bool `protobuf:"varint,3,opt,name=leave_running,json=leaveRunning" json:"leave_running,omitempty"` - ExtUnixSk *bool `protobuf:"varint,4,opt,name=ext_unix_sk,json=extUnixSk" json:"ext_unix_sk,omitempty"` - TcpEstablished *bool `protobuf:"varint,5,opt,name=tcp_established,json=tcpEstablished" json:"tcp_established,omitempty"` - EvasiveDevices *bool `protobuf:"varint,6,opt,name=evasive_devices,json=evasiveDevices" json:"evasive_devices,omitempty"` - ShellJob *bool `protobuf:"varint,7,opt,name=shell_job,json=shellJob" json:"shell_job,omitempty"` - FileLocks *bool `protobuf:"varint,8,opt,name=file_locks,json=fileLocks" json:"file_locks,omitempty"` - LogLevel *int32 `protobuf:"varint,9,opt,name=log_level,json=logLevel,def=2" json:"log_level,omitempty"` - LogFile *string `protobuf:"bytes,10,opt,name=log_file,json=logFile" json:"log_file,omitempty"` - Ps *CriuPageServerInfo `protobuf:"bytes,11,opt,name=ps" json:"ps,omitempty"` - NotifyScripts *bool `protobuf:"varint,12,opt,name=notify_scripts,json=notifyScripts" json:"notify_scripts,omitempty"` - Root *string `protobuf:"bytes,13,opt,name=root" json:"root,omitempty"` - ParentImg *string `protobuf:"bytes,14,opt,name=parent_img,json=parentImg" json:"parent_img,omitempty"` - TrackMem *bool `protobuf:"varint,15,opt,name=track_mem,json=trackMem" json:"track_mem,omitempty"` - AutoDedup *bool `protobuf:"varint,16,opt,name=auto_dedup,json=autoDedup" json:"auto_dedup,omitempty"` - WorkDirFd *int32 `protobuf:"varint,17,opt,name=work_dir_fd,json=workDirFd" json:"work_dir_fd,omitempty"` - LinkRemap *bool `protobuf:"varint,18,opt,name=link_remap,json=linkRemap" json:"link_remap,omitempty"` - Veths []*CriuVethPair `protobuf:"bytes,19,rep,name=veths" json:"veths,omitempty"` - CpuCap *uint32 `protobuf:"varint,20,opt,name=cpu_cap,json=cpuCap,def=4294967295" json:"cpu_cap,omitempty"` - ForceIrmap *bool `protobuf:"varint,21,opt,name=force_irmap,json=forceIrmap" json:"force_irmap,omitempty"` - ExecCmd []string `protobuf:"bytes,22,rep,name=exec_cmd,json=execCmd" json:"exec_cmd,omitempty"` - ExtMnt []*ExtMountMap `protobuf:"bytes,23,rep,name=ext_mnt,json=extMnt" json:"ext_mnt,omitempty"` - ManageCgroups *bool `protobuf:"varint,24,opt,name=manage_cgroups,json=manageCgroups" json:"manage_cgroups,omitempty"` - CgRoot []*CgroupRoot `protobuf:"bytes,25,rep,name=cg_root,json=cgRoot" json:"cg_root,omitempty"` - RstSibling *bool `protobuf:"varint,26,opt,name=rst_sibling,json=rstSibling" json:"rst_sibling,omitempty"` - InheritFd []*InheritFd `protobuf:"bytes,27,rep,name=inherit_fd,json=inheritFd" json:"inherit_fd,omitempty"` - AutoExtMnt *bool `protobuf:"varint,28,opt,name=auto_ext_mnt,json=autoExtMnt" json:"auto_ext_mnt,omitempty"` - ExtSharing *bool `protobuf:"varint,29,opt,name=ext_sharing,json=extSharing" json:"ext_sharing,omitempty"` - ExtMasters *bool `protobuf:"varint,30,opt,name=ext_masters,json=extMasters" json:"ext_masters,omitempty"` - SkipMnt []string `protobuf:"bytes,31,rep,name=skip_mnt,json=skipMnt" json:"skip_mnt,omitempty"` - EnableFs []string `protobuf:"bytes,32,rep,name=enable_fs,json=enableFs" json:"enable_fs,omitempty"` - UnixSkIno []*UnixSk `protobuf:"bytes,33,rep,name=unix_sk_ino,json=unixSkIno" json:"unix_sk_ino,omitempty"` - ManageCgroupsMode *CriuCgMode `protobuf:"varint,34,opt,name=manage_cgroups_mode,json=manageCgroupsMode,enum=CriuCgMode" json:"manage_cgroups_mode,omitempty"` - GhostLimit *uint32 `protobuf:"varint,35,opt,name=ghost_limit,json=ghostLimit,def=1048576" json:"ghost_limit,omitempty"` - IrmapScanPaths []string `protobuf:"bytes,36,rep,name=irmap_scan_paths,json=irmapScanPaths" json:"irmap_scan_paths,omitempty"` - External []string `protobuf:"bytes,37,rep,name=external" json:"external,omitempty"` - EmptyNs *uint32 `protobuf:"varint,38,opt,name=empty_ns,json=emptyNs" json:"empty_ns,omitempty"` - JoinNs []*JoinNamespace `protobuf:"bytes,39,rep,name=join_ns,json=joinNs" json:"join_ns,omitempty"` - CgroupProps *string `protobuf:"bytes,41,opt,name=cgroup_props,json=cgroupProps" json:"cgroup_props,omitempty"` - CgroupPropsFile *string `protobuf:"bytes,42,opt,name=cgroup_props_file,json=cgroupPropsFile" json:"cgroup_props_file,omitempty"` - CgroupDumpController []string `protobuf:"bytes,43,rep,name=cgroup_dump_controller,json=cgroupDumpController" json:"cgroup_dump_controller,omitempty"` - FreezeCgroup *string `protobuf:"bytes,44,opt,name=freeze_cgroup,json=freezeCgroup" json:"freeze_cgroup,omitempty"` - Timeout *uint32 `protobuf:"varint,45,opt,name=timeout" json:"timeout,omitempty"` - TcpSkipInFlight *bool `protobuf:"varint,46,opt,name=tcp_skip_in_flight,json=tcpSkipInFlight" json:"tcp_skip_in_flight,omitempty"` - WeakSysctls *bool `protobuf:"varint,47,opt,name=weak_sysctls,json=weakSysctls" json:"weak_sysctls,omitempty"` - LazyPages *bool `protobuf:"varint,48,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"` - StatusFd *int32 `protobuf:"varint,49,opt,name=status_fd,json=statusFd" json:"status_fd,omitempty"` - OrphanPtsMaster *bool `protobuf:"varint,50,opt,name=orphan_pts_master,json=orphanPtsMaster" json:"orphan_pts_master,omitempty"` - ConfigFile *string `protobuf:"bytes,51,opt,name=config_file,json=configFile" json:"config_file,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuOpts) Reset() { *m = CriuOpts{} } -func (m *CriuOpts) String() string { return proto.CompactTextString(m) } -func (*CriuOpts) ProtoMessage() {} -func (*CriuOpts) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } - -const Default_CriuOpts_LogLevel int32 = 2 -const Default_CriuOpts_CpuCap uint32 = 4294967295 -const Default_CriuOpts_GhostLimit uint32 = 1048576 - -func (m *CriuOpts) GetImagesDirFd() int32 { - if m != nil && m.ImagesDirFd != nil { - return *m.ImagesDirFd - } - return 0 -} - -func (m *CriuOpts) GetPid() int32 { - if m != nil && m.Pid != nil { - return *m.Pid - } - return 0 -} - -func (m *CriuOpts) GetLeaveRunning() bool { - if m != nil && m.LeaveRunning != nil { - return *m.LeaveRunning - } - return false -} - -func (m *CriuOpts) GetExtUnixSk() bool { - if m != nil && m.ExtUnixSk != nil { - return *m.ExtUnixSk - } - return false -} - -func (m *CriuOpts) GetTcpEstablished() bool { - if m != nil && m.TcpEstablished != nil { - return *m.TcpEstablished - } - return false -} - -func (m *CriuOpts) GetEvasiveDevices() bool { - if m != nil && m.EvasiveDevices != nil { - return *m.EvasiveDevices - } - return false -} - -func (m *CriuOpts) GetShellJob() bool { - if m != nil && m.ShellJob != nil { - return *m.ShellJob - } - return false -} - -func (m *CriuOpts) GetFileLocks() bool { - if m != nil && m.FileLocks != nil { - return *m.FileLocks - } - return false -} - -func (m *CriuOpts) GetLogLevel() int32 { - if m != nil && m.LogLevel != nil { - return *m.LogLevel - } - return Default_CriuOpts_LogLevel -} - -func (m *CriuOpts) GetLogFile() string { - if m != nil && m.LogFile != nil { - return *m.LogFile - } - return "" -} - -func (m *CriuOpts) GetPs() *CriuPageServerInfo { - if m != nil { - return m.Ps - } - return nil -} - -func (m *CriuOpts) GetNotifyScripts() bool { - if m != nil && m.NotifyScripts != nil { - return *m.NotifyScripts - } - return false -} - -func (m *CriuOpts) GetRoot() string { - if m != nil && m.Root != nil { - return *m.Root - } - return "" -} - -func (m *CriuOpts) GetParentImg() string { - if m != nil && m.ParentImg != nil { - return *m.ParentImg - } - return "" -} - -func (m *CriuOpts) GetTrackMem() bool { - if m != nil && m.TrackMem != nil { - return *m.TrackMem - } - return false -} - -func (m *CriuOpts) GetAutoDedup() bool { - if m != nil && m.AutoDedup != nil { - return *m.AutoDedup - } - return false -} - -func (m *CriuOpts) GetWorkDirFd() int32 { - if m != nil && m.WorkDirFd != nil { - return *m.WorkDirFd - } - return 0 -} - -func (m *CriuOpts) GetLinkRemap() bool { - if m != nil && m.LinkRemap != nil { - return *m.LinkRemap - } - return false -} - -func (m *CriuOpts) GetVeths() []*CriuVethPair { - if m != nil { - return m.Veths - } - return nil -} - -func (m *CriuOpts) GetCpuCap() uint32 { - if m != nil && m.CpuCap != nil { - return *m.CpuCap - } - return Default_CriuOpts_CpuCap -} - -func (m *CriuOpts) GetForceIrmap() bool { - if m != nil && m.ForceIrmap != nil { - return *m.ForceIrmap - } - return false -} - -func (m *CriuOpts) GetExecCmd() []string { - if m != nil { - return m.ExecCmd - } - return nil -} - -func (m *CriuOpts) GetExtMnt() []*ExtMountMap { - if m != nil { - return m.ExtMnt - } - return nil -} - -func (m *CriuOpts) GetManageCgroups() bool { - if m != nil && m.ManageCgroups != nil { - return *m.ManageCgroups - } - return false -} - -func (m *CriuOpts) GetCgRoot() []*CgroupRoot { - if m != nil { - return m.CgRoot - } - return nil -} - -func (m *CriuOpts) GetRstSibling() bool { - if m != nil && m.RstSibling != nil { - return *m.RstSibling - } - return false -} - -func (m *CriuOpts) GetInheritFd() []*InheritFd { - if m != nil { - return m.InheritFd - } - return nil -} - -func (m *CriuOpts) GetAutoExtMnt() bool { - if m != nil && m.AutoExtMnt != nil { - return *m.AutoExtMnt - } - return false -} - -func (m *CriuOpts) GetExtSharing() bool { - if m != nil && m.ExtSharing != nil { - return *m.ExtSharing - } - return false -} - -func (m *CriuOpts) GetExtMasters() bool { - if m != nil && m.ExtMasters != nil { - return *m.ExtMasters - } - return false -} - -func (m *CriuOpts) GetSkipMnt() []string { - if m != nil { - return m.SkipMnt - } - return nil -} - -func (m *CriuOpts) GetEnableFs() []string { - if m != nil { - return m.EnableFs - } - return nil -} - -func (m *CriuOpts) GetUnixSkIno() []*UnixSk { - if m != nil { - return m.UnixSkIno - } - return nil -} - -func (m *CriuOpts) GetManageCgroupsMode() CriuCgMode { - if m != nil && m.ManageCgroupsMode != nil { - return *m.ManageCgroupsMode - } - return CriuCgMode_IGNORE -} - -func (m *CriuOpts) GetGhostLimit() uint32 { - if m != nil && m.GhostLimit != nil { - return *m.GhostLimit - } - return Default_CriuOpts_GhostLimit -} - -func (m *CriuOpts) GetIrmapScanPaths() []string { - if m != nil { - return m.IrmapScanPaths - } - return nil -} - -func (m *CriuOpts) GetExternal() []string { - if m != nil { - return m.External - } - return nil -} - -func (m *CriuOpts) GetEmptyNs() uint32 { - if m != nil && m.EmptyNs != nil { - return *m.EmptyNs - } - return 0 -} - -func (m *CriuOpts) GetJoinNs() []*JoinNamespace { - if m != nil { - return m.JoinNs - } - return nil -} - -func (m *CriuOpts) GetCgroupProps() string { - if m != nil && m.CgroupProps != nil { - return *m.CgroupProps - } - return "" -} - -func (m *CriuOpts) GetCgroupPropsFile() string { - if m != nil && m.CgroupPropsFile != nil { - return *m.CgroupPropsFile - } - return "" -} - -func (m *CriuOpts) GetCgroupDumpController() []string { - if m != nil { - return m.CgroupDumpController - } - return nil -} - -func (m *CriuOpts) GetFreezeCgroup() string { - if m != nil && m.FreezeCgroup != nil { - return *m.FreezeCgroup - } - return "" -} - -func (m *CriuOpts) GetTimeout() uint32 { - if m != nil && m.Timeout != nil { - return *m.Timeout - } - return 0 -} - -func (m *CriuOpts) GetTcpSkipInFlight() bool { - if m != nil && m.TcpSkipInFlight != nil { - return *m.TcpSkipInFlight - } - return false -} - -func (m *CriuOpts) GetWeakSysctls() bool { - if m != nil && m.WeakSysctls != nil { - return *m.WeakSysctls - } - return false -} - -func (m *CriuOpts) GetLazyPages() bool { - if m != nil && m.LazyPages != nil { - return *m.LazyPages - } - return false -} - -func (m *CriuOpts) GetStatusFd() int32 { - if m != nil && m.StatusFd != nil { - return *m.StatusFd - } - return 0 -} - -func (m *CriuOpts) GetOrphanPtsMaster() bool { - if m != nil && m.OrphanPtsMaster != nil { - return *m.OrphanPtsMaster - } - return false -} - -func (m *CriuOpts) GetConfigFile() string { - if m != nil && m.ConfigFile != nil { - return *m.ConfigFile - } - return "" -} - -type CriuDumpResp struct { - Restored *bool `protobuf:"varint,1,opt,name=restored" json:"restored,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuDumpResp) Reset() { *m = CriuDumpResp{} } -func (m *CriuDumpResp) String() string { return proto.CompactTextString(m) } -func (*CriuDumpResp) ProtoMessage() {} -func (*CriuDumpResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } - -func (m *CriuDumpResp) GetRestored() bool { - if m != nil && m.Restored != nil { - return *m.Restored - } - return false -} - -type CriuRestoreResp struct { - Pid *int32 `protobuf:"varint,1,req,name=pid" json:"pid,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuRestoreResp) Reset() { *m = CriuRestoreResp{} } -func (m *CriuRestoreResp) String() string { return proto.CompactTextString(m) } -func (*CriuRestoreResp) ProtoMessage() {} -func (*CriuRestoreResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } - -func (m *CriuRestoreResp) GetPid() int32 { - if m != nil && m.Pid != nil { - return *m.Pid - } - return 0 -} - -type CriuNotify struct { - Script *string `protobuf:"bytes,1,opt,name=script" json:"script,omitempty"` - Pid *int32 `protobuf:"varint,2,opt,name=pid" json:"pid,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuNotify) Reset() { *m = CriuNotify{} } -func (m *CriuNotify) String() string { return proto.CompactTextString(m) } -func (*CriuNotify) ProtoMessage() {} -func (*CriuNotify) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } - -func (m *CriuNotify) GetScript() string { - if m != nil && m.Script != nil { - return *m.Script - } - return "" -} - -func (m *CriuNotify) GetPid() int32 { - if m != nil && m.Pid != nil { - return *m.Pid - } - return 0 -} - -// -// List of features which can queried via -// CRIU_REQ_TYPE__FEATURE_CHECK -type CriuFeatures struct { - MemTrack *bool `protobuf:"varint,1,opt,name=mem_track,json=memTrack" json:"mem_track,omitempty"` - LazyPages *bool `protobuf:"varint,2,opt,name=lazy_pages,json=lazyPages" json:"lazy_pages,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuFeatures) Reset() { *m = CriuFeatures{} } -func (m *CriuFeatures) String() string { return proto.CompactTextString(m) } -func (*CriuFeatures) ProtoMessage() {} -func (*CriuFeatures) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } - -func (m *CriuFeatures) GetMemTrack() bool { - if m != nil && m.MemTrack != nil { - return *m.MemTrack - } - return false -} - -func (m *CriuFeatures) GetLazyPages() bool { - if m != nil && m.LazyPages != nil { - return *m.LazyPages - } - return false -} - -type CriuReq struct { - Type *CriuReqType `protobuf:"varint,1,req,name=type,enum=CriuReqType" json:"type,omitempty"` - Opts *CriuOpts `protobuf:"bytes,2,opt,name=opts" json:"opts,omitempty"` - NotifySuccess *bool `protobuf:"varint,3,opt,name=notify_success,json=notifySuccess" json:"notify_success,omitempty"` - // - // When set service won't close the connection but - // will wait for more req-s to appear. Works not - // for all request types. - KeepOpen *bool `protobuf:"varint,4,opt,name=keep_open,json=keepOpen" json:"keep_open,omitempty"` - // - // 'features' can be used to query which features - // are supported by the installed criu/kernel - // via RPC. - Features *CriuFeatures `protobuf:"bytes,5,opt,name=features" json:"features,omitempty"` - // 'pid' is used for WAIT_PID - Pid *uint32 `protobuf:"varint,6,opt,name=pid" json:"pid,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuReq) Reset() { *m = CriuReq{} } -func (m *CriuReq) String() string { return proto.CompactTextString(m) } -func (*CriuReq) ProtoMessage() {} -func (*CriuReq) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } - -func (m *CriuReq) GetType() CriuReqType { - if m != nil && m.Type != nil { - return *m.Type - } - return CriuReqType_EMPTY -} - -func (m *CriuReq) GetOpts() *CriuOpts { - if m != nil { - return m.Opts - } - return nil -} - -func (m *CriuReq) GetNotifySuccess() bool { - if m != nil && m.NotifySuccess != nil { - return *m.NotifySuccess - } - return false -} - -func (m *CriuReq) GetKeepOpen() bool { - if m != nil && m.KeepOpen != nil { - return *m.KeepOpen - } - return false -} - -func (m *CriuReq) GetFeatures() *CriuFeatures { - if m != nil { - return m.Features - } - return nil -} - -func (m *CriuReq) GetPid() uint32 { - if m != nil && m.Pid != nil { - return *m.Pid - } - return 0 -} - -type CriuResp struct { - Type *CriuReqType `protobuf:"varint,1,req,name=type,enum=CriuReqType" json:"type,omitempty"` - Success *bool `protobuf:"varint,2,req,name=success" json:"success,omitempty"` - Dump *CriuDumpResp `protobuf:"bytes,3,opt,name=dump" json:"dump,omitempty"` - Restore *CriuRestoreResp `protobuf:"bytes,4,opt,name=restore" json:"restore,omitempty"` - Notify *CriuNotify `protobuf:"bytes,5,opt,name=notify" json:"notify,omitempty"` - Ps *CriuPageServerInfo `protobuf:"bytes,6,opt,name=ps" json:"ps,omitempty"` - CrErrno *int32 `protobuf:"varint,7,opt,name=cr_errno,json=crErrno" json:"cr_errno,omitempty"` - Features *CriuFeatures `protobuf:"bytes,8,opt,name=features" json:"features,omitempty"` - CrErrmsg *string `protobuf:"bytes,9,opt,name=cr_errmsg,json=crErrmsg" json:"cr_errmsg,omitempty"` - Version *CriuVersion `protobuf:"bytes,10,opt,name=version" json:"version,omitempty"` - Status *int32 `protobuf:"varint,11,opt,name=status" json:"status,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuResp) Reset() { *m = CriuResp{} } -func (m *CriuResp) String() string { return proto.CompactTextString(m) } -func (*CriuResp) ProtoMessage() {} -func (*CriuResp) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{13} } - -func (m *CriuResp) GetType() CriuReqType { - if m != nil && m.Type != nil { - return *m.Type - } - return CriuReqType_EMPTY -} - -func (m *CriuResp) GetSuccess() bool { - if m != nil && m.Success != nil { - return *m.Success - } - return false -} - -func (m *CriuResp) GetDump() *CriuDumpResp { - if m != nil { - return m.Dump - } - return nil -} - -func (m *CriuResp) GetRestore() *CriuRestoreResp { - if m != nil { - return m.Restore - } - return nil -} - -func (m *CriuResp) GetNotify() *CriuNotify { - if m != nil { - return m.Notify - } - return nil -} - -func (m *CriuResp) GetPs() *CriuPageServerInfo { - if m != nil { - return m.Ps - } - return nil -} - -func (m *CriuResp) GetCrErrno() int32 { - if m != nil && m.CrErrno != nil { - return *m.CrErrno - } - return 0 -} - -func (m *CriuResp) GetFeatures() *CriuFeatures { - if m != nil { - return m.Features - } - return nil -} - -func (m *CriuResp) GetCrErrmsg() string { - if m != nil && m.CrErrmsg != nil { - return *m.CrErrmsg - } - return "" -} - -func (m *CriuResp) GetVersion() *CriuVersion { - if m != nil { - return m.Version - } - return nil -} - -func (m *CriuResp) GetStatus() int32 { - if m != nil && m.Status != nil { - return *m.Status - } - return 0 -} - -// Answer for criu_req_type.VERSION requests -type CriuVersion struct { - Major *int32 `protobuf:"varint,1,req,name=major" json:"major,omitempty"` - Minor *int32 `protobuf:"varint,2,req,name=minor" json:"minor,omitempty"` - Gitid *string `protobuf:"bytes,3,opt,name=gitid" json:"gitid,omitempty"` - Sublevel *int32 `protobuf:"varint,4,opt,name=sublevel" json:"sublevel,omitempty"` - Extra *int32 `protobuf:"varint,5,opt,name=extra" json:"extra,omitempty"` - Name *string `protobuf:"bytes,6,opt,name=name" json:"name,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CriuVersion) Reset() { *m = CriuVersion{} } -func (m *CriuVersion) String() string { return proto.CompactTextString(m) } -func (*CriuVersion) ProtoMessage() {} -func (*CriuVersion) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{14} } - -func (m *CriuVersion) GetMajor() int32 { - if m != nil && m.Major != nil { - return *m.Major - } - return 0 -} - -func (m *CriuVersion) GetMinor() int32 { - if m != nil && m.Minor != nil { - return *m.Minor - } - return 0 -} - -func (m *CriuVersion) GetGitid() string { - if m != nil && m.Gitid != nil { - return *m.Gitid - } - return "" -} - -func (m *CriuVersion) GetSublevel() int32 { - if m != nil && m.Sublevel != nil { - return *m.Sublevel - } - return 0 -} - -func (m *CriuVersion) GetExtra() int32 { - if m != nil && m.Extra != nil { - return *m.Extra - } - return 0 -} - -func (m *CriuVersion) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func init() { - proto.RegisterType((*CriuPageServerInfo)(nil), "criu_page_server_info") - proto.RegisterType((*CriuVethPair)(nil), "criu_veth_pair") - proto.RegisterType((*ExtMountMap)(nil), "ext_mount_map") - proto.RegisterType((*JoinNamespace)(nil), "join_namespace") - proto.RegisterType((*InheritFd)(nil), "inherit_fd") - proto.RegisterType((*CgroupRoot)(nil), "cgroup_root") - proto.RegisterType((*UnixSk)(nil), "unix_sk") - proto.RegisterType((*CriuOpts)(nil), "criu_opts") - proto.RegisterType((*CriuDumpResp)(nil), "criu_dump_resp") - proto.RegisterType((*CriuRestoreResp)(nil), "criu_restore_resp") - proto.RegisterType((*CriuNotify)(nil), "criu_notify") - proto.RegisterType((*CriuFeatures)(nil), "criu_features") - proto.RegisterType((*CriuReq)(nil), "criu_req") - proto.RegisterType((*CriuResp)(nil), "criu_resp") - proto.RegisterType((*CriuVersion)(nil), "criu_version") - proto.RegisterEnum("CriuCgMode", CriuCgMode_name, CriuCgMode_value) - proto.RegisterEnum("CriuReqType", CriuReqType_name, CriuReqType_value) -} - -func init() { proto.RegisterFile("rpc/rpc.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 1835 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x56, 0xeb, 0x72, 0x5b, 0xb7, - 0x11, 0x0e, 0x29, 0xf1, 0x06, 0x5e, 0x7c, 0x0c, 0x5f, 0x02, 0xc7, 0xb5, 0xad, 0xd0, 0x51, 0xa2, - 0x2a, 0x2e, 0x93, 0x30, 0x76, 0x5c, 0x67, 0xda, 0x1f, 0x1e, 0x8a, 0x74, 0xd8, 0x48, 0x22, 0x07, - 0xa4, 0xdc, 0xc9, 0x2f, 0xcc, 0xd1, 0x39, 0x20, 0x05, 0xf3, 0xdc, 0x0a, 0x80, 0x8a, 0xe4, 0x97, - 0xe8, 0xbf, 0x3e, 0x57, 0xde, 0xa4, 0xaf, 0xd0, 0xd9, 0x05, 0x28, 0x4b, 0x49, 0x66, 0xd2, 0x7f, - 0xd8, 0x0f, 0xbb, 0xc0, 0xde, 0x77, 0x49, 0x5b, 0x17, 0xd1, 0x57, 0xba, 0x88, 0x7a, 0x85, 0xce, - 0x6d, 0xde, 0x5d, 0x92, 0x7b, 0x91, 0x56, 0x6b, 0x51, 0x84, 0x4b, 0x29, 0x8c, 0xd4, 0xe7, 0x52, - 0x0b, 0x95, 0x2d, 0x72, 0xca, 0x48, 0x2d, 0x8c, 0x63, 0x2d, 0x8d, 0x61, 0xa5, 0x9d, 0xd2, 0x5e, - 0x83, 0x6f, 0x48, 0x4a, 0xc9, 0x76, 0x91, 0x6b, 0xcb, 0xca, 0x3b, 0xa5, 0xbd, 0x0a, 0xc7, 0x33, - 0x0d, 0xc8, 0x56, 0xa1, 0x62, 0xb6, 0x85, 0x10, 0x1c, 0x69, 0x87, 0x94, 0x17, 0x31, 0xdb, 0x46, - 0xa0, 0xbc, 0x88, 0xbb, 0x7f, 0x23, 0x1d, 0xfc, 0xe8, 0x5c, 0xda, 0x33, 0x51, 0x84, 0x4a, 0xd3, - 0x3b, 0xa4, 0xa2, 0x16, 0x42, 0x65, 0xac, 0xb4, 0x53, 0xde, 0x6b, 0xf0, 0x6d, 0xb5, 0x18, 0x67, - 0xf4, 0x1e, 0xa9, 0xaa, 0x85, 0xc8, 0xd7, 0xf0, 0x3c, 0xa0, 0x15, 0xb5, 0x98, 0xac, 0x6d, 0xf7, - 0x5b, 0xd2, 0x96, 0x17, 0x56, 0xa4, 0xf9, 0x3a, 0xb3, 0x22, 0x0d, 0x0b, 0xf8, 0x70, 0x25, 0x2f, - 0xbd, 0x28, 0x1c, 0x01, 0x39, 0x0f, 0x13, 0x2f, 0x06, 0xc7, 0xee, 0x5b, 0xd2, 0x79, 0x97, 0xab, - 0x4c, 0x64, 0x61, 0x2a, 0x4d, 0x11, 0x46, 0x12, 0x94, 0xca, 0x8c, 0x17, 0x2a, 0x67, 0x86, 0x7e, - 0x4c, 0x6a, 0x99, 0x11, 0x0b, 0x95, 0x48, 0x2f, 0x57, 0xcd, 0xcc, 0x48, 0x25, 0x92, 0x3e, 0x24, - 0x0d, 0x79, 0x61, 0x75, 0x28, 0xf2, 0xc2, 0xa2, 0x55, 0x0d, 0x5e, 0x47, 0x60, 0x52, 0xd8, 0x6e, - 0x8f, 0x10, 0x95, 0x9d, 0x49, 0xad, 0xac, 0x58, 0xc4, 0xbf, 0xa3, 0x89, 0x33, 0x1d, 0x1e, 0x74, - 0xa6, 0xbf, 0x20, 0xcd, 0x68, 0xa9, 0xf3, 0x75, 0x21, 0x74, 0x9e, 0x5b, 0xf0, 0x5f, 0x64, 0x75, - 0xe2, 0xdd, 0x8a, 0x67, 0xf4, 0x69, 0x68, 0xcf, 0xbc, 0x16, 0x78, 0xee, 0x3e, 0x21, 0xb5, 0x75, - 0xa6, 0x2e, 0x84, 0x59, 0xd1, 0xbb, 0xa4, 0xa2, 0xb2, 0x3c, 0x96, 0xf8, 0x4b, 0x9b, 0x3b, 0xa2, - 0xfb, 0xdf, 0x36, 0x69, 0xa0, 0x4f, 0xf3, 0xc2, 0x1a, 0xda, 0x25, 0x6d, 0x95, 0x86, 0x4b, 0x69, - 0x44, 0xac, 0xb4, 0x58, 0xc4, 0xc8, 0x5b, 0xe1, 0x4d, 0x07, 0x1e, 0x28, 0x3d, 0x8a, 0x37, 0x61, - 0x2a, 0x7f, 0x08, 0xd3, 0x53, 0xd2, 0x4e, 0x64, 0x78, 0x2e, 0x85, 0x5e, 0x67, 0x99, 0xca, 0x96, - 0x68, 0x6c, 0x9d, 0xb7, 0x10, 0xe4, 0x0e, 0xa3, 0x8f, 0x49, 0x13, 0xbc, 0xef, 0xb5, 0xc1, 0xa0, - 0xd6, 0x39, 0x38, 0xe8, 0x24, 0x53, 0x17, 0xb3, 0x15, 0xfd, 0x82, 0xdc, 0xb2, 0x51, 0x21, 0xa4, - 0xb1, 0xe1, 0x69, 0xa2, 0xcc, 0x99, 0x8c, 0x59, 0x05, 0x79, 0x3a, 0x36, 0x2a, 0x86, 0x1f, 0x50, - 0x60, 0x94, 0xe7, 0xa1, 0x51, 0xe7, 0x52, 0xc4, 0xf2, 0x5c, 0x45, 0xd2, 0xb0, 0xaa, 0x63, 0xf4, - 0xf0, 0x81, 0x43, 0xc1, 0xff, 0xe6, 0x4c, 0x26, 0x89, 0x78, 0x97, 0x9f, 0xb2, 0x1a, 0xb2, 0xd4, - 0x11, 0xf8, 0x47, 0x7e, 0x4a, 0x1f, 0x11, 0x02, 0x21, 0x13, 0x49, 0x1e, 0xad, 0x0c, 0xab, 0x3b, - 0x6d, 0x00, 0x39, 0x04, 0x80, 0x3e, 0x26, 0x8d, 0x24, 0x5f, 0x8a, 0x44, 0x9e, 0xcb, 0x84, 0x35, - 0xc0, 0xd4, 0xef, 0x4b, 0x7d, 0x5e, 0x4f, 0xf2, 0xe5, 0x21, 0x40, 0xf4, 0x01, 0x81, 0xb3, 0x8b, - 0x3a, 0x71, 0xa9, 0x9d, 0xe4, 0x4b, 0x0c, 0xfb, 0xe7, 0xa4, 0x5c, 0x18, 0xd6, 0xdc, 0x29, 0xed, - 0x35, 0xfb, 0xf7, 0x7b, 0xbf, 0x5b, 0x18, 0xbc, 0x5c, 0x18, 0xba, 0x4b, 0x3a, 0x59, 0x6e, 0xd5, - 0xe2, 0x52, 0x98, 0x48, 0xab, 0xc2, 0x1a, 0xd6, 0x42, 0x2d, 0xda, 0x0e, 0x9d, 0x39, 0x10, 0xa2, - 0x0a, 0x11, 0x67, 0x6d, 0x17, 0x69, 0x8c, 0xfe, 0x23, 0x42, 0x8a, 0x50, 0xcb, 0xcc, 0x0a, 0x95, - 0x2e, 0x59, 0x07, 0x6f, 0x1a, 0x0e, 0x19, 0xa7, 0x4b, 0x30, 0xdc, 0xea, 0x30, 0x5a, 0x89, 0x54, - 0xa6, 0xec, 0x96, 0x33, 0x1c, 0x81, 0x23, 0x99, 0x82, 0x6c, 0xb8, 0xb6, 0xb9, 0x88, 0x65, 0xbc, - 0x2e, 0x58, 0xe0, 0x0c, 0x07, 0xe4, 0x00, 0x00, 0x08, 0xd3, 0xcf, 0xb9, 0x5e, 0x6d, 0xe2, 0x7f, - 0x1b, 0xa3, 0xdc, 0x00, 0xc8, 0x45, 0xff, 0x11, 0x21, 0x89, 0xca, 0x56, 0x42, 0xcb, 0x34, 0x2c, - 0x18, 0x75, 0xe2, 0x80, 0x70, 0x00, 0xe8, 0x2e, 0xa9, 0x40, 0x71, 0x1a, 0x76, 0x67, 0x67, 0x6b, - 0xaf, 0xd9, 0xbf, 0xd5, 0xbb, 0x59, 0xaf, 0xdc, 0xdd, 0xd2, 0xa7, 0xa4, 0x16, 0x15, 0x6b, 0x11, - 0x85, 0x05, 0xbb, 0xbb, 0x53, 0xda, 0x6b, 0x7f, 0x4f, 0x9e, 0xf7, 0x5f, 0x3d, 0x7f, 0xf5, 0xdd, - 0xcb, 0xfe, 0xab, 0x17, 0xbc, 0x1a, 0x15, 0xeb, 0x41, 0x58, 0xd0, 0x27, 0xa4, 0xb9, 0xc8, 0x75, - 0x24, 0x85, 0xd2, 0xf0, 0xd7, 0x3d, 0xfc, 0x8b, 0x20, 0x34, 0x06, 0x04, 0x82, 0x20, 0x2f, 0x64, - 0x24, 0xa2, 0x34, 0x66, 0xf7, 0x77, 0xb6, 0x20, 0x08, 0x40, 0x0f, 0x52, 0x48, 0x92, 0x1a, 0xd6, - 0x7a, 0x66, 0xd9, 0xc7, 0xa8, 0x49, 0xa7, 0x77, 0xa3, 0xf6, 0x79, 0x55, 0x5e, 0xd8, 0xa3, 0xcc, - 0x42, 0x14, 0xd2, 0x30, 0x83, 0xf8, 0xb8, 0xf2, 0x32, 0x8c, 0xb9, 0x28, 0x38, 0x74, 0xe0, 0x40, - 0xba, 0x4b, 0x6a, 0xd1, 0x12, 0x4b, 0x8f, 0x3d, 0xc0, 0xf7, 0x5a, 0xbd, 0x6b, 0xe5, 0xc8, 0xab, - 0xd1, 0x92, 0x43, 0x60, 0x9e, 0x90, 0xa6, 0x36, 0x56, 0x18, 0x75, 0x9a, 0x40, 0x1d, 0x7c, 0xe2, - 0x54, 0xd6, 0xc6, 0xce, 0x1c, 0x42, 0xf7, 0xaf, 0x97, 0x3d, 0x7b, 0x88, 0x4f, 0x35, 0x7b, 0x1f, - 0x20, 0xde, 0xf0, 0xe7, 0x51, 0x4c, 0x77, 0x48, 0x0b, 0x23, 0xb5, 0x31, 0xe4, 0x4f, 0xee, 0x35, - 0xc0, 0x86, 0x4e, 0xf9, 0x27, 0xae, 0xa6, 0xcc, 0x59, 0xa8, 0xe1, 0xbb, 0x47, 0x8e, 0x41, 0x5e, - 0xd8, 0x99, 0x43, 0x36, 0x0c, 0x69, 0x68, 0xac, 0xd4, 0x86, 0x3d, 0xbe, 0x62, 0x38, 0x72, 0x08, - 0xb8, 0xd0, 0xac, 0x54, 0x81, 0xef, 0x3f, 0x71, 0x2e, 0x04, 0x1a, 0x1e, 0x87, 0xf6, 0x95, 0x85, - 0xa7, 0x89, 0x14, 0x0b, 0xc3, 0x76, 0xf0, 0xae, 0xee, 0x80, 0x91, 0xa1, 0x7b, 0xa4, 0xe9, 0x2b, - 0x59, 0xa8, 0x2c, 0x67, 0x9f, 0xa2, 0x21, 0xf5, 0x9e, 0xc7, 0x78, 0x63, 0x8d, 0x45, 0x3d, 0xce, - 0x72, 0xfa, 0x77, 0x72, 0xe7, 0xa6, 0x83, 0x45, 0x0a, 0x4d, 0xa8, 0xbb, 0x53, 0xda, 0xeb, 0xf4, - 0xdb, 0x2e, 0x3f, 0xa2, 0x25, 0x82, 0xfc, 0xf6, 0x0d, 0xa7, 0x1f, 0xe5, 0xb1, 0x84, 0x8f, 0x96, - 0x67, 0xb9, 0xb1, 0x22, 0x51, 0xa9, 0xb2, 0xec, 0x29, 0x66, 0x4b, 0xed, 0x9b, 0xaf, 0x9f, 0xff, - 0xf5, 0xc5, 0xcb, 0xef, 0x38, 0xc1, 0xbb, 0x43, 0xb8, 0xa2, 0x7b, 0x24, 0xc0, 0x44, 0x11, 0x26, - 0x0a, 0x33, 0x01, 0xdd, 0xcf, 0xb0, 0xcf, 0x50, 0xed, 0x0e, 0xe2, 0xb3, 0x28, 0xcc, 0xa6, 0x80, - 0xd2, 0x4f, 0x20, 0x6f, 0xac, 0xd4, 0x59, 0x98, 0xb0, 0x5d, 0x6f, 0x98, 0xa7, 0x31, 0xa7, 0xd2, - 0xc2, 0x5e, 0x8a, 0xcc, 0xb0, 0xcf, 0xe1, 0x33, 0x5e, 0x43, 0xfa, 0x18, 0x6c, 0xae, 0xb9, 0x51, - 0x60, 0xd8, 0x17, 0x3e, 0xbb, 0x6f, 0x8e, 0x06, 0x5e, 0x05, 0xfa, 0xd8, 0xd0, 0x4f, 0x49, 0xcb, - 0x67, 0x47, 0xa1, 0xf3, 0xc2, 0xb0, 0x3f, 0x63, 0x85, 0xfa, 0x06, 0x3e, 0x05, 0x88, 0xee, 0x93, - 0xdb, 0xd7, 0x59, 0x5c, 0x27, 0xd9, 0x47, 0xbe, 0x5b, 0xd7, 0xf8, 0xb0, 0xa3, 0x3c, 0x27, 0xf7, - 0x3d, 0x6f, 0xbc, 0x4e, 0x0b, 0x11, 0xe5, 0x99, 0xd5, 0x79, 0x92, 0x48, 0xcd, 0xbe, 0x44, 0xed, - 0xef, 0xba, 0xdb, 0x83, 0x75, 0x5a, 0x0c, 0xae, 0xee, 0xa0, 0x2b, 0x2f, 0xb4, 0x94, 0xef, 0x37, - 0x8e, 0x67, 0xcf, 0xf0, 0xf5, 0x96, 0x03, 0x9d, 0x8f, 0x61, 0x42, 0x5b, 0x95, 0x4a, 0x98, 0x95, - 0x7f, 0x71, 0xd6, 0x7a, 0x92, 0x7e, 0x49, 0x28, 0xf4, 0x63, 0xcc, 0x0e, 0x95, 0x89, 0x45, 0xa2, - 0x96, 0x67, 0x96, 0xf5, 0x30, 0x83, 0xa0, 0x53, 0xcf, 0x56, 0xaa, 0x18, 0x67, 0x23, 0x84, 0xc1, - 0xe0, 0x9f, 0x65, 0xb8, 0x12, 0xe6, 0xd2, 0x44, 0x36, 0x31, 0xec, 0x2b, 0x64, 0x6b, 0x02, 0x36, - 0x73, 0x10, 0x36, 0x8e, 0xf0, 0xfd, 0x25, 0xf6, 0x42, 0xc3, 0xbe, 0xf6, 0x8d, 0x23, 0x7c, 0x7f, - 0x39, 0x05, 0x00, 0x9b, 0xb5, 0x0d, 0xed, 0xda, 0x40, 0x5d, 0x7c, 0x83, 0x5d, 0xa7, 0xee, 0x80, - 0x51, 0x0c, 0xce, 0xca, 0x75, 0x71, 0x06, 0x61, 0xb5, 0xc6, 0x67, 0x33, 0xeb, 0x3b, 0x55, 0xdc, - 0xc5, 0xd4, 0x1a, 0x97, 0xd2, 0x90, 0xf2, 0x51, 0x9e, 0x2d, 0x94, 0x6f, 0xce, 0xdf, 0xa2, 0xd1, - 0xc4, 0x41, 0xe0, 0xcd, 0xee, 0x33, 0xbf, 0x44, 0xa0, 0x2f, 0xb5, 0x34, 0x05, 0xe4, 0x83, 0x96, - 0xc6, 0xe6, 0x5a, 0xc6, 0x38, 0x50, 0xeb, 0xfc, 0x8a, 0xee, 0xee, 0x92, 0xdb, 0xc8, 0xed, 0x01, - 0x27, 0xe0, 0x47, 0xa0, 0x1b, 0x8e, 0x70, 0xec, 0xbe, 0x24, 0x4d, 0x64, 0x73, 0xbd, 0x9b, 0xde, - 0x27, 0x55, 0xd7, 0xd4, 0xfd, 0x80, 0xf6, 0xd4, 0x6f, 0x67, 0x67, 0xf7, 0x47, 0xd2, 0x46, 0xc1, - 0x85, 0x0c, 0xed, 0x5a, 0x3b, 0x47, 0xa4, 0x32, 0x15, 0xd8, 0xaf, 0x37, 0xda, 0xa4, 0x32, 0x9d, - 0x03, 0xfd, 0x2b, 0x27, 0x96, 0x7f, 0xe5, 0xc4, 0xee, 0x2f, 0x25, 0x52, 0xf7, 0xda, 0xfe, 0x8b, - 0x76, 0xc9, 0xb6, 0xbd, 0x2c, 0xdc, 0xb8, 0xef, 0xf4, 0x3b, 0xbd, 0xcd, 0x85, 0x00, 0x94, 0xe3, - 0x1d, 0x7d, 0x4c, 0xb6, 0x61, 0xee, 0xe3, 0x4b, 0xcd, 0x3e, 0xe9, 0x5d, 0x6d, 0x02, 0x1c, 0xf1, - 0xeb, 0x33, 0x6a, 0x1d, 0x45, 0xb0, 0xc7, 0x6d, 0xdd, 0x98, 0x51, 0x0e, 0x04, 0x9d, 0x57, 0x52, - 0x16, 0x22, 0x2f, 0x64, 0xe6, 0x27, 0x7b, 0x1d, 0x80, 0x49, 0x21, 0x33, 0xba, 0x4f, 0xea, 0x1b, - 0xe3, 0x70, 0xa2, 0x37, 0x37, 0xba, 0x6c, 0x50, 0x7e, 0x75, 0xbf, 0xf1, 0x4f, 0x15, 0x53, 0x11, - 0xfd, 0xf3, 0xef, 0x2d, 0xbf, 0x9f, 0xa0, 0xe3, 0xff, 0x1f, 0x9b, 0x18, 0xa9, 0x6d, 0x94, 0x85, - 0x4d, 0xa8, 0xce, 0x37, 0x24, 0x7d, 0x4a, 0xb6, 0x21, 0xe8, 0x68, 0xc3, 0xd5, 0x6c, 0xba, 0x4a, - 0x03, 0x8e, 0x97, 0xf4, 0x19, 0xa9, 0xf9, 0x58, 0xa3, 0x25, 0xcd, 0x3e, 0xed, 0xfd, 0x26, 0x01, - 0xf8, 0x86, 0x85, 0x7e, 0x46, 0xaa, 0xce, 0x15, 0xde, 0xb4, 0x56, 0xef, 0x5a, 0x1a, 0x70, 0x7f, - 0xe7, 0x57, 0x82, 0xea, 0x1f, 0xae, 0x04, 0x0f, 0x20, 0x7c, 0x42, 0x6a, 0x9d, 0xe5, 0xb8, 0xb0, - 0x54, 0x78, 0x2d, 0xd2, 0x43, 0x20, 0x6f, 0x78, 0xb1, 0xfe, 0x07, 0x5e, 0x7c, 0x08, 0x2e, 0x83, - 0x67, 0x52, 0xb3, 0xc4, 0xe5, 0xa5, 0xc1, 0xeb, 0xf8, 0x4e, 0x6a, 0x96, 0x30, 0x19, 0xcf, 0xa5, - 0x36, 0x2a, 0xcf, 0x70, 0x71, 0x69, 0x6e, 0x7a, 0xb0, 0x07, 0xf9, 0xe6, 0x16, 0x73, 0x18, 0x0b, - 0x10, 0x77, 0x99, 0x0a, 0xf7, 0x54, 0xf7, 0x3f, 0x25, 0xd2, 0xba, 0x2e, 0x01, 0x8b, 0x65, 0x1a, - 0xbe, 0xcb, 0xb5, 0xaf, 0x07, 0x47, 0x20, 0xaa, 0xb2, 0x5c, 0xfb, 0x1d, 0xd6, 0x11, 0x80, 0x2e, - 0x95, 0xf5, 0x5b, 0x7e, 0x83, 0x3b, 0x02, 0x0a, 0xd0, 0xac, 0x4f, 0xdd, 0xb2, 0xb5, 0xed, 0x6b, - 0xdf, 0xd3, 0x20, 0x81, 0x4b, 0x33, 0x3a, 0xb8, 0xc2, 0x1d, 0x01, 0x5b, 0x11, 0xb4, 0x5d, 0xf4, - 0x69, 0x83, 0xe3, 0x79, 0x5f, 0x78, 0xbd, 0xfc, 0x34, 0xa1, 0x84, 0x54, 0xc7, 0x6f, 0x8e, 0x27, - 0x7c, 0x18, 0x7c, 0x44, 0x9b, 0xa4, 0x36, 0x78, 0x23, 0x8e, 0x27, 0xc7, 0xc3, 0xa0, 0x44, 0x1b, - 0xa4, 0x32, 0xe5, 0x93, 0xe9, 0x2c, 0x28, 0xd3, 0x3a, 0xd9, 0x9e, 0x4d, 0x46, 0xf3, 0x60, 0x0b, - 0x4e, 0xa3, 0x93, 0xc3, 0xc3, 0x60, 0x1b, 0xe4, 0x66, 0x73, 0x3e, 0x1e, 0xcc, 0x83, 0x0a, 0xc8, - 0x1d, 0x0c, 0x47, 0xaf, 0x4f, 0x0e, 0xe7, 0x41, 0x75, 0xff, 0x97, 0x92, 0x2f, 0xd6, 0x4d, 0xc6, - 0xc1, 0x4b, 0xc3, 0xa3, 0xe9, 0xfc, 0xa7, 0xe0, 0x23, 0x90, 0x3f, 0x38, 0x39, 0x9a, 0x06, 0x25, - 0x90, 0xe1, 0xc3, 0xd9, 0x1c, 0x3e, 0x2e, 0x03, 0xc7, 0xe0, 0x87, 0xe1, 0xe0, 0xc7, 0x60, 0x8b, - 0xb6, 0x48, 0x7d, 0xca, 0x87, 0x02, 0xb9, 0xb6, 0xe9, 0x2d, 0xd2, 0x9c, 0xbe, 0x7e, 0x33, 0x14, - 0xb3, 0x21, 0x7f, 0x3b, 0xe4, 0x41, 0x05, 0xbe, 0x3d, 0x9e, 0xcc, 0xc7, 0xa3, 0x9f, 0x82, 0x2a, - 0x0d, 0x48, 0x6b, 0x30, 0x3d, 0x19, 0x1f, 0x8f, 0x26, 0x8e, 0xbd, 0x46, 0x6f, 0x93, 0xf6, 0x06, - 0x71, 0xef, 0xd5, 0x01, 0x1a, 0x0d, 0x5f, 0xcf, 0x4f, 0xf8, 0xd0, 0x43, 0x0d, 0xf8, 0xfa, 0xed, - 0x90, 0xcf, 0xc6, 0x93, 0xe3, 0x80, 0xc0, 0x7f, 0xff, 0x7c, 0x3d, 0x9e, 0x8b, 0xe9, 0xf8, 0x20, - 0x68, 0xd2, 0xbb, 0x24, 0xb8, 0xf6, 0x9f, 0x18, 0xfc, 0x70, 0x78, 0x10, 0xb4, 0xfe, 0x17, 0x00, - 0x00, 0xff, 0xff, 0xf8, 0x9f, 0x0e, 0x7d, 0xca, 0x0d, 0x00, 0x00, -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/.travis.yml libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/.travis.yml --- libpod-3.2.1+ds1/vendor/github.com/checkpoint-restore/go-criu/.travis.yml 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/checkpoint-restore/go-criu/.travis.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -language: go -sudo: required -os: - - linux -go: - - "1.8" - - "1.9" - - "1.10" -env: - # Run the tests with CRIU master and criu-dev - - CRIU_BRANCH="master" - - CRIU_BRANCH="criu-dev" -install: - - sudo apt-get update - - sudo apt-get install -y libprotobuf-dev libprotobuf-c0-dev protobuf-c-compiler protobuf-compiler python-protobuf libnl-3-dev libnet-dev libcap-dev - - go get github.com/checkpoint-restore/go-criu - - git clone --single-branch -b ${CRIU_BRANCH} https://github.com/checkpoint-restore/criu.git - - cd criu; make - - sudo install -D -m 755 criu/criu /usr/sbin/ - - cd .. -script: - # This builds the code without running the tests. - - make build phaul test/test test/phaul test/piggie - # Run actual test as root as it uses CRIU. - - sudo make test phaul-test diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/iptables/iptables.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/iptables/iptables.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/iptables/iptables.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/iptables/iptables.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,659 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package iptables - -import ( - "bytes" - "fmt" - "io" - "net" - "os/exec" - "regexp" - "strconv" - "strings" - "syscall" -) - -// Adds the output of stderr to exec.ExitError -type Error struct { - exec.ExitError - cmd exec.Cmd - msg string - exitStatus *int //for overriding -} - -func (e *Error) ExitStatus() int { - if e.exitStatus != nil { - return *e.exitStatus - } - return e.Sys().(syscall.WaitStatus).ExitStatus() -} - -func (e *Error) Error() string { - return fmt.Sprintf("running %v: exit status %v: %v", e.cmd.Args, e.ExitStatus(), e.msg) -} - -// IsNotExist returns true if the error is due to the chain or rule not existing -func (e *Error) IsNotExist() bool { - if e.ExitStatus() != 1 { - return false - } - msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n" - msgNoChainExist := "No chain/target/match by that name.\n" - return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) -} - -// Protocol to differentiate between IPv4 and IPv6 -type Protocol byte - -const ( - ProtocolIPv4 Protocol = iota - ProtocolIPv6 -) - -type IPTables struct { - path string - proto Protocol - hasCheck bool - hasWait bool - hasRandomFully bool - v1 int - v2 int - v3 int - mode string // the underlying iptables operating mode, e.g. nf_tables - timeout int // time to wait for the iptables lock, default waits forever -} - -// Stat represents a structured statistic entry. -type Stat struct { - Packets uint64 `json:"pkts"` - Bytes uint64 `json:"bytes"` - Target string `json:"target"` - Protocol string `json:"prot"` - Opt string `json:"opt"` - Input string `json:"in"` - Output string `json:"out"` - Source *net.IPNet `json:"source"` - Destination *net.IPNet `json:"destination"` - Options string `json:"options"` -} - -type option func(*IPTables) - -func IPFamily(proto Protocol) option { - return func(ipt *IPTables) { - ipt.proto = proto - } -} - -func Timeout(timeout int) option { - return func(ipt *IPTables) { - ipt.timeout = timeout - } -} - -// New creates a new IPTables configured with the options passed as parameter. -// For backwards compatibility, by default always uses IPv4 and timeout 0. -// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing -// the IPFamily and Timeout options as follow: -// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5)) -func New(opts ...option) (*IPTables, error) { - - ipt := &IPTables{ - proto: ProtocolIPv4, - timeout: 0, - } - - for _, opt := range opts { - opt(ipt) - } - - path, err := exec.LookPath(getIptablesCommand(ipt.proto)) - if err != nil { - return nil, err - } - ipt.path = path - - vstring, err := getIptablesVersionString(path) - if err != nil { - return nil, fmt.Errorf("could not get iptables version: %v", err) - } - v1, v2, v3, mode, err := extractIptablesVersion(vstring) - if err != nil { - return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err) - } - ipt.v1 = v1 - ipt.v2 = v2 - ipt.v3 = v3 - ipt.mode = mode - - checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) - ipt.hasCheck = checkPresent - ipt.hasWait = waitPresent - ipt.hasRandomFully = randomFullyPresent - - return ipt, nil -} - -// New creates a new IPTables for the given proto. -// The proto will determine which command is used, either "iptables" or "ip6tables". -func NewWithProtocol(proto Protocol) (*IPTables, error) { - return New(IPFamily(proto), Timeout(0)) -} - -// Proto returns the protocol used by this IPTables. -func (ipt *IPTables) Proto() Protocol { - return ipt.proto -} - -// Exists checks if given rulespec in specified table/chain exists -func (ipt *IPTables) Exists(table, chain string, rulespec ...string) (bool, error) { - if !ipt.hasCheck { - return ipt.existsForOldIptables(table, chain, rulespec) - - } - cmd := append([]string{"-t", table, "-C", chain}, rulespec...) - err := ipt.run(cmd...) - eerr, eok := err.(*Error) - switch { - case err == nil: - return true, nil - case eok && eerr.ExitStatus() == 1: - return false, nil - default: - return false, err - } -} - -// Insert inserts rulespec to specified table/chain (in specified pos) -func (ipt *IPTables) Insert(table, chain string, pos int, rulespec ...string) error { - cmd := append([]string{"-t", table, "-I", chain, strconv.Itoa(pos)}, rulespec...) - return ipt.run(cmd...) -} - -// Append appends rulespec to specified table/chain -func (ipt *IPTables) Append(table, chain string, rulespec ...string) error { - cmd := append([]string{"-t", table, "-A", chain}, rulespec...) - return ipt.run(cmd...) -} - -// AppendUnique acts like Append except that it won't add a duplicate -func (ipt *IPTables) AppendUnique(table, chain string, rulespec ...string) error { - exists, err := ipt.Exists(table, chain, rulespec...) - if err != nil { - return err - } - - if !exists { - return ipt.Append(table, chain, rulespec...) - } - - return nil -} - -// Delete removes rulespec in specified table/chain -func (ipt *IPTables) Delete(table, chain string, rulespec ...string) error { - cmd := append([]string{"-t", table, "-D", chain}, rulespec...) - return ipt.run(cmd...) -} - -func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error { - exists, err := ipt.Exists(table, chain, rulespec...) - if err == nil && exists { - err = ipt.Delete(table, chain, rulespec...) - } - return err -} - -// List rules in specified table/chain -func (ipt *IPTables) List(table, chain string) ([]string, error) { - args := []string{"-t", table, "-S", chain} - return ipt.executeList(args) -} - -// List rules (with counters) in specified table/chain -func (ipt *IPTables) ListWithCounters(table, chain string) ([]string, error) { - args := []string{"-t", table, "-v", "-S", chain} - return ipt.executeList(args) -} - -// ListChains returns a slice containing the name of each chain in the specified table. -func (ipt *IPTables) ListChains(table string) ([]string, error) { - args := []string{"-t", table, "-S"} - - result, err := ipt.executeList(args) - if err != nil { - return nil, err - } - - // Iterate over rules to find all default (-P) and user-specified (-N) chains. - // Chains definition always come before rules. - // Format is the following: - // -P OUTPUT ACCEPT - // -N Custom - var chains []string - for _, val := range result { - if strings.HasPrefix(val, "-P") || strings.HasPrefix(val, "-N") { - chains = append(chains, strings.Fields(val)[1]) - } else { - break - } - } - return chains, nil -} - -// '-S' is fine with non existing rule index as long as the chain exists -// therefore pass index 1 to reduce overhead for large chains -func (ipt *IPTables) ChainExists(table, chain string) (bool, error) { - err := ipt.run("-t", table, "-S", chain, "1") - eerr, eok := err.(*Error) - switch { - case err == nil: - return true, nil - case eok && eerr.ExitStatus() == 1: - return false, nil - default: - return false, err - } -} - -// Stats lists rules including the byte and packet counts -func (ipt *IPTables) Stats(table, chain string) ([][]string, error) { - args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"} - lines, err := ipt.executeList(args) - if err != nil { - return nil, err - } - - appendSubnet := func(addr string) string { - if strings.IndexByte(addr, byte('/')) < 0 { - if strings.IndexByte(addr, '.') < 0 { - return addr + "/128" - } - return addr + "/32" - } - return addr - } - - ipv6 := ipt.proto == ProtocolIPv6 - - rows := [][]string{} - for i, line := range lines { - // Skip over chain name and field header - if i < 2 { - continue - } - - // Fields: - // 0=pkts 1=bytes 2=target 3=prot 4=opt 5=in 6=out 7=source 8=destination 9=options - line = strings.TrimSpace(line) - fields := strings.Fields(line) - - // The ip6tables verbose output cannot be naively split due to the default "opt" - // field containing 2 single spaces. - if ipv6 { - // Check if field 6 is "opt" or "source" address - dest := fields[6] - ip, _, _ := net.ParseCIDR(dest) - if ip == nil { - ip = net.ParseIP(dest) - } - - // If we detected a CIDR or IP, the "opt" field is empty.. insert it. - if ip != nil { - f := []string{} - f = append(f, fields[:4]...) - f = append(f, " ") // Empty "opt" field for ip6tables - f = append(f, fields[4:]...) - fields = f - } - } - - // Adjust "source" and "destination" to include netmask, to match regular - // List output - fields[7] = appendSubnet(fields[7]) - fields[8] = appendSubnet(fields[8]) - - // Combine "options" fields 9... into a single space-delimited field. - options := fields[9:] - fields = fields[:9] - fields = append(fields, strings.Join(options, " ")) - rows = append(rows, fields) - } - return rows, nil -} - -// ParseStat parses a single statistic row into a Stat struct. The input should -// be a string slice that is returned from calling the Stat method. -func (ipt *IPTables) ParseStat(stat []string) (parsed Stat, err error) { - // For forward-compatibility, expect at least 10 fields in the stat - if len(stat) < 10 { - return parsed, fmt.Errorf("stat contained fewer fields than expected") - } - - // Convert the fields that are not plain strings - parsed.Packets, err = strconv.ParseUint(stat[0], 0, 64) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse packets") - } - parsed.Bytes, err = strconv.ParseUint(stat[1], 0, 64) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse bytes") - } - _, parsed.Source, err = net.ParseCIDR(stat[7]) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse source") - } - _, parsed.Destination, err = net.ParseCIDR(stat[8]) - if err != nil { - return parsed, fmt.Errorf(err.Error(), "could not parse destination") - } - - // Put the fields that are strings - parsed.Target = stat[2] - parsed.Protocol = stat[3] - parsed.Opt = stat[4] - parsed.Input = stat[5] - parsed.Output = stat[6] - parsed.Options = stat[9] - - return parsed, nil -} - -// StructuredStats returns statistics as structured data which may be further -// parsed and marshaled. -func (ipt *IPTables) StructuredStats(table, chain string) ([]Stat, error) { - rawStats, err := ipt.Stats(table, chain) - if err != nil { - return nil, err - } - - structStats := []Stat{} - for _, rawStat := range rawStats { - stat, err := ipt.ParseStat(rawStat) - if err != nil { - return nil, err - } - structStats = append(structStats, stat) - } - - return structStats, nil -} - -func (ipt *IPTables) executeList(args []string) ([]string, error) { - var stdout bytes.Buffer - if err := ipt.runWithOutput(args, &stdout); err != nil { - return nil, err - } - - rules := strings.Split(stdout.String(), "\n") - - // strip trailing newline - if len(rules) > 0 && rules[len(rules)-1] == "" { - rules = rules[:len(rules)-1] - } - - for i, rule := range rules { - rules[i] = filterRuleOutput(rule) - } - - return rules, nil -} - -// NewChain creates a new chain in the specified table. -// If the chain already exists, it will result in an error. -func (ipt *IPTables) NewChain(table, chain string) error { - return ipt.run("-t", table, "-N", chain) -} - -const existsErr = 1 - -// ClearChain flushed (deletes all rules) in the specified table/chain. -// If the chain does not exist, a new one will be created -func (ipt *IPTables) ClearChain(table, chain string) error { - err := ipt.NewChain(table, chain) - - eerr, eok := err.(*Error) - switch { - case err == nil: - return nil - case eok && eerr.ExitStatus() == existsErr: - // chain already exists. Flush (clear) it. - return ipt.run("-t", table, "-F", chain) - default: - return err - } -} - -// RenameChain renames the old chain to the new one. -func (ipt *IPTables) RenameChain(table, oldChain, newChain string) error { - return ipt.run("-t", table, "-E", oldChain, newChain) -} - -// DeleteChain deletes the chain in the specified table. -// The chain must be empty -func (ipt *IPTables) DeleteChain(table, chain string) error { - return ipt.run("-t", table, "-X", chain) -} - -func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error { - exists, err := ipt.ChainExists(table, chain) - if err != nil || !exists { - return err - } - err = ipt.run("-t", table, "-F", chain) - if err == nil { - err = ipt.run("-t", table, "-X", chain) - } - return err -} - -// ChangePolicy changes policy on chain to target -func (ipt *IPTables) ChangePolicy(table, chain, target string) error { - return ipt.run("-t", table, "-P", chain, target) -} - -// Check if the underlying iptables command supports the --random-fully flag -func (ipt *IPTables) HasRandomFully() bool { - return ipt.hasRandomFully -} - -// Return version components of the underlying iptables command -func (ipt *IPTables) GetIptablesVersion() (int, int, int) { - return ipt.v1, ipt.v2, ipt.v3 -} - -// run runs an iptables command with the given arguments, ignoring -// any stdout output -func (ipt *IPTables) run(args ...string) error { - return ipt.runWithOutput(args, nil) -} - -// runWithOutput runs an iptables command with the given arguments, -// writing any stdout output to the given writer -func (ipt *IPTables) runWithOutput(args []string, stdout io.Writer) error { - args = append([]string{ipt.path}, args...) - if ipt.hasWait { - args = append(args, "--wait") - if ipt.timeout != 0 { - args = append(args, strconv.Itoa(ipt.timeout)) - } - } else { - fmu, err := newXtablesFileLock() - if err != nil { - return err - } - ul, err := fmu.tryLock() - if err != nil { - syscall.Close(fmu.fd) - return err - } - defer ul.Unlock() - } - - var stderr bytes.Buffer - cmd := exec.Cmd{ - Path: ipt.path, - Args: args, - Stdout: stdout, - Stderr: &stderr, - } - - if err := cmd.Run(); err != nil { - switch e := err.(type) { - case *exec.ExitError: - return &Error{*e, cmd, stderr.String(), nil} - default: - return err - } - } - - return nil -} - -// getIptablesCommand returns the correct command for the given protocol, either "iptables" or "ip6tables". -func getIptablesCommand(proto Protocol) string { - if proto == ProtocolIPv6 { - return "ip6tables" - } else { - return "iptables" - } -} - -// Checks if iptables has the "-C" and "--wait" flag -func getIptablesCommandSupport(v1 int, v2 int, v3 int) (bool, bool, bool) { - return iptablesHasCheckCommand(v1, v2, v3), iptablesHasWaitCommand(v1, v2, v3), iptablesHasRandomFully(v1, v2, v3) -} - -// getIptablesVersion returns the first three components of the iptables version -// and the operating mode (e.g. nf_tables or legacy) -// e.g. "iptables v1.3.66" would return (1, 3, 66, legacy, nil) -func extractIptablesVersion(str string) (int, int, int, string, error) { - versionMatcher := regexp.MustCompile(`v([0-9]+)\.([0-9]+)\.([0-9]+)(?:\s+\((\w+))?`) - result := versionMatcher.FindStringSubmatch(str) - if result == nil { - return 0, 0, 0, "", fmt.Errorf("no iptables version found in string: %s", str) - } - - v1, err := strconv.Atoi(result[1]) - if err != nil { - return 0, 0, 0, "", err - } - - v2, err := strconv.Atoi(result[2]) - if err != nil { - return 0, 0, 0, "", err - } - - v3, err := strconv.Atoi(result[3]) - if err != nil { - return 0, 0, 0, "", err - } - - mode := "legacy" - if result[4] != "" { - mode = result[4] - } - return v1, v2, v3, mode, nil -} - -// Runs "iptables --version" to get the version string -func getIptablesVersionString(path string) (string, error) { - cmd := exec.Command(path, "--version") - var out bytes.Buffer - cmd.Stdout = &out - err := cmd.Run() - if err != nil { - return "", err - } - return out.String(), nil -} - -// Checks if an iptables version is after 1.4.11, when --check was added -func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 4 { - return true - } - if v1 == 1 && v2 == 4 && v3 >= 11 { - return true - } - return false -} - -// Checks if an iptables version is after 1.4.20, when --wait was added -func iptablesHasWaitCommand(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 4 { - return true - } - if v1 == 1 && v2 == 4 && v3 >= 20 { - return true - } - return false -} - -// Checks if an iptables version is after 1.6.2, when --random-fully was added -func iptablesHasRandomFully(v1 int, v2 int, v3 int) bool { - if v1 > 1 { - return true - } - if v1 == 1 && v2 > 6 { - return true - } - if v1 == 1 && v2 == 6 && v3 >= 2 { - return true - } - return false -} - -// Checks if a rule specification exists for a table -func (ipt *IPTables) existsForOldIptables(table, chain string, rulespec []string) (bool, error) { - rs := strings.Join(append([]string{"-A", chain}, rulespec...), " ") - args := []string{"-t", table, "-S"} - var stdout bytes.Buffer - err := ipt.runWithOutput(args, &stdout) - if err != nil { - return false, err - } - return strings.Contains(stdout.String(), rs), nil -} - -// counterRegex is the regex used to detect nftables counter format -var counterRegex = regexp.MustCompile(`^\[([0-9]+):([0-9]+)\] `) - -// filterRuleOutput works around some inconsistencies in output. -// For example, when iptables is in legacy vs. nftables mode, it produces -// different results. -func filterRuleOutput(rule string) string { - out := rule - - // work around an output difference in nftables mode where counters - // are output in iptables-save format, rather than iptables -S format - // The string begins with "[0:0]" - // - // Fixes #49 - if groups := counterRegex.FindStringSubmatch(out); groups != nil { - // drop the brackets - out = out[len(groups[0]):] - out = fmt.Sprintf("%s -c %s %s", out, groups[1], groups[2]) - } - - return out -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/iptables/lock.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/iptables/lock.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/iptables/lock.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/iptables/lock.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package iptables - -import ( - "os" - "sync" - "syscall" -) - -const ( - // In earlier versions of iptables, the xtables lock was implemented - // via a Unix socket, but now flock is used via this lockfile: - // http://git.netfilter.org/iptables/commit/?id=aa562a660d1555b13cffbac1e744033e91f82707 - // Note the LSB-conforming "/run" directory does not exist on old - // distributions, so assume "/var" is symlinked - xtablesLockFilePath = "/var/run/xtables.lock" - - defaultFilePerm = 0600 -) - -type Unlocker interface { - Unlock() error -} - -type nopUnlocker struct{} - -func (_ nopUnlocker) Unlock() error { return nil } - -type fileLock struct { - // mu is used to protect against concurrent invocations from within this process - mu sync.Mutex - fd int -} - -// tryLock takes an exclusive lock on the xtables lock file without blocking. -// This is best-effort only: if the exclusive lock would block (i.e. because -// another process already holds it), no error is returned. Otherwise, any -// error encountered during the locking operation is returned. -// The returned Unlocker should be used to release the lock when the caller is -// done invoking iptables commands. -func (l *fileLock) tryLock() (Unlocker, error) { - l.mu.Lock() - err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB) - switch err { - case syscall.EWOULDBLOCK: - l.mu.Unlock() - return nopUnlocker{}, nil - case nil: - return l, nil - default: - l.mu.Unlock() - return nil, err - } -} - -// Unlock closes the underlying file, which implicitly unlocks it as well. It -// also unlocks the associated mutex. -func (l *fileLock) Unlock() error { - defer l.mu.Unlock() - return syscall.Close(l.fd) -} - -// newXtablesFileLock opens a new lock on the xtables lockfile without -// acquiring the lock -func newXtablesFileLock() (*fileLock, error) { - fd, err := syscall.Open(xtablesLockFilePath, os.O_CREATE, defaultFilePerm) - if err != nil { - return nil, err - } - return &fileLock{fd: fd}, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/LICENSE libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/LICENSE --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/NOTICE libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/NOTICE --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-iptables/NOTICE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-iptables/NOTICE 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2018 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/activation/files.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/activation/files.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/activation/files.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/activation/files.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package activation implements primitives for systemd socket activation. -package activation - -import ( - "os" - "strconv" - "strings" - "syscall" -) - -const ( - // listenFdsStart corresponds to `SD_LISTEN_FDS_START`. - listenFdsStart = 3 -) - -// Files returns a slice containing a `os.File` object for each -// file descriptor passed to this process via systemd fd-passing protocol. -// -// The order of the file descriptors is preserved in the returned slice. -// `unsetEnv` is typically set to `true` in order to avoid clashes in -// fd usage and to avoid leaking environment flags to child processes. -func Files(unsetEnv bool) []*os.File { - if unsetEnv { - defer os.Unsetenv("LISTEN_PID") - defer os.Unsetenv("LISTEN_FDS") - defer os.Unsetenv("LISTEN_FDNAMES") - } - - pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) - if err != nil || pid != os.Getpid() { - return nil - } - - nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) - if err != nil || nfds == 0 { - return nil - } - - names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") - - files := make([]*os.File, 0, nfds) - for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { - syscall.CloseOnExec(fd) - name := "LISTEN_FD_" + strconv.Itoa(fd) - offset := fd - listenFdsStart - if offset < len(names) && len(names[offset]) > 0 { - name = names[offset] - } - files = append(files, os.NewFile(uintptr(fd), name)) - } - - return files -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/activation/listeners.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/activation/listeners.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/activation/listeners.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/activation/listeners.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package activation - -import ( - "crypto/tls" - "net" -) - -// Listeners returns a slice containing a net.Listener for each matching socket type -// passed to this process. -// -// The order of the file descriptors is preserved in the returned slice. -// Nil values are used to fill any gaps. For example if systemd were to return file descriptors -// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} -func Listeners() ([]net.Listener, error) { - files := Files(true) - listeners := make([]net.Listener, len(files)) - - for i, f := range files { - if pc, err := net.FileListener(f); err == nil { - listeners[i] = pc - f.Close() - } - } - return listeners, nil -} - -// ListenersWithNames maps a listener name to a set of net.Listener instances. -func ListenersWithNames() (map[string][]net.Listener, error) { - files := Files(true) - listeners := map[string][]net.Listener{} - - for _, f := range files { - if pc, err := net.FileListener(f); err == nil { - current, ok := listeners[f.Name()] - if !ok { - listeners[f.Name()] = []net.Listener{pc} - } else { - listeners[f.Name()] = append(current, pc) - } - f.Close() - } - } - return listeners, nil -} - -// TLSListeners returns a slice containing a net.listener for each matching TCP socket type -// passed to this process. -// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. -func TLSListeners(tlsConfig *tls.Config) ([]net.Listener, error) { - listeners, err := Listeners() - - if listeners == nil || err != nil { - return nil, err - } - - if tlsConfig != nil && err == nil { - for i, l := range listeners { - // Activate TLS only for TCP sockets - if l.Addr().Network() == "tcp" { - listeners[i] = tls.NewListener(l, tlsConfig) - } - } - } - - return listeners, err -} - -// TLSListenersWithNames maps a listener name to a net.Listener with -// the associated TLS configuration. -func TLSListenersWithNames(tlsConfig *tls.Config) (map[string][]net.Listener, error) { - listeners, err := ListenersWithNames() - - if listeners == nil || err != nil { - return nil, err - } - - if tlsConfig != nil && err == nil { - for _, ll := range listeners { - // Activate TLS only for TCP sockets - for i, l := range ll { - if l.Addr().Network() == "tcp" { - ll[i] = tls.NewListener(l, tlsConfig) - } - } - } - } - - return listeners, err -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/activation/packetconns.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/activation/packetconns.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/activation/packetconns.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/activation/packetconns.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package activation - -import ( - "net" -) - -// PacketConns returns a slice containing a net.PacketConn for each matching socket type -// passed to this process. -// -// The order of the file descriptors is preserved in the returned slice. -// Nil values are used to fill any gaps. For example if systemd were to return file descriptors -// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} -func PacketConns() ([]net.PacketConn, error) { - files := Files(true) - conns := make([]net.PacketConn, len(files)) - - for i, f := range files { - if pc, err := net.FilePacketConn(f); err == nil { - conns[i] = pc - f.Close() - } - } - return conns, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/LICENSE libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/LICENSE --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/NOTICE libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/NOTICE --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/NOTICE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/NOTICE 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2018 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_unix.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -// Package activation implements primitives for systemd socket activation. -package activation - -import ( - "os" - "strconv" - "strings" - "syscall" -) - -const ( - // listenFdsStart corresponds to `SD_LISTEN_FDS_START`. - listenFdsStart = 3 -) - -// Files returns a slice containing a `os.File` object for each -// file descriptor passed to this process via systemd fd-passing protocol. -// -// The order of the file descriptors is preserved in the returned slice. -// `unsetEnv` is typically set to `true` in order to avoid clashes in -// fd usage and to avoid leaking environment flags to child processes. -func Files(unsetEnv bool) []*os.File { - if unsetEnv { - defer os.Unsetenv("LISTEN_PID") - defer os.Unsetenv("LISTEN_FDS") - defer os.Unsetenv("LISTEN_FDNAMES") - } - - pid, err := strconv.Atoi(os.Getenv("LISTEN_PID")) - if err != nil || pid != os.Getpid() { - return nil - } - - nfds, err := strconv.Atoi(os.Getenv("LISTEN_FDS")) - if err != nil || nfds == 0 { - return nil - } - - names := strings.Split(os.Getenv("LISTEN_FDNAMES"), ":") - - files := make([]*os.File, 0, nfds) - for fd := listenFdsStart; fd < listenFdsStart+nfds; fd++ { - syscall.CloseOnExec(fd) - name := "LISTEN_FD_" + strconv.Itoa(fd) - offset := fd - listenFdsStart - if offset < len(names) && len(names[offset]) > 0 { - name = names[offset] - } - files = append(files, os.NewFile(uintptr(fd), name)) - } - - return files -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/files_windows.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package activation - -import "os" - -func Files(unsetEnv bool) []*os.File { - return nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/listeners.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package activation - -import ( - "crypto/tls" - "net" -) - -// Listeners returns a slice containing a net.Listener for each matching socket type -// passed to this process. -// -// The order of the file descriptors is preserved in the returned slice. -// Nil values are used to fill any gaps. For example if systemd were to return file descriptors -// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener} -func Listeners() ([]net.Listener, error) { - files := Files(true) - listeners := make([]net.Listener, len(files)) - - for i, f := range files { - if pc, err := net.FileListener(f); err == nil { - listeners[i] = pc - f.Close() - } - } - return listeners, nil -} - -// ListenersWithNames maps a listener name to a set of net.Listener instances. -func ListenersWithNames() (map[string][]net.Listener, error) { - files := Files(true) - listeners := map[string][]net.Listener{} - - for _, f := range files { - if pc, err := net.FileListener(f); err == nil { - current, ok := listeners[f.Name()] - if !ok { - listeners[f.Name()] = []net.Listener{pc} - } else { - listeners[f.Name()] = append(current, pc) - } - f.Close() - } - } - return listeners, nil -} - -// TLSListeners returns a slice containing a net.listener for each matching TCP socket type -// passed to this process. -// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig. -func TLSListeners(tlsConfig *tls.Config) ([]net.Listener, error) { - listeners, err := Listeners() - - if listeners == nil || err != nil { - return nil, err - } - - if tlsConfig != nil { - for i, l := range listeners { - // Activate TLS only for TCP sockets - if l.Addr().Network() == "tcp" { - listeners[i] = tls.NewListener(l, tlsConfig) - } - } - } - - return listeners, err -} - -// TLSListenersWithNames maps a listener name to a net.Listener with -// the associated TLS configuration. -func TLSListenersWithNames(tlsConfig *tls.Config) (map[string][]net.Listener, error) { - listeners, err := ListenersWithNames() - - if listeners == nil || err != nil { - return nil, err - } - - if tlsConfig != nil { - for _, ll := range listeners { - // Activate TLS only for TCP sockets - for i, l := range ll { - if l.Addr().Network() == "tcp" { - ll[i] = tls.NewListener(l, tlsConfig) - } - } - } - } - - return listeners, err -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/activation/packetconns.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package activation - -import ( - "net" -) - -// PacketConns returns a slice containing a net.PacketConn for each matching socket type -// passed to this process. -// -// The order of the file descriptors is preserved in the returned slice. -// Nil values are used to fill any gaps. For example if systemd were to return file descriptors -// corresponding with "udp, tcp, udp", then the slice would contain {net.PacketConn, nil, net.PacketConn} -func PacketConns() ([]net.PacketConn, error) { - files := Files(true) - conns := make([]net.PacketConn, len(files)) - - for i, f := range files { - if pc, err := net.FilePacketConn(f); err == nil { - conns[i] = pc - f.Close() - } - } - return conns, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/sdnotify.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -// Copyright 2014 Docker, Inc. -// Copyright 2015-2018 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Package daemon provides a Go implementation of the sd_notify protocol. -// It can be used to inform systemd of service start-up completion, watchdog -// events, and other status changes. -// -// https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description -package daemon - -import ( - "net" - "os" -) - -const ( - // SdNotifyReady tells the service manager that service startup is finished - // or the service finished loading its configuration. - SdNotifyReady = "READY=1" - - // SdNotifyStopping tells the service manager that the service is beginning - // its shutdown. - SdNotifyStopping = "STOPPING=1" - - // SdNotifyReloading tells the service manager that this service is - // reloading its configuration. Note that you must call SdNotifyReady when - // it completed reloading. - SdNotifyReloading = "RELOADING=1" - - // SdNotifyWatchdog tells the service manager to update the watchdog - // timestamp for the service. - SdNotifyWatchdog = "WATCHDOG=1" -) - -// SdNotify sends a message to the init daemon. It is common to ignore the error. -// If `unsetEnvironment` is true, the environment variable `NOTIFY_SOCKET` -// will be unconditionally unset. -// -// It returns one of the following: -// (false, nil) - notification not supported (i.e. NOTIFY_SOCKET is unset) -// (false, err) - notification supported, but failure happened (e.g. error connecting to NOTIFY_SOCKET or while sending data) -// (true, nil) - notification supported, data has been sent -func SdNotify(unsetEnvironment bool, state string) (bool, error) { - socketAddr := &net.UnixAddr{ - Name: os.Getenv("NOTIFY_SOCKET"), - Net: "unixgram", - } - - // NOTIFY_SOCKET not set - if socketAddr.Name == "" { - return false, nil - } - - if unsetEnvironment { - if err := os.Unsetenv("NOTIFY_SOCKET"); err != nil { - return false, err - } - } - - conn, err := net.DialUnix(socketAddr.Net, nil, socketAddr) - // Error connecting to NOTIFY_SOCKET - if err != nil { - return false, err - } - defer conn.Close() - - if _, err = conn.Write([]byte(state)); err != nil { - return false, err - } - return true, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/daemon/watchdog.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -// Copyright 2016 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package daemon - -import ( - "fmt" - "os" - "strconv" - "time" -) - -// SdWatchdogEnabled returns watchdog information for a service. -// Processes should call daemon.SdNotify(false, daemon.SdNotifyWatchdog) every -// time / 2. -// If `unsetEnvironment` is true, the environment variables `WATCHDOG_USEC` and -// `WATCHDOG_PID` will be unconditionally unset. -// -// It returns one of the following: -// (0, nil) - watchdog isn't enabled or we aren't the watched PID. -// (0, err) - an error happened (e.g. error converting time). -// (time, nil) - watchdog is enabled and we can send ping. -// time is delay before inactive service will be killed. -func SdWatchdogEnabled(unsetEnvironment bool) (time.Duration, error) { - wusec := os.Getenv("WATCHDOG_USEC") - wpid := os.Getenv("WATCHDOG_PID") - if unsetEnvironment { - wusecErr := os.Unsetenv("WATCHDOG_USEC") - wpidErr := os.Unsetenv("WATCHDOG_PID") - if wusecErr != nil { - return 0, wusecErr - } - if wpidErr != nil { - return 0, wpidErr - } - } - - if wusec == "" { - return 0, nil - } - s, err := strconv.Atoi(wusec) - if err != nil { - return 0, fmt.Errorf("error converting WATCHDOG_USEC: %s", err) - } - if s <= 0 { - return 0, fmt.Errorf("error WATCHDOG_USEC must be a positive number") - } - interval := time.Duration(s) * time.Microsecond - - if wpid == "" { - return interval, nil - } - p, err := strconv.Atoi(wpid) - if err != nil { - return 0, fmt.Errorf("error converting WATCHDOG_PID: %s", err) - } - if os.Getpid() != p { - return 0, nil - } - - return interval, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,261 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Integration with the systemd D-Bus API. See http://www.freedesktop.org/wiki/Software/systemd/dbus/ -package dbus - -import ( - "context" - "encoding/hex" - "fmt" - "os" - "strconv" - "strings" - "sync" - - "github.com/godbus/dbus/v5" -) - -const ( - alpha = `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ` - num = `0123456789` - alphanum = alpha + num - signalBuffer = 100 -) - -// needsEscape checks whether a byte in a potential dbus ObjectPath needs to be escaped -func needsEscape(i int, b byte) bool { - // Escape everything that is not a-z-A-Z-0-9 - // Also escape 0-9 if it's the first character - return strings.IndexByte(alphanum, b) == -1 || - (i == 0 && strings.IndexByte(num, b) != -1) -} - -// PathBusEscape sanitizes a constituent string of a dbus ObjectPath using the -// rules that systemd uses for serializing special characters. -func PathBusEscape(path string) string { - // Special case the empty string - if len(path) == 0 { - return "_" - } - n := []byte{} - for i := 0; i < len(path); i++ { - c := path[i] - if needsEscape(i, c) { - e := fmt.Sprintf("_%x", c) - n = append(n, []byte(e)...) - } else { - n = append(n, c) - } - } - return string(n) -} - -// pathBusUnescape is the inverse of PathBusEscape. -func pathBusUnescape(path string) string { - if path == "_" { - return "" - } - n := []byte{} - for i := 0; i < len(path); i++ { - c := path[i] - if c == '_' && i+2 < len(path) { - res, err := hex.DecodeString(path[i+1 : i+3]) - if err == nil { - n = append(n, res...) - } - i += 2 - } else { - n = append(n, c) - } - } - return string(n) -} - -// Conn is a connection to systemd's dbus endpoint. -type Conn struct { - // sysconn/sysobj are only used to call dbus methods - sysconn *dbus.Conn - sysobj dbus.BusObject - - // sigconn/sigobj are only used to receive dbus signals - sigconn *dbus.Conn - sigobj dbus.BusObject - - jobListener struct { - jobs map[dbus.ObjectPath]chan<- string - sync.Mutex - } - subStateSubscriber struct { - updateCh chan<- *SubStateUpdate - errCh chan<- error - sync.Mutex - ignore map[dbus.ObjectPath]int64 - cleanIgnore int64 - } - propertiesSubscriber struct { - updateCh chan<- *PropertiesUpdate - errCh chan<- error - sync.Mutex - } -} - -// Deprecated: use NewWithContext instead. -func New() (*Conn, error) { - return NewWithContext(context.Background()) -} - -// NewWithContext establishes a connection to any available bus and authenticates. -// Callers should call Close() when done with the connection. -func NewWithContext(ctx context.Context) (*Conn, error) { - conn, err := NewSystemConnectionContext(ctx) - if err != nil && os.Geteuid() == 0 { - return NewSystemdConnectionContext(ctx) - } - return conn, err -} - -// Deprecated: use NewSystemConnectionContext instead. -func NewSystemConnection() (*Conn, error) { - return NewSystemConnectionContext(context.Background()) -} - -// NewSystemConnectionContext establishes a connection to the system bus and authenticates. -// Callers should call Close() when done with the connection. -func NewSystemConnectionContext(ctx context.Context) (*Conn, error) { - return NewConnection(func() (*dbus.Conn, error) { - return dbusAuthHelloConnection(ctx, dbus.SystemBusPrivate) - }) -} - -// Deprecated: use NewUserConnectionContext instead. -func NewUserConnection() (*Conn, error) { - return NewUserConnectionContext(context.Background()) -} - -// NewUserConnectionContext establishes a connection to the session bus and -// authenticates. This can be used to connect to systemd user instances. -// Callers should call Close() when done with the connection. -func NewUserConnectionContext(ctx context.Context) (*Conn, error) { - return NewConnection(func() (*dbus.Conn, error) { - return dbusAuthHelloConnection(ctx, dbus.SessionBusPrivate) - }) -} - -// Deprecated: use NewSystemdConnectionContext instead. -func NewSystemdConnection() (*Conn, error) { - return NewSystemdConnectionContext(context.Background()) -} - -// NewSystemdConnectionContext establishes a private, direct connection to systemd. -// This can be used for communicating with systemd without a dbus daemon. -// Callers should call Close() when done with the connection. -func NewSystemdConnectionContext(ctx context.Context) (*Conn, error) { - return NewConnection(func() (*dbus.Conn, error) { - // We skip Hello when talking directly to systemd. - return dbusAuthConnection(ctx, func(opts ...dbus.ConnOption) (*dbus.Conn, error) { - return dbus.Dial("unix:path=/run/systemd/private", opts...) - }) - }) -} - -// Close closes an established connection. -func (c *Conn) Close() { - c.sysconn.Close() - c.sigconn.Close() -} - -// NewConnection establishes a connection to a bus using a caller-supplied function. -// This allows connecting to remote buses through a user-supplied mechanism. -// The supplied function may be called multiple times, and should return independent connections. -// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded, -// and any authentication should be handled by the function. -func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) { - sysconn, err := dialBus() - if err != nil { - return nil, err - } - - sigconn, err := dialBus() - if err != nil { - sysconn.Close() - return nil, err - } - - c := &Conn{ - sysconn: sysconn, - sysobj: systemdObject(sysconn), - sigconn: sigconn, - sigobj: systemdObject(sigconn), - } - - c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64) - c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string) - - // Setup the listeners on jobs so that we can get completions - c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, - "type='signal', interface='org.freedesktop.systemd1.Manager', member='JobRemoved'") - - c.dispatch() - return c, nil -} - -// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager -// interface. The value is returned in its string representation, as defined at -// https://developer.gnome.org/glib/unstable/gvariant-text.html. -func (c *Conn) GetManagerProperty(prop string) (string, error) { - variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop) - if err != nil { - return "", err - } - return variant.String(), nil -} - -func dbusAuthConnection(ctx context.Context, createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { - conn, err := createBus(dbus.WithContext(ctx)) - if err != nil { - return nil, err - } - - // Only use EXTERNAL method, and hardcode the uid (not username) - // to avoid a username lookup (which requires a dynamically linked - // libc) - methods := []dbus.Auth{dbus.AuthExternal(strconv.Itoa(os.Getuid()))} - - err = conn.Auth(methods) - if err != nil { - conn.Close() - return nil, err - } - - return conn, nil -} - -func dbusAuthHelloConnection(ctx context.Context, createBus func(opts ...dbus.ConnOption) (*dbus.Conn, error)) (*dbus.Conn, error) { - conn, err := dbusAuthConnection(ctx, createBus) - if err != nil { - return nil, err - } - - if err = conn.Hello(); err != nil { - conn.Close() - return nil, err - } - - return conn, nil -} - -func systemdObject(conn *dbus.Conn) dbus.BusObject { - return conn.Object("org.freedesktop.systemd1", dbus.ObjectPath("/org/freedesktop/systemd1")) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/methods.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,830 +0,0 @@ -// Copyright 2015, 2018 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dbus - -import ( - "context" - "errors" - "fmt" - "path" - "strconv" - - "github.com/godbus/dbus/v5" -) - -// Who can be used to specify which process to kill in the unit via the KillUnitWithTarget API -type Who string - -const ( - // All sends the signal to all processes in the unit - All Who = "all" - // Main sends the signal to the main process of the unit - Main Who = "main" - // Control sends the signal to the control process of the unit - Control Who = "control" -) - -func (c *Conn) jobComplete(signal *dbus.Signal) { - var id uint32 - var job dbus.ObjectPath - var unit string - var result string - dbus.Store(signal.Body, &id, &job, &unit, &result) - c.jobListener.Lock() - out, ok := c.jobListener.jobs[job] - if ok { - out <- result - delete(c.jobListener.jobs, job) - } - c.jobListener.Unlock() -} - -func (c *Conn) startJob(ctx context.Context, ch chan<- string, job string, args ...interface{}) (int, error) { - if ch != nil { - c.jobListener.Lock() - defer c.jobListener.Unlock() - } - - var p dbus.ObjectPath - err := c.sysobj.CallWithContext(ctx, job, 0, args...).Store(&p) - if err != nil { - return 0, err - } - - if ch != nil { - c.jobListener.jobs[p] = ch - } - - // ignore error since 0 is fine if conversion fails - jobID, _ := strconv.Atoi(path.Base(string(p))) - - return jobID, nil -} - -// Deprecated: use StartUnitContext instead. -func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.StartUnitContext(context.Background(), name, mode, ch) -} - -// StartUnitContext enqueues a start job and depending jobs, if any (unless otherwise -// specified by the mode string). -// -// Takes the unit to activate, plus a mode string. The mode needs to be one of -// replace, fail, isolate, ignore-dependencies, ignore-requirements. If -// "replace" the call will start the unit and its dependencies, possibly -// replacing already queued jobs that conflict with this. If "fail" the call -// will start the unit and its dependencies, but will fail if this would change -// an already queued job. If "isolate" the call will start the unit in question -// and terminate all units that aren't dependencies of it. If -// "ignore-dependencies" it will start a unit but ignore all its dependencies. -// If "ignore-requirements" it will start a unit but only ignore the -// requirement dependencies. It is not recommended to make use of the latter -// two options. -// -// If the provided channel is non-nil, a result string will be sent to it upon -// job completion: one of done, canceled, timeout, failed, dependency, skipped. -// done indicates successful execution of a job. canceled indicates that a job -// has been canceled before it finished execution. timeout indicates that the -// job timeout was reached. failed indicates that the job failed. dependency -// indicates that a job this job has been depending on failed and the job hence -// has been removed too. skipped indicates that a job was skipped because it -// didn't apply to the units current state. -// -// If no error occurs, the ID of the underlying systemd job will be returned. There -// does exist the possibility for no error to be returned, but for the returned job -// ID to be 0. In this case, the actual underlying ID is not 0 and this datapoint -// should not be considered authoritative. -// -// If an error does occur, it will be returned to the user alongside a job ID of 0. -func (c *Conn) StartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode) -} - -// Deprecated: use StopUnitContext instead. -func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) { - return c.StopUnitContext(context.Background(), name, mode, ch) -} - -// StopUnitContext is similar to StartUnitContext, but stops the specified unit -// rather than starting it. -func (c *Conn) StopUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode) -} - -// Deprecated: use ReloadUnitContext instead. -func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) { - return c.ReloadUnitContext(context.Background(), name, mode, ch) -} - -// ReloadUnitContext reloads a unit. Reloading is done only if the unit -// is already running, and fails otherwise. -func (c *Conn) ReloadUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode) -} - -// Deprecated: use RestartUnitContext instead. -func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.RestartUnitContext(context.Background(), name, mode, ch) -} - -// RestartUnitContext restarts a service. If a service is restarted that isn't -// running it will be started. -func (c *Conn) RestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode) -} - -// Deprecated: use TryRestartUnitContext instead. -func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.TryRestartUnitContext(context.Background(), name, mode, ch) -} - -// TryRestartUnitContext is like RestartUnitContext, except that a service that -// isn't running is not affected by the restart. -func (c *Conn) TryRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode) -} - -// Deprecated: use ReloadOrRestartUnitContext instead. -func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.ReloadOrRestartUnitContext(context.Background(), name, mode, ch) -} - -// ReloadOrRestartUnitContext attempts a reload if the unit supports it and use -// a restart otherwise. -func (c *Conn) ReloadOrRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode) -} - -// Deprecated: use ReloadOrTryRestartUnitContext instead. -func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) { - return c.ReloadOrTryRestartUnitContext(context.Background(), name, mode, ch) -} - -// ReloadOrTryRestartUnitContext attempts a reload if the unit supports it, -// and use a "Try" flavored restart otherwise. -func (c *Conn) ReloadOrTryRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode) -} - -// Deprecated: use StartTransientUnitContext instead. -func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) { - return c.StartTransientUnitContext(context.Background(), name, mode, properties, ch) -} - -// StartTransientUnitContext may be used to create and start a transient unit, which -// will be released as soon as it is not running or referenced anymore or the -// system is rebooted. name is the unit name including suffix, and must be -// unique. mode is the same as in StartUnitContext, properties contains properties -// of the unit. -func (c *Conn) StartTransientUnitContext(ctx context.Context, name string, mode string, properties []Property, ch chan<- string) (int, error) { - return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0)) -} - -// Deprecated: use KillUnitContext instead. -func (c *Conn) KillUnit(name string, signal int32) { - c.KillUnitContext(context.Background(), name, signal) -} - -// KillUnitContext takes the unit name and a UNIX signal number to send. -// All of the unit's processes are killed. -func (c *Conn) KillUnitContext(ctx context.Context, name string, signal int32) { - c.KillUnitWithTarget(ctx, name, All, signal) -} - -// KillUnitWithTarget is like KillUnitContext, but allows you to specify which -// process in the unit to send the signal to. -func (c *Conn) KillUnitWithTarget(ctx context.Context, name string, target Who, signal int32) error { - return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.KillUnit", 0, name, string(target), signal).Store() -} - -// Deprecated: use ResetFailedUnitContext instead. -func (c *Conn) ResetFailedUnit(name string) error { - return c.ResetFailedUnitContext(context.Background(), name) -} - -// ResetFailedUnitContext resets the "failed" state of a specific unit. -func (c *Conn) ResetFailedUnitContext(ctx context.Context, name string) error { - return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store() -} - -// Deprecated: use SystemStateContext instead. -func (c *Conn) SystemState() (*Property, error) { - return c.SystemStateContext(context.Background()) -} - -// SystemStateContext returns the systemd state. Equivalent to -// systemctl is-system-running. -func (c *Conn) SystemStateContext(ctx context.Context) (*Property, error) { - var err error - var prop dbus.Variant - - obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1") - err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop) - if err != nil { - return nil, err - } - - return &Property{Name: "SystemState", Value: prop}, nil -} - -// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface. -func (c *Conn) getProperties(ctx context.Context, path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) { - var err error - var props map[string]dbus.Variant - - if !path.IsValid() { - return nil, fmt.Errorf("invalid unit name: %v", path) - } - - obj := c.sysconn.Object("org.freedesktop.systemd1", path) - err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.GetAll", 0, dbusInterface).Store(&props) - if err != nil { - return nil, err - } - - out := make(map[string]interface{}, len(props)) - for k, v := range props { - out[k] = v.Value() - } - - return out, nil -} - -// Deprecated: use GetUnitPropertiesContext instead. -func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) { - return c.GetUnitPropertiesContext(context.Background(), unit) -} - -// GetUnitPropertiesContext takes the (unescaped) unit name and returns all of -// its dbus object properties. -func (c *Conn) GetUnitPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error) { - path := unitPath(unit) - return c.getProperties(ctx, path, "org.freedesktop.systemd1.Unit") -} - -// Deprecated: use GetUnitPathPropertiesContext instead. -func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) { - return c.GetUnitPathPropertiesContext(context.Background(), path) -} - -// GetUnitPathPropertiesContext takes the (escaped) unit path and returns all -// of its dbus object properties. -func (c *Conn) GetUnitPathPropertiesContext(ctx context.Context, path dbus.ObjectPath) (map[string]interface{}, error) { - return c.getProperties(ctx, path, "org.freedesktop.systemd1.Unit") -} - -// Deprecated: use GetAllPropertiesContext instead. -func (c *Conn) GetAllProperties(unit string) (map[string]interface{}, error) { - return c.GetAllPropertiesContext(context.Background(), unit) -} - -// GetAllPropertiesContext takes the (unescaped) unit name and returns all of -// its dbus object properties. -func (c *Conn) GetAllPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error) { - path := unitPath(unit) - return c.getProperties(ctx, path, "") -} - -func (c *Conn) getProperty(ctx context.Context, unit string, dbusInterface string, propertyName string) (*Property, error) { - var err error - var prop dbus.Variant - - path := unitPath(unit) - if !path.IsValid() { - return nil, errors.New("invalid unit name: " + unit) - } - - obj := c.sysconn.Object("org.freedesktop.systemd1", path) - err = obj.CallWithContext(ctx, "org.freedesktop.DBus.Properties.Get", 0, dbusInterface, propertyName).Store(&prop) - if err != nil { - return nil, err - } - - return &Property{Name: propertyName, Value: prop}, nil -} - -// Deprecated: use GetUnitPropertyContext instead. -func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) { - return c.GetUnitPropertyContext(context.Background(), unit, propertyName) -} - -// GetUnitPropertyContext takes an (unescaped) unit name, and a property name, -// and returns the property value. -func (c *Conn) GetUnitPropertyContext(ctx context.Context, unit string, propertyName string) (*Property, error) { - return c.getProperty(ctx, unit, "org.freedesktop.systemd1.Unit", propertyName) -} - -// Deprecated: use GetServicePropertyContext instead. -func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) { - return c.GetServicePropertyContext(context.Background(), service, propertyName) -} - -// GetServiceProperty returns property for given service name and property name. -func (c *Conn) GetServicePropertyContext(ctx context.Context, service string, propertyName string) (*Property, error) { - return c.getProperty(ctx, service, "org.freedesktop.systemd1.Service", propertyName) -} - -// Deprecated: use GetUnitTypePropertiesContext instead. -func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) { - return c.GetUnitTypePropertiesContext(context.Background(), unit, unitType) -} - -// GetUnitTypePropertiesContext returns the extra properties for a unit, specific to the unit type. -// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope. -// Returns "dbus.Error: Unknown interface" error if the unitType is not the correct type of the unit. -func (c *Conn) GetUnitTypePropertiesContext(ctx context.Context, unit string, unitType string) (map[string]interface{}, error) { - path := unitPath(unit) - return c.getProperties(ctx, path, "org.freedesktop.systemd1."+unitType) -} - -// Deprecated: use SetUnitPropertiesContext instead. -func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error { - return c.SetUnitPropertiesContext(context.Background(), name, runtime, properties...) -} - -// SetUnitPropertiesContext may be used to modify certain unit properties at runtime. -// Not all properties may be changed at runtime, but many resource management -// settings (primarily those in systemd.cgroup(5)) may. The changes are applied -// instantly, and stored on disk for future boots, unless runtime is true, in which -// case the settings only apply until the next reboot. name is the name of the unit -// to modify. properties are the settings to set, encoded as an array of property -// name and value pairs. -func (c *Conn) SetUnitPropertiesContext(ctx context.Context, name string, runtime bool, properties ...Property) error { - return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store() -} - -// Deprecated: use GetUnitTypePropertyContext instead. -func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) { - return c.GetUnitTypePropertyContext(context.Background(), unit, unitType, propertyName) -} - -// GetUnitTypePropertyContext takes a property name, a unit name, and a unit type, -// and returns a property value. For valid values of unitType, see GetUnitTypePropertiesContext. -func (c *Conn) GetUnitTypePropertyContext(ctx context.Context, unit string, unitType string, propertyName string) (*Property, error) { - return c.getProperty(ctx, unit, "org.freedesktop.systemd1."+unitType, propertyName) -} - -type UnitStatus struct { - Name string // The primary unit name as string - Description string // The human readable description string - LoadState string // The load state (i.e. whether the unit file has been loaded successfully) - ActiveState string // The active state (i.e. whether the unit is currently started or not) - SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not) - Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string. - Path dbus.ObjectPath // The unit object path - JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise - JobType string // The job type as string - JobPath dbus.ObjectPath // The job object path -} - -type storeFunc func(retvalues ...interface{}) error - -func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) { - result := make([][]interface{}, 0) - err := f(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - status := make([]UnitStatus, len(result)) - statusInterface := make([]interface{}, len(status)) - for i := range status { - statusInterface[i] = &status[i] - } - - err = dbus.Store(resultInterface, statusInterface...) - if err != nil { - return nil, err - } - - return status, nil -} - -// Deprecated: use ListUnitsContext instead. -func (c *Conn) ListUnits() ([]UnitStatus, error) { - return c.ListUnitsContext(context.Background()) -} - -// ListUnitsContext returns an array with all currently loaded units. Note that -// units may be known by multiple names at the same time, and hence there might -// be more unit names loaded than actual units behind them. -// Also note that a unit is only loaded if it is active and/or enabled. -// Units that are both disabled and inactive will thus not be returned. -func (c *Conn) ListUnitsContext(ctx context.Context) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnits", 0).Store) -} - -// Deprecated: use ListUnitsFilteredContext instead. -func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) { - return c.ListUnitsFilteredContext(context.Background(), states) -} - -// ListUnitsFilteredContext returns an array with units filtered by state. -// It takes a list of units' statuses to filter. -func (c *Conn) ListUnitsFilteredContext(ctx context.Context, states []string) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store) -} - -// Deprecated: use ListUnitsByPatternsContext instead. -func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) { - return c.ListUnitsByPatternsContext(context.Background(), states, patterns) -} - -// ListUnitsByPatternsContext returns an array with units. -// It takes a list of units' statuses and names to filter. -// Note that units may be known by multiple names at the same time, -// and hence there might be more unit names loaded than actual units behind them. -func (c *Conn) ListUnitsByPatternsContext(ctx context.Context, states []string, patterns []string) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store) -} - -// Deprecated: use ListUnitsByNamesContext instead. -func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) { - return c.ListUnitsByNamesContext(context.Background(), units) -} - -// ListUnitsByNamesContext returns an array with units. It takes a list of units' -// names and returns an UnitStatus array. Comparing to ListUnitsByPatternsContext -// method, this method returns statuses even for inactive or non-existing -// units. Input array should contain exact unit names, but not patterns. -// -// Requires systemd v230 or higher. -func (c *Conn) ListUnitsByNamesContext(ctx context.Context, units []string) ([]UnitStatus, error) { - return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store) -} - -type UnitFile struct { - Path string - Type string -} - -func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) { - result := make([][]interface{}, 0) - err := f(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - files := make([]UnitFile, len(result)) - fileInterface := make([]interface{}, len(files)) - for i := range files { - fileInterface[i] = &files[i] - } - - err = dbus.Store(resultInterface, fileInterface...) - if err != nil { - return nil, err - } - - return files, nil -} - -// Deprecated: use ListUnitFilesContext instead. -func (c *Conn) ListUnitFiles() ([]UnitFile, error) { - return c.ListUnitFilesContext(context.Background()) -} - -// ListUnitFiles returns an array of all available units on disk. -func (c *Conn) ListUnitFilesContext(ctx context.Context) ([]UnitFile, error) { - return c.listUnitFilesInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store) -} - -// Deprecated: use ListUnitFilesByPatternsContext instead. -func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) { - return c.ListUnitFilesByPatternsContext(context.Background(), states, patterns) -} - -// ListUnitFilesByPatternsContext returns an array of all available units on disk matched the patterns. -func (c *Conn) ListUnitFilesByPatternsContext(ctx context.Context, states []string, patterns []string) ([]UnitFile, error) { - return c.listUnitFilesInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store) -} - -type LinkUnitFileChange EnableUnitFileChange - -// Deprecated: use LinkUnitFilesContext instead. -func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { - return c.LinkUnitFilesContext(context.Background(), files, runtime, force) -} - -// LinkUnitFilesContext links unit files (that are located outside of the -// usual unit search paths) into the unit search path. -// -// It takes a list of absolute paths to unit files to link and two -// booleans. -// -// The first boolean controls whether the unit shall be -// enabled for runtime only (true, /run), or persistently (false, -// /etc). -// -// The second controls whether symlinks pointing to other units shall -// be replaced if necessary. -// -// This call returns a list of the changes made. The list consists of -// structures with three strings: the type of the change (one of symlink -// or unlink), the file name of the symlink and the destination of the -// symlink. -func (c *Conn) LinkUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]LinkUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -// Deprecated: use EnableUnitFilesContext instead. -func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { - return c.EnableUnitFilesContext(context.Background(), files, runtime, force) -} - -// EnableUnitFilesContext may be used to enable one or more units in the system -// (by creating symlinks to them in /etc or /run). -// -// It takes a list of unit files to enable (either just file names or full -// absolute paths if the unit files are residing outside the usual unit -// search paths), and two booleans: the first controls whether the unit shall -// be enabled for runtime only (true, /run), or persistently (false, /etc). -// The second one controls whether symlinks pointing to other units shall -// be replaced if necessary. -// -// This call returns one boolean and an array with the changes made. The -// boolean signals whether the unit files contained any enablement -// information (i.e. an [Install]) section. The changes list consists of -// structures with three strings: the type of the change (one of symlink -// or unlink), the file name of the symlink and the destination of the -// symlink. -func (c *Conn) EnableUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) { - var carries_install_info bool - - result := make([][]interface{}, 0) - err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.EnableUnitFiles", 0, files, runtime, force).Store(&carries_install_info, &result) - if err != nil { - return false, nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]EnableUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return false, nil, err - } - - return carries_install_info, changes, nil -} - -type EnableUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// Deprecated: use DisableUnitFilesContext instead. -func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) { - return c.DisableUnitFilesContext(context.Background(), files, runtime) -} - -// DisableUnitFilesContext may be used to disable one or more units in the -// system (by removing symlinks to them from /etc or /run). -// -// It takes a list of unit files to disable (either just file names or full -// absolute paths if the unit files are residing outside the usual unit -// search paths), and one boolean: whether the unit was enabled for runtime -// only (true, /run), or persistently (false, /etc). -// -// This call returns an array with the changes made. The changes list -// consists of structures with three strings: the type of the change (one of -// symlink or unlink), the file name of the symlink and the destination of the -// symlink. -func (c *Conn) DisableUnitFilesContext(ctx context.Context, files []string, runtime bool) ([]DisableUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]DisableUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -type DisableUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// Deprecated: use MaskUnitFilesContext instead. -func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) { - return c.MaskUnitFilesContext(context.Background(), files, runtime, force) -} - -// MaskUnitFilesContext masks one or more units in the system. -// -// The files argument contains a list of units to mask (either just file names -// or full absolute paths if the unit files are residing outside the usual unit -// search paths). -// -// The runtime argument is used to specify whether the unit was enabled for -// runtime only (true, /run/systemd/..), or persistently (false, -// /etc/systemd/..). -func (c *Conn) MaskUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]MaskUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -type MaskUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// Deprecated: use UnmaskUnitFilesContext instead. -func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) { - return c.UnmaskUnitFilesContext(context.Background(), files, runtime) -} - -// UnmaskUnitFilesContext unmasks one or more units in the system. -// -// It takes the list of unit files to mask (either just file names or full -// absolute paths if the unit files are residing outside the usual unit search -// paths), and a boolean runtime flag to specify whether the unit was enabled -// for runtime only (true, /run/systemd/..), or persistently (false, -// /etc/systemd/..). -func (c *Conn) UnmaskUnitFilesContext(ctx context.Context, files []string, runtime bool) ([]UnmaskUnitFileChange, error) { - result := make([][]interface{}, 0) - err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result) - if err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - changes := make([]UnmaskUnitFileChange, len(result)) - changesInterface := make([]interface{}, len(changes)) - for i := range changes { - changesInterface[i] = &changes[i] - } - - err = dbus.Store(resultInterface, changesInterface...) - if err != nil { - return nil, err - } - - return changes, nil -} - -type UnmaskUnitFileChange struct { - Type string // Type of the change (one of symlink or unlink) - Filename string // File name of the symlink - Destination string // Destination of the symlink -} - -// Deprecated: use ReloadContext instead. -func (c *Conn) Reload() error { - return c.ReloadContext(context.Background()) -} - -// ReloadContext instructs systemd to scan for and reload unit files. This is -// an equivalent to systemctl daemon-reload. -func (c *Conn) ReloadContext(ctx context.Context) error { - return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.Reload", 0).Store() -} - -func unitPath(name string) dbus.ObjectPath { - return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name)) -} - -// unitName returns the unescaped base element of the supplied escaped path. -func unitName(dpath dbus.ObjectPath) string { - return pathBusUnescape(path.Base(string(dpath))) -} - -// JobStatus holds a currently queued job definition. -type JobStatus struct { - Id uint32 // The numeric job id - Unit string // The primary unit name for this job - JobType string // The job type as string - Status string // The job state as string - JobPath dbus.ObjectPath // The job object path - UnitPath dbus.ObjectPath // The unit object path -} - -// Deprecated: use ListJobsContext instead. -func (c *Conn) ListJobs() ([]JobStatus, error) { - return c.ListJobsContext(context.Background()) -} - -// ListJobsContext returns an array with all currently queued jobs. -func (c *Conn) ListJobsContext(ctx context.Context) ([]JobStatus, error) { - return c.listJobsInternal(ctx) -} - -func (c *Conn) listJobsInternal(ctx context.Context) ([]JobStatus, error) { - result := make([][]interface{}, 0) - if err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListJobs", 0).Store(&result); err != nil { - return nil, err - } - - resultInterface := make([]interface{}, len(result)) - for i := range result { - resultInterface[i] = result[i] - } - - status := make([]JobStatus, len(result)) - statusInterface := make([]interface{}, len(status)) - for i := range status { - statusInterface[i] = &status[i] - } - - if err := dbus.Store(resultInterface, statusInterface...); err != nil { - return nil, err - } - - return status, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/properties.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,237 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dbus - -import ( - "github.com/godbus/dbus/v5" -) - -// From the systemd docs: -// -// The properties array of StartTransientUnit() may take many of the settings -// that may also be configured in unit files. Not all parameters are currently -// accepted though, but we plan to cover more properties with future release. -// Currently you may set the Description, Slice and all dependency types of -// units, as well as RemainAfterExit, ExecStart for service units, -// TimeoutStopUSec and PIDs for scope units, and CPUAccounting, CPUShares, -// BlockIOAccounting, BlockIOWeight, BlockIOReadBandwidth, -// BlockIOWriteBandwidth, BlockIODeviceWeight, MemoryAccounting, MemoryLimit, -// DevicePolicy, DeviceAllow for services/scopes/slices. These fields map -// directly to their counterparts in unit files and as normal D-Bus object -// properties. The exception here is the PIDs field of scope units which is -// used for construction of the scope only and specifies the initial PIDs to -// add to the scope object. - -type Property struct { - Name string - Value dbus.Variant -} - -type PropertyCollection struct { - Name string - Properties []Property -} - -type execStart struct { - Path string // the binary path to execute - Args []string // an array with all arguments to pass to the executed command, starting with argument 0 - UncleanIsFailure bool // a boolean whether it should be considered a failure if the process exits uncleanly -} - -// PropExecStart sets the ExecStart service property. The first argument is a -// slice with the binary path to execute followed by the arguments to pass to -// the executed command. See -// http://www.freedesktop.org/software/systemd/man/systemd.service.html#ExecStart= -func PropExecStart(command []string, uncleanIsFailure bool) Property { - execStarts := []execStart{ - { - Path: command[0], - Args: command, - UncleanIsFailure: uncleanIsFailure, - }, - } - - return Property{ - Name: "ExecStart", - Value: dbus.MakeVariant(execStarts), - } -} - -// PropRemainAfterExit sets the RemainAfterExit service property. See -// http://www.freedesktop.org/software/systemd/man/systemd.service.html#RemainAfterExit= -func PropRemainAfterExit(b bool) Property { - return Property{ - Name: "RemainAfterExit", - Value: dbus.MakeVariant(b), - } -} - -// PropType sets the Type service property. See -// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type= -func PropType(t string) Property { - return Property{ - Name: "Type", - Value: dbus.MakeVariant(t), - } -} - -// PropDescription sets the Description unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description= -func PropDescription(desc string) Property { - return Property{ - Name: "Description", - Value: dbus.MakeVariant(desc), - } -} - -func propDependency(name string, units []string) Property { - return Property{ - Name: name, - Value: dbus.MakeVariant(units), - } -} - -// PropRequires sets the Requires unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requires= -func PropRequires(units ...string) Property { - return propDependency("Requires", units) -} - -// PropRequiresOverridable sets the RequiresOverridable unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresOverridable= -func PropRequiresOverridable(units ...string) Property { - return propDependency("RequiresOverridable", units) -} - -// PropRequisite sets the Requisite unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Requisite= -func PropRequisite(units ...string) Property { - return propDependency("Requisite", units) -} - -// PropRequisiteOverridable sets the RequisiteOverridable unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequisiteOverridable= -func PropRequisiteOverridable(units ...string) Property { - return propDependency("RequisiteOverridable", units) -} - -// PropWants sets the Wants unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Wants= -func PropWants(units ...string) Property { - return propDependency("Wants", units) -} - -// PropBindsTo sets the BindsTo unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#BindsTo= -func PropBindsTo(units ...string) Property { - return propDependency("BindsTo", units) -} - -// PropRequiredBy sets the RequiredBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredBy= -func PropRequiredBy(units ...string) Property { - return propDependency("RequiredBy", units) -} - -// PropRequiredByOverridable sets the RequiredByOverridable unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiredByOverridable= -func PropRequiredByOverridable(units ...string) Property { - return propDependency("RequiredByOverridable", units) -} - -// PropWantedBy sets the WantedBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#WantedBy= -func PropWantedBy(units ...string) Property { - return propDependency("WantedBy", units) -} - -// PropBoundBy sets the BoundBy unit property. See -// http://www.freedesktop.org/software/systemd/main/systemd.unit.html#BoundBy= -func PropBoundBy(units ...string) Property { - return propDependency("BoundBy", units) -} - -// PropConflicts sets the Conflicts unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Conflicts= -func PropConflicts(units ...string) Property { - return propDependency("Conflicts", units) -} - -// PropConflictedBy sets the ConflictedBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#ConflictedBy= -func PropConflictedBy(units ...string) Property { - return propDependency("ConflictedBy", units) -} - -// PropBefore sets the Before unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Before= -func PropBefore(units ...string) Property { - return propDependency("Before", units) -} - -// PropAfter sets the After unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#After= -func PropAfter(units ...string) Property { - return propDependency("After", units) -} - -// PropOnFailure sets the OnFailure unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#OnFailure= -func PropOnFailure(units ...string) Property { - return propDependency("OnFailure", units) -} - -// PropTriggers sets the Triggers unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#Triggers= -func PropTriggers(units ...string) Property { - return propDependency("Triggers", units) -} - -// PropTriggeredBy sets the TriggeredBy unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#TriggeredBy= -func PropTriggeredBy(units ...string) Property { - return propDependency("TriggeredBy", units) -} - -// PropPropagatesReloadTo sets the PropagatesReloadTo unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#PropagatesReloadTo= -func PropPropagatesReloadTo(units ...string) Property { - return propDependency("PropagatesReloadTo", units) -} - -// PropRequiresMountsFor sets the RequiresMountsFor unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.unit.html#RequiresMountsFor= -func PropRequiresMountsFor(units ...string) Property { - return propDependency("RequiresMountsFor", units) -} - -// PropSlice sets the Slice unit property. See -// http://www.freedesktop.org/software/systemd/man/systemd.resource-control.html#Slice= -func PropSlice(slice string) Property { - return Property{ - Name: "Slice", - Value: dbus.MakeVariant(slice), - } -} - -// PropPids sets the PIDs field of scope units used in the initial construction -// of the scope only and specifies the initial PIDs to add to the scope object. -// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties -func PropPids(pids ...uint32) Property { - return Property{ - Name: "PIDs", - Value: dbus.MakeVariant(pids), - } -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/set.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/set.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/set.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/set.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dbus - -type set struct { - data map[string]bool -} - -func (s *set) Add(value string) { - s.data[value] = true -} - -func (s *set) Remove(value string) { - delete(s.data, value) -} - -func (s *set) Contains(value string) (exists bool) { - _, exists = s.data[value] - return -} - -func (s *set) Length() int { - return len(s.data) -} - -func (s *set) Values() (values []string) { - for val := range s.data { - values = append(values, val) - } - return -} - -func newSet() *set { - return &set{make(map[string]bool)} -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,333 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dbus - -import ( - "errors" - "log" - "time" - - "github.com/godbus/dbus/v5" -) - -const ( - cleanIgnoreInterval = int64(10 * time.Second) - ignoreInterval = int64(30 * time.Millisecond) -) - -// Subscribe sets up this connection to subscribe to all systemd dbus events. -// This is required before calling SubscribeUnits. When the connection closes -// systemd will automatically stop sending signals so there is no need to -// explicitly call Unsubscribe(). -func (c *Conn) Subscribe() error { - c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, - "type='signal',interface='org.freedesktop.systemd1.Manager',member='UnitNew'") - c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, - "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'") - - return c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store() -} - -// Unsubscribe this connection from systemd dbus events. -func (c *Conn) Unsubscribe() error { - return c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store() -} - -func (c *Conn) dispatch() { - ch := make(chan *dbus.Signal, signalBuffer) - - c.sigconn.Signal(ch) - - go func() { - for { - signal, ok := <-ch - if !ok { - return - } - - if signal.Name == "org.freedesktop.systemd1.Manager.JobRemoved" { - c.jobComplete(signal) - } - - if c.subStateSubscriber.updateCh == nil && - c.propertiesSubscriber.updateCh == nil { - continue - } - - var unitPath dbus.ObjectPath - switch signal.Name { - case "org.freedesktop.systemd1.Manager.JobRemoved": - unitName := signal.Body[2].(string) - c.sysobj.Call("org.freedesktop.systemd1.Manager.GetUnit", 0, unitName).Store(&unitPath) - case "org.freedesktop.systemd1.Manager.UnitNew": - unitPath = signal.Body[1].(dbus.ObjectPath) - case "org.freedesktop.DBus.Properties.PropertiesChanged": - if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" { - unitPath = signal.Path - - if len(signal.Body) >= 2 { - if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok { - c.sendPropertiesUpdate(unitPath, changed) - } - } - } - } - - if unitPath == dbus.ObjectPath("") { - continue - } - - c.sendSubStateUpdate(unitPath) - } - }() -} - -// SubscribeUnits returns two unbuffered channels which will receive all changed units every -// interval. Deleted units are sent as nil. -func (c *Conn) SubscribeUnits(interval time.Duration) (<-chan map[string]*UnitStatus, <-chan error) { - return c.SubscribeUnitsCustom(interval, 0, func(u1, u2 *UnitStatus) bool { return *u1 != *u2 }, nil) -} - -// SubscribeUnitsCustom is like SubscribeUnits but lets you specify the buffer -// size of the channels, the comparison function for detecting changes and a filter -// function for cutting down on the noise that your channel receives. -func (c *Conn) SubscribeUnitsCustom(interval time.Duration, buffer int, isChanged func(*UnitStatus, *UnitStatus) bool, filterUnit func(string) bool) (<-chan map[string]*UnitStatus, <-chan error) { - old := make(map[string]*UnitStatus) - statusChan := make(chan map[string]*UnitStatus, buffer) - errChan := make(chan error, buffer) - - go func() { - for { - timerChan := time.After(interval) - - units, err := c.ListUnits() - if err == nil { - cur := make(map[string]*UnitStatus) - for i := range units { - if filterUnit != nil && filterUnit(units[i].Name) { - continue - } - cur[units[i].Name] = &units[i] - } - - // add all new or changed units - changed := make(map[string]*UnitStatus) - for n, u := range cur { - if oldU, ok := old[n]; !ok || isChanged(oldU, u) { - changed[n] = u - } - delete(old, n) - } - - // add all deleted units - for oldN := range old { - changed[oldN] = nil - } - - old = cur - - if len(changed) != 0 { - statusChan <- changed - } - } else { - errChan <- err - } - - <-timerChan - } - }() - - return statusChan, errChan -} - -type SubStateUpdate struct { - UnitName string - SubState string -} - -// SetSubStateSubscriber writes to updateCh when any unit's substate changes. -// Although this writes to updateCh on every state change, the reported state -// may be more recent than the change that generated it (due to an unavoidable -// race in the systemd dbus interface). That is, this method provides a good -// way to keep a current view of all units' states, but is not guaranteed to -// show every state transition they go through. Furthermore, state changes -// will only be written to the channel with non-blocking writes. If updateCh -// is full, it attempts to write an error to errCh; if errCh is full, the error -// passes silently. -func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) { - if c == nil { - msg := "nil receiver" - select { - case errCh <- errors.New(msg): - default: - log.Printf("full error channel while reporting: %s\n", msg) - } - return - } - - c.subStateSubscriber.Lock() - defer c.subStateSubscriber.Unlock() - c.subStateSubscriber.updateCh = updateCh - c.subStateSubscriber.errCh = errCh -} - -func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) { - c.subStateSubscriber.Lock() - defer c.subStateSubscriber.Unlock() - - if c.subStateSubscriber.updateCh == nil { - return - } - - isIgnored := c.shouldIgnore(unitPath) - defer c.cleanIgnore() - if isIgnored { - return - } - - info, err := c.GetUnitPathProperties(unitPath) - if err != nil { - select { - case c.subStateSubscriber.errCh <- err: - default: - log.Printf("full error channel while reporting: %s\n", err) - } - return - } - defer c.updateIgnore(unitPath, info) - - name, ok := info["Id"].(string) - if !ok { - msg := "failed to cast info.Id" - select { - case c.subStateSubscriber.errCh <- errors.New(msg): - default: - log.Printf("full error channel while reporting: %s\n", err) - } - return - } - substate, ok := info["SubState"].(string) - if !ok { - msg := "failed to cast info.SubState" - select { - case c.subStateSubscriber.errCh <- errors.New(msg): - default: - log.Printf("full error channel while reporting: %s\n", msg) - } - return - } - - update := &SubStateUpdate{name, substate} - select { - case c.subStateSubscriber.updateCh <- update: - default: - msg := "update channel is full" - select { - case c.subStateSubscriber.errCh <- errors.New(msg): - default: - log.Printf("full error channel while reporting: %s\n", msg) - } - return - } -} - -// The ignore functions work around a wart in the systemd dbus interface. -// Requesting the properties of an unloaded unit will cause systemd to send a -// pair of UnitNew/UnitRemoved signals. Because we need to get a unit's -// properties on UnitNew (as that's the only indication of a new unit coming up -// for the first time), we would enter an infinite loop if we did not attempt -// to detect and ignore these spurious signals. The signal themselves are -// indistinguishable from relevant ones, so we (somewhat hackishly) ignore an -// unloaded unit's signals for a short time after requesting its properties. -// This means that we will miss e.g. a transient unit being restarted -// *immediately* upon failure and also a transient unit being started -// immediately after requesting its status (with systemctl status, for example, -// because this causes a UnitNew signal to be sent which then causes us to fetch -// the properties). - -func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool { - t, ok := c.subStateSubscriber.ignore[path] - return ok && t >= time.Now().UnixNano() -} - -func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) { - loadState, ok := info["LoadState"].(string) - if !ok { - return - } - - // unit is unloaded - it will trigger bad systemd dbus behavior - if loadState == "not-found" { - c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval - } -} - -// without this, ignore would grow unboundedly over time -func (c *Conn) cleanIgnore() { - now := time.Now().UnixNano() - if c.subStateSubscriber.cleanIgnore < now { - c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval - - for p, t := range c.subStateSubscriber.ignore { - if t < now { - delete(c.subStateSubscriber.ignore, p) - } - } - } -} - -// PropertiesUpdate holds a map of a unit's changed properties -type PropertiesUpdate struct { - UnitName string - Changed map[string]dbus.Variant -} - -// SetPropertiesSubscriber writes to updateCh when any unit's properties -// change. Every property change reported by systemd will be sent; that is, no -// transitions will be "missed" (as they might be with SetSubStateSubscriber). -// However, state changes will only be written to the channel with non-blocking -// writes. If updateCh is full, it attempts to write an error to errCh; if -// errCh is full, the error passes silently. -func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) { - c.propertiesSubscriber.Lock() - defer c.propertiesSubscriber.Unlock() - c.propertiesSubscriber.updateCh = updateCh - c.propertiesSubscriber.errCh = errCh -} - -// we don't need to worry about shouldIgnore() here because -// sendPropertiesUpdate doesn't call GetProperties() -func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) { - c.propertiesSubscriber.Lock() - defer c.propertiesSubscriber.Unlock() - - if c.propertiesSubscriber.updateCh == nil { - return - } - - update := &PropertiesUpdate{unitName(unitPath), changedProps} - - select { - case c.propertiesSubscriber.updateCh <- update: - default: - msg := "update channel is full" - select { - case c.propertiesSubscriber.errCh <- errors.New(msg): - default: - log.Printf("full error channel while reporting: %s\n", msg) - } - return - } -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/dbus/subscription_set.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package dbus - -import ( - "time" -) - -// SubscriptionSet returns a subscription set which is like conn.Subscribe but -// can filter to only return events for a set of units. -type SubscriptionSet struct { - *set - conn *Conn -} - -func (s *SubscriptionSet) filter(unit string) bool { - return !s.Contains(unit) -} - -// Subscribe starts listening for dbus events for all of the units in the set. -// Returns channels identical to conn.SubscribeUnits. -func (s *SubscriptionSet) Subscribe() (<-chan map[string]*UnitStatus, <-chan error) { - // TODO: Make fully evented by using systemd 209 with properties changed values - return s.conn.SubscribeUnitsCustom(time.Second, 0, - mismatchUnitStatus, - func(unit string) bool { return s.filter(unit) }, - ) -} - -// NewSubscriptionSet returns a new subscription set. -func (conn *Conn) NewSubscriptionSet() *SubscriptionSet { - return &SubscriptionSet{newSet(), conn} -} - -// mismatchUnitStatus returns true if the provided UnitStatus objects -// are not equivalent. false is returned if the objects are equivalent. -// Only the Name, Description and state-related fields are used in -// the comparison. -func mismatchUnitStatus(u1, u2 *UnitStatus) bool { - return u1.Name != u2.Name || - u1.Description != u2.Description || - u1.LoadState != u2.LoadState || - u1.ActiveState != u2.ActiveState || - u1.SubState != u2.SubState -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/internal/dlopen/dlopen.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -// Copyright 2016 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package dlopen provides some convenience functions to dlopen a library and -// get its symbols. -package dlopen - -// #cgo LDFLAGS: -ldl -// #include -// #include -import "C" -import ( - "errors" - "fmt" - "unsafe" -) - -var ErrSoNotFound = errors.New("unable to open a handle to the library") - -// LibHandle represents an open handle to a library (.so) -type LibHandle struct { - Handle unsafe.Pointer - Libname string -} - -// GetHandle tries to get a handle to a library (.so), attempting to access it -// by the names specified in libs and returning the first that is successfully -// opened. Callers are responsible for closing the handler. If no library can -// be successfully opened, an error is returned. -func GetHandle(libs []string) (*LibHandle, error) { - for _, name := range libs { - libname := C.CString(name) - defer C.free(unsafe.Pointer(libname)) - handle := C.dlopen(libname, C.RTLD_LAZY) - if handle != nil { - h := &LibHandle{ - Handle: handle, - Libname: name, - } - return h, nil - } - } - return nil, ErrSoNotFound -} - -// GetSymbolPointer takes a symbol name and returns a pointer to the symbol. -func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) { - sym := C.CString(symbol) - defer C.free(unsafe.Pointer(sym)) - - C.dlerror() - p := C.dlsym(l.Handle, sym) - e := C.dlerror() - if e != nil { - return nil, fmt.Errorf("error resolving symbol %q: %v", symbol, errors.New(C.GoString(e))) - } - - return p, nil -} - -// Close closes a LibHandle. -func (l *LibHandle) Close() error { - C.dlerror() - C.dlclose(l.Handle) - e := C.dlerror() - if e != nil { - return fmt.Errorf("error closing %v: %v", l.Libname, errors.New(C.GoString(e))) - } - - return nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package journal provides write bindings to the local systemd journal. -// It is implemented in pure Go and connects to the journal directly over its -// unix socket. -// -// To read from the journal, see the "sdjournal" package, which wraps the -// sd-journal a C API. -// -// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html -package journal - -import ( - "fmt" -) - -// Priority of a journal message -type Priority int - -const ( - PriEmerg Priority = iota - PriAlert - PriCrit - PriErr - PriWarning - PriNotice - PriInfo - PriDebug -) - -// Print prints a message to the local systemd journal using Send(). -func Print(priority Priority, format string, a ...interface{}) error { - return Send(fmt.Sprintf(format, a...), priority, nil) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,210 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build !windows - -// Package journal provides write bindings to the local systemd journal. -// It is implemented in pure Go and connects to the journal directly over its -// unix socket. -// -// To read from the journal, see the "sdjournal" package, which wraps the -// sd-journal a C API. -// -// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html -package journal - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "io" - "io/ioutil" - "net" - "os" - "strconv" - "strings" - "sync" - "sync/atomic" - "syscall" - "unsafe" -) - -var ( - // This can be overridden at build-time: - // https://github.com/golang/go/wiki/GcToolchainTricks#including-build-information-in-the-executable - journalSocket = "/run/systemd/journal/socket" - - // unixConnPtr atomically holds the local unconnected Unix-domain socket. - // Concrete safe pointer type: *net.UnixConn - unixConnPtr unsafe.Pointer - // onceConn ensures that unixConnPtr is initialized exactly once. - onceConn sync.Once -) - -func init() { - onceConn.Do(initConn) -} - -// Enabled checks whether the local systemd journal is available for logging. -func Enabled() bool { - onceConn.Do(initConn) - - if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil { - return false - } - - conn, err := net.Dial("unixgram", journalSocket) - if err != nil { - return false - } - defer conn.Close() - - return true -} - -// Send a message to the local systemd journal. vars is a map of journald -// fields to values. Fields must be composed of uppercase letters, numbers, -// and underscores, but must not start with an underscore. Within these -// restrictions, any arbitrary field name may be used. Some names have special -// significance: see the journalctl documentation -// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html) -// for more details. vars may be nil. -func Send(message string, priority Priority, vars map[string]string) error { - conn := (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) - if conn == nil { - return errors.New("could not initialize socket to journald") - } - - socketAddr := &net.UnixAddr{ - Name: journalSocket, - Net: "unixgram", - } - - data := new(bytes.Buffer) - appendVariable(data, "PRIORITY", strconv.Itoa(int(priority))) - appendVariable(data, "MESSAGE", message) - for k, v := range vars { - appendVariable(data, k, v) - } - - _, _, err := conn.WriteMsgUnix(data.Bytes(), nil, socketAddr) - if err == nil { - return nil - } - if !isSocketSpaceError(err) { - return err - } - - // Large log entry, send it via tempfile and ancillary-fd. - file, err := tempFd() - if err != nil { - return err - } - defer file.Close() - _, err = io.Copy(file, data) - if err != nil { - return err - } - rights := syscall.UnixRights(int(file.Fd())) - _, _, err = conn.WriteMsgUnix([]byte{}, rights, socketAddr) - if err != nil { - return err - } - - return nil -} - -func appendVariable(w io.Writer, name, value string) { - if err := validVarName(name); err != nil { - fmt.Fprintf(os.Stderr, "variable name %s contains invalid character, ignoring\n", name) - } - if strings.ContainsRune(value, '\n') { - /* When the value contains a newline, we write: - * - the variable name, followed by a newline - * - the size (in 64bit little endian format) - * - the data, followed by a newline - */ - fmt.Fprintln(w, name) - binary.Write(w, binary.LittleEndian, uint64(len(value))) - fmt.Fprintln(w, value) - } else { - /* just write the variable and value all on one line */ - fmt.Fprintf(w, "%s=%s\n", name, value) - } -} - -// validVarName validates a variable name to make sure journald will accept it. -// The variable name must be in uppercase and consist only of characters, -// numbers and underscores, and may not begin with an underscore: -// https://www.freedesktop.org/software/systemd/man/sd_journal_print.html -func validVarName(name string) error { - if name == "" { - return errors.New("Empty variable name") - } else if name[0] == '_' { - return errors.New("Variable name begins with an underscore") - } - - for _, c := range name { - if !(('A' <= c && c <= 'Z') || ('0' <= c && c <= '9') || c == '_') { - return errors.New("Variable name contains invalid characters") - } - } - return nil -} - -// isSocketSpaceError checks whether the error is signaling -// an "overlarge message" condition. -func isSocketSpaceError(err error) bool { - opErr, ok := err.(*net.OpError) - if !ok || opErr == nil { - return false - } - - sysErr, ok := opErr.Err.(*os.SyscallError) - if !ok || sysErr == nil { - return false - } - - return sysErr.Err == syscall.EMSGSIZE || sysErr.Err == syscall.ENOBUFS -} - -// tempFd creates a temporary, unlinked file under `/dev/shm`. -func tempFd() (*os.File, error) { - file, err := ioutil.TempFile("/dev/shm/", "journal.XXXXX") - if err != nil { - return nil, err - } - err = syscall.Unlink(file.Name()) - if err != nil { - return nil, err - } - return file, nil -} - -// initConn initializes the global `unixConnPtr` socket. -// It is meant to be called exactly once, at program startup. -func initConn() { - autobind, err := net.ResolveUnixAddr("unixgram", "") - if err != nil { - return - } - - sock, err := net.ListenUnixgram("unixgram", autobind) - if err != nil { - return - } - - atomic.StorePointer(&unixConnPtr, unsafe.Pointer(sock)) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/journal/journal_windows.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package journal provides write bindings to the local systemd journal. -// It is implemented in pure Go and connects to the journal directly over its -// unix socket. -// -// To read from the journal, see the "sdjournal" package, which wraps the -// sd-journal a C API. -// -// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html -package journal - -import ( - "errors" -) - -func Enabled() bool { - return false -} - -func Send(message string, priority Priority, vars map[string]string) error { - return errors.New("could not initialize socket to journald") -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/LICENSE libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/LICENSE --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/NOTICE libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/NOTICE --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/NOTICE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/NOTICE 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -CoreOS Project -Copyright 2018 CoreOS, Inc - -This product includes software developed at CoreOS, Inc. -(http://www.coreos.com/). diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/functions.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -// Copyright 2015 RedHat, Inc. -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sdjournal - -import ( - "github.com/coreos/go-systemd/v22/internal/dlopen" - "sync" - "unsafe" -) - -var ( - // lazy initialized - libsystemdHandle *dlopen.LibHandle - - libsystemdMutex = &sync.Mutex{} - libsystemdFunctions = map[string]unsafe.Pointer{} - libsystemdNames = []string{ - // systemd < 209 - "libsystemd-journal.so.0", - "libsystemd-journal.so", - - // systemd >= 209 merged libsystemd-journal into libsystemd proper - "libsystemd.so.0", - "libsystemd.so", - } -) - -func getFunction(name string) (unsafe.Pointer, error) { - libsystemdMutex.Lock() - defer libsystemdMutex.Unlock() - - if libsystemdHandle == nil { - h, err := dlopen.GetHandle(libsystemdNames) - if err != nil { - return nil, err - } - - libsystemdHandle = h - } - - f, ok := libsystemdFunctions[name] - if !ok { - var err error - f, err = libsystemdHandle.GetSymbolPointer(name) - if err != nil { - return nil, err - } - - libsystemdFunctions[name] = f - } - - return f, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/journal.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,1169 +0,0 @@ -// Copyright 2015 RedHat, Inc. -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sdjournal provides a low-level Go interface to the -// systemd journal wrapped around the sd-journal C API. -// -// All public read methods map closely to the sd-journal API functions. See the -// sd-journal.h documentation[1] for information about each function. -// -// To write to the journal, see the pure-Go "journal" package -// -// [1] http://www.freedesktop.org/software/systemd/man/sd-journal.html -package sdjournal - -// #include -// #include -// #include -// #include -// -// int -// my_sd_journal_open(void *f, sd_journal **ret, int flags) -// { -// int (*sd_journal_open)(sd_journal **, int); -// -// sd_journal_open = f; -// return sd_journal_open(ret, flags); -// } -// -// int -// my_sd_journal_open_directory(void *f, sd_journal **ret, const char *path, int flags) -// { -// int (*sd_journal_open_directory)(sd_journal **, const char *, int); -// -// sd_journal_open_directory = f; -// return sd_journal_open_directory(ret, path, flags); -// } -// -// int -// my_sd_journal_open_files(void *f, sd_journal **ret, const char **paths, int flags) -// { -// int (*sd_journal_open_files)(sd_journal **, const char **, int); -// -// sd_journal_open_files = f; -// return sd_journal_open_files(ret, paths, flags); -// } -// -// void -// my_sd_journal_close(void *f, sd_journal *j) -// { -// int (*sd_journal_close)(sd_journal *); -// -// sd_journal_close = f; -// sd_journal_close(j); -// } -// -// int -// my_sd_journal_get_usage(void *f, sd_journal *j, uint64_t *bytes) -// { -// int (*sd_journal_get_usage)(sd_journal *, uint64_t *); -// -// sd_journal_get_usage = f; -// return sd_journal_get_usage(j, bytes); -// } -// -// int -// my_sd_journal_add_match(void *f, sd_journal *j, const void *data, size_t size) -// { -// int (*sd_journal_add_match)(sd_journal *, const void *, size_t); -// -// sd_journal_add_match = f; -// return sd_journal_add_match(j, data, size); -// } -// -// int -// my_sd_journal_add_disjunction(void *f, sd_journal *j) -// { -// int (*sd_journal_add_disjunction)(sd_journal *); -// -// sd_journal_add_disjunction = f; -// return sd_journal_add_disjunction(j); -// } -// -// int -// my_sd_journal_add_conjunction(void *f, sd_journal *j) -// { -// int (*sd_journal_add_conjunction)(sd_journal *); -// -// sd_journal_add_conjunction = f; -// return sd_journal_add_conjunction(j); -// } -// -// void -// my_sd_journal_flush_matches(void *f, sd_journal *j) -// { -// int (*sd_journal_flush_matches)(sd_journal *); -// -// sd_journal_flush_matches = f; -// sd_journal_flush_matches(j); -// } -// -// int -// my_sd_journal_next(void *f, sd_journal *j) -// { -// int (*sd_journal_next)(sd_journal *); -// -// sd_journal_next = f; -// return sd_journal_next(j); -// } -// -// int -// my_sd_journal_next_skip(void *f, sd_journal *j, uint64_t skip) -// { -// int (*sd_journal_next_skip)(sd_journal *, uint64_t); -// -// sd_journal_next_skip = f; -// return sd_journal_next_skip(j, skip); -// } -// -// int -// my_sd_journal_previous(void *f, sd_journal *j) -// { -// int (*sd_journal_previous)(sd_journal *); -// -// sd_journal_previous = f; -// return sd_journal_previous(j); -// } -// -// int -// my_sd_journal_previous_skip(void *f, sd_journal *j, uint64_t skip) -// { -// int (*sd_journal_previous_skip)(sd_journal *, uint64_t); -// -// sd_journal_previous_skip = f; -// return sd_journal_previous_skip(j, skip); -// } -// -// int -// my_sd_journal_get_data(void *f, sd_journal *j, const char *field, const void **data, size_t *length) -// { -// int (*sd_journal_get_data)(sd_journal *, const char *, const void **, size_t *); -// -// sd_journal_get_data = f; -// return sd_journal_get_data(j, field, data, length); -// } -// -// int -// my_sd_journal_set_data_threshold(void *f, sd_journal *j, size_t sz) -// { -// int (*sd_journal_set_data_threshold)(sd_journal *, size_t); -// -// sd_journal_set_data_threshold = f; -// return sd_journal_set_data_threshold(j, sz); -// } -// -// int -// my_sd_journal_get_cursor(void *f, sd_journal *j, char **cursor) -// { -// int (*sd_journal_get_cursor)(sd_journal *, char **); -// -// sd_journal_get_cursor = f; -// return sd_journal_get_cursor(j, cursor); -// } -// -// int -// my_sd_journal_test_cursor(void *f, sd_journal *j, const char *cursor) -// { -// int (*sd_journal_test_cursor)(sd_journal *, const char *); -// -// sd_journal_test_cursor = f; -// return sd_journal_test_cursor(j, cursor); -// } -// -// int -// my_sd_journal_get_realtime_usec(void *f, sd_journal *j, uint64_t *usec) -// { -// int (*sd_journal_get_realtime_usec)(sd_journal *, uint64_t *); -// -// sd_journal_get_realtime_usec = f; -// return sd_journal_get_realtime_usec(j, usec); -// } -// -// int -// my_sd_journal_get_monotonic_usec(void *f, sd_journal *j, uint64_t *usec, sd_id128_t *boot_id) -// { -// int (*sd_journal_get_monotonic_usec)(sd_journal *, uint64_t *, sd_id128_t *); -// -// sd_journal_get_monotonic_usec = f; -// return sd_journal_get_monotonic_usec(j, usec, boot_id); -// } -// -// int -// my_sd_journal_seek_head(void *f, sd_journal *j) -// { -// int (*sd_journal_seek_head)(sd_journal *); -// -// sd_journal_seek_head = f; -// return sd_journal_seek_head(j); -// } -// -// int -// my_sd_journal_seek_tail(void *f, sd_journal *j) -// { -// int (*sd_journal_seek_tail)(sd_journal *); -// -// sd_journal_seek_tail = f; -// return sd_journal_seek_tail(j); -// } -// -// -// int -// my_sd_journal_seek_cursor(void *f, sd_journal *j, const char *cursor) -// { -// int (*sd_journal_seek_cursor)(sd_journal *, const char *); -// -// sd_journal_seek_cursor = f; -// return sd_journal_seek_cursor(j, cursor); -// } -// -// int -// my_sd_journal_seek_realtime_usec(void *f, sd_journal *j, uint64_t usec) -// { -// int (*sd_journal_seek_realtime_usec)(sd_journal *, uint64_t); -// -// sd_journal_seek_realtime_usec = f; -// return sd_journal_seek_realtime_usec(j, usec); -// } -// -// int -// my_sd_journal_wait(void *f, sd_journal *j, uint64_t timeout_usec) -// { -// int (*sd_journal_wait)(sd_journal *, uint64_t); -// -// sd_journal_wait = f; -// return sd_journal_wait(j, timeout_usec); -// } -// -// void -// my_sd_journal_restart_data(void *f, sd_journal *j) -// { -// void (*sd_journal_restart_data)(sd_journal *); -// -// sd_journal_restart_data = f; -// sd_journal_restart_data(j); -// } -// -// int -// my_sd_journal_enumerate_data(void *f, sd_journal *j, const void **data, size_t *length) -// { -// int (*sd_journal_enumerate_data)(sd_journal *, const void **, size_t *); -// -// sd_journal_enumerate_data = f; -// return sd_journal_enumerate_data(j, data, length); -// } -// -// int -// my_sd_journal_query_unique(void *f, sd_journal *j, const char *field) -// { -// int(*sd_journal_query_unique)(sd_journal *, const char *); -// -// sd_journal_query_unique = f; -// return sd_journal_query_unique(j, field); -// } -// -// int -// my_sd_journal_enumerate_unique(void *f, sd_journal *j, const void **data, size_t *length) -// { -// int(*sd_journal_enumerate_unique)(sd_journal *, const void **, size_t *); -// -// sd_journal_enumerate_unique = f; -// return sd_journal_enumerate_unique(j, data, length); -// } -// -// void -// my_sd_journal_restart_unique(void *f, sd_journal *j) -// { -// void(*sd_journal_restart_unique)(sd_journal *); -// -// sd_journal_restart_unique = f; -// sd_journal_restart_unique(j); -// } -// -// int -// my_sd_journal_get_catalog(void *f, sd_journal *j, char **ret) -// { -// int(*sd_journal_get_catalog)(sd_journal *, char **); -// -// sd_journal_get_catalog = f; -// return sd_journal_get_catalog(j, ret); -// } -// -// int -// my_sd_id128_get_boot(void *f, sd_id128_t *boot_id) -// { -// int(*sd_id128_get_boot)(sd_id128_t *); -// -// sd_id128_get_boot = f; -// return sd_id128_get_boot(boot_id); -// } -// -// char * -// my_sd_id128_to_string(void *f, sd_id128_t boot_id, char s[SD_ID128_STRING_MAX]) -// { -// char *(*sd_id128_to_string)(sd_id128_t, char *); -// -// sd_id128_to_string = f; -// return sd_id128_to_string(boot_id, s); -// } -// -import "C" -import ( - "bytes" - "errors" - "fmt" - "strings" - "sync" - "syscall" - "time" - "unsafe" -) - -// Journal entry field strings which correspond to: -// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html -const ( - // User Journal Fields - SD_JOURNAL_FIELD_MESSAGE = "MESSAGE" - SD_JOURNAL_FIELD_MESSAGE_ID = "MESSAGE_ID" - SD_JOURNAL_FIELD_PRIORITY = "PRIORITY" - SD_JOURNAL_FIELD_CODE_FILE = "CODE_FILE" - SD_JOURNAL_FIELD_CODE_LINE = "CODE_LINE" - SD_JOURNAL_FIELD_CODE_FUNC = "CODE_FUNC" - SD_JOURNAL_FIELD_ERRNO = "ERRNO" - SD_JOURNAL_FIELD_SYSLOG_FACILITY = "SYSLOG_FACILITY" - SD_JOURNAL_FIELD_SYSLOG_IDENTIFIER = "SYSLOG_IDENTIFIER" - SD_JOURNAL_FIELD_SYSLOG_PID = "SYSLOG_PID" - - // Trusted Journal Fields - SD_JOURNAL_FIELD_PID = "_PID" - SD_JOURNAL_FIELD_UID = "_UID" - SD_JOURNAL_FIELD_GID = "_GID" - SD_JOURNAL_FIELD_COMM = "_COMM" - SD_JOURNAL_FIELD_EXE = "_EXE" - SD_JOURNAL_FIELD_CMDLINE = "_CMDLINE" - SD_JOURNAL_FIELD_CAP_EFFECTIVE = "_CAP_EFFECTIVE" - SD_JOURNAL_FIELD_AUDIT_SESSION = "_AUDIT_SESSION" - SD_JOURNAL_FIELD_AUDIT_LOGINUID = "_AUDIT_LOGINUID" - SD_JOURNAL_FIELD_SYSTEMD_CGROUP = "_SYSTEMD_CGROUP" - SD_JOURNAL_FIELD_SYSTEMD_SESSION = "_SYSTEMD_SESSION" - SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT" - SD_JOURNAL_FIELD_SYSTEMD_USER_UNIT = "_SYSTEMD_USER_UNIT" - SD_JOURNAL_FIELD_SYSTEMD_OWNER_UID = "_SYSTEMD_OWNER_UID" - SD_JOURNAL_FIELD_SYSTEMD_SLICE = "_SYSTEMD_SLICE" - SD_JOURNAL_FIELD_SELINUX_CONTEXT = "_SELINUX_CONTEXT" - SD_JOURNAL_FIELD_SOURCE_REALTIME_TIMESTAMP = "_SOURCE_REALTIME_TIMESTAMP" - SD_JOURNAL_FIELD_BOOT_ID = "_BOOT_ID" - SD_JOURNAL_FIELD_MACHINE_ID = "_MACHINE_ID" - SD_JOURNAL_FIELD_HOSTNAME = "_HOSTNAME" - SD_JOURNAL_FIELD_TRANSPORT = "_TRANSPORT" - - // Address Fields - SD_JOURNAL_FIELD_CURSOR = "__CURSOR" - SD_JOURNAL_FIELD_REALTIME_TIMESTAMP = "__REALTIME_TIMESTAMP" - SD_JOURNAL_FIELD_MONOTONIC_TIMESTAMP = "__MONOTONIC_TIMESTAMP" -) - -// Journal event constants -const ( - SD_JOURNAL_NOP = int(C.SD_JOURNAL_NOP) - SD_JOURNAL_APPEND = int(C.SD_JOURNAL_APPEND) - SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE) -) - -const ( - // IndefiniteWait is a sentinel value that can be passed to - // sdjournal.Wait() to signal an indefinite wait for new journal - // events. It is implemented as the maximum value for a time.Duration: - // https://github.com/golang/go/blob/e4dcf5c8c22d98ac9eac7b9b226596229624cb1d/src/time/time.go#L434 - IndefiniteWait time.Duration = 1<<63 - 1 -) - -var ( - // ErrNoTestCursor gets returned when using TestCursor function and cursor - // parameter is not the same as the current cursor position. - ErrNoTestCursor = errors.New("Cursor parameter is not the same as current position") -) - -// Journal is a Go wrapper of an sd_journal structure. -type Journal struct { - cjournal *C.sd_journal - mu sync.Mutex -} - -// JournalEntry represents all fields of a journal entry plus address fields. -type JournalEntry struct { - Fields map[string]string - Cursor string - RealtimeTimestamp uint64 - MonotonicTimestamp uint64 -} - -// Match is a convenience wrapper to describe filters supplied to AddMatch. -type Match struct { - Field string - Value string -} - -// String returns a string representation of a Match suitable for use with AddMatch. -func (m *Match) String() string { - return m.Field + "=" + m.Value -} - -// NewJournal returns a new Journal instance pointing to the local journal -func NewJournal() (j *Journal, err error) { - j = &Journal{} - - sd_journal_open, err := getFunction("sd_journal_open") - if err != nil { - return nil, err - } - - r := C.my_sd_journal_open(sd_journal_open, &j.cjournal, C.SD_JOURNAL_LOCAL_ONLY) - - if r < 0 { - return nil, fmt.Errorf("failed to open journal: %s", syscall.Errno(-r).Error()) - } - - return j, nil -} - -// NewJournalFromDir returns a new Journal instance pointing to a journal residing -// in a given directory. -func NewJournalFromDir(path string) (j *Journal, err error) { - j = &Journal{} - - sd_journal_open_directory, err := getFunction("sd_journal_open_directory") - if err != nil { - return nil, err - } - - p := C.CString(path) - defer C.free(unsafe.Pointer(p)) - - r := C.my_sd_journal_open_directory(sd_journal_open_directory, &j.cjournal, p, 0) - if r < 0 { - return nil, fmt.Errorf("failed to open journal in directory %q: %s", path, syscall.Errno(-r).Error()) - } - - return j, nil -} - -// NewJournalFromFiles returns a new Journal instance pointing to a journals residing -// in a given files. -func NewJournalFromFiles(paths ...string) (j *Journal, err error) { - j = &Journal{} - - sd_journal_open_files, err := getFunction("sd_journal_open_files") - if err != nil { - return nil, err - } - - // by making the slice 1 elem too long, we guarantee it'll be null-terminated - cPaths := make([]*C.char, len(paths)+1) - for idx, path := range paths { - p := C.CString(path) - cPaths[idx] = p - defer C.free(unsafe.Pointer(p)) - } - - r := C.my_sd_journal_open_files(sd_journal_open_files, &j.cjournal, &cPaths[0], 0) - if r < 0 { - return nil, fmt.Errorf("failed to open journals in paths %q: %s", paths, syscall.Errno(-r).Error()) - } - - return j, nil -} - -// Close closes a journal opened with NewJournal. -func (j *Journal) Close() error { - sd_journal_close, err := getFunction("sd_journal_close") - if err != nil { - return err - } - - j.mu.Lock() - C.my_sd_journal_close(sd_journal_close, j.cjournal) - j.mu.Unlock() - - return nil -} - -// AddMatch adds a match by which to filter the entries of the journal. -func (j *Journal) AddMatch(match string) error { - sd_journal_add_match, err := getFunction("sd_journal_add_match") - if err != nil { - return err - } - - m := C.CString(match) - defer C.free(unsafe.Pointer(m)) - - j.mu.Lock() - r := C.my_sd_journal_add_match(sd_journal_add_match, j.cjournal, unsafe.Pointer(m), C.size_t(len(match))) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to add match: %s", syscall.Errno(-r).Error()) - } - - return nil -} - -// AddDisjunction inserts a logical OR in the match list. -func (j *Journal) AddDisjunction() error { - sd_journal_add_disjunction, err := getFunction("sd_journal_add_disjunction") - if err != nil { - return err - } - - j.mu.Lock() - r := C.my_sd_journal_add_disjunction(sd_journal_add_disjunction, j.cjournal) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to add a disjunction in the match list: %s", syscall.Errno(-r).Error()) - } - - return nil -} - -// AddConjunction inserts a logical AND in the match list. -func (j *Journal) AddConjunction() error { - sd_journal_add_conjunction, err := getFunction("sd_journal_add_conjunction") - if err != nil { - return err - } - - j.mu.Lock() - r := C.my_sd_journal_add_conjunction(sd_journal_add_conjunction, j.cjournal) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to add a conjunction in the match list: %s", syscall.Errno(-r).Error()) - } - - return nil -} - -// FlushMatches flushes all matches, disjunctions and conjunctions. -func (j *Journal) FlushMatches() { - sd_journal_flush_matches, err := getFunction("sd_journal_flush_matches") - if err != nil { - return - } - - j.mu.Lock() - C.my_sd_journal_flush_matches(sd_journal_flush_matches, j.cjournal) - j.mu.Unlock() -} - -// Next advances the read pointer into the journal by one entry. -func (j *Journal) Next() (uint64, error) { - sd_journal_next, err := getFunction("sd_journal_next") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_next(sd_journal_next, j.cjournal) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) - } - - return uint64(r), nil -} - -// NextSkip advances the read pointer by multiple entries at once, -// as specified by the skip parameter. -func (j *Journal) NextSkip(skip uint64) (uint64, error) { - sd_journal_next_skip, err := getFunction("sd_journal_next_skip") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_next_skip(sd_journal_next_skip, j.cjournal, C.uint64_t(skip)) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) - } - - return uint64(r), nil -} - -// Previous sets the read pointer into the journal back by one entry. -func (j *Journal) Previous() (uint64, error) { - sd_journal_previous, err := getFunction("sd_journal_previous") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_previous(sd_journal_previous, j.cjournal) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) - } - - return uint64(r), nil -} - -// PreviousSkip sets back the read pointer by multiple entries at once, -// as specified by the skip parameter. -func (j *Journal) PreviousSkip(skip uint64) (uint64, error) { - sd_journal_previous_skip, err := getFunction("sd_journal_previous_skip") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_previous_skip(sd_journal_previous_skip, j.cjournal, C.uint64_t(skip)) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) - } - - return uint64(r), nil -} - -func (j *Journal) getData(field string) (unsafe.Pointer, C.int, error) { - sd_journal_get_data, err := getFunction("sd_journal_get_data") - if err != nil { - return nil, 0, err - } - - f := C.CString(field) - defer C.free(unsafe.Pointer(f)) - - var d unsafe.Pointer - var l C.size_t - - j.mu.Lock() - r := C.my_sd_journal_get_data(sd_journal_get_data, j.cjournal, f, &d, &l) - j.mu.Unlock() - - if r < 0 { - return nil, 0, fmt.Errorf("failed to read message: %s", syscall.Errno(-r).Error()) - } - - return d, C.int(l), nil -} - -// GetData gets the data object associated with a specific field from the -// the journal entry referenced by the last completed Next/Previous function -// call. To call GetData, you must have first called one of these functions. -func (j *Journal) GetData(field string) (string, error) { - d, l, err := j.getData(field) - if err != nil { - return "", err - } - - return C.GoStringN((*C.char)(d), l), nil -} - -// GetDataValue gets the data object associated with a specific field from the -// journal entry referenced by the last completed Next/Previous function call, -// returning only the value of the object. To call GetDataValue, you must first -// have called one of the Next/Previous functions. -func (j *Journal) GetDataValue(field string) (string, error) { - val, err := j.GetData(field) - if err != nil { - return "", err - } - - return strings.SplitN(val, "=", 2)[1], nil -} - -// GetDataBytes gets the data object associated with a specific field from the -// journal entry referenced by the last completed Next/Previous function call. -// To call GetDataBytes, you must first have called one of these functions. -func (j *Journal) GetDataBytes(field string) ([]byte, error) { - d, l, err := j.getData(field) - if err != nil { - return nil, err - } - - return C.GoBytes(d, l), nil -} - -// GetDataValueBytes gets the data object associated with a specific field from the -// journal entry referenced by the last completed Next/Previous function call, -// returning only the value of the object. To call GetDataValueBytes, you must first -// have called one of the Next/Previous functions. -func (j *Journal) GetDataValueBytes(field string) ([]byte, error) { - val, err := j.GetDataBytes(field) - if err != nil { - return nil, err - } - - return bytes.SplitN(val, []byte("="), 2)[1], nil -} - -// GetEntry returns a full representation of the journal entry referenced by the -// last completed Next/Previous function call, with all key-value pairs of data -// as well as address fields (cursor, realtime timestamp and monotonic timestamp). -// To call GetEntry, you must first have called one of the Next/Previous functions. -func (j *Journal) GetEntry() (*JournalEntry, error) { - sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec") - if err != nil { - return nil, err - } - - sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec") - if err != nil { - return nil, err - } - - sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor") - if err != nil { - return nil, err - } - - sd_journal_restart_data, err := getFunction("sd_journal_restart_data") - if err != nil { - return nil, err - } - - sd_journal_enumerate_data, err := getFunction("sd_journal_enumerate_data") - if err != nil { - return nil, err - } - - j.mu.Lock() - defer j.mu.Unlock() - - var r C.int - entry := &JournalEntry{Fields: make(map[string]string)} - - var realtimeUsec C.uint64_t - r = C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &realtimeUsec) - if r < 0 { - return nil, fmt.Errorf("failed to get realtime timestamp: %s", syscall.Errno(-r).Error()) - } - - entry.RealtimeTimestamp = uint64(realtimeUsec) - - var monotonicUsec C.uint64_t - var boot_id C.sd_id128_t - - r = C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &monotonicUsec, &boot_id) - if r < 0 { - return nil, fmt.Errorf("failed to get monotonic timestamp: %s", syscall.Errno(-r).Error()) - } - - entry.MonotonicTimestamp = uint64(monotonicUsec) - - var c *C.char - // since the pointer is mutated by sd_journal_get_cursor, need to wait - // until after the call to free the memory - r = C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &c) - defer C.free(unsafe.Pointer(c)) - if r < 0 { - return nil, fmt.Errorf("failed to get cursor: %s", syscall.Errno(-r).Error()) - } - - entry.Cursor = C.GoString(c) - - // Implements the JOURNAL_FOREACH_DATA_RETVAL macro from journal-internal.h - var d unsafe.Pointer - var l C.size_t - C.my_sd_journal_restart_data(sd_journal_restart_data, j.cjournal) - for { - r = C.my_sd_journal_enumerate_data(sd_journal_enumerate_data, j.cjournal, &d, &l) - if r == 0 { - break - } - - if r < 0 { - return nil, fmt.Errorf("failed to read message field: %s", syscall.Errno(-r).Error()) - } - - msg := C.GoStringN((*C.char)(d), C.int(l)) - kv := strings.SplitN(msg, "=", 2) - if len(kv) < 2 { - return nil, fmt.Errorf("failed to parse field") - } - - entry.Fields[kv[0]] = kv[1] - } - - return entry, nil -} - -// SetDataThreshold sets the data field size threshold for data returned by -// GetData. To retrieve the complete data fields this threshold should be -// turned off by setting it to 0, so that the library always returns the -// complete data objects. -func (j *Journal) SetDataThreshold(threshold uint64) error { - sd_journal_set_data_threshold, err := getFunction("sd_journal_set_data_threshold") - if err != nil { - return err - } - - j.mu.Lock() - r := C.my_sd_journal_set_data_threshold(sd_journal_set_data_threshold, j.cjournal, C.size_t(threshold)) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to set data threshold: %s", syscall.Errno(-r).Error()) - } - - return nil -} - -// GetRealtimeUsec gets the realtime (wallclock) timestamp of the journal -// entry referenced by the last completed Next/Previous function call. To -// call GetRealtimeUsec, you must first have called one of the Next/Previous -// functions. -func (j *Journal) GetRealtimeUsec() (uint64, error) { - var usec C.uint64_t - - sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &usec) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to get realtime timestamp: %s", syscall.Errno(-r).Error()) - } - - return uint64(usec), nil -} - -// GetMonotonicUsec gets the monotonic timestamp of the journal entry -// referenced by the last completed Next/Previous function call. To call -// GetMonotonicUsec, you must first have called one of the Next/Previous -// functions. -func (j *Journal) GetMonotonicUsec() (uint64, error) { - var usec C.uint64_t - var boot_id C.sd_id128_t - - sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &usec, &boot_id) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to get monotonic timestamp: %s", syscall.Errno(-r).Error()) - } - - return uint64(usec), nil -} - -// GetCursor gets the cursor of the last journal entry reeferenced by the -// last completed Next/Previous function call. To call GetCursor, you must -// first have called one of the Next/Previous functions. -func (j *Journal) GetCursor() (string, error) { - sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor") - if err != nil { - return "", err - } - - var d *C.char - // since the pointer is mutated by sd_journal_get_cursor, need to wait - // until after the call to free the memory - - j.mu.Lock() - r := C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &d) - j.mu.Unlock() - defer C.free(unsafe.Pointer(d)) - - if r < 0 { - return "", fmt.Errorf("failed to get cursor: %s", syscall.Errno(-r).Error()) - } - - cursor := C.GoString(d) - - return cursor, nil -} - -// TestCursor checks whether the current position in the journal matches the -// specified cursor -func (j *Journal) TestCursor(cursor string) error { - sd_journal_test_cursor, err := getFunction("sd_journal_test_cursor") - if err != nil { - return err - } - - c := C.CString(cursor) - defer C.free(unsafe.Pointer(c)) - - j.mu.Lock() - r := C.my_sd_journal_test_cursor(sd_journal_test_cursor, j.cjournal, c) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to test to cursor %q: %s", cursor, syscall.Errno(-r).Error()) - } else if r == 0 { - return ErrNoTestCursor - } - - return nil -} - -// SeekHead seeks to the beginning of the journal, i.e. the oldest available -// entry. This call must be followed by a call to Next before any call to -// Get* will return data about the first element. -func (j *Journal) SeekHead() error { - sd_journal_seek_head, err := getFunction("sd_journal_seek_head") - if err != nil { - return err - } - - j.mu.Lock() - r := C.my_sd_journal_seek_head(sd_journal_seek_head, j.cjournal) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to seek to head of journal: %s", syscall.Errno(-r).Error()) - } - - return nil -} - -// SeekTail may be used to seek to the end of the journal, i.e. the most recent -// available entry. This call must be followed by a call to Previous before any -// call to Get* will return data about the last element. -func (j *Journal) SeekTail() error { - sd_journal_seek_tail, err := getFunction("sd_journal_seek_tail") - if err != nil { - return err - } - - j.mu.Lock() - r := C.my_sd_journal_seek_tail(sd_journal_seek_tail, j.cjournal) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to seek to tail of journal: %s", syscall.Errno(-r).Error()) - } - - return nil -} - -// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock) -// timestamp, i.e. CLOCK_REALTIME. This call must be followed by a call to -// Next/Previous before any call to Get* will return data about the sought entry. -func (j *Journal) SeekRealtimeUsec(usec uint64) error { - sd_journal_seek_realtime_usec, err := getFunction("sd_journal_seek_realtime_usec") - if err != nil { - return err - } - - j.mu.Lock() - r := C.my_sd_journal_seek_realtime_usec(sd_journal_seek_realtime_usec, j.cjournal, C.uint64_t(usec)) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to seek to %d: %s", usec, syscall.Errno(-r).Error()) - } - - return nil -} - -// SeekCursor seeks to a concrete journal cursor. This call must be -// followed by a call to Next/Previous before any call to Get* will return -// data about the sought entry. -func (j *Journal) SeekCursor(cursor string) error { - sd_journal_seek_cursor, err := getFunction("sd_journal_seek_cursor") - if err != nil { - return err - } - - c := C.CString(cursor) - defer C.free(unsafe.Pointer(c)) - - j.mu.Lock() - r := C.my_sd_journal_seek_cursor(sd_journal_seek_cursor, j.cjournal, c) - j.mu.Unlock() - - if r < 0 { - return fmt.Errorf("failed to seek to cursor %q: %s", cursor, syscall.Errno(-r).Error()) - } - - return nil -} - -// Wait will synchronously wait until the journal gets changed. The maximum time -// this call sleeps may be controlled with the timeout parameter. If -// sdjournal.IndefiniteWait is passed as the timeout parameter, Wait will -// wait indefinitely for a journal change. -func (j *Journal) Wait(timeout time.Duration) int { - var to uint64 - - sd_journal_wait, err := getFunction("sd_journal_wait") - if err != nil { - return -1 - } - - if timeout == IndefiniteWait { - // sd_journal_wait(3) calls for a (uint64_t) -1 to be passed to signify - // indefinite wait, but using a -1 overflows our C.uint64_t, so we use an - // equivalent hex value. - to = 0xffffffffffffffff - } else { - to = uint64(timeout / time.Microsecond) - } - j.mu.Lock() - r := C.my_sd_journal_wait(sd_journal_wait, j.cjournal, C.uint64_t(to)) - j.mu.Unlock() - - return int(r) -} - -// GetUsage returns the journal disk space usage, in bytes. -func (j *Journal) GetUsage() (uint64, error) { - var out C.uint64_t - - sd_journal_get_usage, err := getFunction("sd_journal_get_usage") - if err != nil { - return 0, err - } - - j.mu.Lock() - r := C.my_sd_journal_get_usage(sd_journal_get_usage, j.cjournal, &out) - j.mu.Unlock() - - if r < 0 { - return 0, fmt.Errorf("failed to get journal disk space usage: %s", syscall.Errno(-r).Error()) - } - - return uint64(out), nil -} - -// GetUniqueValues returns all unique values for a given field. -func (j *Journal) GetUniqueValues(field string) ([]string, error) { - var result []string - - sd_journal_query_unique, err := getFunction("sd_journal_query_unique") - if err != nil { - return nil, err - } - - sd_journal_enumerate_unique, err := getFunction("sd_journal_enumerate_unique") - if err != nil { - return nil, err - } - - sd_journal_restart_unique, err := getFunction("sd_journal_restart_unique") - if err != nil { - return nil, err - } - - j.mu.Lock() - defer j.mu.Unlock() - - f := C.CString(field) - defer C.free(unsafe.Pointer(f)) - - r := C.my_sd_journal_query_unique(sd_journal_query_unique, j.cjournal, f) - - if r < 0 { - return nil, fmt.Errorf("failed to query journal: %s", syscall.Errno(-r).Error()) - } - - // Implements the SD_JOURNAL_FOREACH_UNIQUE macro from sd-journal.h - var d unsafe.Pointer - var l C.size_t - C.my_sd_journal_restart_unique(sd_journal_restart_unique, j.cjournal) - for { - r = C.my_sd_journal_enumerate_unique(sd_journal_enumerate_unique, j.cjournal, &d, &l) - if r == 0 { - break - } - - if r < 0 { - return nil, fmt.Errorf("failed to read message field: %s", syscall.Errno(-r).Error()) - } - - msg := C.GoStringN((*C.char)(d), C.int(l)) - kv := strings.SplitN(msg, "=", 2) - if len(kv) < 2 { - return nil, fmt.Errorf("failed to parse field") - } - - result = append(result, kv[1]) - } - - return result, nil -} - -// GetCatalog retrieves a message catalog entry for the journal entry referenced -// by the last completed Next/Previous function call. To call GetCatalog, you -// must first have called one of these functions. -func (j *Journal) GetCatalog() (string, error) { - sd_journal_get_catalog, err := getFunction("sd_journal_get_catalog") - if err != nil { - return "", err - } - - var c *C.char - - j.mu.Lock() - r := C.my_sd_journal_get_catalog(sd_journal_get_catalog, j.cjournal, &c) - j.mu.Unlock() - defer C.free(unsafe.Pointer(c)) - - if r < 0 { - return "", fmt.Errorf("failed to retrieve catalog entry for current journal entry: %s", syscall.Errno(-r).Error()) - } - - catalog := C.GoString(c) - - return catalog, nil -} - -// GetBootID get systemd boot id -func (j *Journal) GetBootID() (string, error) { - sd_id128_get_boot, err := getFunction("sd_id128_get_boot") - if err != nil { - return "", err - } - - var boot_id C.sd_id128_t - r := C.my_sd_id128_get_boot(sd_id128_get_boot, &boot_id) - if r < 0 { - return "", fmt.Errorf("failed to get boot id: %s", syscall.Errno(-r).Error()) - } - - sd_id128_to_string, err := getFunction("sd_id128_to_string") - if err != nil { - return "", err - } - - id128StringMax := C.size_t(C.SD_ID128_STRING_MAX) - c := (*C.char)(C.malloc(id128StringMax)) - defer C.free(unsafe.Pointer(c)) - C.my_sd_id128_to_string(sd_id128_to_string, boot_id, c) - - bootID := C.GoString(c) - if len(bootID) <= 0 { - return "", fmt.Errorf("get boot id %s is not valid", bootID) - } - - return bootID, nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go --- libpod-3.2.1+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/coreos/go-systemd/v22/sdjournal/read.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,272 +0,0 @@ -// Copyright 2015 RedHat, Inc. -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sdjournal - -import ( - "errors" - "fmt" - "io" - "log" - "strings" - "sync" - "time" -) - -var ( - // ErrExpired gets returned when the Follow function runs into the - // specified timeout. - ErrExpired = errors.New("Timeout expired") -) - -// JournalReaderConfig represents options to drive the behavior of a JournalReader. -type JournalReaderConfig struct { - // The Since, NumFromTail and Cursor options are mutually exclusive and - // determine where the reading begins within the journal. The order in which - // options are written is exactly the order of precedence. - Since time.Duration // start relative to a Duration from now - NumFromTail uint64 // start relative to the tail - Cursor string // start relative to the cursor - - // Show only journal entries whose fields match the supplied values. If - // the array is empty, entries will not be filtered. - Matches []Match - - // If not empty, the journal instance will point to a journal residing - // in this directory. The supplied path may be relative or absolute. - Path string - - // If not nil, Formatter will be used to translate the resulting entries - // into strings. If not set, the default format (timestamp and message field) - // will be used. If Formatter returns an error, Read will stop and return the error. - Formatter func(entry *JournalEntry) (string, error) -} - -// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the -// systemd journal. A JournalReader is not safe for concurrent use by multiple goroutines. -type JournalReader struct { - journal *Journal - msgReader *strings.Reader - formatter func(entry *JournalEntry) (string, error) -} - -// NewJournalReader creates a new JournalReader with configuration options that are similar to the -// systemd journalctl tool's iteration and filtering features. -func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) { - // use simpleMessageFormatter as default formatter. - if config.Formatter == nil { - config.Formatter = simpleMessageFormatter - } - - r := &JournalReader{ - formatter: config.Formatter, - } - - // Open the journal - var err error - if config.Path != "" { - r.journal, err = NewJournalFromDir(config.Path) - } else { - r.journal, err = NewJournal() - } - if err != nil { - return nil, err - } - - // Add any supplied matches - for _, m := range config.Matches { - if err = r.journal.AddMatch(m.String()); err != nil { - return nil, err - } - } - - // Set the start position based on options - if config.Since != 0 { - // Start based on a relative time - start := time.Now().Add(config.Since) - if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil { - return nil, err - } - } else if config.NumFromTail != 0 { - // Start based on a number of lines before the tail - if err := r.journal.SeekTail(); err != nil { - return nil, err - } - - // Move the read pointer into position near the tail. Go one further than - // the option so that the initial cursor advancement positions us at the - // correct starting point. - skip, err := r.journal.PreviousSkip(config.NumFromTail + 1) - if err != nil { - return nil, err - } - // If we skipped fewer lines than expected, we have reached journal start. - // Thus, we seek to head so that next invocation can read the first line. - if skip != config.NumFromTail+1 { - if err := r.journal.SeekHead(); err != nil { - return nil, err - } - } - } else if config.Cursor != "" { - // Start based on a custom cursor - if err := r.journal.SeekCursor(config.Cursor); err != nil { - return nil, err - } - } - - return r, nil -} - -// Read reads entries from the journal. Read follows the Reader interface so -// it must be able to read a specific amount of bytes. Journald on the other -// hand only allows us to read full entries of arbitrary size (without byte -// granularity). JournalReader is therefore internally buffering entries that -// don't fit in the read buffer. Callers should keep calling until 0 and/or an -// error is returned. -func (r *JournalReader) Read(b []byte) (int, error) { - if r.msgReader == nil { - // Advance the journal cursor. It has to be called at least one time - // before reading - c, err := r.journal.Next() - - // An unexpected error - if err != nil { - return 0, err - } - - // EOF detection - if c == 0 { - return 0, io.EOF - } - - entry, err := r.journal.GetEntry() - if err != nil { - return 0, err - } - - // Build a message - msg, err := r.formatter(entry) - if err != nil { - return 0, err - } - r.msgReader = strings.NewReader(msg) - } - - // Copy and return the message - sz, err := r.msgReader.Read(b) - if err == io.EOF { - // The current entry has been fully read. Don't propagate this - // EOF, so the next entry can be read at the next Read() - // iteration. - r.msgReader = nil - return sz, nil - } - if err != nil { - return sz, err - } - if r.msgReader.Len() == 0 { - r.msgReader = nil - } - - return sz, nil -} - -// Close closes the JournalReader's handle to the journal. -func (r *JournalReader) Close() error { - return r.journal.Close() -} - -// Rewind attempts to rewind the JournalReader to the first entry. -func (r *JournalReader) Rewind() error { - r.msgReader = nil - return r.journal.SeekHead() -} - -// Follow synchronously follows the JournalReader, writing each new journal entry to writer. The -// follow will continue until a single time.Time is received on the until channel. -func (r *JournalReader) Follow(until <-chan time.Time, writer io.Writer) error { - - // Process journal entries and events. Entries are flushed until the tail or - // timeout is reached, and then we wait for new events or the timeout. - var msg = make([]byte, 64*1<<(10)) - var waitCh = make(chan int, 1) - var waitGroup sync.WaitGroup - defer waitGroup.Wait() - -process: - for { - c, err := r.Read(msg) - if err != nil && err != io.EOF { - return err - } - - select { - case <-until: - return ErrExpired - default: - } - if c > 0 { - if _, err = writer.Write(msg[:c]); err != nil { - return err - } - continue process - } - - // We're at the tail, so wait for new events or time out. - // Holds journal events to process. Tightly bounded for now unless there's a - // reason to unblock the journal watch routine more quickly. - for { - waitGroup.Add(1) - go func() { - status := r.journal.Wait(100 * time.Millisecond) - waitCh <- status - waitGroup.Done() - }() - - select { - case <-until: - return ErrExpired - case e := <-waitCh: - switch e { - case SD_JOURNAL_NOP: - // the journal did not change since the last invocation - case SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE: - continue process - default: - if e < 0 { - return fmt.Errorf("received error event: %d", e) - } - - log.Printf("received unknown event: %d\n", e) - } - } - } - } -} - -// simpleMessageFormatter is the default formatter. -// It returns a string representing the current journal entry in a simple format which -// includes the entry timestamp and MESSAGE field. -func simpleMessageFormatter(entry *JournalEntry) (string, error) { - msg, ok := entry.Fields["MESSAGE"] - if !ok { - return "", fmt.Errorf("no MESSAGE field present in journal entry") - } - - usec := entry.RealtimeTimestamp - timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond)) - - return fmt.Sprintf("%s %s\n", timestamp, msg), nil -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go libpod-3.4.4+ds1/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go --- libpod-3.2.1+ds1/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/cri-o/ocicni/pkg/ocicni/ocicni.go 2021-12-26 00:45:51.000000000 +0000 @@ -195,16 +195,21 @@ // If defaultNetName is empty, CNI config files should be reloaded real-time and // defaultNetName should be changeable and determined by file sorting. func InitCNI(defaultNetName string, confDir string, binDirs ...string) (CNIPlugin, error) { - return initCNI(nil, "", defaultNetName, confDir, binDirs...) + return initCNI(nil, "", defaultNetName, confDir, true, binDirs...) } // InitCNIWithCache works like InitCNI except that it takes the cni cache directory as third param. func InitCNIWithCache(defaultNetName, confDir, cacheDir string, binDirs ...string) (CNIPlugin, error) { - return initCNI(nil, cacheDir, defaultNetName, confDir, binDirs...) + return initCNI(nil, cacheDir, defaultNetName, confDir, true, binDirs...) +} + +// InitCNINoInotify works like InitCNI except that it does not use inotify to watch for changes in the CNI config dir. +func InitCNINoInotify(defaultNetName, confDir, cacheDir string, binDirs ...string) (CNIPlugin, error) { + return initCNI(nil, cacheDir, defaultNetName, confDir, false, binDirs...) } // Internal function to allow faking out exec functions for testing -func initCNI(exec cniinvoke.Exec, cacheDir, defaultNetName string, confDir string, binDirs ...string) (CNIPlugin, error) { +func initCNI(exec cniinvoke.Exec, cacheDir, defaultNetName string, confDir string, useInotify bool, binDirs ...string) (CNIPlugin, error) { if confDir == "" { confDir = DefaultConfDir } @@ -245,22 +250,26 @@ plugin.syncNetworkConfig() - plugin.watcher, err = newWatcher(plugin.confDir) - if err != nil { - return nil, err - } + if useInotify { + plugin.watcher, err = newWatcher(plugin.confDir) + if err != nil { + return nil, err + } - startWg := sync.WaitGroup{} - startWg.Add(1) - go plugin.monitorConfDir(&startWg) - startWg.Wait() + startWg := sync.WaitGroup{} + startWg.Add(1) + go plugin.monitorConfDir(&startWg) + startWg.Wait() + } return plugin, nil } func (plugin *cniNetworkPlugin) Shutdown() error { close(plugin.shutdownChan) - plugin.watcher.Close() + if plugin.watcher != nil { + plugin.watcher.Close() + } plugin.done.Wait() return nil } @@ -539,10 +548,11 @@ results := make([]NetResult, 0) if err := plugin.forEachNetwork(&podNetwork, false, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { + fullPodName := buildFullPodName(*podNetwork) + logrus.Infof("Adding pod %s to CNI network %q (type=%v)", fullPodName, network.name, network.config.Plugins[0].Network.Type) result, err := network.addToNetwork(ctx, rt, plugin.cniConfig) if err != nil { - logrus.Errorf("Error while adding pod to CNI network %q: %s", network.name, err) - return err + return fmt.Errorf("error adding pod %s to CNI network %q: %v", fullPodName, network.name, err) } results = append(results, NetResult{ Result: result, @@ -654,8 +664,10 @@ } return plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { + fullPodName := buildFullPodName(*podNetwork) + logrus.Infof("Deleting pod %s from CNI network %q (type=%v)", fullPodName, network.name, network.config.Plugins[0].Network.Type) if err := network.deleteFromNetwork(ctx, rt, plugin.cniConfig); err != nil { - return fmt.Errorf("Error while removing pod from CNI network %q: %s", network.name, err) + return fmt.Errorf("error removing pod %s from CNI network %q: %v", fullPodName, network.name, err) } return nil }) @@ -680,10 +692,11 @@ results := make([]NetResult, 0) if err := plugin.forEachNetwork(&podNetwork, true, func(network *cniNetwork, podNetwork *PodNetwork, rt *libcni.RuntimeConf) error { + fullPodName := buildFullPodName(*podNetwork) + logrus.Infof("Checking pod %s for CNI network %s (type=%v)", fullPodName, network.name, network.config.Plugins[0].Network.Type) result, err := network.checkNetwork(ctx, rt, plugin.cniConfig, plugin.nsManager, podNetwork.NetNS) if err != nil { - logrus.Errorf("Error while checking pod to CNI network %q: %s", network.name, err) - return err + return fmt.Errorf("error checking pod %s for CNI network %q: %v", fullPodName, network.name, err) } if result != nil { results = append(results, NetResult{ @@ -703,19 +716,10 @@ } func (network *cniNetwork) addToNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig) (cnitypes.Result, error) { - logrus.Infof("About to add CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type) - res, err := cni.AddNetworkList(ctx, network.config, rt) - if err != nil { - logrus.Errorf("Error adding network: %v", err) - return nil, err - } - - return res, nil + return cni.AddNetworkList(ctx, network.config, rt) } func (network *cniNetwork) checkNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig, nsManager *nsManager, netns string) (cnitypes.Result, error) { - logrus.Infof("About to check CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type) - gtet, err := cniversion.GreaterThanOrEqualTo(network.config.CNIVersion, "0.4.0") if err != nil { return nil, err @@ -786,11 +790,7 @@ } func (network *cniNetwork) deleteFromNetwork(ctx context.Context, rt *libcni.RuntimeConf, cni *libcni.CNIConfig) error { - logrus.Infof("About to del CNI network %s (type=%v)", network.name, network.config.Plugins[0].Network.Type) - if err := cni.DelNetworkList(ctx, network.config, rt); err != nil { - return err - } - return nil + return cni.DelNetworkList(ctx, network.config, rt) } func buildCNIRuntimeConf(podNetwork *PodNetwork, ifName string, runtimeConfig RuntimeConfig) (*libcni.RuntimeConf, error) { @@ -809,6 +809,13 @@ CapabilityArgs: map[string]interface{}{}, } + // Propagate existing CNI_ARGS to non-k8s consumers + for _, kvpairs := range strings.Split(os.Getenv("CNI_ARGS"), ";") { + if keyval := strings.SplitN(kvpairs, "=", 2); len(keyval) == 2 { + rt.Args = append(rt.Args, [2]string{keyval[0], keyval[1]}) + } + } + // Add requested static IP to CNI_ARGS ip := runtimeConfig.IP if ip != "" { diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/application_exception.go 2021-12-26 00:45:51.000000000 +0000 @@ -19,6 +19,10 @@ package thrift +import ( + "context" +) + const ( UNKNOWN_APPLICATION_EXCEPTION = 0 UNKNOWN_METHOD = 1 @@ -28,14 +32,31 @@ MISSING_RESULT = 5 INTERNAL_ERROR = 6 PROTOCOL_ERROR = 7 + INVALID_TRANSFORM = 8 + INVALID_PROTOCOL = 9 + UNSUPPORTED_CLIENT_TYPE = 10 ) +var defaultApplicationExceptionMessage = map[int32]string{ + UNKNOWN_APPLICATION_EXCEPTION: "unknown application exception", + UNKNOWN_METHOD: "unknown method", + INVALID_MESSAGE_TYPE_EXCEPTION: "invalid message type", + WRONG_METHOD_NAME: "wrong method name", + BAD_SEQUENCE_ID: "bad sequence ID", + MISSING_RESULT: "missing result", + INTERNAL_ERROR: "unknown internal error", + PROTOCOL_ERROR: "unknown protocol error", + INVALID_TRANSFORM: "Invalid transform", + INVALID_PROTOCOL: "Invalid protocol", + UNSUPPORTED_CLIENT_TYPE: "Unsupported client type", +} + // Application level Thrift exception type TApplicationException interface { TException TypeId() int32 - Read(iprot TProtocol) (TApplicationException, error) - Write(oprot TProtocol) error + Read(ctx context.Context, iprot TProtocol) error + Write(ctx context.Context, oprot TProtocol) error } type tApplicationException struct { @@ -43,8 +64,17 @@ type_ int32 } +var _ TApplicationException = (*tApplicationException)(nil) + +func (tApplicationException) TExceptionType() TExceptionType { + return TExceptionTypeApplication +} + func (e tApplicationException) Error() string { - return e.message + if e.message != "" { + return e.message + } + return defaultApplicationExceptionMessage[e.type_] } func NewTApplicationException(type_ int32, message string) TApplicationException { @@ -55,19 +85,20 @@ return p.type_ } -func (p *tApplicationException) Read(iprot TProtocol) (TApplicationException, error) { - _, err := iprot.ReadStructBegin() +func (p *tApplicationException) Read(ctx context.Context, iprot TProtocol) error { + // TODO: this should really be generated by the compiler + _, err := iprot.ReadStructBegin(ctx) if err != nil { - return nil, err + return err } message := "" type_ := int32(UNKNOWN_APPLICATION_EXCEPTION) for { - _, ttype, id, err := iprot.ReadFieldBegin() + _, ttype, id, err := iprot.ReadFieldBegin(ctx) if err != nil { - return nil, err + return err } if ttype == STOP { break @@ -75,68 +106,75 @@ switch id { case 1: if ttype == STRING { - if message, err = iprot.ReadString(); err != nil { - return nil, err + if message, err = iprot.ReadString(ctx); err != nil { + return err } } else { - if err = SkipDefaultDepth(iprot, ttype); err != nil { - return nil, err + if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { + return err } } case 2: if ttype == I32 { - if type_, err = iprot.ReadI32(); err != nil { - return nil, err + if type_, err = iprot.ReadI32(ctx); err != nil { + return err } } else { - if err = SkipDefaultDepth(iprot, ttype); err != nil { - return nil, err + if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { + return err } } default: - if err = SkipDefaultDepth(iprot, ttype); err != nil { - return nil, err + if err = SkipDefaultDepth(ctx, iprot, ttype); err != nil { + return err } } - if err = iprot.ReadFieldEnd(); err != nil { - return nil, err + if err = iprot.ReadFieldEnd(ctx); err != nil { + return err } } - return NewTApplicationException(type_, message), iprot.ReadStructEnd() + if err := iprot.ReadStructEnd(ctx); err != nil { + return err + } + + p.message = message + p.type_ = type_ + + return nil } -func (p *tApplicationException) Write(oprot TProtocol) (err error) { - err = oprot.WriteStructBegin("TApplicationException") +func (p *tApplicationException) Write(ctx context.Context, oprot TProtocol) (err error) { + err = oprot.WriteStructBegin(ctx, "TApplicationException") if len(p.Error()) > 0 { - err = oprot.WriteFieldBegin("message", STRING, 1) + err = oprot.WriteFieldBegin(ctx, "message", STRING, 1) if err != nil { return } - err = oprot.WriteString(p.Error()) + err = oprot.WriteString(ctx, p.Error()) if err != nil { return } - err = oprot.WriteFieldEnd() + err = oprot.WriteFieldEnd(ctx) if err != nil { return } } - err = oprot.WriteFieldBegin("type", I32, 2) + err = oprot.WriteFieldBegin(ctx, "type", I32, 2) if err != nil { return } - err = oprot.WriteI32(p.type_) + err = oprot.WriteI32(ctx, p.type_) if err != nil { return } - err = oprot.WriteFieldEnd() + err = oprot.WriteFieldEnd(ctx) if err != nil { return } - err = oprot.WriteFieldStop() + err = oprot.WriteFieldStop(ctx) if err != nil { return } - err = oprot.WriteStructEnd() + err = oprot.WriteStructEnd(ctx) return } diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/binary_protocol.go 2021-12-26 00:45:51.000000000 +0000 @@ -21,6 +21,7 @@ import ( "bytes" + "context" "encoding/binary" "errors" "fmt" @@ -31,190 +32,220 @@ type TBinaryProtocol struct { trans TRichTransport origTransport TTransport - reader io.Reader - writer io.Writer - strictRead bool - strictWrite bool + cfg *TConfiguration buffer [64]byte } type TBinaryProtocolFactory struct { - strictRead bool - strictWrite bool + cfg *TConfiguration } +// Deprecated: Use NewTBinaryProtocolConf instead. func NewTBinaryProtocolTransport(t TTransport) *TBinaryProtocol { - return NewTBinaryProtocol(t, false, true) + return NewTBinaryProtocolConf(t, &TConfiguration{ + noPropagation: true, + }) } +// Deprecated: Use NewTBinaryProtocolConf instead. func NewTBinaryProtocol(t TTransport, strictRead, strictWrite bool) *TBinaryProtocol { - p := &TBinaryProtocol{origTransport: t, strictRead: strictRead, strictWrite: strictWrite} + return NewTBinaryProtocolConf(t, &TConfiguration{ + TBinaryStrictRead: &strictRead, + TBinaryStrictWrite: &strictWrite, + + noPropagation: true, + }) +} + +func NewTBinaryProtocolConf(t TTransport, conf *TConfiguration) *TBinaryProtocol { + PropagateTConfiguration(t, conf) + p := &TBinaryProtocol{ + origTransport: t, + cfg: conf, + } if et, ok := t.(TRichTransport); ok { p.trans = et } else { p.trans = NewTRichTransport(t) } - p.reader = p.trans - p.writer = p.trans return p } +// Deprecated: Use NewTBinaryProtocolFactoryConf instead. func NewTBinaryProtocolFactoryDefault() *TBinaryProtocolFactory { - return NewTBinaryProtocolFactory(false, true) + return NewTBinaryProtocolFactoryConf(&TConfiguration{ + noPropagation: true, + }) } +// Deprecated: Use NewTBinaryProtocolFactoryConf instead. func NewTBinaryProtocolFactory(strictRead, strictWrite bool) *TBinaryProtocolFactory { - return &TBinaryProtocolFactory{strictRead: strictRead, strictWrite: strictWrite} + return NewTBinaryProtocolFactoryConf(&TConfiguration{ + TBinaryStrictRead: &strictRead, + TBinaryStrictWrite: &strictWrite, + + noPropagation: true, + }) +} + +func NewTBinaryProtocolFactoryConf(conf *TConfiguration) *TBinaryProtocolFactory { + return &TBinaryProtocolFactory{ + cfg: conf, + } } func (p *TBinaryProtocolFactory) GetProtocol(t TTransport) TProtocol { - return NewTBinaryProtocol(t, p.strictRead, p.strictWrite) + return NewTBinaryProtocolConf(t, p.cfg) +} + +func (p *TBinaryProtocolFactory) SetTConfiguration(conf *TConfiguration) { + p.cfg = conf } /** * Writing Methods */ -func (p *TBinaryProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { - if p.strictWrite { +func (p *TBinaryProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { + if p.cfg.GetTBinaryStrictWrite() { version := uint32(VERSION_1) | uint32(typeId) - e := p.WriteI32(int32(version)) + e := p.WriteI32(ctx, int32(version)) if e != nil { return e } - e = p.WriteString(name) + e = p.WriteString(ctx, name) if e != nil { return e } - e = p.WriteI32(seqId) + e = p.WriteI32(ctx, seqId) return e } else { - e := p.WriteString(name) + e := p.WriteString(ctx, name) if e != nil { return e } - e = p.WriteByte(int8(typeId)) + e = p.WriteByte(ctx, int8(typeId)) if e != nil { return e } - e = p.WriteI32(seqId) + e = p.WriteI32(ctx, seqId) return e } return nil } -func (p *TBinaryProtocol) WriteMessageEnd() error { +func (p *TBinaryProtocol) WriteMessageEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteStructBegin(name string) error { +func (p *TBinaryProtocol) WriteStructBegin(ctx context.Context, name string) error { return nil } -func (p *TBinaryProtocol) WriteStructEnd() error { +func (p *TBinaryProtocol) WriteStructEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { - e := p.WriteByte(int8(typeId)) +func (p *TBinaryProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { + e := p.WriteByte(ctx, int8(typeId)) if e != nil { return e } - e = p.WriteI16(id) + e = p.WriteI16(ctx, id) return e } -func (p *TBinaryProtocol) WriteFieldEnd() error { +func (p *TBinaryProtocol) WriteFieldEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteFieldStop() error { - e := p.WriteByte(STOP) +func (p *TBinaryProtocol) WriteFieldStop(ctx context.Context) error { + e := p.WriteByte(ctx, STOP) return e } -func (p *TBinaryProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { - e := p.WriteByte(int8(keyType)) +func (p *TBinaryProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { + e := p.WriteByte(ctx, int8(keyType)) if e != nil { return e } - e = p.WriteByte(int8(valueType)) + e = p.WriteByte(ctx, int8(valueType)) if e != nil { return e } - e = p.WriteI32(int32(size)) + e = p.WriteI32(ctx, int32(size)) return e } -func (p *TBinaryProtocol) WriteMapEnd() error { +func (p *TBinaryProtocol) WriteMapEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteListBegin(elemType TType, size int) error { - e := p.WriteByte(int8(elemType)) +func (p *TBinaryProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { + e := p.WriteByte(ctx, int8(elemType)) if e != nil { return e } - e = p.WriteI32(int32(size)) + e = p.WriteI32(ctx, int32(size)) return e } -func (p *TBinaryProtocol) WriteListEnd() error { +func (p *TBinaryProtocol) WriteListEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteSetBegin(elemType TType, size int) error { - e := p.WriteByte(int8(elemType)) +func (p *TBinaryProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { + e := p.WriteByte(ctx, int8(elemType)) if e != nil { return e } - e = p.WriteI32(int32(size)) + e = p.WriteI32(ctx, int32(size)) return e } -func (p *TBinaryProtocol) WriteSetEnd() error { +func (p *TBinaryProtocol) WriteSetEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) WriteBool(value bool) error { +func (p *TBinaryProtocol) WriteBool(ctx context.Context, value bool) error { if value { - return p.WriteByte(1) + return p.WriteByte(ctx, 1) } - return p.WriteByte(0) + return p.WriteByte(ctx, 0) } -func (p *TBinaryProtocol) WriteByte(value int8) error { +func (p *TBinaryProtocol) WriteByte(ctx context.Context, value int8) error { e := p.trans.WriteByte(byte(value)) return NewTProtocolException(e) } -func (p *TBinaryProtocol) WriteI16(value int16) error { +func (p *TBinaryProtocol) WriteI16(ctx context.Context, value int16) error { v := p.buffer[0:2] binary.BigEndian.PutUint16(v, uint16(value)) - _, e := p.writer.Write(v) + _, e := p.trans.Write(v) return NewTProtocolException(e) } -func (p *TBinaryProtocol) WriteI32(value int32) error { +func (p *TBinaryProtocol) WriteI32(ctx context.Context, value int32) error { v := p.buffer[0:4] binary.BigEndian.PutUint32(v, uint32(value)) - _, e := p.writer.Write(v) + _, e := p.trans.Write(v) return NewTProtocolException(e) } -func (p *TBinaryProtocol) WriteI64(value int64) error { +func (p *TBinaryProtocol) WriteI64(ctx context.Context, value int64) error { v := p.buffer[0:8] binary.BigEndian.PutUint64(v, uint64(value)) - _, err := p.writer.Write(v) + _, err := p.trans.Write(v) return NewTProtocolException(err) } -func (p *TBinaryProtocol) WriteDouble(value float64) error { - return p.WriteI64(int64(math.Float64bits(value))) +func (p *TBinaryProtocol) WriteDouble(ctx context.Context, value float64) error { + return p.WriteI64(ctx, int64(math.Float64bits(value))) } -func (p *TBinaryProtocol) WriteString(value string) error { - e := p.WriteI32(int32(len(value))) +func (p *TBinaryProtocol) WriteString(ctx context.Context, value string) error { + e := p.WriteI32(ctx, int32(len(value))) if e != nil { return e } @@ -222,12 +253,12 @@ return NewTProtocolException(err) } -func (p *TBinaryProtocol) WriteBinary(value []byte) error { - e := p.WriteI32(int32(len(value))) +func (p *TBinaryProtocol) WriteBinary(ctx context.Context, value []byte) error { + e := p.WriteI32(ctx, int32(len(value))) if e != nil { return e } - _, err := p.writer.Write(value) + _, err := p.trans.Write(value) return NewTProtocolException(err) } @@ -235,8 +266,8 @@ * Reading methods */ -func (p *TBinaryProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { - size, e := p.ReadI32() +func (p *TBinaryProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { + size, e := p.ReadI32(ctx) if e != nil { return "", typeId, 0, NewTProtocolException(e) } @@ -246,79 +277,79 @@ if version != VERSION_1 { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Bad version in ReadMessageBegin")) } - name, e = p.ReadString() + name, e = p.ReadString(ctx) if e != nil { return name, typeId, seqId, NewTProtocolException(e) } - seqId, e = p.ReadI32() + seqId, e = p.ReadI32(ctx) if e != nil { return name, typeId, seqId, NewTProtocolException(e) } return name, typeId, seqId, nil } - if p.strictRead { + if p.cfg.GetTBinaryStrictRead() { return name, typeId, seqId, NewTProtocolExceptionWithType(BAD_VERSION, fmt.Errorf("Missing version in ReadMessageBegin")) } name, e2 := p.readStringBody(size) if e2 != nil { return name, typeId, seqId, e2 } - b, e3 := p.ReadByte() + b, e3 := p.ReadByte(ctx) if e3 != nil { return name, typeId, seqId, e3 } typeId = TMessageType(b) - seqId, e4 := p.ReadI32() + seqId, e4 := p.ReadI32(ctx) if e4 != nil { return name, typeId, seqId, e4 } return name, typeId, seqId, nil } -func (p *TBinaryProtocol) ReadMessageEnd() error { +func (p *TBinaryProtocol) ReadMessageEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadStructBegin() (name string, err error) { +func (p *TBinaryProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { return } -func (p *TBinaryProtocol) ReadStructEnd() error { +func (p *TBinaryProtocol) ReadStructEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadFieldBegin() (name string, typeId TType, seqId int16, err error) { - t, err := p.ReadByte() +func (p *TBinaryProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, seqId int16, err error) { + t, err := p.ReadByte(ctx) typeId = TType(t) if err != nil { return name, typeId, seqId, err } if t != STOP { - seqId, err = p.ReadI16() + seqId, err = p.ReadI16(ctx) } return name, typeId, seqId, err } -func (p *TBinaryProtocol) ReadFieldEnd() error { +func (p *TBinaryProtocol) ReadFieldEnd(ctx context.Context) error { return nil } var invalidDataLength = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Invalid data length")) -func (p *TBinaryProtocol) ReadMapBegin() (kType, vType TType, size int, err error) { - k, e := p.ReadByte() +func (p *TBinaryProtocol) ReadMapBegin(ctx context.Context) (kType, vType TType, size int, err error) { + k, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } kType = TType(k) - v, e := p.ReadByte() + v, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } vType = TType(v) - size32, e := p.ReadI32() + size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return @@ -331,18 +362,18 @@ return kType, vType, size, nil } -func (p *TBinaryProtocol) ReadMapEnd() error { +func (p *TBinaryProtocol) ReadMapEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadListBegin() (elemType TType, size int, err error) { - b, e := p.ReadByte() +func (p *TBinaryProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { + b, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) - size32, e := p.ReadI32() + size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return @@ -356,18 +387,18 @@ return } -func (p *TBinaryProtocol) ReadListEnd() error { +func (p *TBinaryProtocol) ReadListEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadSetBegin() (elemType TType, size int, err error) { - b, e := p.ReadByte() +func (p *TBinaryProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + b, e := p.ReadByte(ctx) if e != nil { err = NewTProtocolException(e) return } elemType = TType(b) - size32, e := p.ReadI32() + size32, e := p.ReadI32(ctx) if e != nil { err = NewTProtocolException(e) return @@ -380,12 +411,12 @@ return elemType, size, nil } -func (p *TBinaryProtocol) ReadSetEnd() error { +func (p *TBinaryProtocol) ReadSetEnd(ctx context.Context) error { return nil } -func (p *TBinaryProtocol) ReadBool() (bool, error) { - b, e := p.ReadByte() +func (p *TBinaryProtocol) ReadBool(ctx context.Context) (bool, error) { + b, e := p.ReadByte(ctx) v := true if b != 1 { v = false @@ -393,122 +424,132 @@ return v, e } -func (p *TBinaryProtocol) ReadByte() (int8, error) { +func (p *TBinaryProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.trans.ReadByte() return int8(v), err } -func (p *TBinaryProtocol) ReadI16() (value int16, err error) { +func (p *TBinaryProtocol) ReadI16(ctx context.Context) (value int16, err error) { buf := p.buffer[0:2] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = int16(binary.BigEndian.Uint16(buf)) return value, err } -func (p *TBinaryProtocol) ReadI32() (value int32, err error) { +func (p *TBinaryProtocol) ReadI32(ctx context.Context) (value int32, err error) { buf := p.buffer[0:4] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = int32(binary.BigEndian.Uint32(buf)) return value, err } -func (p *TBinaryProtocol) ReadI64() (value int64, err error) { +func (p *TBinaryProtocol) ReadI64(ctx context.Context) (value int64, err error) { buf := p.buffer[0:8] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = int64(binary.BigEndian.Uint64(buf)) return value, err } -func (p *TBinaryProtocol) ReadDouble() (value float64, err error) { +func (p *TBinaryProtocol) ReadDouble(ctx context.Context) (value float64, err error) { buf := p.buffer[0:8] - err = p.readAll(buf) + err = p.readAll(ctx, buf) value = math.Float64frombits(binary.BigEndian.Uint64(buf)) return value, err } -func (p *TBinaryProtocol) ReadString() (value string, err error) { - size, e := p.ReadI32() +func (p *TBinaryProtocol) ReadString(ctx context.Context) (value string, err error) { + size, e := p.ReadI32(ctx) if e != nil { return "", e } + err = checkSizeForProtocol(size, p.cfg) + if err != nil { + return + } if size < 0 { err = invalidDataLength return } + if size == 0 { + return "", nil + } + if size < int32(len(p.buffer)) { + // Avoid allocation on small reads + buf := p.buffer[:size] + read, e := io.ReadFull(p.trans, buf) + return string(buf[:read]), NewTProtocolException(e) + } return p.readStringBody(size) } -func (p *TBinaryProtocol) ReadBinary() ([]byte, error) { - size, e := p.ReadI32() +func (p *TBinaryProtocol) ReadBinary(ctx context.Context) ([]byte, error) { + size, e := p.ReadI32(ctx) if e != nil { return nil, e } - if size < 0 { - return nil, invalidDataLength - } - if uint64(size) > p.trans.RemainingBytes() { - return nil, invalidDataLength + if err := checkSizeForProtocol(size, p.cfg); err != nil { + return nil, err } - isize := int(size) - buf := make([]byte, isize) - _, err := io.ReadFull(p.trans, buf) + buf, err := safeReadBytes(size, p.trans) return buf, NewTProtocolException(err) } -func (p *TBinaryProtocol) Flush() (err error) { - return NewTProtocolException(p.trans.Flush()) +func (p *TBinaryProtocol) Flush(ctx context.Context) (err error) { + return NewTProtocolException(p.trans.Flush(ctx)) } -func (p *TBinaryProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TBinaryProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TBinaryProtocol) Transport() TTransport { return p.origTransport } -func (p *TBinaryProtocol) readAll(buf []byte) error { - _, err := io.ReadFull(p.reader, buf) +func (p *TBinaryProtocol) readAll(ctx context.Context, buf []byte) (err error) { + var read int + _, deadlineSet := ctx.Deadline() + for { + read, err = io.ReadFull(p.trans, buf) + if deadlineSet && read == 0 && isTimeoutError(err) && ctx.Err() == nil { + // This is I/O timeout without anything read, + // and we still have time left, keep retrying. + continue + } + // For anything else, don't retry + break + } return NewTProtocolException(err) } -const readLimit = 32768 - func (p *TBinaryProtocol) readStringBody(size int32) (value string, err error) { + buf, err := safeReadBytes(size, p.trans) + return string(buf), NewTProtocolException(err) +} + +func (p *TBinaryProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.trans, conf) + PropagateTConfiguration(p.origTransport, conf) + p.cfg = conf +} + +var ( + _ TConfigurationSetter = (*TBinaryProtocolFactory)(nil) + _ TConfigurationSetter = (*TBinaryProtocol)(nil) +) + +// This function is shared between TBinaryProtocol and TCompactProtocol. +// +// It tries to read size bytes from trans, in a way that prevents large +// allocations when size is insanely large (mostly caused by malformed message). +func safeReadBytes(size int32, trans io.Reader) ([]byte, error) { if size < 0 { - return "", nil - } - if uint64(size) > p.trans.RemainingBytes() { - return "", invalidDataLength + return nil, nil } - var ( - buf bytes.Buffer - e error - b []byte - ) - - switch { - case int(size) <= len(p.buffer): - b = p.buffer[:size] // avoids allocation for small reads - case int(size) < readLimit: - b = make([]byte, size) - default: - b = make([]byte, readLimit) - } - - for size > 0 { - _, e = io.ReadFull(p.trans, b) - buf.Write(b) - if e != nil { - break - } - size -= readLimit - if size < readLimit && size > 0 { - b = b[:size] - } - } - return buf.String(), NewTProtocolException(e) + buf := new(bytes.Buffer) + _, err := io.CopyN(buf, trans, int64(size)) + return buf.Bytes(), err } diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/client.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/client.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/client.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/client.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,109 @@ +package thrift + +import ( + "context" + "fmt" +) + +// ResponseMeta represents the metadata attached to the response. +type ResponseMeta struct { + // The headers in the response, if any. + // If the underlying transport/protocol is not THeader, this will always be nil. + Headers THeaderMap +} + +type TClient interface { + Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) +} + +type TStandardClient struct { + seqId int32 + iprot, oprot TProtocol +} + +// TStandardClient implements TClient, and uses the standard message format for Thrift. +// It is not safe for concurrent use. +func NewTStandardClient(inputProtocol, outputProtocol TProtocol) *TStandardClient { + return &TStandardClient{ + iprot: inputProtocol, + oprot: outputProtocol, + } +} + +func (p *TStandardClient) Send(ctx context.Context, oprot TProtocol, seqId int32, method string, args TStruct) error { + // Set headers from context object on THeaderProtocol + if headerProt, ok := oprot.(*THeaderProtocol); ok { + headerProt.ClearWriteHeaders() + for _, key := range GetWriteHeaderList(ctx) { + if value, ok := GetHeader(ctx, key); ok { + headerProt.SetWriteHeader(key, value) + } + } + } + + if err := oprot.WriteMessageBegin(ctx, method, CALL, seqId); err != nil { + return err + } + if err := args.Write(ctx, oprot); err != nil { + return err + } + if err := oprot.WriteMessageEnd(ctx); err != nil { + return err + } + return oprot.Flush(ctx) +} + +func (p *TStandardClient) Recv(ctx context.Context, iprot TProtocol, seqId int32, method string, result TStruct) error { + rMethod, rTypeId, rSeqId, err := iprot.ReadMessageBegin(ctx) + if err != nil { + return err + } + + if method != rMethod { + return NewTApplicationException(WRONG_METHOD_NAME, fmt.Sprintf("%s: wrong method name", method)) + } else if seqId != rSeqId { + return NewTApplicationException(BAD_SEQUENCE_ID, fmt.Sprintf("%s: out of order sequence response", method)) + } else if rTypeId == EXCEPTION { + var exception tApplicationException + if err := exception.Read(ctx, iprot); err != nil { + return err + } + + if err := iprot.ReadMessageEnd(ctx); err != nil { + return err + } + + return &exception + } else if rTypeId != REPLY { + return NewTApplicationException(INVALID_MESSAGE_TYPE_EXCEPTION, fmt.Sprintf("%s: invalid message type", method)) + } + + if err := result.Read(ctx, iprot); err != nil { + return err + } + + return iprot.ReadMessageEnd(ctx) +} + +func (p *TStandardClient) Call(ctx context.Context, method string, args, result TStruct) (ResponseMeta, error) { + p.seqId++ + seqId := p.seqId + + if err := p.Send(ctx, p.oprot, seqId, method, args); err != nil { + return ResponseMeta{}, err + } + + // method is oneway + if result == nil { + return ResponseMeta{}, nil + } + + err := p.Recv(ctx, p.iprot, seqId, method, result) + var headers THeaderMap + if hp, ok := p.iprot.(*THeaderProtocol); ok { + headers = hp.transport.readHeaders + } + return ResponseMeta{ + Headers: headers, + }, err +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/compact_protocol.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,7 +20,9 @@ package thrift import ( + "context" "encoding/binary" + "errors" "fmt" "io" "math" @@ -73,20 +75,37 @@ } } -type TCompactProtocolFactory struct{} +type TCompactProtocolFactory struct { + cfg *TConfiguration +} +// Deprecated: Use NewTCompactProtocolFactoryConf instead. func NewTCompactProtocolFactory() *TCompactProtocolFactory { - return &TCompactProtocolFactory{} + return NewTCompactProtocolFactoryConf(&TConfiguration{ + noPropagation: true, + }) +} + +func NewTCompactProtocolFactoryConf(conf *TConfiguration) *TCompactProtocolFactory { + return &TCompactProtocolFactory{ + cfg: conf, + } } func (p *TCompactProtocolFactory) GetProtocol(trans TTransport) TProtocol { - return NewTCompactProtocol(trans) + return NewTCompactProtocolConf(trans, p.cfg) +} + +func (p *TCompactProtocolFactory) SetTConfiguration(conf *TConfiguration) { + p.cfg = conf } type TCompactProtocol struct { trans TRichTransport origTransport TTransport + cfg *TConfiguration + // Used to keep track of the last field for the current and previous structs, // so we can do the delta stuff. lastField []int @@ -105,9 +124,19 @@ buffer [64]byte } -// Create a TCompactProtocol given a TTransport +// Deprecated: Use NewTCompactProtocolConf instead. func NewTCompactProtocol(trans TTransport) *TCompactProtocol { - p := &TCompactProtocol{origTransport: trans, lastField: []int{}} + return NewTCompactProtocolConf(trans, &TConfiguration{ + noPropagation: true, + }) +} + +func NewTCompactProtocolConf(trans TTransport, conf *TConfiguration) *TCompactProtocol { + PropagateTConfiguration(trans, conf) + p := &TCompactProtocol{ + origTransport: trans, + cfg: conf, + } if et, ok := trans.(TRichTransport); ok { p.trans = et } else { @@ -115,7 +144,6 @@ } return p - } // @@ -124,7 +152,7 @@ // Write a message header to the wire. Compact Protocol messages contain the // protocol version so we can migrate forwards in the future if need be. -func (p *TCompactProtocol) WriteMessageBegin(name string, typeId TMessageType, seqid int32) error { +func (p *TCompactProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error { err := p.writeByteDirect(COMPACT_PROTOCOL_ID) if err != nil { return NewTProtocolException(err) @@ -137,17 +165,17 @@ if err != nil { return NewTProtocolException(err) } - e := p.WriteString(name) + e := p.WriteString(ctx, name) return e } -func (p *TCompactProtocol) WriteMessageEnd() error { return nil } +func (p *TCompactProtocol) WriteMessageEnd(ctx context.Context) error { return nil } // Write a struct begin. This doesn't actually put anything on the wire. We // use it as an opportunity to put special placeholder markers on the field // stack so we can get the field id deltas correct. -func (p *TCompactProtocol) WriteStructBegin(name string) error { +func (p *TCompactProtocol) WriteStructBegin(ctx context.Context, name string) error { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return nil @@ -156,26 +184,29 @@ // Write a struct end. This doesn't actually put anything on the wire. We use // this as an opportunity to pop the last field from the current struct off // of the field stack. -func (p *TCompactProtocol) WriteStructEnd() error { +func (p *TCompactProtocol) WriteStructEnd(ctx context.Context) error { + if len(p.lastField) <= 0 { + return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("WriteStructEnd called without matching WriteStructBegin call before")) + } p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } -func (p *TCompactProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { +func (p *TCompactProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { if typeId == BOOL { // we want to possibly include the value, so we'll wait. p.booleanFieldName, p.booleanFieldId, p.booleanFieldPending = name, id, true return nil } - _, err := p.writeFieldBeginInternal(name, typeId, id, 0xFF) + _, err := p.writeFieldBeginInternal(ctx, name, typeId, id, 0xFF) return NewTProtocolException(err) } // The workhorse of writeFieldBegin. It has the option of doing a // 'type override' of the type header. This is used specifically in the // boolean field case. -func (p *TCompactProtocol) writeFieldBeginInternal(name string, typeId TType, id int16, typeOverride byte) (int, error) { +func (p *TCompactProtocol) writeFieldBeginInternal(ctx context.Context, name string, typeId TType, id int16, typeOverride byte) (int, error) { // short lastField = lastField_.pop(); // if there's a type override, use that. @@ -200,7 +231,7 @@ if err != nil { return 0, err } - err = p.WriteI16(id) + err = p.WriteI16(ctx, id) written = 1 + 2 if err != nil { return 0, err @@ -208,18 +239,17 @@ } p.lastFieldId = fieldId - // p.lastField.Push(field.id); return written, nil } -func (p *TCompactProtocol) WriteFieldEnd() error { return nil } +func (p *TCompactProtocol) WriteFieldEnd(ctx context.Context) error { return nil } -func (p *TCompactProtocol) WriteFieldStop() error { +func (p *TCompactProtocol) WriteFieldStop(ctx context.Context) error { err := p.writeByteDirect(STOP) return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { +func (p *TCompactProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if size == 0 { err := p.writeByteDirect(0) return NewTProtocolException(err) @@ -232,32 +262,32 @@ return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteMapEnd() error { return nil } +func (p *TCompactProtocol) WriteMapEnd(ctx context.Context) error { return nil } // Write a list header. -func (p *TCompactProtocol) WriteListBegin(elemType TType, size int) error { +func (p *TCompactProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteListEnd() error { return nil } +func (p *TCompactProtocol) WriteListEnd(ctx context.Context) error { return nil } // Write a set header. -func (p *TCompactProtocol) WriteSetBegin(elemType TType, size int) error { +func (p *TCompactProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { _, err := p.writeCollectionBegin(elemType, size) return NewTProtocolException(err) } -func (p *TCompactProtocol) WriteSetEnd() error { return nil } +func (p *TCompactProtocol) WriteSetEnd(ctx context.Context) error { return nil } -func (p *TCompactProtocol) WriteBool(value bool) error { +func (p *TCompactProtocol) WriteBool(ctx context.Context, value bool) error { v := byte(COMPACT_BOOLEAN_FALSE) if value { v = byte(COMPACT_BOOLEAN_TRUE) } if p.booleanFieldPending { // we haven't written the field header yet - _, err := p.writeFieldBeginInternal(p.booleanFieldName, BOOL, p.booleanFieldId, v) + _, err := p.writeFieldBeginInternal(ctx, p.booleanFieldName, BOOL, p.booleanFieldId, v) p.booleanFieldPending = false return NewTProtocolException(err) } @@ -267,31 +297,31 @@ } // Write a byte. Nothing to see here! -func (p *TCompactProtocol) WriteByte(value int8) error { +func (p *TCompactProtocol) WriteByte(ctx context.Context, value int8) error { err := p.writeByteDirect(byte(value)) return NewTProtocolException(err) } // Write an I16 as a zigzag varint. -func (p *TCompactProtocol) WriteI16(value int16) error { +func (p *TCompactProtocol) WriteI16(ctx context.Context, value int16) error { _, err := p.writeVarint32(p.int32ToZigzag(int32(value))) return NewTProtocolException(err) } // Write an i32 as a zigzag varint. -func (p *TCompactProtocol) WriteI32(value int32) error { +func (p *TCompactProtocol) WriteI32(ctx context.Context, value int32) error { _, err := p.writeVarint32(p.int32ToZigzag(value)) return NewTProtocolException(err) } // Write an i64 as a zigzag varint. -func (p *TCompactProtocol) WriteI64(value int64) error { +func (p *TCompactProtocol) WriteI64(ctx context.Context, value int64) error { _, err := p.writeVarint64(p.int64ToZigzag(value)) return NewTProtocolException(err) } // Write a double to the wire as 8 bytes. -func (p *TCompactProtocol) WriteDouble(value float64) error { +func (p *TCompactProtocol) WriteDouble(ctx context.Context, value float64) error { buf := p.buffer[0:8] binary.LittleEndian.PutUint64(buf, math.Float64bits(value)) _, err := p.trans.Write(buf) @@ -299,7 +329,7 @@ } // Write a string to the wire with a varint size preceding. -func (p *TCompactProtocol) WriteString(value string) error { +func (p *TCompactProtocol) WriteString(ctx context.Context, value string) error { _, e := p.writeVarint32(int32(len(value))) if e != nil { return NewTProtocolException(e) @@ -311,7 +341,7 @@ } // Write a byte array, using a varint for the size. -func (p *TCompactProtocol) WriteBinary(bin []byte) error { +func (p *TCompactProtocol) WriteBinary(ctx context.Context, bin []byte) error { _, e := p.writeVarint32(int32(len(bin))) if e != nil { return NewTProtocolException(e) @@ -328,9 +358,20 @@ // // Read a message header. -func (p *TCompactProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { +func (p *TCompactProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { + var protocolId byte - protocolId, err := p.readByteDirect() + _, deadlineSet := ctx.Deadline() + for { + protocolId, err = p.readByteDirect() + if deadlineSet && isTimeoutError(err) && ctx.Err() == nil { + // keep retrying I/O timeout errors since we still have + // time left + continue + } + // For anything else, don't retry + break + } if err != nil { return } @@ -357,15 +398,15 @@ err = NewTProtocolException(e) return } - name, err = p.ReadString() + name, err = p.ReadString(ctx) return } -func (p *TCompactProtocol) ReadMessageEnd() error { return nil } +func (p *TCompactProtocol) ReadMessageEnd(ctx context.Context) error { return nil } // Read a struct begin. There's nothing on the wire for this, but it is our // opportunity to push a new struct begin marker onto the field stack. -func (p *TCompactProtocol) ReadStructBegin() (name string, err error) { +func (p *TCompactProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { p.lastField = append(p.lastField, p.lastFieldId) p.lastFieldId = 0 return @@ -373,15 +414,18 @@ // Doesn't actually consume any wire data, just removes the last field for // this struct from the field stack. -func (p *TCompactProtocol) ReadStructEnd() error { +func (p *TCompactProtocol) ReadStructEnd(ctx context.Context) error { // consume the last field we read off the wire. + if len(p.lastField) <= 0 { + return NewTProtocolExceptionWithType(INVALID_DATA, errors.New("ReadStructEnd called without matching ReadStructBegin call before")) + } p.lastFieldId = p.lastField[len(p.lastField)-1] p.lastField = p.lastField[:len(p.lastField)-1] return nil } // Read a field header off the wire. -func (p *TCompactProtocol) ReadFieldBegin() (name string, typeId TType, id int16, err error) { +func (p *TCompactProtocol) ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) { t, err := p.readByteDirect() if err != nil { return @@ -396,7 +440,7 @@ modifier := int16((t & 0xf0) >> 4) if modifier == 0 { // not a delta. look ahead for the zigzag varint field id. - id, err = p.ReadI16() + id, err = p.ReadI16(ctx) if err != nil { return } @@ -422,12 +466,12 @@ return } -func (p *TCompactProtocol) ReadFieldEnd() error { return nil } +func (p *TCompactProtocol) ReadFieldEnd(ctx context.Context) error { return nil } // Read a map header off the wire. If the size is zero, skip reading the key // and value type. This means that 0-length maps will yield TMaps without the // "correct" types. -func (p *TCompactProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, err error) { +func (p *TCompactProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { size32, e := p.readVarint32() if e != nil { err = NewTProtocolException(e) @@ -451,13 +495,13 @@ return } -func (p *TCompactProtocol) ReadMapEnd() error { return nil } +func (p *TCompactProtocol) ReadMapEnd(ctx context.Context) error { return nil } // Read a list header off the wire. If the list size is 0-14, the size will // be packed into the element type header. If it's a longer list, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. -func (p *TCompactProtocol) ReadListBegin() (elemType TType, size int, err error) { +func (p *TCompactProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { size_and_type, err := p.readByteDirect() if err != nil { return @@ -483,22 +527,22 @@ return } -func (p *TCompactProtocol) ReadListEnd() error { return nil } +func (p *TCompactProtocol) ReadListEnd(ctx context.Context) error { return nil } // Read a set header off the wire. If the set size is 0-14, the size will // be packed into the element type header. If it's a longer set, the 4 MSB // of the element type header will be 0xF, and a varint will follow with the // true size. -func (p *TCompactProtocol) ReadSetBegin() (elemType TType, size int, err error) { - return p.ReadListBegin() +func (p *TCompactProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + return p.ReadListBegin(ctx) } -func (p *TCompactProtocol) ReadSetEnd() error { return nil } +func (p *TCompactProtocol) ReadSetEnd(ctx context.Context) error { return nil } // Read a boolean off the wire. If this is a boolean field, the value should // already have been read during readFieldBegin, so we'll just consume the // pre-stored value. Otherwise, read a byte. -func (p *TCompactProtocol) ReadBool() (value bool, err error) { +func (p *TCompactProtocol) ReadBool(ctx context.Context) (value bool, err error) { if p.boolValueIsNotNull { p.boolValueIsNotNull = false return p.boolValue, nil @@ -508,7 +552,7 @@ } // Read a single byte off the wire. Nothing interesting here. -func (p *TCompactProtocol) ReadByte() (int8, error) { +func (p *TCompactProtocol) ReadByte(ctx context.Context) (int8, error) { v, err := p.readByteDirect() if err != nil { return 0, NewTProtocolException(err) @@ -517,13 +561,13 @@ } // Read an i16 from the wire as a zigzag varint. -func (p *TCompactProtocol) ReadI16() (value int16, err error) { - v, err := p.ReadI32() +func (p *TCompactProtocol) ReadI16(ctx context.Context) (value int16, err error) { + v, err := p.ReadI32(ctx) return int16(v), err } // Read an i32 from the wire as a zigzag varint. -func (p *TCompactProtocol) ReadI32() (value int32, err error) { +func (p *TCompactProtocol) ReadI32(ctx context.Context) (value int32, err error) { v, e := p.readVarint32() if e != nil { return 0, NewTProtocolException(e) @@ -533,7 +577,7 @@ } // Read an i64 from the wire as a zigzag varint. -func (p *TCompactProtocol) ReadI64() (value int64, err error) { +func (p *TCompactProtocol) ReadI64(ctx context.Context) (value int64, err error) { v, e := p.readVarint64() if e != nil { return 0, NewTProtocolException(e) @@ -543,7 +587,7 @@ } // No magic here - just read a double off the wire. -func (p *TCompactProtocol) ReadDouble() (value float64, err error) { +func (p *TCompactProtocol) ReadDouble(ctx context.Context) (value float64, err error) { longBits := p.buffer[0:8] _, e := io.ReadFull(p.trans, longBits) if e != nil { @@ -553,58 +597,53 @@ } // Reads a []byte (via readBinary), and then UTF-8 decodes it. -func (p *TCompactProtocol) ReadString() (value string, err error) { +func (p *TCompactProtocol) ReadString(ctx context.Context) (value string, err error) { length, e := p.readVarint32() if e != nil { return "", NewTProtocolException(e) } - if length < 0 { - return "", invalidDataLength - } - if uint64(length) > p.trans.RemainingBytes() { - return "", invalidDataLength + err = checkSizeForProtocol(length, p.cfg) + if err != nil { + return } - if length == 0 { return "", nil } - var buf []byte - if length <= int32(len(p.buffer)) { - buf = p.buffer[0:length] - } else { - buf = make([]byte, length) + if length < int32(len(p.buffer)) { + // Avoid allocation on small reads + buf := p.buffer[:length] + read, e := io.ReadFull(p.trans, buf) + return string(buf[:read]), NewTProtocolException(e) } - _, e = io.ReadFull(p.trans, buf) + + buf, e := safeReadBytes(length, p.trans) return string(buf), NewTProtocolException(e) } // Read a []byte from the wire. -func (p *TCompactProtocol) ReadBinary() (value []byte, err error) { +func (p *TCompactProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { length, e := p.readVarint32() if e != nil { return nil, NewTProtocolException(e) } + err = checkSizeForProtocol(length, p.cfg) + if err != nil { + return + } if length == 0 { return []byte{}, nil } - if length < 0 { - return nil, invalidDataLength - } - if uint64(length) > p.trans.RemainingBytes() { - return nil, invalidDataLength - } - buf := make([]byte, length) - _, e = io.ReadFull(p.trans, buf) + buf, e := safeReadBytes(length, p.trans) return buf, NewTProtocolException(e) } -func (p *TCompactProtocol) Flush() (err error) { - return NewTProtocolException(p.trans.Flush()) +func (p *TCompactProtocol) Flush(ctx context.Context) (err error) { + return NewTProtocolException(p.trans.Flush(ctx)) } -func (p *TCompactProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TCompactProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TCompactProtocol) Transport() TTransport { @@ -806,10 +845,21 @@ case COMPACT_STRUCT: return STRUCT, nil } - return STOP, TException(fmt.Errorf("don't know what type: %d", t&0x0f)) + return STOP, NewTProtocolException(fmt.Errorf("don't know what type: %v", t&0x0f)) } // Given a TType value, find the appropriate TCompactProtocol.Types constant. func (p *TCompactProtocol) getCompactType(t TType) tCompactType { return ttypeToCompactType[t] } + +func (p *TCompactProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.trans, conf) + PropagateTConfiguration(p.origTransport, conf) + p.cfg = conf +} + +var ( + _ TConfigurationSetter = (*TCompactProtocolFactory)(nil) + _ TConfigurationSetter = (*TCompactProtocol)(nil) +) diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/configuration.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/configuration.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/configuration.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/configuration.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,378 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "crypto/tls" + "fmt" + "time" +) + +// Default TConfiguration values. +const ( + DEFAULT_MAX_MESSAGE_SIZE = 100 * 1024 * 1024 + DEFAULT_MAX_FRAME_SIZE = 16384000 + + DEFAULT_TBINARY_STRICT_READ = false + DEFAULT_TBINARY_STRICT_WRITE = true + + DEFAULT_CONNECT_TIMEOUT = 0 + DEFAULT_SOCKET_TIMEOUT = 0 +) + +// TConfiguration defines some configurations shared between TTransport, +// TProtocol, TTransportFactory, TProtocolFactory, and other implementations. +// +// When constructing TConfiguration, you only need to specify the non-default +// fields. All zero values have sane default values. +// +// Not all configurations defined are applicable to all implementations. +// Implementations are free to ignore the configurations not applicable to them. +// +// All functions attached to this type are nil-safe. +// +// See [1] for spec. +// +// NOTE: When using TConfiguration, fill in all the configurations you want to +// set across the stack, not only the ones you want to set in the immediate +// TTransport/TProtocol. +// +// For example, say you want to migrate this old code into using TConfiguration: +// +// sccket := thrift.NewTSocketTimeout("host:port", time.Second) +// transFactory := thrift.NewTFramedTransportFactoryMaxLength( +// thrift.NewTTransportFactory(), +// 1024 * 1024 * 256, +// ) +// protoFactory := thrift.NewTBinaryProtocolFactory(true, true) +// +// This is the wrong way to do it because in the end the TConfiguration used by +// socket and transFactory will be overwritten by the one used by protoFactory +// because of TConfiguration propagation: +// +// // bad example, DO NOT USE +// sccket := thrift.NewTSocketConf("host:port", &thrift.TConfiguration{ +// ConnectTimeout: time.Second, +// SocketTimeout: time.Second, +// }) +// transFactory := thrift.NewTFramedTransportFactoryConf( +// thrift.NewTTransportFactory(), +// &thrift.TConfiguration{ +// MaxFrameSize: 1024 * 1024 * 256, +// }, +// ) +// protoFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{ +// TBinaryStrictRead: thrift.BoolPtr(true), +// TBinaryStrictWrite: thrift.BoolPtr(true), +// }) +// +// This is the correct way to do it: +// +// conf := &thrift.TConfiguration{ +// ConnectTimeout: time.Second, +// SocketTimeout: time.Second, +// +// MaxFrameSize: 1024 * 1024 * 256, +// +// TBinaryStrictRead: thrift.BoolPtr(true), +// TBinaryStrictWrite: thrift.BoolPtr(true), +// } +// sccket := thrift.NewTSocketConf("host:port", conf) +// transFactory := thrift.NewTFramedTransportFactoryConf(thrift.NewTTransportFactory(), conf) +// protoFactory := thrift.NewTBinaryProtocolFactoryConf(conf) +// +// [1]: https://github.com/apache/thrift/blob/master/doc/specs/thrift-tconfiguration.md +type TConfiguration struct { + // If <= 0, DEFAULT_MAX_MESSAGE_SIZE will be used instead. + MaxMessageSize int32 + + // If <= 0, DEFAULT_MAX_FRAME_SIZE will be used instead. + // + // Also if MaxMessageSize < MaxFrameSize, + // MaxMessageSize will be used instead. + MaxFrameSize int32 + + // Connect and socket timeouts to be used by TSocket and TSSLSocket. + // + // 0 means no timeout. + // + // If <0, DEFAULT_CONNECT_TIMEOUT and DEFAULT_SOCKET_TIMEOUT will be + // used. + ConnectTimeout time.Duration + SocketTimeout time.Duration + + // TLS config to be used by TSSLSocket. + TLSConfig *tls.Config + + // Strict read/write configurations for TBinaryProtocol. + // + // BoolPtr helper function is available to use literal values. + TBinaryStrictRead *bool + TBinaryStrictWrite *bool + + // The wrapped protocol id to be used in THeader transport/protocol. + // + // THeaderProtocolIDPtr and THeaderProtocolIDPtrMust helper functions + // are provided to help filling this value. + THeaderProtocolID *THeaderProtocolID + + // Used internally by deprecated constructors, to avoid overriding + // underlying TTransport/TProtocol's cfg by accidental propagations. + // + // For external users this is always false. + noPropagation bool +} + +// GetMaxMessageSize returns the max message size an implementation should +// follow. +// +// It's nil-safe. DEFAULT_MAX_MESSAGE_SIZE will be returned if tc is nil. +func (tc *TConfiguration) GetMaxMessageSize() int32 { + if tc == nil || tc.MaxMessageSize <= 0 { + return DEFAULT_MAX_MESSAGE_SIZE + } + return tc.MaxMessageSize +} + +// GetMaxFrameSize returns the max frame size an implementation should follow. +// +// It's nil-safe. DEFAULT_MAX_FRAME_SIZE will be returned if tc is nil. +// +// If the configured max message size is smaller than the configured max frame +// size, the smaller one will be returned instead. +func (tc *TConfiguration) GetMaxFrameSize() int32 { + if tc == nil { + return DEFAULT_MAX_FRAME_SIZE + } + maxFrameSize := tc.MaxFrameSize + if maxFrameSize <= 0 { + maxFrameSize = DEFAULT_MAX_FRAME_SIZE + } + if maxMessageSize := tc.GetMaxMessageSize(); maxMessageSize < maxFrameSize { + return maxMessageSize + } + return maxFrameSize +} + +// GetConnectTimeout returns the connect timeout should be used by TSocket and +// TSSLSocket. +// +// It's nil-safe. If tc is nil, DEFAULT_CONNECT_TIMEOUT will be returned instead. +func (tc *TConfiguration) GetConnectTimeout() time.Duration { + if tc == nil || tc.ConnectTimeout < 0 { + return DEFAULT_CONNECT_TIMEOUT + } + return tc.ConnectTimeout +} + +// GetSocketTimeout returns the socket timeout should be used by TSocket and +// TSSLSocket. +// +// It's nil-safe. If tc is nil, DEFAULT_SOCKET_TIMEOUT will be returned instead. +func (tc *TConfiguration) GetSocketTimeout() time.Duration { + if tc == nil || tc.SocketTimeout < 0 { + return DEFAULT_SOCKET_TIMEOUT + } + return tc.SocketTimeout +} + +// GetTLSConfig returns the tls config should be used by TSSLSocket. +// +// It's nil-safe. If tc is nil, nil will be returned instead. +func (tc *TConfiguration) GetTLSConfig() *tls.Config { + if tc == nil { + return nil + } + return tc.TLSConfig +} + +// GetTBinaryStrictRead returns the strict read configuration TBinaryProtocol +// should follow. +// +// It's nil-safe. DEFAULT_TBINARY_STRICT_READ will be returned if either tc or +// tc.TBinaryStrictRead is nil. +func (tc *TConfiguration) GetTBinaryStrictRead() bool { + if tc == nil || tc.TBinaryStrictRead == nil { + return DEFAULT_TBINARY_STRICT_READ + } + return *tc.TBinaryStrictRead +} + +// GetTBinaryStrictWrite returns the strict read configuration TBinaryProtocol +// should follow. +// +// It's nil-safe. DEFAULT_TBINARY_STRICT_WRITE will be returned if either tc or +// tc.TBinaryStrictWrite is nil. +func (tc *TConfiguration) GetTBinaryStrictWrite() bool { + if tc == nil || tc.TBinaryStrictWrite == nil { + return DEFAULT_TBINARY_STRICT_WRITE + } + return *tc.TBinaryStrictWrite +} + +// GetTHeaderProtocolID returns the THeaderProtocolID should be used by +// THeaderProtocol clients (for servers, they always use the same one as the +// client instead). +// +// It's nil-safe. If either tc or tc.THeaderProtocolID is nil, +// THeaderProtocolDefault will be returned instead. +// THeaderProtocolDefault will also be returned if configured value is invalid. +func (tc *TConfiguration) GetTHeaderProtocolID() THeaderProtocolID { + if tc == nil || tc.THeaderProtocolID == nil { + return THeaderProtocolDefault + } + protoID := *tc.THeaderProtocolID + if err := protoID.Validate(); err != nil { + return THeaderProtocolDefault + } + return protoID +} + +// THeaderProtocolIDPtr validates and returns the pointer to id. +// +// If id is not a valid THeaderProtocolID, a pointer to THeaderProtocolDefault +// and the validation error will be returned. +func THeaderProtocolIDPtr(id THeaderProtocolID) (*THeaderProtocolID, error) { + err := id.Validate() + if err != nil { + id = THeaderProtocolDefault + } + return &id, err +} + +// THeaderProtocolIDPtrMust validates and returns the pointer to id. +// +// It's similar to THeaderProtocolIDPtr, but it panics on validation errors +// instead of returning them. +func THeaderProtocolIDPtrMust(id THeaderProtocolID) *THeaderProtocolID { + ptr, err := THeaderProtocolIDPtr(id) + if err != nil { + panic(err) + } + return ptr +} + +// TConfigurationSetter is an optional interface TProtocol, TTransport, +// TProtocolFactory, TTransportFactory, and other implementations can implement. +// +// It's intended to be called during intializations. +// The behavior of calling SetTConfiguration on a TTransport/TProtocol in the +// middle of a message is undefined: +// It may or may not change the behavior of the current processing message, +// and it may even cause the current message to fail. +// +// Note for implementations: SetTConfiguration might be called multiple times +// with the same value in quick successions due to the implementation of the +// propagation. Implementations should make SetTConfiguration as simple as +// possible (usually just overwrite the stored configuration and propagate it to +// the wrapped TTransports/TProtocols). +type TConfigurationSetter interface { + SetTConfiguration(*TConfiguration) +} + +// PropagateTConfiguration propagates cfg to impl if impl implements +// TConfigurationSetter and cfg is non-nil, otherwise it does nothing. +// +// NOTE: nil cfg is not propagated. If you want to propagate a TConfiguration +// with everything being default value, use &TConfiguration{} explicitly instead. +func PropagateTConfiguration(impl interface{}, cfg *TConfiguration) { + if cfg == nil || cfg.noPropagation { + return + } + + if setter, ok := impl.(TConfigurationSetter); ok { + setter.SetTConfiguration(cfg) + } +} + +func checkSizeForProtocol(size int32, cfg *TConfiguration) error { + if size < 0 { + return NewTProtocolExceptionWithType( + NEGATIVE_SIZE, + fmt.Errorf("negative size: %d", size), + ) + } + if size > cfg.GetMaxMessageSize() { + return NewTProtocolExceptionWithType( + SIZE_LIMIT, + fmt.Errorf("size exceeded max allowed: %d", size), + ) + } + return nil +} + +type tTransportFactoryConf struct { + delegate TTransportFactory + cfg *TConfiguration +} + +func (f *tTransportFactoryConf) GetTransport(orig TTransport) (TTransport, error) { + trans, err := f.delegate.GetTransport(orig) + if err == nil { + PropagateTConfiguration(orig, f.cfg) + PropagateTConfiguration(trans, f.cfg) + } + return trans, err +} + +func (f *tTransportFactoryConf) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(f.delegate, f.cfg) + f.cfg = cfg +} + +// TTransportFactoryConf wraps a TTransportFactory to propagate +// TConfiguration on the factory's GetTransport calls. +func TTransportFactoryConf(delegate TTransportFactory, conf *TConfiguration) TTransportFactory { + return &tTransportFactoryConf{ + delegate: delegate, + cfg: conf, + } +} + +type tProtocolFactoryConf struct { + delegate TProtocolFactory + cfg *TConfiguration +} + +func (f *tProtocolFactoryConf) GetProtocol(trans TTransport) TProtocol { + proto := f.delegate.GetProtocol(trans) + PropagateTConfiguration(trans, f.cfg) + PropagateTConfiguration(proto, f.cfg) + return proto +} + +func (f *tProtocolFactoryConf) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(f.delegate, f.cfg) + f.cfg = cfg +} + +// TProtocolFactoryConf wraps a TProtocolFactory to propagate +// TConfiguration on the factory's GetProtocol calls. +func TProtocolFactoryConf(delegate TProtocolFactory, conf *TConfiguration) TProtocolFactory { + return &tProtocolFactoryConf{ + delegate: delegate, + cfg: conf, + } +} + +var ( + _ TConfigurationSetter = (*tTransportFactoryConf)(nil) + _ TConfigurationSetter = (*tProtocolFactoryConf)(nil) +) diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/context.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/context.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/context.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/context.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import "context" + +var defaultCtx = context.Background() diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/exception.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/exception.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/exception.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/exception.go 2021-12-26 00:45:51.000000000 +0000 @@ -26,19 +26,91 @@ // Generic Thrift exception type TException interface { error + + TExceptionType() TExceptionType } // Prepends additional information to an error without losing the Thrift exception interface func PrependError(prepend string, err error) error { - if t, ok := err.(TTransportException); ok { - return NewTTransportException(t.TypeId(), prepend+t.Error()) + msg := prepend + err.Error() + + var te TException + if errors.As(err, &te) { + switch te.TExceptionType() { + case TExceptionTypeTransport: + if t, ok := err.(TTransportException); ok { + return prependTTransportException(prepend, t) + } + case TExceptionTypeProtocol: + if t, ok := err.(TProtocolException); ok { + return prependTProtocolException(prepend, t) + } + case TExceptionTypeApplication: + var t TApplicationException + if errors.As(err, &t) { + return NewTApplicationException(t.TypeId(), msg) + } + } + + return wrappedTException{ + err: err, + msg: msg, + tExceptionType: te.TExceptionType(), + } } - if t, ok := err.(TProtocolException); ok { - return NewTProtocolExceptionWithType(t.TypeId(), errors.New(prepend+err.Error())) + + return errors.New(msg) +} + +// TExceptionType is an enum type to categorize different "subclasses" of TExceptions. +type TExceptionType byte + +// TExceptionType values +const ( + TExceptionTypeUnknown TExceptionType = iota + TExceptionTypeCompiled // TExceptions defined in thrift files and generated by thrift compiler + TExceptionTypeApplication // TApplicationExceptions + TExceptionTypeProtocol // TProtocolExceptions + TExceptionTypeTransport // TTransportExceptions +) + +// WrapTException wraps an error into TException. +// +// If err is nil or already TException, it's returned as-is. +// Otherwise it will be wraped into TException with TExceptionType() returning +// TExceptionTypeUnknown, and Unwrap() returning the original error. +func WrapTException(err error) TException { + if err == nil { + return nil } - if t, ok := err.(TApplicationException); ok { - return NewTApplicationException(t.TypeId(), prepend+t.Error()) + + if te, ok := err.(TException); ok { + return te } - return errors.New(prepend + err.Error()) + return wrappedTException{ + err: err, + msg: err.Error(), + tExceptionType: TExceptionTypeUnknown, + } } + +type wrappedTException struct { + err error + msg string + tExceptionType TExceptionType +} + +func (w wrappedTException) Error() string { + return w.msg +} + +func (w wrappedTException) TExceptionType() TExceptionType { + return w.tExceptionType +} + +func (w wrappedTException) Unwrap() error { + return w.err +} + +var _ TException = wrappedTException{} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_context.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_context.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_context.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_context.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" +) + +// See https://godoc.org/context#WithValue on why do we need the unexported typedefs. +type ( + headerKey string + headerKeyList int +) + +// Values for headerKeyList. +const ( + headerKeyListRead headerKeyList = iota + headerKeyListWrite +) + +// SetHeader sets a header in the context. +func SetHeader(ctx context.Context, key, value string) context.Context { + return context.WithValue( + ctx, + headerKey(key), + value, + ) +} + +// UnsetHeader unsets a previously set header in the context. +func UnsetHeader(ctx context.Context, key string) context.Context { + return context.WithValue( + ctx, + headerKey(key), + nil, + ) +} + +// GetHeader returns a value of the given header from the context. +func GetHeader(ctx context.Context, key string) (value string, ok bool) { + if v := ctx.Value(headerKey(key)); v != nil { + value, ok = v.(string) + } + return +} + +// SetReadHeaderList sets the key list of read THeaders in the context. +func SetReadHeaderList(ctx context.Context, keys []string) context.Context { + return context.WithValue( + ctx, + headerKeyListRead, + keys, + ) +} + +// GetReadHeaderList returns the key list of read THeaders from the context. +func GetReadHeaderList(ctx context.Context) []string { + if v := ctx.Value(headerKeyListRead); v != nil { + if value, ok := v.([]string); ok { + return value + } + } + return nil +} + +// SetWriteHeaderList sets the key list of THeaders to write in the context. +func SetWriteHeaderList(ctx context.Context, keys []string) context.Context { + return context.WithValue( + ctx, + headerKeyListWrite, + keys, + ) +} + +// GetWriteHeaderList returns the key list of THeaders to write from the context. +func GetWriteHeaderList(ctx context.Context) []string { + if v := ctx.Value(headerKeyListWrite); v != nil { + if value, ok := v.([]string); ok { + return value + } + } + return nil +} + +// AddReadTHeaderToContext adds the whole THeader headers into context. +func AddReadTHeaderToContext(ctx context.Context, headers THeaderMap) context.Context { + keys := make([]string, 0, len(headers)) + for key, value := range headers { + ctx = SetHeader(ctx, key, value) + keys = append(keys, key) + } + return SetReadHeaderList(ctx, keys) +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_protocol.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_protocol.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_protocol.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_protocol.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" + "errors" +) + +// THeaderProtocol is a thrift protocol that implements THeader: +// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md +// +// It supports either binary or compact protocol as the wrapped protocol. +// +// Most of the THeader handlings are happening inside THeaderTransport. +type THeaderProtocol struct { + transport *THeaderTransport + + // Will be initialized on first read/write. + protocol TProtocol + + cfg *TConfiguration +} + +// Deprecated: Use NewTHeaderProtocolConf instead. +func NewTHeaderProtocol(trans TTransport) *THeaderProtocol { + return newTHeaderProtocolConf(trans, &TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderProtocolConf creates a new THeaderProtocol from the underlying +// transport with given TConfiguration. +// +// The passed in transport will be wrapped with THeaderTransport. +// +// Note that THeaderTransport handles frame and zlib by itself, +// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket), +// instead of rich transports like TZlibTransport or TFramedTransport. +func NewTHeaderProtocolConf(trans TTransport, conf *TConfiguration) *THeaderProtocol { + return newTHeaderProtocolConf(trans, conf) +} + +func newTHeaderProtocolConf(trans TTransport, cfg *TConfiguration) *THeaderProtocol { + t := NewTHeaderTransportConf(trans, cfg) + p, _ := t.cfg.GetTHeaderProtocolID().GetProtocol(t) + PropagateTConfiguration(p, cfg) + return &THeaderProtocol{ + transport: t, + protocol: p, + cfg: cfg, + } +} + +type tHeaderProtocolFactory struct { + cfg *TConfiguration +} + +func (f tHeaderProtocolFactory) GetProtocol(trans TTransport) TProtocol { + return newTHeaderProtocolConf(trans, f.cfg) +} + +func (f *tHeaderProtocolFactory) SetTConfiguration(cfg *TConfiguration) { + f.cfg = cfg +} + +// Deprecated: Use NewTHeaderProtocolFactoryConf instead. +func NewTHeaderProtocolFactory() TProtocolFactory { + return NewTHeaderProtocolFactoryConf(&TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderProtocolFactoryConf creates a factory for THeader with given +// TConfiguration. +func NewTHeaderProtocolFactoryConf(conf *TConfiguration) TProtocolFactory { + return tHeaderProtocolFactory{ + cfg: conf, + } +} + +// Transport returns the underlying transport. +// +// It's guaranteed to be of type *THeaderTransport. +func (p *THeaderProtocol) Transport() TTransport { + return p.transport +} + +// GetReadHeaders returns the THeaderMap read from transport. +func (p *THeaderProtocol) GetReadHeaders() THeaderMap { + return p.transport.GetReadHeaders() +} + +// SetWriteHeader sets a header for write. +func (p *THeaderProtocol) SetWriteHeader(key, value string) { + p.transport.SetWriteHeader(key, value) +} + +// ClearWriteHeaders clears all write headers previously set. +func (p *THeaderProtocol) ClearWriteHeaders() { + p.transport.ClearWriteHeaders() +} + +// AddTransform add a transform for writing. +func (p *THeaderProtocol) AddTransform(transform THeaderTransformID) error { + return p.transport.AddTransform(transform) +} + +func (p *THeaderProtocol) Flush(ctx context.Context) error { + return p.transport.Flush(ctx) +} + +func (p *THeaderProtocol) WriteMessageBegin(ctx context.Context, name string, typeID TMessageType, seqID int32) error { + newProto, err := p.transport.Protocol().GetProtocol(p.transport) + if err != nil { + return err + } + PropagateTConfiguration(newProto, p.cfg) + p.protocol = newProto + p.transport.SequenceID = seqID + return p.protocol.WriteMessageBegin(ctx, name, typeID, seqID) +} + +func (p *THeaderProtocol) WriteMessageEnd(ctx context.Context) error { + if err := p.protocol.WriteMessageEnd(ctx); err != nil { + return err + } + return p.transport.Flush(ctx) +} + +func (p *THeaderProtocol) WriteStructBegin(ctx context.Context, name string) error { + return p.protocol.WriteStructBegin(ctx, name) +} + +func (p *THeaderProtocol) WriteStructEnd(ctx context.Context) error { + return p.protocol.WriteStructEnd(ctx) +} + +func (p *THeaderProtocol) WriteFieldBegin(ctx context.Context, name string, typeID TType, id int16) error { + return p.protocol.WriteFieldBegin(ctx, name, typeID, id) +} + +func (p *THeaderProtocol) WriteFieldEnd(ctx context.Context) error { + return p.protocol.WriteFieldEnd(ctx) +} + +func (p *THeaderProtocol) WriteFieldStop(ctx context.Context) error { + return p.protocol.WriteFieldStop(ctx) +} + +func (p *THeaderProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { + return p.protocol.WriteMapBegin(ctx, keyType, valueType, size) +} + +func (p *THeaderProtocol) WriteMapEnd(ctx context.Context) error { + return p.protocol.WriteMapEnd(ctx) +} + +func (p *THeaderProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { + return p.protocol.WriteListBegin(ctx, elemType, size) +} + +func (p *THeaderProtocol) WriteListEnd(ctx context.Context) error { + return p.protocol.WriteListEnd(ctx) +} + +func (p *THeaderProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { + return p.protocol.WriteSetBegin(ctx, elemType, size) +} + +func (p *THeaderProtocol) WriteSetEnd(ctx context.Context) error { + return p.protocol.WriteSetEnd(ctx) +} + +func (p *THeaderProtocol) WriteBool(ctx context.Context, value bool) error { + return p.protocol.WriteBool(ctx, value) +} + +func (p *THeaderProtocol) WriteByte(ctx context.Context, value int8) error { + return p.protocol.WriteByte(ctx, value) +} + +func (p *THeaderProtocol) WriteI16(ctx context.Context, value int16) error { + return p.protocol.WriteI16(ctx, value) +} + +func (p *THeaderProtocol) WriteI32(ctx context.Context, value int32) error { + return p.protocol.WriteI32(ctx, value) +} + +func (p *THeaderProtocol) WriteI64(ctx context.Context, value int64) error { + return p.protocol.WriteI64(ctx, value) +} + +func (p *THeaderProtocol) WriteDouble(ctx context.Context, value float64) error { + return p.protocol.WriteDouble(ctx, value) +} + +func (p *THeaderProtocol) WriteString(ctx context.Context, value string) error { + return p.protocol.WriteString(ctx, value) +} + +func (p *THeaderProtocol) WriteBinary(ctx context.Context, value []byte) error { + return p.protocol.WriteBinary(ctx, value) +} + +// ReadFrame calls underlying THeaderTransport's ReadFrame function. +func (p *THeaderProtocol) ReadFrame(ctx context.Context) error { + return p.transport.ReadFrame(ctx) +} + +func (p *THeaderProtocol) ReadMessageBegin(ctx context.Context) (name string, typeID TMessageType, seqID int32, err error) { + if err = p.transport.ReadFrame(ctx); err != nil { + return + } + + var newProto TProtocol + newProto, err = p.transport.Protocol().GetProtocol(p.transport) + if err != nil { + var tAppExc TApplicationException + if !errors.As(err, &tAppExc) { + return + } + if e := p.protocol.WriteMessageBegin(ctx, "", EXCEPTION, seqID); e != nil { + return + } + if e := tAppExc.Write(ctx, p.protocol); e != nil { + return + } + if e := p.protocol.WriteMessageEnd(ctx); e != nil { + return + } + if e := p.transport.Flush(ctx); e != nil { + return + } + return + } + PropagateTConfiguration(newProto, p.cfg) + p.protocol = newProto + + return p.protocol.ReadMessageBegin(ctx) +} + +func (p *THeaderProtocol) ReadMessageEnd(ctx context.Context) error { + return p.protocol.ReadMessageEnd(ctx) +} + +func (p *THeaderProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { + return p.protocol.ReadStructBegin(ctx) +} + +func (p *THeaderProtocol) ReadStructEnd(ctx context.Context) error { + return p.protocol.ReadStructEnd(ctx) +} + +func (p *THeaderProtocol) ReadFieldBegin(ctx context.Context) (name string, typeID TType, id int16, err error) { + return p.protocol.ReadFieldBegin(ctx) +} + +func (p *THeaderProtocol) ReadFieldEnd(ctx context.Context) error { + return p.protocol.ReadFieldEnd(ctx) +} + +func (p *THeaderProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) { + return p.protocol.ReadMapBegin(ctx) +} + +func (p *THeaderProtocol) ReadMapEnd(ctx context.Context) error { + return p.protocol.ReadMapEnd(ctx) +} + +func (p *THeaderProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, err error) { + return p.protocol.ReadListBegin(ctx) +} + +func (p *THeaderProtocol) ReadListEnd(ctx context.Context) error { + return p.protocol.ReadListEnd(ctx) +} + +func (p *THeaderProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) { + return p.protocol.ReadSetBegin(ctx) +} + +func (p *THeaderProtocol) ReadSetEnd(ctx context.Context) error { + return p.protocol.ReadSetEnd(ctx) +} + +func (p *THeaderProtocol) ReadBool(ctx context.Context) (value bool, err error) { + return p.protocol.ReadBool(ctx) +} + +func (p *THeaderProtocol) ReadByte(ctx context.Context) (value int8, err error) { + return p.protocol.ReadByte(ctx) +} + +func (p *THeaderProtocol) ReadI16(ctx context.Context) (value int16, err error) { + return p.protocol.ReadI16(ctx) +} + +func (p *THeaderProtocol) ReadI32(ctx context.Context) (value int32, err error) { + return p.protocol.ReadI32(ctx) +} + +func (p *THeaderProtocol) ReadI64(ctx context.Context) (value int64, err error) { + return p.protocol.ReadI64(ctx) +} + +func (p *THeaderProtocol) ReadDouble(ctx context.Context) (value float64, err error) { + return p.protocol.ReadDouble(ctx) +} + +func (p *THeaderProtocol) ReadString(ctx context.Context) (value string, err error) { + return p.protocol.ReadString(ctx) +} + +func (p *THeaderProtocol) ReadBinary(ctx context.Context) (value []byte, err error) { + return p.protocol.ReadBinary(ctx) +} + +func (p *THeaderProtocol) Skip(ctx context.Context, fieldType TType) error { + return p.protocol.Skip(ctx, fieldType) +} + +// SetTConfiguration implements TConfigurationSetter. +func (p *THeaderProtocol) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(p.transport, cfg) + PropagateTConfiguration(p.protocol, cfg) + p.cfg = cfg +} + +var ( + _ TConfigurationSetter = (*tHeaderProtocolFactory)(nil) + _ TConfigurationSetter = (*THeaderProtocol)(nil) +) diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_transport.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_transport.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_transport.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/header_transport.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,810 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "bufio" + "bytes" + "compress/zlib" + "context" + "encoding/binary" + "errors" + "fmt" + "io" + "io/ioutil" +) + +// Size in bytes for 32-bit ints. +const size32 = 4 + +type headerMeta struct { + MagicFlags uint32 + SequenceID int32 + HeaderLength uint16 +} + +const headerMetaSize = 10 + +type clientType int + +const ( + clientUnknown clientType = iota + clientHeaders + clientFramedBinary + clientUnframedBinary + clientFramedCompact + clientUnframedCompact +) + +// Constants defined in THeader format: +// https://github.com/apache/thrift/blob/master/doc/specs/HeaderFormat.md +const ( + THeaderHeaderMagic uint32 = 0x0fff0000 + THeaderHeaderMask uint32 = 0xffff0000 + THeaderFlagsMask uint32 = 0x0000ffff + THeaderMaxFrameSize uint32 = 0x3fffffff +) + +// THeaderMap is the type of the header map in THeader transport. +type THeaderMap map[string]string + +// THeaderProtocolID is the wrapped protocol id used in THeader. +type THeaderProtocolID int32 + +// Supported THeaderProtocolID values. +const ( + THeaderProtocolBinary THeaderProtocolID = 0x00 + THeaderProtocolCompact THeaderProtocolID = 0x02 + THeaderProtocolDefault = THeaderProtocolBinary +) + +// Declared globally to avoid repetitive allocations, not really used. +var globalMemoryBuffer = NewTMemoryBuffer() + +// Validate checks whether the THeaderProtocolID is a valid/supported one. +func (id THeaderProtocolID) Validate() error { + _, err := id.GetProtocol(globalMemoryBuffer) + return err +} + +// GetProtocol gets the corresponding TProtocol from the wrapped protocol id. +func (id THeaderProtocolID) GetProtocol(trans TTransport) (TProtocol, error) { + switch id { + default: + return nil, NewTApplicationException( + INVALID_PROTOCOL, + fmt.Sprintf("THeader protocol id %d not supported", id), + ) + case THeaderProtocolBinary: + return NewTBinaryProtocolTransport(trans), nil + case THeaderProtocolCompact: + return NewTCompactProtocol(trans), nil + } +} + +// THeaderTransformID defines the numeric id of the transform used. +type THeaderTransformID int32 + +// THeaderTransformID values. +// +// Values not defined here are not currently supported, namely HMAC and Snappy. +const ( + TransformNone THeaderTransformID = iota // 0, no special handling + TransformZlib // 1, zlib +) + +var supportedTransformIDs = map[THeaderTransformID]bool{ + TransformNone: true, + TransformZlib: true, +} + +// TransformReader is an io.ReadCloser that handles transforms reading. +type TransformReader struct { + io.Reader + + closers []io.Closer +} + +var _ io.ReadCloser = (*TransformReader)(nil) + +// NewTransformReaderWithCapacity initializes a TransformReader with expected +// closers capacity. +// +// If you don't know the closers capacity beforehand, just use +// +// &TransformReader{Reader: baseReader} +// +// instead would be sufficient. +func NewTransformReaderWithCapacity(baseReader io.Reader, capacity int) *TransformReader { + return &TransformReader{ + Reader: baseReader, + closers: make([]io.Closer, 0, capacity), + } +} + +// Close calls the underlying closers in appropriate order, +// stops at and returns the first error encountered. +func (tr *TransformReader) Close() error { + // Call closers in reversed order + for i := len(tr.closers) - 1; i >= 0; i-- { + if err := tr.closers[i].Close(); err != nil { + return err + } + } + return nil +} + +// AddTransform adds a transform. +func (tr *TransformReader) AddTransform(id THeaderTransformID) error { + switch id { + default: + return NewTApplicationException( + INVALID_TRANSFORM, + fmt.Sprintf("THeaderTransformID %d not supported", id), + ) + case TransformNone: + // no-op + case TransformZlib: + readCloser, err := zlib.NewReader(tr.Reader) + if err != nil { + return err + } + tr.Reader = readCloser + tr.closers = append(tr.closers, readCloser) + } + return nil +} + +// TransformWriter is an io.WriteCloser that handles transforms writing. +type TransformWriter struct { + io.Writer + + closers []io.Closer +} + +var _ io.WriteCloser = (*TransformWriter)(nil) + +// NewTransformWriter creates a new TransformWriter with base writer and transforms. +func NewTransformWriter(baseWriter io.Writer, transforms []THeaderTransformID) (io.WriteCloser, error) { + writer := &TransformWriter{ + Writer: baseWriter, + closers: make([]io.Closer, 0, len(transforms)), + } + for _, id := range transforms { + if err := writer.AddTransform(id); err != nil { + return nil, err + } + } + return writer, nil +} + +// Close calls the underlying closers in appropriate order, +// stops at and returns the first error encountered. +func (tw *TransformWriter) Close() error { + // Call closers in reversed order + for i := len(tw.closers) - 1; i >= 0; i-- { + if err := tw.closers[i].Close(); err != nil { + return err + } + } + return nil +} + +// AddTransform adds a transform. +func (tw *TransformWriter) AddTransform(id THeaderTransformID) error { + switch id { + default: + return NewTApplicationException( + INVALID_TRANSFORM, + fmt.Sprintf("THeaderTransformID %d not supported", id), + ) + case TransformNone: + // no-op + case TransformZlib: + writeCloser := zlib.NewWriter(tw.Writer) + tw.Writer = writeCloser + tw.closers = append(tw.closers, writeCloser) + } + return nil +} + +// THeaderInfoType is the type id of the info headers. +type THeaderInfoType int32 + +// Supported THeaderInfoType values. +const ( + _ THeaderInfoType = iota // Skip 0 + InfoKeyValue // 1 + // Rest of the info types are not supported. +) + +// THeaderTransport is a Transport mode that implements THeader. +// +// Note that THeaderTransport handles frame and zlib by itself, +// so the underlying transport should be a raw socket transports (TSocket or TSSLSocket), +// instead of rich transports like TZlibTransport or TFramedTransport. +type THeaderTransport struct { + SequenceID int32 + Flags uint32 + + transport TTransport + + // THeaderMap for read and write + readHeaders THeaderMap + writeHeaders THeaderMap + + // Reading related variables. + reader *bufio.Reader + // When frame is detected, we read the frame fully into frameBuffer. + frameBuffer bytes.Buffer + // When it's non-nil, Read should read from frameReader instead of + // reader, and EOF error indicates end of frame instead of end of all + // transport. + frameReader io.ReadCloser + + // Writing related variables + writeBuffer bytes.Buffer + writeTransforms []THeaderTransformID + + clientType clientType + protocolID THeaderProtocolID + cfg *TConfiguration + + // buffer is used in the following scenarios to avoid repetitive + // allocations, while 4 is big enough for all those scenarios: + // + // * header padding (max size 4) + // * write the frame size (size 4) + buffer [4]byte +} + +var _ TTransport = (*THeaderTransport)(nil) + +// Deprecated: Use NewTHeaderTransportConf instead. +func NewTHeaderTransport(trans TTransport) *THeaderTransport { + return NewTHeaderTransportConf(trans, &TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderTransportConf creates THeaderTransport from the +// underlying transport, with given TConfiguration attached. +// +// If trans is already a *THeaderTransport, it will be returned as is, +// but with TConfiguration overridden by the value passed in. +// +// The protocol ID in TConfiguration is only useful for client transports. +// For servers, +// the protocol ID will be overridden again to the one set by the client, +// to ensure that servers always speak the same dialect as the client. +func NewTHeaderTransportConf(trans TTransport, conf *TConfiguration) *THeaderTransport { + if ht, ok := trans.(*THeaderTransport); ok { + ht.SetTConfiguration(conf) + return ht + } + PropagateTConfiguration(trans, conf) + return &THeaderTransport{ + transport: trans, + reader: bufio.NewReader(trans), + writeHeaders: make(THeaderMap), + protocolID: conf.GetTHeaderProtocolID(), + cfg: conf, + } +} + +// Open calls the underlying transport's Open function. +func (t *THeaderTransport) Open() error { + return t.transport.Open() +} + +// IsOpen calls the underlying transport's IsOpen function. +func (t *THeaderTransport) IsOpen() bool { + return t.transport.IsOpen() +} + +// ReadFrame tries to read the frame header, guess the client type, and handle +// unframed clients. +func (t *THeaderTransport) ReadFrame(ctx context.Context) error { + if !t.needReadFrame() { + // No need to read frame, skipping. + return nil + } + + // Peek and handle the first 32 bits. + // They could either be the length field of a framed message, + // or the first bytes of an unframed message. + var buf []byte + var err error + // This is also usually the first read from a connection, + // so handle retries around socket timeouts. + _, deadlineSet := ctx.Deadline() + for { + buf, err = t.reader.Peek(size32) + if deadlineSet && isTimeoutError(err) && ctx.Err() == nil { + // This is I/O timeout and we still have time, + // continue trying + continue + } + // For anything else, do not retry + break + } + if err != nil { + return err + } + + frameSize := binary.BigEndian.Uint32(buf) + if frameSize&VERSION_MASK == VERSION_1 { + t.clientType = clientUnframedBinary + return nil + } + if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION { + t.clientType = clientUnframedCompact + return nil + } + + // At this point it should be a framed message, + // sanity check on frameSize then discard the peeked part. + if frameSize > THeaderMaxFrameSize || frameSize > uint32(t.cfg.GetMaxFrameSize()) { + return NewTProtocolExceptionWithType( + SIZE_LIMIT, + errors.New("frame too large"), + ) + } + t.reader.Discard(size32) + + // Read the frame fully into frameBuffer. + _, err = io.CopyN(&t.frameBuffer, t.reader, int64(frameSize)) + if err != nil { + return err + } + t.frameReader = ioutil.NopCloser(&t.frameBuffer) + + // Peek and handle the next 32 bits. + buf = t.frameBuffer.Bytes()[:size32] + version := binary.BigEndian.Uint32(buf) + if version&THeaderHeaderMask == THeaderHeaderMagic { + t.clientType = clientHeaders + return t.parseHeaders(ctx, frameSize) + } + if version&VERSION_MASK == VERSION_1 { + t.clientType = clientFramedBinary + return nil + } + if buf[0] == COMPACT_PROTOCOL_ID && buf[1]&COMPACT_VERSION_MASK == COMPACT_VERSION { + t.clientType = clientFramedCompact + return nil + } + if err := t.endOfFrame(); err != nil { + return err + } + return NewTProtocolExceptionWithType( + NOT_IMPLEMENTED, + errors.New("unsupported client transport type"), + ) +} + +// endOfFrame does end of frame handling. +// +// It closes frameReader, and also resets frame related states. +func (t *THeaderTransport) endOfFrame() error { + defer func() { + t.frameBuffer.Reset() + t.frameReader = nil + }() + return t.frameReader.Close() +} + +func (t *THeaderTransport) parseHeaders(ctx context.Context, frameSize uint32) error { + if t.clientType != clientHeaders { + return nil + } + + var err error + var meta headerMeta + if err = binary.Read(&t.frameBuffer, binary.BigEndian, &meta); err != nil { + return err + } + frameSize -= headerMetaSize + t.Flags = meta.MagicFlags & THeaderFlagsMask + t.SequenceID = meta.SequenceID + headerLength := int64(meta.HeaderLength) * 4 + if int64(frameSize) < headerLength { + return NewTProtocolExceptionWithType( + SIZE_LIMIT, + errors.New("header size is larger than the whole frame"), + ) + } + headerBuf := NewTMemoryBuffer() + _, err = io.CopyN(headerBuf, &t.frameBuffer, headerLength) + if err != nil { + return err + } + hp := NewTCompactProtocol(headerBuf) + hp.SetTConfiguration(t.cfg) + + // At this point the header is already read into headerBuf, + // and t.frameBuffer starts from the actual payload. + protoID, err := hp.readVarint32() + if err != nil { + return err + } + t.protocolID = THeaderProtocolID(protoID) + + var transformCount int32 + transformCount, err = hp.readVarint32() + if err != nil { + return err + } + if transformCount > 0 { + reader := NewTransformReaderWithCapacity( + &t.frameBuffer, + int(transformCount), + ) + t.frameReader = reader + transformIDs := make([]THeaderTransformID, transformCount) + for i := 0; i < int(transformCount); i++ { + id, err := hp.readVarint32() + if err != nil { + return err + } + transformIDs[i] = THeaderTransformID(id) + } + // The transform IDs on the wire was added based on the order of + // writing, so on the reading side we need to reverse the order. + for i := transformCount - 1; i >= 0; i-- { + id := transformIDs[i] + if err := reader.AddTransform(id); err != nil { + return err + } + } + } + + // The info part does not use the transforms yet, so it's + // important to continue using headerBuf. + headers := make(THeaderMap) + for { + infoType, err := hp.readVarint32() + if errors.Is(err, io.EOF) { + break + } + if err != nil { + return err + } + if THeaderInfoType(infoType) == InfoKeyValue { + count, err := hp.readVarint32() + if err != nil { + return err + } + for i := 0; i < int(count); i++ { + key, err := hp.ReadString(ctx) + if err != nil { + return err + } + value, err := hp.ReadString(ctx) + if err != nil { + return err + } + headers[key] = value + } + } else { + // Skip reading info section on the first + // unsupported info type. + break + } + } + t.readHeaders = headers + + return nil +} + +func (t *THeaderTransport) needReadFrame() bool { + if t.clientType == clientUnknown { + // This is a new connection that's never read before. + return true + } + if t.isFramed() && t.frameReader == nil { + // We just finished the last frame. + return true + } + return false +} + +func (t *THeaderTransport) Read(p []byte) (read int, err error) { + // Here using context.Background instead of a context passed in is safe. + // First is that there's no way to pass context into this function. + // Then, 99% of the case when calling this Read frame is already read + // into frameReader. ReadFrame here is more of preventing bugs that + // didn't call ReadFrame before calling Read. + err = t.ReadFrame(context.Background()) + if err != nil { + return + } + if t.frameReader != nil { + read, err = t.frameReader.Read(p) + if err == nil && t.frameBuffer.Len() <= 0 { + // the last Read finished the frame, do endOfFrame + // handling here. + err = t.endOfFrame() + } else if err == io.EOF { + err = t.endOfFrame() + if err != nil { + return + } + if read == 0 { + // Try to read the next frame when we hit EOF + // (end of frame) immediately. + // When we got here, it means the last read + // finished the previous frame, but didn't + // do endOfFrame handling yet. + // We have to read the next frame here, + // as otherwise we would return 0 and nil, + // which is a case not handled well by most + // protocol implementations. + return t.Read(p) + } + } + return + } + return t.reader.Read(p) +} + +// Write writes data to the write buffer. +// +// You need to call Flush to actually write them to the transport. +func (t *THeaderTransport) Write(p []byte) (int, error) { + return t.writeBuffer.Write(p) +} + +// Flush writes the appropriate header and the write buffer to the underlying transport. +func (t *THeaderTransport) Flush(ctx context.Context) error { + if t.writeBuffer.Len() == 0 { + return nil + } + + defer t.writeBuffer.Reset() + + switch t.clientType { + default: + fallthrough + case clientUnknown: + t.clientType = clientHeaders + fallthrough + case clientHeaders: + headers := NewTMemoryBuffer() + hp := NewTCompactProtocol(headers) + hp.SetTConfiguration(t.cfg) + if _, err := hp.writeVarint32(int32(t.protocolID)); err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := hp.writeVarint32(int32(len(t.writeTransforms))); err != nil { + return NewTTransportExceptionFromError(err) + } + for _, transform := range t.writeTransforms { + if _, err := hp.writeVarint32(int32(transform)); err != nil { + return NewTTransportExceptionFromError(err) + } + } + if len(t.writeHeaders) > 0 { + if _, err := hp.writeVarint32(int32(InfoKeyValue)); err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := hp.writeVarint32(int32(len(t.writeHeaders))); err != nil { + return NewTTransportExceptionFromError(err) + } + for key, value := range t.writeHeaders { + if err := hp.WriteString(ctx, key); err != nil { + return NewTTransportExceptionFromError(err) + } + if err := hp.WriteString(ctx, value); err != nil { + return NewTTransportExceptionFromError(err) + } + } + } + padding := 4 - headers.Len()%4 + if padding < 4 { + buf := t.buffer[:padding] + for i := range buf { + buf[i] = 0 + } + if _, err := headers.Write(buf); err != nil { + return NewTTransportExceptionFromError(err) + } + } + + var payload bytes.Buffer + meta := headerMeta{ + MagicFlags: THeaderHeaderMagic + t.Flags&THeaderFlagsMask, + SequenceID: t.SequenceID, + HeaderLength: uint16(headers.Len() / 4), + } + if err := binary.Write(&payload, binary.BigEndian, meta); err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := io.Copy(&payload, headers); err != nil { + return NewTTransportExceptionFromError(err) + } + + writer, err := NewTransformWriter(&payload, t.writeTransforms) + if err != nil { + return NewTTransportExceptionFromError(err) + } + if _, err := io.Copy(writer, &t.writeBuffer); err != nil { + return NewTTransportExceptionFromError(err) + } + if err := writer.Close(); err != nil { + return NewTTransportExceptionFromError(err) + } + + // First write frame length + buf := t.buffer[:size32] + binary.BigEndian.PutUint32(buf, uint32(payload.Len())) + if _, err := t.transport.Write(buf); err != nil { + return NewTTransportExceptionFromError(err) + } + // Then write the payload + if _, err := io.Copy(t.transport, &payload); err != nil { + return NewTTransportExceptionFromError(err) + } + + case clientFramedBinary, clientFramedCompact: + buf := t.buffer[:size32] + binary.BigEndian.PutUint32(buf, uint32(t.writeBuffer.Len())) + if _, err := t.transport.Write(buf); err != nil { + return NewTTransportExceptionFromError(err) + } + fallthrough + case clientUnframedBinary, clientUnframedCompact: + if _, err := io.Copy(t.transport, &t.writeBuffer); err != nil { + return NewTTransportExceptionFromError(err) + } + } + + select { + default: + case <-ctx.Done(): + return NewTTransportExceptionFromError(ctx.Err()) + } + + return t.transport.Flush(ctx) +} + +// Close closes the transport, along with its underlying transport. +func (t *THeaderTransport) Close() error { + if err := t.Flush(context.Background()); err != nil { + return err + } + return t.transport.Close() +} + +// RemainingBytes calls underlying transport's RemainingBytes. +// +// Even in framed cases, because of all the possible compression transforms +// involved, the remaining frame size is likely to be different from the actual +// remaining readable bytes, so we don't bother to keep tracking the remaining +// frame size by ourselves and just use the underlying transport's +// RemainingBytes directly. +func (t *THeaderTransport) RemainingBytes() uint64 { + return t.transport.RemainingBytes() +} + +// GetReadHeaders returns the THeaderMap read from transport. +func (t *THeaderTransport) GetReadHeaders() THeaderMap { + return t.readHeaders +} + +// SetWriteHeader sets a header for write. +func (t *THeaderTransport) SetWriteHeader(key, value string) { + t.writeHeaders[key] = value +} + +// ClearWriteHeaders clears all write headers previously set. +func (t *THeaderTransport) ClearWriteHeaders() { + t.writeHeaders = make(THeaderMap) +} + +// AddTransform add a transform for writing. +func (t *THeaderTransport) AddTransform(transform THeaderTransformID) error { + if !supportedTransformIDs[transform] { + return NewTProtocolExceptionWithType( + NOT_IMPLEMENTED, + fmt.Errorf("THeaderTransformID %d not supported", transform), + ) + } + t.writeTransforms = append(t.writeTransforms, transform) + return nil +} + +// Protocol returns the wrapped protocol id used in this THeaderTransport. +func (t *THeaderTransport) Protocol() THeaderProtocolID { + switch t.clientType { + default: + return t.protocolID + case clientFramedBinary, clientUnframedBinary: + return THeaderProtocolBinary + case clientFramedCompact, clientUnframedCompact: + return THeaderProtocolCompact + } +} + +func (t *THeaderTransport) isFramed() bool { + switch t.clientType { + default: + return false + case clientHeaders, clientFramedBinary, clientFramedCompact: + return true + } +} + +// SetTConfiguration implements TConfigurationSetter. +func (t *THeaderTransport) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(t.transport, cfg) + t.cfg = cfg +} + +// THeaderTransportFactory is a TTransportFactory implementation to create +// THeaderTransport. +// +// It also implements TConfigurationSetter. +type THeaderTransportFactory struct { + // The underlying factory, could be nil. + Factory TTransportFactory + + cfg *TConfiguration +} + +// Deprecated: Use NewTHeaderTransportFactoryConf instead. +func NewTHeaderTransportFactory(factory TTransportFactory) TTransportFactory { + return NewTHeaderTransportFactoryConf(factory, &TConfiguration{ + noPropagation: true, + }) +} + +// NewTHeaderTransportFactoryConf creates a new *THeaderTransportFactory with +// the given *TConfiguration. +func NewTHeaderTransportFactoryConf(factory TTransportFactory, conf *TConfiguration) TTransportFactory { + return &THeaderTransportFactory{ + Factory: factory, + + cfg: conf, + } +} + +// GetTransport implements TTransportFactory. +func (f *THeaderTransportFactory) GetTransport(trans TTransport) (TTransport, error) { + if f.Factory != nil { + t, err := f.Factory.GetTransport(trans) + if err != nil { + return nil, err + } + return NewTHeaderTransportConf(t, f.cfg), nil + } + return NewTHeaderTransportConf(trans, f.cfg), nil +} + +// SetTConfiguration implements TConfigurationSetter. +func (f *THeaderTransportFactory) SetTConfiguration(cfg *TConfiguration) { + PropagateTConfiguration(f.Factory, f.cfg) + f.cfg = cfg +} + +var ( + _ TConfigurationSetter = (*THeaderTransportFactory)(nil) + _ TConfigurationSetter = (*THeaderTransport)(nil) +) diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/logger.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/logger.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/logger.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/logger.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "log" + "os" +) + +// Logger is a simple wrapper of a logging function. +// +// In reality the users might actually use different logging libraries, and they +// are not always compatible with each other. +// +// Logger is meant to be a simple common ground that it's easy to wrap whatever +// logging library they use into. +// +// See https://issues.apache.org/jira/browse/THRIFT-4985 for the design +// discussion behind it. +type Logger func(msg string) + +// NopLogger is a Logger implementation that does nothing. +func NopLogger(msg string) {} + +// StdLogger wraps stdlib log package into a Logger. +// +// If logger passed in is nil, it will fallback to use stderr and default flags. +func StdLogger(logger *log.Logger) Logger { + if logger == nil { + logger = log.New(os.Stderr, "", log.LstdFlags) + } + return func(msg string) { + logger.Print(msg) + } +} + +func fallbackLogger(logger Logger) Logger { + if logger == nil { + return StdLogger(nil) + } + return logger +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/memory_buffer.go 2021-12-26 00:45:51.000000000 +0000 @@ -21,6 +21,7 @@ import ( "bytes" + "context" ) // Memory buffer-based implementation of the TTransport interface. @@ -33,14 +34,14 @@ size int } -func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) TTransport { +func (p *TMemoryBufferTransportFactory) GetTransport(trans TTransport) (TTransport, error) { if trans != nil { t, ok := trans.(*TMemoryBuffer) if ok && t.size > 0 { - return NewTMemoryBufferLen(t.size) + return NewTMemoryBufferLen(t.size), nil } } - return NewTMemoryBufferLen(p.size) + return NewTMemoryBufferLen(p.size), nil } func NewTMemoryBufferTransportFactory(size int) *TMemoryBufferTransportFactory { @@ -70,7 +71,7 @@ } // Flushing a memory buffer is a no-op -func (p *TMemoryBuffer) Flush() error { +func (p *TMemoryBuffer) Flush(ctx context.Context) error { return nil } diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/numeric.go 2021-12-26 00:45:51.000000000 +0000 @@ -69,14 +69,14 @@ func NewNumericFromI64(iValue int64) Numeric { dValue := float64(iValue) - sValue := string(iValue) + sValue := strconv.FormatInt(iValue, 10) isNil := false return &numeric{iValue: iValue, dValue: dValue, sValue: sValue, isNil: isNil} } func NewNumericFromI32(iValue int32) Numeric { dValue := float64(iValue) - sValue := string(iValue) + sValue := strconv.FormatInt(int64(iValue), 10) isNil := false return &numeric{iValue: int64(iValue), dValue: dValue, sValue: sValue, isNil: isNil} } diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor_factory.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor_factory.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor_factory.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor_factory.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import "context" + +// A processor is a generic object which operates upon an input stream and +// writes to some output stream. +type TProcessor interface { + Process(ctx context.Context, in, out TProtocol) (bool, TException) + + // ProcessorMap returns a map of thrift method names to TProcessorFunctions. + ProcessorMap() map[string]TProcessorFunction + + // AddToProcessorMap adds the given TProcessorFunction to the internal + // processor map at the given key. + // + // If one is already set at the given key, it will be replaced with the new + // TProcessorFunction. + AddToProcessorMap(string, TProcessorFunction) +} + +type TProcessorFunction interface { + Process(ctx context.Context, seqId int32, in, out TProtocol) (bool, TException) +} + +// The default processor factory just returns a singleton +// instance. +type TProcessorFactory interface { + GetProcessor(trans TTransport) TProcessor +} + +type tProcessorFactory struct { + processor TProcessor +} + +func NewTProcessorFactory(p TProcessor) TProcessorFactory { + return &tProcessorFactory{processor: p} +} + +func (p *tProcessorFactory) GetProcessor(trans TTransport) TProcessor { + return p.processor +} + +/** + * The default processor factory just returns a singleton + * instance. + */ +type TProcessorFunctionFactory interface { + GetProcessorFunction(trans TTransport) TProcessorFunction +} + +type tProcessorFunctionFactory struct { + processor TProcessorFunction +} + +func NewTProcessorFunctionFactory(p TProcessorFunction) TProcessorFunctionFactory { + return &tProcessorFunctionFactory{processor: p} +} + +func (p *tProcessorFunctionFactory) GetProcessorFunction(trans TTransport) TProcessorFunction { + return p.processor +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/processor.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package thrift - -// A processor is a generic object which operates upon an input stream and -// writes to some output stream. -type TProcessor interface { - Process(in, out TProtocol) (bool, TException) -} - -type TProcessorFunction interface { - Process(seqId int32, in, out TProtocol) (bool, TException) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol_exception.go 2021-12-26 00:45:51.000000000 +0000 @@ -21,6 +21,7 @@ import ( "encoding/base64" + "errors" ) // Thrift Protocol exception @@ -40,8 +41,15 @@ ) type tProtocolException struct { - typeId int - message string + typeId int + err error + msg string +} + +var _ TProtocolException = (*tProtocolException)(nil) + +func (tProtocolException) TExceptionType() TExceptionType { + return TExceptionTypeProtocol } func (p *tProtocolException) TypeId() int { @@ -49,30 +57,48 @@ } func (p *tProtocolException) String() string { - return p.message + return p.msg } func (p *tProtocolException) Error() string { - return p.message + return p.msg +} + +func (p *tProtocolException) Unwrap() error { + return p.err } func NewTProtocolException(err error) TProtocolException { if err == nil { return nil } - if e,ok := err.(TProtocolException); ok { + + if e, ok := err.(TProtocolException); ok { return e } - if _, ok := err.(base64.CorruptInputError); ok { - return &tProtocolException{INVALID_DATA, err.Error()} + + if errors.As(err, new(base64.CorruptInputError)) { + return NewTProtocolExceptionWithType(INVALID_DATA, err) } - return &tProtocolException{UNKNOWN_PROTOCOL_EXCEPTION, err.Error()} + + return NewTProtocolExceptionWithType(UNKNOWN_PROTOCOL_EXCEPTION, err) } func NewTProtocolExceptionWithType(errType int, err error) TProtocolException { if err == nil { return nil } - return &tProtocolException{errType, err.Error()} + return &tProtocolException{ + typeId: errType, + err: err, + msg: err.Error(), + } } +func prependTProtocolException(prepend string, err TProtocolException) TProtocolException { + return &tProtocolException{ + typeId: err.TypeId(), + err: err, + msg: prepend + err.Error(), + } +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/protocol.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,7 +20,9 @@ package thrift import ( + "context" "errors" + "fmt" ) const ( @@ -29,51 +31,51 @@ ) type TProtocol interface { - WriteMessageBegin(name string, typeId TMessageType, seqid int32) error - WriteMessageEnd() error - WriteStructBegin(name string) error - WriteStructEnd() error - WriteFieldBegin(name string, typeId TType, id int16) error - WriteFieldEnd() error - WriteFieldStop() error - WriteMapBegin(keyType TType, valueType TType, size int) error - WriteMapEnd() error - WriteListBegin(elemType TType, size int) error - WriteListEnd() error - WriteSetBegin(elemType TType, size int) error - WriteSetEnd() error - WriteBool(value bool) error - WriteByte(value int8) error - WriteI16(value int16) error - WriteI32(value int32) error - WriteI64(value int64) error - WriteDouble(value float64) error - WriteString(value string) error - WriteBinary(value []byte) error - - ReadMessageBegin() (name string, typeId TMessageType, seqid int32, err error) - ReadMessageEnd() error - ReadStructBegin() (name string, err error) - ReadStructEnd() error - ReadFieldBegin() (name string, typeId TType, id int16, err error) - ReadFieldEnd() error - ReadMapBegin() (keyType TType, valueType TType, size int, err error) - ReadMapEnd() error - ReadListBegin() (elemType TType, size int, err error) - ReadListEnd() error - ReadSetBegin() (elemType TType, size int, err error) - ReadSetEnd() error - ReadBool() (value bool, err error) - ReadByte() (value int8, err error) - ReadI16() (value int16, err error) - ReadI32() (value int32, err error) - ReadI64() (value int64, err error) - ReadDouble() (value float64, err error) - ReadString() (value string, err error) - ReadBinary() (value []byte, err error) + WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqid int32) error + WriteMessageEnd(ctx context.Context) error + WriteStructBegin(ctx context.Context, name string) error + WriteStructEnd(ctx context.Context) error + WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error + WriteFieldEnd(ctx context.Context) error + WriteFieldStop(ctx context.Context) error + WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error + WriteMapEnd(ctx context.Context) error + WriteListBegin(ctx context.Context, elemType TType, size int) error + WriteListEnd(ctx context.Context) error + WriteSetBegin(ctx context.Context, elemType TType, size int) error + WriteSetEnd(ctx context.Context) error + WriteBool(ctx context.Context, value bool) error + WriteByte(ctx context.Context, value int8) error + WriteI16(ctx context.Context, value int16) error + WriteI32(ctx context.Context, value int32) error + WriteI64(ctx context.Context, value int64) error + WriteDouble(ctx context.Context, value float64) error + WriteString(ctx context.Context, value string) error + WriteBinary(ctx context.Context, value []byte) error + + ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqid int32, err error) + ReadMessageEnd(ctx context.Context) error + ReadStructBegin(ctx context.Context) (name string, err error) + ReadStructEnd(ctx context.Context) error + ReadFieldBegin(ctx context.Context) (name string, typeId TType, id int16, err error) + ReadFieldEnd(ctx context.Context) error + ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, err error) + ReadMapEnd(ctx context.Context) error + ReadListBegin(ctx context.Context) (elemType TType, size int, err error) + ReadListEnd(ctx context.Context) error + ReadSetBegin(ctx context.Context) (elemType TType, size int, err error) + ReadSetEnd(ctx context.Context) error + ReadBool(ctx context.Context) (value bool, err error) + ReadByte(ctx context.Context) (value int8, err error) + ReadI16(ctx context.Context) (value int16, err error) + ReadI32(ctx context.Context) (value int32, err error) + ReadI64(ctx context.Context) (value int64, err error) + ReadDouble(ctx context.Context) (value float64, err error) + ReadString(ctx context.Context) (value string, err error) + ReadBinary(ctx context.Context) (value []byte, err error) - Skip(fieldType TType) (err error) - Flush() (err error) + Skip(ctx context.Context, fieldType TType) (err error) + Flush(ctx context.Context) (err error) Transport() TTransport } @@ -82,94 +84,94 @@ const DEFAULT_RECURSION_DEPTH = 64 // Skips over the next data element from the provided input TProtocol object. -func SkipDefaultDepth(prot TProtocol, typeId TType) (err error) { - return Skip(prot, typeId, DEFAULT_RECURSION_DEPTH) +func SkipDefaultDepth(ctx context.Context, prot TProtocol, typeId TType) (err error) { + return Skip(ctx, prot, typeId, DEFAULT_RECURSION_DEPTH) } // Skips over the next data element from the provided input TProtocol object. -func Skip(self TProtocol, fieldType TType, maxDepth int) (err error) { - - if maxDepth <= 0 { - return NewTProtocolExceptionWithType( DEPTH_LIMIT, errors.New("Depth limit exceeded")) +func Skip(ctx context.Context, self TProtocol, fieldType TType, maxDepth int) (err error) { + + if maxDepth <= 0 { + return NewTProtocolExceptionWithType(DEPTH_LIMIT, errors.New("Depth limit exceeded")) } switch fieldType { - case STOP: - return case BOOL: - _, err = self.ReadBool() + _, err = self.ReadBool(ctx) return case BYTE: - _, err = self.ReadByte() + _, err = self.ReadByte(ctx) return case I16: - _, err = self.ReadI16() + _, err = self.ReadI16(ctx) return case I32: - _, err = self.ReadI32() + _, err = self.ReadI32(ctx) return case I64: - _, err = self.ReadI64() + _, err = self.ReadI64(ctx) return case DOUBLE: - _, err = self.ReadDouble() + _, err = self.ReadDouble(ctx) return case STRING: - _, err = self.ReadString() + _, err = self.ReadString(ctx) return case STRUCT: - if _, err = self.ReadStructBegin(); err != nil { + if _, err = self.ReadStructBegin(ctx); err != nil { return err } for { - _, typeId, _, _ := self.ReadFieldBegin() + _, typeId, _, _ := self.ReadFieldBegin(ctx) if typeId == STOP { break } - err := Skip(self, typeId, maxDepth-1) + err := Skip(ctx, self, typeId, maxDepth-1) if err != nil { return err } - self.ReadFieldEnd() + self.ReadFieldEnd(ctx) } - return self.ReadStructEnd() + return self.ReadStructEnd(ctx) case MAP: - keyType, valueType, size, err := self.ReadMapBegin() + keyType, valueType, size, err := self.ReadMapBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { - err := Skip(self, keyType, maxDepth-1) + err := Skip(ctx, self, keyType, maxDepth-1) if err != nil { return err } - self.Skip(valueType) + self.Skip(ctx, valueType) } - return self.ReadMapEnd() + return self.ReadMapEnd(ctx) case SET: - elemType, size, err := self.ReadSetBegin() + elemType, size, err := self.ReadSetBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { - err := Skip(self, elemType, maxDepth-1) + err := Skip(ctx, self, elemType, maxDepth-1) if err != nil { return err } } - return self.ReadSetEnd() + return self.ReadSetEnd(ctx) case LIST: - elemType, size, err := self.ReadListBegin() + elemType, size, err := self.ReadListBegin(ctx) if err != nil { return err } for i := 0; i < size; i++ { - err := Skip(self, elemType, maxDepth-1) + err := Skip(ctx, self, elemType, maxDepth-1) if err != nil { return err } } - return self.ReadListEnd() + return self.ReadListEnd(ctx) + default: + return NewTProtocolExceptionWithType(INVALID_DATA, errors.New(fmt.Sprintf("Unknown data type %d", fieldType))) } return nil } diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/README.md libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/README.md --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/README.md 2021-12-26 00:45:51.000000000 +0000 @@ -1,7 +1,11 @@ # Apache Thrift -This is a partial copy of Apache Thrift v0.10 (https://github.com/apache/thrift/commit/b2a4d4ae21c789b689dd162deb819665567f481c). +This is a partial copy of Apache Thrift v0.14.1 (https://github.com/apache/thrift/commit/f6fa1794539e68ac294038ac388d6bde40a6c237). -It is vendored code to avoid compatibility issues introduced in Thrift v0.11. +It is vendored code to avoid compatibility issues with Thrift versions. -See https://github.com/jaegertracing/jaeger-client-go/pull/303. +The file logger.go is modified to remove dependency on "testing" (see Issue #585). + +See: + * https://github.com/jaegertracing/jaeger-client-go/pull/584 + * https://github.com/jaegertracing/jaeger-client-go/pull/303 diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/response_helper.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/response_helper.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/response_helper.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/response_helper.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "context" +) + +// See https://godoc.org/context#WithValue on why do we need the unexported typedefs. +type responseHelperKey struct{} + +// TResponseHelper defines a object with a set of helper functions that can be +// retrieved from the context object passed into server handler functions. +// +// Use GetResponseHelper to retrieve the injected TResponseHelper implementation +// from the context object. +// +// The zero value of TResponseHelper is valid with all helper functions being +// no-op. +type TResponseHelper struct { + // THeader related functions + *THeaderResponseHelper +} + +// THeaderResponseHelper defines THeader related TResponseHelper functions. +// +// The zero value of *THeaderResponseHelper is valid with all helper functions +// being no-op. +type THeaderResponseHelper struct { + proto *THeaderProtocol +} + +// NewTHeaderResponseHelper creates a new THeaderResponseHelper from the +// underlying TProtocol. +func NewTHeaderResponseHelper(proto TProtocol) *THeaderResponseHelper { + if hp, ok := proto.(*THeaderProtocol); ok { + return &THeaderResponseHelper{ + proto: hp, + } + } + return nil +} + +// SetHeader sets a response header. +// +// It's no-op if the underlying protocol/transport does not support THeader. +func (h *THeaderResponseHelper) SetHeader(key, value string) { + if h != nil && h.proto != nil { + h.proto.SetWriteHeader(key, value) + } +} + +// ClearHeaders clears all the response headers previously set. +// +// It's no-op if the underlying protocol/transport does not support THeader. +func (h *THeaderResponseHelper) ClearHeaders() { + if h != nil && h.proto != nil { + h.proto.ClearWriteHeaders() + } +} + +// GetResponseHelper retrieves the TResponseHelper implementation injected into +// the context object. +// +// If no helper was found in the context object, a nop helper with ok == false +// will be returned. +func GetResponseHelper(ctx context.Context) (helper TResponseHelper, ok bool) { + if v := ctx.Value(responseHelperKey{}); v != nil { + helper, ok = v.(TResponseHelper) + } + return +} + +// SetResponseHelper injects TResponseHelper into the context object. +func SetResponseHelper(ctx context.Context, helper TResponseHelper) context.Context { + return context.WithValue(ctx, responseHelperKey{}, helper) +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/rich_transport.go 2021-12-26 00:45:51.000000000 +0000 @@ -19,7 +19,10 @@ package thrift -import "io" +import ( + "errors" + "io" +) type RichTransport struct { TTransport @@ -49,7 +52,7 @@ func readByte(r io.Reader) (c byte, err error) { v := [1]byte{0} n, err := r.Read(v[0:1]) - if n > 0 && (err == nil || err == io.EOF) { + if n > 0 && (err == nil || errors.Is(err, io.EOF)) { return v[0], nil } if n > 0 && err != nil { @@ -66,4 +69,3 @@ _, err := w.Write(v[0:1]) return err } - diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/serializer.go 2021-12-26 00:45:51.000000000 +0000 @@ -19,57 +19,118 @@ package thrift +import ( + "context" + "sync" +) + type TSerializer struct { Transport *TMemoryBuffer Protocol TProtocol } type TStruct interface { - Write(p TProtocol) error - Read(p TProtocol) error + Write(ctx context.Context, p TProtocol) error + Read(ctx context.Context, p TProtocol) error } func NewTSerializer() *TSerializer { transport := NewTMemoryBufferLen(1024) - protocol := NewTBinaryProtocolFactoryDefault().GetProtocol(transport) + protocol := NewTBinaryProtocolTransport(transport) return &TSerializer{ - transport, - protocol} + Transport: transport, + Protocol: protocol, + } } -func (t *TSerializer) WriteString(msg TStruct) (s string, err error) { +func (t *TSerializer) WriteString(ctx context.Context, msg TStruct) (s string, err error) { t.Transport.Reset() - if err = msg.Write(t.Protocol); err != nil { + if err = msg.Write(ctx, t.Protocol); err != nil { return } - if err = t.Protocol.Flush(); err != nil { + if err = t.Protocol.Flush(ctx); err != nil { return } - if err = t.Transport.Flush(); err != nil { + if err = t.Transport.Flush(ctx); err != nil { return } return t.Transport.String(), nil } -func (t *TSerializer) Write(msg TStruct) (b []byte, err error) { +func (t *TSerializer) Write(ctx context.Context, msg TStruct) (b []byte, err error) { t.Transport.Reset() - if err = msg.Write(t.Protocol); err != nil { + if err = msg.Write(ctx, t.Protocol); err != nil { return } - if err = t.Protocol.Flush(); err != nil { + if err = t.Protocol.Flush(ctx); err != nil { return } - if err = t.Transport.Flush(); err != nil { + if err = t.Transport.Flush(ctx); err != nil { return } b = append(b, t.Transport.Bytes()...) return } + +// TSerializerPool is the thread-safe version of TSerializer, it uses resource +// pool of TSerializer under the hood. +// +// It must be initialized with either NewTSerializerPool or +// NewTSerializerPoolSizeFactory. +type TSerializerPool struct { + pool sync.Pool +} + +// NewTSerializerPool creates a new TSerializerPool. +// +// NewTSerializer can be used as the arg here. +func NewTSerializerPool(f func() *TSerializer) *TSerializerPool { + return &TSerializerPool{ + pool: sync.Pool{ + New: func() interface{} { + return f() + }, + }, + } +} + +// NewTSerializerPoolSizeFactory creates a new TSerializerPool with the given +// size and protocol factory. +// +// Note that the size is not the limit. The TMemoryBuffer underneath can grow +// larger than that. It just dictates the initial size. +func NewTSerializerPoolSizeFactory(size int, factory TProtocolFactory) *TSerializerPool { + return &TSerializerPool{ + pool: sync.Pool{ + New: func() interface{} { + transport := NewTMemoryBufferLen(size) + protocol := factory.GetProtocol(transport) + + return &TSerializer{ + Transport: transport, + Protocol: protocol, + } + }, + }, + } +} + +func (t *TSerializerPool) WriteString(ctx context.Context, msg TStruct) (string, error) { + s := t.pool.Get().(*TSerializer) + defer t.pool.Put(s) + return s.WriteString(ctx, msg) +} + +func (t *TSerializerPool) Write(ctx context.Context, msg TStruct) ([]byte, error) { + s := t.pool.Get().(*TSerializer) + defer t.pool.Put(s) + return s.Write(ctx, msg) +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/server_transport.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/server_transport.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/server_transport.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/server_transport.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +// Server transport. Object which provides client transports. +type TServerTransport interface { + Listen() error + Accept() (TTransport, error) + Close() error + + // Optional method implementation. This signals to the server transport + // that it should break out of any accept() or listen() that it is currently + // blocked on. This method, if implemented, MUST be thread safe, as it may + // be called from a different thread context than the other TServerTransport + // methods. + Interrupt() error +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_json_protocol.go 2021-12-26 00:45:51.000000000 +0000 @@ -22,8 +22,10 @@ import ( "bufio" "bytes" + "context" "encoding/base64" "encoding/json" + "errors" "fmt" "io" "math" @@ -33,12 +35,13 @@ type _ParseContext int const ( - _CONTEXT_IN_TOPLEVEL _ParseContext = 1 - _CONTEXT_IN_LIST_FIRST _ParseContext = 2 - _CONTEXT_IN_LIST _ParseContext = 3 - _CONTEXT_IN_OBJECT_FIRST _ParseContext = 4 - _CONTEXT_IN_OBJECT_NEXT_KEY _ParseContext = 5 - _CONTEXT_IN_OBJECT_NEXT_VALUE _ParseContext = 6 + _CONTEXT_INVALID _ParseContext = iota + _CONTEXT_IN_TOPLEVEL // 1 + _CONTEXT_IN_LIST_FIRST // 2 + _CONTEXT_IN_LIST // 3 + _CONTEXT_IN_OBJECT_FIRST // 4 + _CONTEXT_IN_OBJECT_NEXT_KEY // 5 + _CONTEXT_IN_OBJECT_NEXT_VALUE // 6 ) func (p _ParseContext) String() string { @@ -59,7 +62,33 @@ return "UNKNOWN-PARSE-CONTEXT" } -// JSON protocol implementation for thrift. +type jsonContextStack []_ParseContext + +func (s *jsonContextStack) push(v _ParseContext) { + *s = append(*s, v) +} + +func (s jsonContextStack) peek() (v _ParseContext, ok bool) { + l := len(s) + if l <= 0 { + return + } + return s[l-1], true +} + +func (s *jsonContextStack) pop() (v _ParseContext, ok bool) { + l := len(*s) + if l <= 0 { + return + } + v = (*s)[l-1] + *s = (*s)[0 : l-1] + return v, true +} + +var errEmptyJSONContextStack = NewTProtocolExceptionWithType(INVALID_DATA, errors.New("Unexpected empty json protocol context stack")) + +// Simple JSON protocol implementation for thrift. // // This protocol produces/consumes a simple output format // suitable for parsing by scripting languages. It should not be @@ -68,8 +97,8 @@ type TSimpleJSONProtocol struct { trans TTransport - parseContextStack []int - dumpContext []int + parseContextStack jsonContextStack + dumpContext jsonContextStack writer *bufio.Writer reader *bufio.Reader @@ -81,8 +110,8 @@ writer: bufio.NewWriter(t), reader: bufio.NewReader(t), } - v.parseContextStack = append(v.parseContextStack, int(_CONTEXT_IN_TOPLEVEL)) - v.dumpContext = append(v.dumpContext, int(_CONTEXT_IN_TOPLEVEL)) + v.parseContextStack.push(_CONTEXT_IN_TOPLEVEL) + v.dumpContext.push(_CONTEXT_IN_TOPLEVEL) return v } @@ -155,114 +184,113 @@ return fmt.Errorf("Expected '%s' but found '%s' while parsing JSON.", expected, actual) } -func (p *TSimpleJSONProtocol) WriteMessageBegin(name string, typeId TMessageType, seqId int32) error { +func (p *TSimpleJSONProtocol) WriteMessageBegin(ctx context.Context, name string, typeId TMessageType, seqId int32) error { p.resetContextStack() // THRIFT-3735 if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteString(name); e != nil { + if e := p.WriteString(ctx, name); e != nil { return e } - if e := p.WriteByte(int8(typeId)); e != nil { + if e := p.WriteByte(ctx, int8(typeId)); e != nil { return e } - if e := p.WriteI32(seqId); e != nil { + if e := p.WriteI32(ctx, seqId); e != nil { return e } return nil } -func (p *TSimpleJSONProtocol) WriteMessageEnd() error { +func (p *TSimpleJSONProtocol) WriteMessageEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteStructBegin(name string) error { +func (p *TSimpleJSONProtocol) WriteStructBegin(ctx context.Context, name string) error { if e := p.OutputObjectBegin(); e != nil { return e } return nil } -func (p *TSimpleJSONProtocol) WriteStructEnd() error { +func (p *TSimpleJSONProtocol) WriteStructEnd(ctx context.Context) error { return p.OutputObjectEnd() } -func (p *TSimpleJSONProtocol) WriteFieldBegin(name string, typeId TType, id int16) error { - if e := p.WriteString(name); e != nil { +func (p *TSimpleJSONProtocol) WriteFieldBegin(ctx context.Context, name string, typeId TType, id int16) error { + if e := p.WriteString(ctx, name); e != nil { return e } return nil } -func (p *TSimpleJSONProtocol) WriteFieldEnd() error { - //return p.OutputListEnd() +func (p *TSimpleJSONProtocol) WriteFieldEnd(ctx context.Context) error { return nil } -func (p *TSimpleJSONProtocol) WriteFieldStop() error { return nil } +func (p *TSimpleJSONProtocol) WriteFieldStop(ctx context.Context) error { return nil } -func (p *TSimpleJSONProtocol) WriteMapBegin(keyType TType, valueType TType, size int) error { +func (p *TSimpleJSONProtocol) WriteMapBegin(ctx context.Context, keyType TType, valueType TType, size int) error { if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteByte(int8(keyType)); e != nil { + if e := p.WriteByte(ctx, int8(keyType)); e != nil { return e } - if e := p.WriteByte(int8(valueType)); e != nil { + if e := p.WriteByte(ctx, int8(valueType)); e != nil { return e } - return p.WriteI32(int32(size)) + return p.WriteI32(ctx, int32(size)) } -func (p *TSimpleJSONProtocol) WriteMapEnd() error { +func (p *TSimpleJSONProtocol) WriteMapEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteListBegin(elemType TType, size int) error { +func (p *TSimpleJSONProtocol) WriteListBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } -func (p *TSimpleJSONProtocol) WriteListEnd() error { +func (p *TSimpleJSONProtocol) WriteListEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteSetBegin(elemType TType, size int) error { +func (p *TSimpleJSONProtocol) WriteSetBegin(ctx context.Context, elemType TType, size int) error { return p.OutputElemListBegin(elemType, size) } -func (p *TSimpleJSONProtocol) WriteSetEnd() error { +func (p *TSimpleJSONProtocol) WriteSetEnd(ctx context.Context) error { return p.OutputListEnd() } -func (p *TSimpleJSONProtocol) WriteBool(b bool) error { +func (p *TSimpleJSONProtocol) WriteBool(ctx context.Context, b bool) error { return p.OutputBool(b) } -func (p *TSimpleJSONProtocol) WriteByte(b int8) error { - return p.WriteI32(int32(b)) +func (p *TSimpleJSONProtocol) WriteByte(ctx context.Context, b int8) error { + return p.WriteI32(ctx, int32(b)) } -func (p *TSimpleJSONProtocol) WriteI16(v int16) error { - return p.WriteI32(int32(v)) +func (p *TSimpleJSONProtocol) WriteI16(ctx context.Context, v int16) error { + return p.WriteI32(ctx, int32(v)) } -func (p *TSimpleJSONProtocol) WriteI32(v int32) error { +func (p *TSimpleJSONProtocol) WriteI32(ctx context.Context, v int32) error { return p.OutputI64(int64(v)) } -func (p *TSimpleJSONProtocol) WriteI64(v int64) error { +func (p *TSimpleJSONProtocol) WriteI64(ctx context.Context, v int64) error { return p.OutputI64(int64(v)) } -func (p *TSimpleJSONProtocol) WriteDouble(v float64) error { +func (p *TSimpleJSONProtocol) WriteDouble(ctx context.Context, v float64) error { return p.OutputF64(v) } -func (p *TSimpleJSONProtocol) WriteString(v string) error { +func (p *TSimpleJSONProtocol) WriteString(ctx context.Context, v string) error { return p.OutputString(v) } -func (p *TSimpleJSONProtocol) WriteBinary(v []byte) error { +func (p *TSimpleJSONProtocol) WriteBinary(ctx context.Context, v []byte) error { // JSON library only takes in a string, // not an arbitrary byte array, to ensure bytes are transmitted // efficiently we must convert this into a valid JSON string @@ -288,39 +316,39 @@ } // Reading methods. -func (p *TSimpleJSONProtocol) ReadMessageBegin() (name string, typeId TMessageType, seqId int32, err error) { +func (p *TSimpleJSONProtocol) ReadMessageBegin(ctx context.Context) (name string, typeId TMessageType, seqId int32, err error) { p.resetContextStack() // THRIFT-3735 if isNull, err := p.ParseListBegin(); isNull || err != nil { return name, typeId, seqId, err } - if name, err = p.ReadString(); err != nil { + if name, err = p.ReadString(ctx); err != nil { return name, typeId, seqId, err } - bTypeId, err := p.ReadByte() + bTypeId, err := p.ReadByte(ctx) typeId = TMessageType(bTypeId) if err != nil { return name, typeId, seqId, err } - if seqId, err = p.ReadI32(); err != nil { + if seqId, err = p.ReadI32(ctx); err != nil { return name, typeId, seqId, err } return name, typeId, seqId, nil } -func (p *TSimpleJSONProtocol) ReadMessageEnd() error { +func (p *TSimpleJSONProtocol) ReadMessageEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadStructBegin() (name string, err error) { +func (p *TSimpleJSONProtocol) ReadStructBegin(ctx context.Context) (name string, err error) { _, err = p.ParseObjectStart() return "", err } -func (p *TSimpleJSONProtocol) ReadStructEnd() error { +func (p *TSimpleJSONProtocol) ReadStructEnd(ctx context.Context) error { return p.ParseObjectEnd() } -func (p *TSimpleJSONProtocol) ReadFieldBegin() (string, TType, int16, error) { +func (p *TSimpleJSONProtocol) ReadFieldBegin(ctx context.Context) (string, TType, int16, error) { if err := p.ParsePreValue(); err != nil { return "", STOP, 0, err } @@ -339,21 +367,6 @@ return name, STOP, 0, err } return name, STOP, -1, p.ParsePostValue() - /* - if err = p.ParsePostValue(); err != nil { - return name, STOP, 0, err - } - if isNull, err := p.ParseListBegin(); isNull || err != nil { - return name, STOP, 0, err - } - bType, err := p.ReadByte() - thetype := TType(bType) - if err != nil { - return name, thetype, 0, err - } - id, err := p.ReadI16() - return name, thetype, id, err - */ } e := fmt.Errorf("Expected \"}\" or '\"', but found: '%s'", string(b)) return "", STOP, 0, NewTProtocolExceptionWithType(INVALID_DATA, e) @@ -361,57 +374,56 @@ return "", STOP, 0, NewTProtocolException(io.EOF) } -func (p *TSimpleJSONProtocol) ReadFieldEnd() error { +func (p *TSimpleJSONProtocol) ReadFieldEnd(ctx context.Context) error { return nil - //return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadMapBegin() (keyType TType, valueType TType, size int, e error) { +func (p *TSimpleJSONProtocol) ReadMapBegin(ctx context.Context) (keyType TType, valueType TType, size int, e error) { if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, VOID, 0, e } // read keyType - bKeyType, e := p.ReadByte() + bKeyType, e := p.ReadByte(ctx) keyType = TType(bKeyType) if e != nil { return keyType, valueType, size, e } // read valueType - bValueType, e := p.ReadByte() + bValueType, e := p.ReadByte(ctx) valueType = TType(bValueType) if e != nil { return keyType, valueType, size, e } // read size - iSize, err := p.ReadI64() + iSize, err := p.ReadI64(ctx) size = int(iSize) return keyType, valueType, size, err } -func (p *TSimpleJSONProtocol) ReadMapEnd() error { +func (p *TSimpleJSONProtocol) ReadMapEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadListBegin() (elemType TType, size int, e error) { +func (p *TSimpleJSONProtocol) ReadListBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } -func (p *TSimpleJSONProtocol) ReadListEnd() error { +func (p *TSimpleJSONProtocol) ReadListEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadSetBegin() (elemType TType, size int, e error) { +func (p *TSimpleJSONProtocol) ReadSetBegin(ctx context.Context) (elemType TType, size int, e error) { return p.ParseElemListBegin() } -func (p *TSimpleJSONProtocol) ReadSetEnd() error { +func (p *TSimpleJSONProtocol) ReadSetEnd(ctx context.Context) error { return p.ParseListEnd() } -func (p *TSimpleJSONProtocol) ReadBool() (bool, error) { +func (p *TSimpleJSONProtocol) ReadBool(ctx context.Context) (bool, error) { var value bool if err := p.ParsePreValue(); err != nil { @@ -466,32 +478,32 @@ return value, p.ParsePostValue() } -func (p *TSimpleJSONProtocol) ReadByte() (int8, error) { - v, err := p.ReadI64() +func (p *TSimpleJSONProtocol) ReadByte(ctx context.Context) (int8, error) { + v, err := p.ReadI64(ctx) return int8(v), err } -func (p *TSimpleJSONProtocol) ReadI16() (int16, error) { - v, err := p.ReadI64() +func (p *TSimpleJSONProtocol) ReadI16(ctx context.Context) (int16, error) { + v, err := p.ReadI64(ctx) return int16(v), err } -func (p *TSimpleJSONProtocol) ReadI32() (int32, error) { - v, err := p.ReadI64() +func (p *TSimpleJSONProtocol) ReadI32(ctx context.Context) (int32, error) { + v, err := p.ReadI64(ctx) return int32(v), err } -func (p *TSimpleJSONProtocol) ReadI64() (int64, error) { +func (p *TSimpleJSONProtocol) ReadI64(ctx context.Context) (int64, error) { v, _, err := p.ParseI64() return v, err } -func (p *TSimpleJSONProtocol) ReadDouble() (float64, error) { +func (p *TSimpleJSONProtocol) ReadDouble(ctx context.Context) (float64, error) { v, _, err := p.ParseF64() return v, err } -func (p *TSimpleJSONProtocol) ReadString() (string, error) { +func (p *TSimpleJSONProtocol) ReadString(ctx context.Context) (string, error) { var v string if err := p.ParsePreValue(); err != nil { return v, err @@ -521,7 +533,7 @@ return v, p.ParsePostValue() } -func (p *TSimpleJSONProtocol) ReadBinary() ([]byte, error) { +func (p *TSimpleJSONProtocol) ReadBinary(ctx context.Context) ([]byte, error) { var v []byte if err := p.ParsePreValue(); err != nil { return nil, err @@ -552,12 +564,12 @@ return v, p.ParsePostValue() } -func (p *TSimpleJSONProtocol) Flush() (err error) { +func (p *TSimpleJSONProtocol) Flush(ctx context.Context) (err error) { return NewTProtocolException(p.writer.Flush()) } -func (p *TSimpleJSONProtocol) Skip(fieldType TType) (err error) { - return SkipDefaultDepth(p, fieldType) +func (p *TSimpleJSONProtocol) Skip(ctx context.Context, fieldType TType) (err error) { + return SkipDefaultDepth(ctx, p, fieldType) } func (p *TSimpleJSONProtocol) Transport() TTransport { @@ -565,41 +577,41 @@ } func (p *TSimpleJSONProtocol) OutputPreValue() error { - cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } switch cxt { case _CONTEXT_IN_LIST, _CONTEXT_IN_OBJECT_NEXT_KEY: if _, e := p.write(JSON_COMMA); e != nil { return NewTProtocolException(e) } - break case _CONTEXT_IN_OBJECT_NEXT_VALUE: if _, e := p.write(JSON_COLON); e != nil { return NewTProtocolException(e) } - break } return nil } func (p *TSimpleJSONProtocol) OutputPostValue() error { - cxt := _ParseContext(p.dumpContext[len(p.dumpContext)-1]) + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } switch cxt { case _CONTEXT_IN_LIST_FIRST: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_LIST) case _CONTEXT_IN_OBJECT_FIRST: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_KEY: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_VALUE: - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) - break + p.dumpContext.pop() + p.dumpContext.push(_CONTEXT_IN_OBJECT_NEXT_KEY) } return nil } @@ -614,10 +626,13 @@ } else { v = string(JSON_FALSE) } - switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } + switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) - default: } if e := p.OutputStringData(v); e != nil { return e @@ -647,11 +662,14 @@ } else if math.IsInf(value, -1) { v = string(JSON_QUOTE) + JSON_NEGATIVE_INFINITY + string(JSON_QUOTE) } else { + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } v = strconv.FormatFloat(value, 'g', -1, 64) - switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = string(JSON_QUOTE) + v + string(JSON_QUOTE) - default: } } if e := p.OutputStringData(v); e != nil { @@ -664,11 +682,14 @@ if e := p.OutputPreValue(); e != nil { return e } + cxt, ok := p.dumpContext.peek() + if !ok { + return errEmptyJSONContextStack + } v := strconv.FormatInt(value, 10) - switch _ParseContext(p.dumpContext[len(p.dumpContext)-1]) { + switch cxt { case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: v = jsonQuote(v) - default: } if e := p.OutputStringData(v); e != nil { return e @@ -698,7 +719,7 @@ if _, e := p.write(JSON_LBRACE); e != nil { return NewTProtocolException(e) } - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_OBJECT_FIRST)) + p.dumpContext.push(_CONTEXT_IN_OBJECT_FIRST) return nil } @@ -706,7 +727,10 @@ if _, e := p.write(JSON_RBRACE); e != nil { return NewTProtocolException(e) } - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + _, ok := p.dumpContext.pop() + if !ok { + return errEmptyJSONContextStack + } if e := p.OutputPostValue(); e != nil { return e } @@ -720,7 +744,7 @@ if _, e := p.write(JSON_LBRACKET); e != nil { return NewTProtocolException(e) } - p.dumpContext = append(p.dumpContext, int(_CONTEXT_IN_LIST_FIRST)) + p.dumpContext.push(_CONTEXT_IN_LIST_FIRST) return nil } @@ -728,7 +752,10 @@ if _, e := p.write(JSON_RBRACKET); e != nil { return NewTProtocolException(e) } - p.dumpContext = p.dumpContext[:len(p.dumpContext)-1] + _, ok := p.dumpContext.pop() + if !ok { + return errEmptyJSONContextStack + } if e := p.OutputPostValue(); e != nil { return e } @@ -739,10 +766,10 @@ if e := p.OutputListBegin(); e != nil { return e } - if e := p.WriteByte(int8(elemType)); e != nil { + if e := p.OutputI64(int64(elemType)); e != nil { return e } - if e := p.WriteI64(int64(size)); e != nil { + if e := p.OutputI64(int64(size)); e != nil { return e } return nil @@ -752,7 +779,10 @@ if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, ok := p.parseContextStack.peek() + if !ok { + return errEmptyJSONContextStack + } b, _ := p.reader.Peek(1) switch cxt { case _CONTEXT_IN_LIST: @@ -771,7 +801,6 @@ return NewTProtocolExceptionWithType(INVALID_DATA, e) } } - break case _CONTEXT_IN_OBJECT_NEXT_KEY: if len(b) > 0 { switch b[0] { @@ -788,7 +817,6 @@ return NewTProtocolExceptionWithType(INVALID_DATA, e) } } - break case _CONTEXT_IN_OBJECT_NEXT_VALUE: if len(b) > 0 { switch b[0] { @@ -803,7 +831,6 @@ return NewTProtocolExceptionWithType(INVALID_DATA, e) } } - break } return nil } @@ -812,20 +839,20 @@ if e := p.readNonSignificantWhitespace(); e != nil { return NewTProtocolException(e) } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, ok := p.parseContextStack.peek() + if !ok { + return errEmptyJSONContextStack + } switch cxt { case _CONTEXT_IN_LIST_FIRST: - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST)) - break + p.parseContextStack.pop() + p.parseContextStack.push(_CONTEXT_IN_LIST) case _CONTEXT_IN_OBJECT_FIRST, _CONTEXT_IN_OBJECT_NEXT_KEY: - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_VALUE)) - break + p.parseContextStack.pop() + p.parseContextStack.push(_CONTEXT_IN_OBJECT_NEXT_VALUE) case _CONTEXT_IN_OBJECT_NEXT_VALUE: - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_NEXT_KEY)) - break + p.parseContextStack.pop() + p.parseContextStack.push(_CONTEXT_IN_OBJECT_NEXT_KEY) } return nil } @@ -978,7 +1005,7 @@ } if len(b) > 0 && b[0] == JSON_LBRACE[0] { p.reader.ReadByte() - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_OBJECT_FIRST)) + p.parseContextStack.push(_CONTEXT_IN_OBJECT_FIRST) return false, nil } else if p.safePeekContains(JSON_NULL) { return true, nil @@ -991,7 +1018,7 @@ if isNull, err := p.readIfNull(); isNull || err != nil { return err } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, _ := p.parseContextStack.peek() if (cxt != _CONTEXT_IN_OBJECT_FIRST) && (cxt != _CONTEXT_IN_OBJECT_NEXT_KEY) { e := fmt.Errorf("Expected to be in the Object Context, but not in Object Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) @@ -1009,7 +1036,7 @@ break } } - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] + p.parseContextStack.pop() return p.ParsePostValue() } @@ -1023,7 +1050,7 @@ return false, err } if len(b) >= 1 && b[0] == JSON_LBRACKET[0] { - p.parseContextStack = append(p.parseContextStack, int(_CONTEXT_IN_LIST_FIRST)) + p.parseContextStack.push(_CONTEXT_IN_LIST_FIRST) p.reader.ReadByte() isNull = false } else if p.safePeekContains(JSON_NULL) { @@ -1038,12 +1065,12 @@ if isNull, e := p.ParseListBegin(); isNull || e != nil { return VOID, 0, e } - bElemType, err := p.ReadByte() + bElemType, _, err := p.ParseI64() elemType = TType(bElemType) if err != nil { return elemType, size, err } - nSize, err2 := p.ReadI64() + nSize, _, err2 := p.ParseI64() size = int(nSize) return elemType, size, err2 } @@ -1052,7 +1079,7 @@ if isNull, err := p.readIfNull(); isNull || err != nil { return err } - cxt := _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) + cxt, _ := p.parseContextStack.peek() if cxt != _CONTEXT_IN_LIST { e := fmt.Errorf("Expected to be in the List Context, but not in List Context (%d)", cxt) return NewTProtocolExceptionWithType(INVALID_DATA, e) @@ -1064,14 +1091,16 @@ for _, char := range line { switch char { default: - e := fmt.Errorf("Expecting end of list \"]\", but found: \"%s\"", line) + e := fmt.Errorf("Expecting end of list \"]\", but found: \"%v\"", line) return NewTProtocolExceptionWithType(INVALID_DATA, e) case ' ', '\n', '\r', '\t', rune(JSON_RBRACKET[0]): break } } - p.parseContextStack = p.parseContextStack[:len(p.parseContextStack)-1] - if _ParseContext(p.parseContextStack[len(p.parseContextStack)-1]) == _CONTEXT_IN_TOPLEVEL { + p.parseContextStack.pop() + if cxt, ok := p.parseContextStack.peek(); !ok { + return errEmptyJSONContextStack + } else if cxt == _CONTEXT_IN_TOPLEVEL { return nil } return p.ParsePostValue() @@ -1315,7 +1344,7 @@ func (p *TSimpleJSONProtocol) safePeekContains(b []byte) bool { for i := 0; i < len(b); i++ { a, _ := p.reader.Peek(i + 1) - if len(a) == 0 || a[i] != b[i] { + if len(a) < (i+1) || a[i] != b[i] { return false } } @@ -1324,8 +1353,8 @@ // Reset the context stack to its initial state. func (p *TSimpleJSONProtocol) resetContextStack() { - p.parseContextStack = []int{int(_CONTEXT_IN_TOPLEVEL)} - p.dumpContext = []int{int(_CONTEXT_IN_TOPLEVEL)} + p.parseContextStack = jsonContextStack{_CONTEXT_IN_TOPLEVEL} + p.dumpContext = jsonContextStack{_CONTEXT_IN_TOPLEVEL} } func (p *TSimpleJSONProtocol) write(b []byte) (int, error) { @@ -1335,3 +1364,10 @@ } return n, err } + +// SetTConfiguration implements TConfigurationSetter for propagation. +func (p *TSimpleJSONProtocol) SetTConfiguration(conf *TConfiguration) { + PropagateTConfiguration(p.trans, conf) +} + +var _ TConfigurationSetter = (*TSimpleJSONProtocol)(nil) diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_server.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_server.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_server.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/simple_server.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,332 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package thrift + +import ( + "errors" + "fmt" + "io" + "sync" + "sync/atomic" + "time" +) + +// ErrAbandonRequest is a special error server handler implementations can +// return to indicate that the request has been abandoned. +// +// TSimpleServer will check for this error, and close the client connection +// instead of writing the response/error back to the client. +// +// It shall only be used when the server handler implementation know that the +// client already abandoned the request (by checking that the passed in context +// is already canceled, for example). +var ErrAbandonRequest = errors.New("request abandoned") + +// ServerConnectivityCheckInterval defines the ticker interval used by +// connectivity check in thrift compiled TProcessorFunc implementations. +// +// It's defined as a variable instead of constant, so that thrift server +// implementations can change its value to control the behavior. +// +// If it's changed to <=0, the feature will be disabled. +var ServerConnectivityCheckInterval = time.Millisecond * 5 + +/* + * This is not a typical TSimpleServer as it is not blocked after accept a socket. + * It is more like a TThreadedServer that can handle different connections in different goroutines. + * This will work if golang user implements a conn-pool like thing in client side. + */ +type TSimpleServer struct { + closed int32 + wg sync.WaitGroup + mu sync.Mutex + + processorFactory TProcessorFactory + serverTransport TServerTransport + inputTransportFactory TTransportFactory + outputTransportFactory TTransportFactory + inputProtocolFactory TProtocolFactory + outputProtocolFactory TProtocolFactory + + // Headers to auto forward in THeaderProtocol + forwardHeaders []string + + logger Logger +} + +func NewTSimpleServer2(processor TProcessor, serverTransport TServerTransport) *TSimpleServer { + return NewTSimpleServerFactory2(NewTProcessorFactory(processor), serverTransport) +} + +func NewTSimpleServer4(processor TProcessor, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { + return NewTSimpleServerFactory4(NewTProcessorFactory(processor), + serverTransport, + transportFactory, + protocolFactory, + ) +} + +func NewTSimpleServer6(processor TProcessor, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { + return NewTSimpleServerFactory6(NewTProcessorFactory(processor), + serverTransport, + inputTransportFactory, + outputTransportFactory, + inputProtocolFactory, + outputProtocolFactory, + ) +} + +func NewTSimpleServerFactory2(processorFactory TProcessorFactory, serverTransport TServerTransport) *TSimpleServer { + return NewTSimpleServerFactory6(processorFactory, + serverTransport, + NewTTransportFactory(), + NewTTransportFactory(), + NewTBinaryProtocolFactoryDefault(), + NewTBinaryProtocolFactoryDefault(), + ) +} + +func NewTSimpleServerFactory4(processorFactory TProcessorFactory, serverTransport TServerTransport, transportFactory TTransportFactory, protocolFactory TProtocolFactory) *TSimpleServer { + return NewTSimpleServerFactory6(processorFactory, + serverTransport, + transportFactory, + transportFactory, + protocolFactory, + protocolFactory, + ) +} + +func NewTSimpleServerFactory6(processorFactory TProcessorFactory, serverTransport TServerTransport, inputTransportFactory TTransportFactory, outputTransportFactory TTransportFactory, inputProtocolFactory TProtocolFactory, outputProtocolFactory TProtocolFactory) *TSimpleServer { + return &TSimpleServer{ + processorFactory: processorFactory, + serverTransport: serverTransport, + inputTransportFactory: inputTransportFactory, + outputTransportFactory: outputTransportFactory, + inputProtocolFactory: inputProtocolFactory, + outputProtocolFactory: outputProtocolFactory, + } +} + +func (p *TSimpleServer) ProcessorFactory() TProcessorFactory { + return p.processorFactory +} + +func (p *TSimpleServer) ServerTransport() TServerTransport { + return p.serverTransport +} + +func (p *TSimpleServer) InputTransportFactory() TTransportFactory { + return p.inputTransportFactory +} + +func (p *TSimpleServer) OutputTransportFactory() TTransportFactory { + return p.outputTransportFactory +} + +func (p *TSimpleServer) InputProtocolFactory() TProtocolFactory { + return p.inputProtocolFactory +} + +func (p *TSimpleServer) OutputProtocolFactory() TProtocolFactory { + return p.outputProtocolFactory +} + +func (p *TSimpleServer) Listen() error { + return p.serverTransport.Listen() +} + +// SetForwardHeaders sets the list of header keys that will be auto forwarded +// while using THeaderProtocol. +// +// "forward" means that when the server is also a client to other upstream +// thrift servers, the context object user gets in the processor functions will +// have both read and write headers set, with write headers being forwarded. +// Users can always override the write headers by calling SetWriteHeaderList +// before calling thrift client functions. +func (p *TSimpleServer) SetForwardHeaders(headers []string) { + size := len(headers) + if size == 0 { + p.forwardHeaders = nil + return + } + + keys := make([]string, size) + copy(keys, headers) + p.forwardHeaders = keys +} + +// SetLogger sets the logger used by this TSimpleServer. +// +// If no logger was set before Serve is called, a default logger using standard +// log library will be used. +func (p *TSimpleServer) SetLogger(logger Logger) { + p.logger = logger +} + +func (p *TSimpleServer) innerAccept() (int32, error) { + client, err := p.serverTransport.Accept() + p.mu.Lock() + defer p.mu.Unlock() + closed := atomic.LoadInt32(&p.closed) + if closed != 0 { + return closed, nil + } + if err != nil { + return 0, err + } + if client != nil { + p.wg.Add(1) + go func() { + defer p.wg.Done() + if err := p.processRequests(client); err != nil { + p.logger(fmt.Sprintf("error processing request: %v", err)) + } + }() + } + return 0, nil +} + +func (p *TSimpleServer) AcceptLoop() error { + for { + closed, err := p.innerAccept() + if err != nil { + return err + } + if closed != 0 { + return nil + } + } +} + +func (p *TSimpleServer) Serve() error { + p.logger = fallbackLogger(p.logger) + + err := p.Listen() + if err != nil { + return err + } + p.AcceptLoop() + return nil +} + +func (p *TSimpleServer) Stop() error { + p.mu.Lock() + defer p.mu.Unlock() + if atomic.LoadInt32(&p.closed) != 0 { + return nil + } + atomic.StoreInt32(&p.closed, 1) + p.serverTransport.Interrupt() + p.wg.Wait() + return nil +} + +// If err is actually EOF, return nil, otherwise return err as-is. +func treatEOFErrorsAsNil(err error) error { + if err == nil { + return nil + } + if errors.Is(err, io.EOF) { + return nil + } + var te TTransportException + if errors.As(err, &te) && te.TypeId() == END_OF_FILE { + return nil + } + return err +} + +func (p *TSimpleServer) processRequests(client TTransport) (err error) { + defer func() { + err = treatEOFErrorsAsNil(err) + }() + + processor := p.processorFactory.GetProcessor(client) + inputTransport, err := p.inputTransportFactory.GetTransport(client) + if err != nil { + return err + } + inputProtocol := p.inputProtocolFactory.GetProtocol(inputTransport) + var outputTransport TTransport + var outputProtocol TProtocol + + // for THeaderProtocol, we must use the same protocol instance for + // input and output so that the response is in the same dialect that + // the server detected the request was in. + headerProtocol, ok := inputProtocol.(*THeaderProtocol) + if ok { + outputProtocol = inputProtocol + } else { + oTrans, err := p.outputTransportFactory.GetTransport(client) + if err != nil { + return err + } + outputTransport = oTrans + outputProtocol = p.outputProtocolFactory.GetProtocol(outputTransport) + } + + if inputTransport != nil { + defer inputTransport.Close() + } + if outputTransport != nil { + defer outputTransport.Close() + } + for { + if atomic.LoadInt32(&p.closed) != 0 { + return nil + } + + ctx := SetResponseHelper( + defaultCtx, + TResponseHelper{ + THeaderResponseHelper: NewTHeaderResponseHelper(outputProtocol), + }, + ) + if headerProtocol != nil { + // We need to call ReadFrame here, otherwise we won't + // get any headers on the AddReadTHeaderToContext call. + // + // ReadFrame is safe to be called multiple times so it + // won't break when it's called again later when we + // actually start to read the message. + if err := headerProtocol.ReadFrame(ctx); err != nil { + return err + } + ctx = AddReadTHeaderToContext(ctx, headerProtocol.GetReadHeaders()) + ctx = SetWriteHeaderList(ctx, p.forwardHeaders) + } + + ok, err := processor.Process(ctx, inputProtocol, outputProtocol) + if errors.Is(err, ErrAbandonRequest) { + return client.Close() + } + if errors.As(err, new(TTransportException)) && err != nil { + return err + } + var tae TApplicationException + if errors.As(err, &tae) && tae.TypeId() == UNKNOWN_METHOD { + continue + } + if !ok { + break + } + } + return nil +} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_exception.go 2021-12-26 00:45:51.000000000 +0000 @@ -46,6 +46,13 @@ type tTransportException struct { typeId int err error + msg string +} + +var _ TTransportException = (*tTransportException)(nil) + +func (tTransportException) TExceptionType() TExceptionType { + return TExceptionTypeTransport } func (p *tTransportException) TypeId() int { @@ -53,15 +60,27 @@ } func (p *tTransportException) Error() string { - return p.err.Error() + return p.msg } func (p *tTransportException) Err() error { return p.err } +func (p *tTransportException) Unwrap() error { + return p.err +} + +func (p *tTransportException) Timeout() bool { + return p.typeId == TIMED_OUT +} + func NewTTransportException(t int, e string) TTransportException { - return &tTransportException{typeId: t, err: errors.New(e)} + return &tTransportException{ + typeId: t, + err: errors.New(e), + msg: e, + } } func NewTTransportExceptionFromError(e error) TTransportException { @@ -73,18 +92,40 @@ return t } - switch v := e.(type) { - case TTransportException: - return v - case timeoutable: - if v.Timeout() { - return &tTransportException{typeId: TIMED_OUT, err: e} - } + te := &tTransportException{ + typeId: UNKNOWN_TRANSPORT_EXCEPTION, + err: e, + msg: e.Error(), } - if e == io.EOF { - return &tTransportException{typeId: END_OF_FILE, err: e} + if isTimeoutError(e) { + te.typeId = TIMED_OUT + return te } - return &tTransportException{typeId: UNKNOWN_TRANSPORT_EXCEPTION, err: e} + if errors.Is(e, io.EOF) { + te.typeId = END_OF_FILE + return te + } + + return te +} + +func prependTTransportException(prepend string, e TTransportException) TTransportException { + return &tTransportException{ + typeId: e.TypeId(), + err: e, + msg: prepend + e.Error(), + } +} + +// isTimeoutError returns true when err is an error caused by timeout. +// +// Note that this also includes TTransportException wrapped timeout errors. +func isTimeoutError(err error) bool { + var t timeoutable + if errors.As(err, &t) { + return t.Timeout() + } + return false } diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport_factory.go 2021-12-26 00:45:51.000000000 +0000 @@ -24,14 +24,14 @@ // a ServerTransport and then may want to mutate them (i.e. create // a BufferedTransport from the underlying base transport) type TTransportFactory interface { - GetTransport(trans TTransport) TTransport + GetTransport(trans TTransport) (TTransport, error) } type tTransportFactory struct{} // Return a wrapped instance of the base Transport. -func (p *tTransportFactory) GetTransport(trans TTransport) TTransport { - return trans +func (p *tTransportFactory) GetTransport(trans TTransport) (TTransport, error) { + return trans, nil } func NewTTransportFactory() TTransportFactory { diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift/transport.go 2021-12-26 00:45:51.000000000 +0000 @@ -20,6 +20,7 @@ package thrift import ( + "context" "errors" "io" ) @@ -30,15 +31,18 @@ Flush() (err error) } +type ContextFlusher interface { + Flush(ctx context.Context) (err error) +} + type ReadSizeProvider interface { RemainingBytes() (num_bytes uint64) } - // Encapsulates the I/O layer type TTransport interface { io.ReadWriteCloser - Flusher + ContextFlusher ReadSizeProvider // Opens the transport for communication @@ -52,7 +56,6 @@ WriteString(s string) (n int, err error) } - // This is "enchanced" transport with extra capabilities. You need to use one of these // to construct protocol. // Notably, TSocket does not implement this interface, and it is always a mistake to use @@ -62,7 +65,6 @@ io.ByteReader io.ByteWriter stringWriter - Flusher + ContextFlusher ReadSizeProvider } - diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent-consts.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent-consts.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent-consts.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent-consts.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,28 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package agent + +import( + "bytes" + "context" + "fmt" + "time" + "github.com/uber/jaeger-client-go/thrift" + "github.com/uber/jaeger-client-go/thrift-gen/jaeger" + "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = context.Background +var _ = time.Now +var _ = bytes.Equal + +var _ = jaeger.GoUnusedProtection__ +var _ = zipkincore.GoUnusedProtection__ + +func init() { +} + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/agent.go 2021-12-26 00:45:51.000000000 +0000 @@ -1,411 +1,396 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. package agent -import ( +import( "bytes" + "context" "fmt" + "time" "github.com/uber/jaeger-client-go/thrift" "github.com/uber/jaeger-client-go/thrift-gen/jaeger" "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" + ) // (needed to ensure safety because of naive import list construction.) var _ = thrift.ZERO var _ = fmt.Printf +var _ = context.Background +var _ = time.Now var _ = bytes.Equal var _ = jaeger.GoUnusedProtection__ var _ = zipkincore.GoUnusedProtection__ - type Agent interface { - // Parameters: - // - Spans - EmitZipkinBatch(spans []*zipkincore.Span) (err error) - // Parameters: - // - Batch - EmitBatch(batch *jaeger.Batch) (err error) + // Parameters: + // - Spans + EmitZipkinBatch(ctx context.Context, spans []*zipkincore.Span) (_err error) + // Parameters: + // - Batch + EmitBatch(ctx context.Context, batch *jaeger.Batch) (_err error) } type AgentClient struct { - Transport thrift.TTransport - ProtocolFactory thrift.TProtocolFactory - InputProtocol thrift.TProtocol - OutputProtocol thrift.TProtocol - SeqId int32 + c thrift.TClient + meta thrift.ResponseMeta } func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { - return &AgentClient{Transport: t, - ProtocolFactory: f, - InputProtocol: f.GetProtocol(t), - OutputProtocol: f.GetProtocol(t), - SeqId: 0, - } + return &AgentClient{ + c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)), + } } func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { - return &AgentClient{Transport: t, - ProtocolFactory: nil, - InputProtocol: iprot, - OutputProtocol: oprot, - SeqId: 0, - } + return &AgentClient{ + c: thrift.NewTStandardClient(iprot, oprot), + } +} + +func NewAgentClient(c thrift.TClient) *AgentClient { + return &AgentClient{ + c: c, + } +} + +func (p *AgentClient) Client_() thrift.TClient { + return p.c +} + +func (p *AgentClient) LastResponseMeta_() thrift.ResponseMeta { + return p.meta +} + +func (p *AgentClient) SetLastResponseMeta_(meta thrift.ResponseMeta) { + p.meta = meta } // Parameters: // - Spans -func (p *AgentClient) EmitZipkinBatch(spans []*zipkincore.Span) (err error) { - if err = p.sendEmitZipkinBatch(spans); err != nil { - return - } - return -} - -func (p *AgentClient) sendEmitZipkinBatch(spans []*zipkincore.Span) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("emitZipkinBatch", thrift.ONEWAY, p.SeqId); err != nil { - return - } - args := AgentEmitZipkinBatchArgs{ - Spans: spans, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() +func (p *AgentClient) EmitZipkinBatch(ctx context.Context, spans []*zipkincore.Span) (_err error) { + var _args0 AgentEmitZipkinBatchArgs + _args0.Spans = spans + p.SetLastResponseMeta_(thrift.ResponseMeta{}) + if _, err := p.Client_().Call(ctx, "emitZipkinBatch", &_args0, nil); err != nil { + return err + } + return nil } // Parameters: // - Batch -func (p *AgentClient) EmitBatch(batch *jaeger.Batch) (err error) { - if err = p.sendEmitBatch(batch); err != nil { - return - } - return -} - -func (p *AgentClient) sendEmitBatch(batch *jaeger.Batch) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { - return - } - args := AgentEmitBatchArgs{ - Batch: batch, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() +func (p *AgentClient) EmitBatch(ctx context.Context, batch *jaeger.Batch) (_err error) { + var _args1 AgentEmitBatchArgs + _args1.Batch = batch + p.SetLastResponseMeta_(thrift.ResponseMeta{}) + if _, err := p.Client_().Call(ctx, "emitBatch", &_args1, nil); err != nil { + return err + } + return nil } type AgentProcessor struct { - processorMap map[string]thrift.TProcessorFunction - handler Agent + processorMap map[string]thrift.TProcessorFunction + handler Agent } func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { - p.processorMap[key] = processor + p.processorMap[key] = processor } func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { - processor, ok = p.processorMap[key] - return processor, ok + processor, ok = p.processorMap[key] + return processor, ok } func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { - return p.processorMap + return p.processorMap } func NewAgentProcessor(handler Agent) *AgentProcessor { - self0 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} - self0.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler: handler} - self0.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} - return self0 -} - -func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - name, _, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return false, err - } - if processor, ok := p.GetProcessorFunction(name); ok { - return processor.Process(seqId, iprot, oprot) - } - iprot.Skip(thrift.STRUCT) - iprot.ReadMessageEnd() - x1 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) - oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) - x1.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, x1 + self2 := &AgentProcessor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)} + self2.processorMap["emitZipkinBatch"] = &agentProcessorEmitZipkinBatch{handler:handler} + self2.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler:handler} +return self2 +} + +func (p *AgentProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err2 := iprot.ReadMessageBegin(ctx) + if err2 != nil { return false, thrift.WrapTException(err2) } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(ctx, seqId, iprot, oprot) + } + iprot.Skip(ctx, thrift.STRUCT) + iprot.ReadMessageEnd(ctx) + x3 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function " + name) + oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId) + x3.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return false, x3 } type agentProcessorEmitZipkinBatch struct { - handler Agent + handler Agent } -func (p *agentProcessorEmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := AgentEmitZipkinBatchArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - return false, err - } - - iprot.ReadMessageEnd() - var err2 error - if err2 = p.handler.EmitZipkinBatch(args.Spans); err2 != nil { - return true, err2 - } - return true, nil +func (p *agentProcessorEmitZipkinBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitZipkinBatchArgs{} + var err2 error + if err2 = args.Read(ctx, iprot); err2 != nil { + iprot.ReadMessageEnd(ctx) + return false, thrift.WrapTException(err2) + } + iprot.ReadMessageEnd(ctx) + + tickerCancel := func() {} + _ = tickerCancel + + if err2 = p.handler.EmitZipkinBatch(ctx, args.Spans); err2 != nil { + tickerCancel() + return true, thrift.WrapTException(err2) + } + tickerCancel() + return true, nil } type agentProcessorEmitBatch struct { - handler Agent + handler Agent } -func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := AgentEmitBatchArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - return false, err - } - - iprot.ReadMessageEnd() - var err2 error - if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { - return true, err2 - } - return true, nil +func (p *agentProcessorEmitBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := AgentEmitBatchArgs{} + var err2 error + if err2 = args.Read(ctx, iprot); err2 != nil { + iprot.ReadMessageEnd(ctx) + return false, thrift.WrapTException(err2) + } + iprot.ReadMessageEnd(ctx) + + tickerCancel := func() {} + _ = tickerCancel + + if err2 = p.handler.EmitBatch(ctx, args.Batch); err2 != nil { + tickerCancel() + return true, thrift.WrapTException(err2) + } + tickerCancel() + return true, nil } + // HELPER FUNCTIONS AND STRUCTURES // Attributes: // - Spans type AgentEmitZipkinBatchArgs struct { - Spans []*zipkincore.Span `thrift:"spans,1" json:"spans"` + Spans []*zipkincore.Span `thrift:"spans,1" db:"spans" json:"spans"` } func NewAgentEmitZipkinBatchArgs() *AgentEmitZipkinBatchArgs { - return &AgentEmitZipkinBatchArgs{} + return &AgentEmitZipkinBatchArgs{} } + func (p *AgentEmitZipkinBatchArgs) GetSpans() []*zipkincore.Span { - return p.Spans + return p.Spans } -func (p *AgentEmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AgentEmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*zipkincore.Span, 0, size) - p.Spans = tSlice - for i := 0; i < size; i++ { - _elem2 := &zipkincore.Span{} - if err := _elem2.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) - } - p.Spans = append(p.Spans, _elem2) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *AgentEmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("emitZipkinBatch_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *AgentEmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Spans { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) - } - return err +func (p *AgentEmitZipkinBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.LIST { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*zipkincore.Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i ++ { + _elem4 := &zipkincore.Span{} + if err := _elem4.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Spans = append(p.Spans, _elem4) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "emitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *AgentEmitZipkinBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) } + return err } func (p *AgentEmitZipkinBatchArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p) + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitZipkinBatchArgs(%+v)", *p) } // Attributes: // - Batch type AgentEmitBatchArgs struct { - Batch *jaeger.Batch `thrift:"batch,1" json:"batch"` + Batch *jaeger.Batch `thrift:"batch,1" db:"batch" json:"batch"` } func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { - return &AgentEmitBatchArgs{} + return &AgentEmitBatchArgs{} } var AgentEmitBatchArgs_Batch_DEFAULT *jaeger.Batch - func (p *AgentEmitBatchArgs) GetBatch() *jaeger.Batch { - if !p.IsSetBatch() { - return AgentEmitBatchArgs_Batch_DEFAULT - } - return p.Batch + if !p.IsSetBatch() { + return AgentEmitBatchArgs_Batch_DEFAULT + } +return p.Batch } func (p *AgentEmitBatchArgs) IsSetBatch() bool { - return p.Batch != nil + return p.Batch != nil } -func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { - p.Batch = &jaeger.Batch{} - if err := p.Batch.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) - } - return nil -} - -func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) - } - if err := p.Batch.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) - } - return err +func (p *AgentEmitBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.STRUCT { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + p.Batch = &jaeger.Batch{} + if err := p.Batch.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) + } + return nil +} + +func (p *AgentEmitBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "emitBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *AgentEmitBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "batch", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) } + if err := p.Batch.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) } + return err } func (p *AgentEmitBatchArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) + if p == nil { + return "" + } + return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) } + + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/constants.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package agent - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" - "github.com/uber/jaeger-client-go/thrift-gen/jaeger" - "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -var _ = jaeger.GoUnusedProtection__ -var _ = zipkincore.GoUnusedProtection__ - -func init() { -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/GoUnusedProtection__.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/GoUnusedProtection__.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/GoUnusedProtection__.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/GoUnusedProtection__.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,6 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package agent + +var GoUnusedProtection__ int; + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/agent/ttypes.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package agent - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" - "github.com/uber/jaeger-client-go/thrift-gen/jaeger" - "github.com/uber/jaeger-client-go/thrift-gen/zipkincore" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -var _ = jaeger.GoUnusedProtection__ -var _ = zipkincore.GoUnusedProtection__ -var GoUnusedProtection__ int diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/agent.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,242 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package jaeger - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -type Agent interface { - // Parameters: - // - Batch - EmitBatch(batch *Batch) (err error) -} - -type AgentClient struct { - Transport thrift.TTransport - ProtocolFactory thrift.TProtocolFactory - InputProtocol thrift.TProtocol - OutputProtocol thrift.TProtocol - SeqId int32 -} - -func NewAgentClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *AgentClient { - return &AgentClient{Transport: t, - ProtocolFactory: f, - InputProtocol: f.GetProtocol(t), - OutputProtocol: f.GetProtocol(t), - SeqId: 0, - } -} - -func NewAgentClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *AgentClient { - return &AgentClient{Transport: t, - ProtocolFactory: nil, - InputProtocol: iprot, - OutputProtocol: oprot, - SeqId: 0, - } -} - -// Parameters: -// - Batch -func (p *AgentClient) EmitBatch(batch *Batch) (err error) { - if err = p.sendEmitBatch(batch); err != nil { - return - } - return -} - -func (p *AgentClient) sendEmitBatch(batch *Batch) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("emitBatch", thrift.ONEWAY, p.SeqId); err != nil { - return - } - args := AgentEmitBatchArgs{ - Batch: batch, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - -type AgentProcessor struct { - processorMap map[string]thrift.TProcessorFunction - handler Agent -} - -func (p *AgentProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { - p.processorMap[key] = processor -} - -func (p *AgentProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { - processor, ok = p.processorMap[key] - return processor, ok -} - -func (p *AgentProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { - return p.processorMap -} - -func NewAgentProcessor(handler Agent) *AgentProcessor { - - self6 := &AgentProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} - self6.processorMap["emitBatch"] = &agentProcessorEmitBatch{handler: handler} - return self6 -} - -func (p *AgentProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - name, _, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return false, err - } - if processor, ok := p.GetProcessorFunction(name); ok { - return processor.Process(seqId, iprot, oprot) - } - iprot.Skip(thrift.STRUCT) - iprot.ReadMessageEnd() - x7 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) - oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) - x7.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, x7 - -} - -type agentProcessorEmitBatch struct { - handler Agent -} - -func (p *agentProcessorEmitBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := AgentEmitBatchArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - return false, err - } - - iprot.ReadMessageEnd() - var err2 error - if err2 = p.handler.EmitBatch(args.Batch); err2 != nil { - return true, err2 - } - return true, nil -} - -// HELPER FUNCTIONS AND STRUCTURES - -// Attributes: -// - Batch -type AgentEmitBatchArgs struct { - Batch *Batch `thrift:"batch,1" json:"batch"` -} - -func NewAgentEmitBatchArgs() *AgentEmitBatchArgs { - return &AgentEmitBatchArgs{} -} - -var AgentEmitBatchArgs_Batch_DEFAULT *Batch - -func (p *AgentEmitBatchArgs) GetBatch() *Batch { - if !p.IsSetBatch() { - return AgentEmitBatchArgs_Batch_DEFAULT - } - return p.Batch -} -func (p *AgentEmitBatchArgs) IsSetBatch() bool { - return p.Batch != nil -} - -func (p *AgentEmitBatchArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *AgentEmitBatchArgs) readField1(iprot thrift.TProtocol) error { - p.Batch = &Batch{} - if err := p.Batch.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Batch), err) - } - return nil -} - -func (p *AgentEmitBatchArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("emitBatch_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *AgentEmitBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("batch", thrift.STRUCT, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batch: ", p), err) - } - if err := p.Batch.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Batch), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batch: ", p), err) - } - return err -} - -func (p *AgentEmitBatchArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("AgentEmitBatchArgs(%+v)", *p) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/constants.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package jaeger - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -func init() { -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/GoUnusedProtection__.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/GoUnusedProtection__.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/GoUnusedProtection__.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/GoUnusedProtection__.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,6 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package jaeger + +var GoUnusedProtection__ int; + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger-consts.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger-consts.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger-consts.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger-consts.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,23 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package jaeger + +import( + "bytes" + "context" + "fmt" + "time" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = context.Background +var _ = time.Now +var _ = bytes.Equal + + +func init() { +} + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/jaeger.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,2698 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package jaeger + +import( + "bytes" + "context" + "database/sql/driver" + "errors" + "fmt" + "time" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = context.Background +var _ = time.Now +var _ = bytes.Equal + +type TagType int64 +const ( + TagType_STRING TagType = 0 + TagType_DOUBLE TagType = 1 + TagType_BOOL TagType = 2 + TagType_LONG TagType = 3 + TagType_BINARY TagType = 4 +) + +func (p TagType) String() string { + switch p { + case TagType_STRING: return "STRING" + case TagType_DOUBLE: return "DOUBLE" + case TagType_BOOL: return "BOOL" + case TagType_LONG: return "LONG" + case TagType_BINARY: return "BINARY" + } + return "" +} + +func TagTypeFromString(s string) (TagType, error) { + switch s { + case "STRING": return TagType_STRING, nil + case "DOUBLE": return TagType_DOUBLE, nil + case "BOOL": return TagType_BOOL, nil + case "LONG": return TagType_LONG, nil + case "BINARY": return TagType_BINARY, nil + } + return TagType(0), fmt.Errorf("not a valid TagType string") +} + + +func TagTypePtr(v TagType) *TagType { return &v } + +func (p TagType) MarshalText() ([]byte, error) { +return []byte(p.String()), nil +} + +func (p *TagType) UnmarshalText(text []byte) error { +q, err := TagTypeFromString(string(text)) +if (err != nil) { +return err +} +*p = q +return nil +} + +func (p *TagType) Scan(value interface{}) error { +v, ok := value.(int64) +if !ok { +return errors.New("Scan value is not int64") +} +*p = TagType(v) +return nil +} + +func (p * TagType) Value() (driver.Value, error) { + if p == nil { + return nil, nil + } +return int64(*p), nil +} +type SpanRefType int64 +const ( + SpanRefType_CHILD_OF SpanRefType = 0 + SpanRefType_FOLLOWS_FROM SpanRefType = 1 +) + +func (p SpanRefType) String() string { + switch p { + case SpanRefType_CHILD_OF: return "CHILD_OF" + case SpanRefType_FOLLOWS_FROM: return "FOLLOWS_FROM" + } + return "" +} + +func SpanRefTypeFromString(s string) (SpanRefType, error) { + switch s { + case "CHILD_OF": return SpanRefType_CHILD_OF, nil + case "FOLLOWS_FROM": return SpanRefType_FOLLOWS_FROM, nil + } + return SpanRefType(0), fmt.Errorf("not a valid SpanRefType string") +} + + +func SpanRefTypePtr(v SpanRefType) *SpanRefType { return &v } + +func (p SpanRefType) MarshalText() ([]byte, error) { +return []byte(p.String()), nil +} + +func (p *SpanRefType) UnmarshalText(text []byte) error { +q, err := SpanRefTypeFromString(string(text)) +if (err != nil) { +return err +} +*p = q +return nil +} + +func (p *SpanRefType) Scan(value interface{}) error { +v, ok := value.(int64) +if !ok { +return errors.New("Scan value is not int64") +} +*p = SpanRefType(v) +return nil +} + +func (p * SpanRefType) Value() (driver.Value, error) { + if p == nil { + return nil, nil + } +return int64(*p), nil +} +// Attributes: +// - Key +// - VType +// - VStr +// - VDouble +// - VBool +// - VLong +// - VBinary +type Tag struct { + Key string `thrift:"key,1,required" db:"key" json:"key"` + VType TagType `thrift:"vType,2,required" db:"vType" json:"vType"` + VStr *string `thrift:"vStr,3" db:"vStr" json:"vStr,omitempty"` + VDouble *float64 `thrift:"vDouble,4" db:"vDouble" json:"vDouble,omitempty"` + VBool *bool `thrift:"vBool,5" db:"vBool" json:"vBool,omitempty"` + VLong *int64 `thrift:"vLong,6" db:"vLong" json:"vLong,omitempty"` + VBinary []byte `thrift:"vBinary,7" db:"vBinary" json:"vBinary,omitempty"` +} + +func NewTag() *Tag { + return &Tag{} +} + + +func (p *Tag) GetKey() string { + return p.Key +} + +func (p *Tag) GetVType() TagType { + return p.VType +} +var Tag_VStr_DEFAULT string +func (p *Tag) GetVStr() string { + if !p.IsSetVStr() { + return Tag_VStr_DEFAULT + } +return *p.VStr +} +var Tag_VDouble_DEFAULT float64 +func (p *Tag) GetVDouble() float64 { + if !p.IsSetVDouble() { + return Tag_VDouble_DEFAULT + } +return *p.VDouble +} +var Tag_VBool_DEFAULT bool +func (p *Tag) GetVBool() bool { + if !p.IsSetVBool() { + return Tag_VBool_DEFAULT + } +return *p.VBool +} +var Tag_VLong_DEFAULT int64 +func (p *Tag) GetVLong() int64 { + if !p.IsSetVLong() { + return Tag_VLong_DEFAULT + } +return *p.VLong +} +var Tag_VBinary_DEFAULT []byte + +func (p *Tag) GetVBinary() []byte { + return p.VBinary +} +func (p *Tag) IsSetVStr() bool { + return p.VStr != nil +} + +func (p *Tag) IsSetVDouble() bool { + return p.VDouble != nil +} + +func (p *Tag) IsSetVBool() bool { + return p.VBool != nil +} + +func (p *Tag) IsSetVLong() bool { + return p.VLong != nil +} + +func (p *Tag) IsSetVBinary() bool { + return p.VBinary != nil +} + +func (p *Tag) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetKey bool = false; + var issetVType bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.STRING { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetKey = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.I32 { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + issetVType = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.STRING { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.DOUBLE { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 5: + if fieldTypeId == thrift.BOOL { + if err := p.ReadField5(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 6: + if fieldTypeId == thrift.I64 { + if err := p.ReadField6(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 7: + if fieldTypeId == thrift.STRING { + if err := p.ReadField7(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetKey{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")); + } + if !issetVType{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field VType is not set")); + } + return nil +} + +func (p *Tag) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Key = v +} + return nil +} + +func (p *Tag) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + temp := TagType(v) + p.VType = temp +} + return nil +} + +func (p *Tag) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.VStr = &v +} + return nil +} + +func (p *Tag) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadDouble(ctx); err != nil { + return thrift.PrependError("error reading field 4: ", err) +} else { + p.VDouble = &v +} + return nil +} + +func (p *Tag) ReadField5(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(ctx); err != nil { + return thrift.PrependError("error reading field 5: ", err) +} else { + p.VBool = &v +} + return nil +} + +func (p *Tag) ReadField6(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 6: ", err) +} else { + p.VLong = &v +} + return nil +} + +func (p *Tag) ReadField7(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(ctx); err != nil { + return thrift.PrependError("error reading field 7: ", err) +} else { + p.VBinary = v +} + return nil +} + +func (p *Tag) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Tag"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + if err := p.writeField5(ctx, oprot); err != nil { return err } + if err := p.writeField6(ctx, oprot); err != nil { return err } + if err := p.writeField7(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Tag) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) } + if err := oprot.WriteString(ctx, string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) } + return err +} + +func (p *Tag) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "vType", thrift.I32, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:vType: ", p), err) } + if err := oprot.WriteI32(ctx, int32(p.VType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vType (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:vType: ", p), err) } + return err +} + +func (p *Tag) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetVStr() { + if err := oprot.WriteFieldBegin(ctx, "vStr", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:vStr: ", p), err) } + if err := oprot.WriteString(ctx, string(*p.VStr)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vStr (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:vStr: ", p), err) } + } + return err +} + +func (p *Tag) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetVDouble() { + if err := oprot.WriteFieldBegin(ctx, "vDouble", thrift.DOUBLE, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:vDouble: ", p), err) } + if err := oprot.WriteDouble(ctx, float64(*p.VDouble)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vDouble (4) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:vDouble: ", p), err) } + } + return err +} + +func (p *Tag) writeField5(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetVBool() { + if err := oprot.WriteFieldBegin(ctx, "vBool", thrift.BOOL, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:vBool: ", p), err) } + if err := oprot.WriteBool(ctx, bool(*p.VBool)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBool (5) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:vBool: ", p), err) } + } + return err +} + +func (p *Tag) writeField6(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetVLong() { + if err := oprot.WriteFieldBegin(ctx, "vLong", thrift.I64, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:vLong: ", p), err) } + if err := oprot.WriteI64(ctx, int64(*p.VLong)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vLong (6) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:vLong: ", p), err) } + } + return err +} + +func (p *Tag) writeField7(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetVBinary() { + if err := oprot.WriteFieldBegin(ctx, "vBinary", thrift.STRING, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:vBinary: ", p), err) } + if err := oprot.WriteBinary(ctx, p.VBinary); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.vBinary (7) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:vBinary: ", p), err) } + } + return err +} + +func (p *Tag) Equals(other *Tag) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Key != other.Key { return false } + if p.VType != other.VType { return false } + if p.VStr != other.VStr { + if p.VStr == nil || other.VStr == nil { + return false + } + if (*p.VStr) != (*other.VStr) { return false } + } + if p.VDouble != other.VDouble { + if p.VDouble == nil || other.VDouble == nil { + return false + } + if (*p.VDouble) != (*other.VDouble) { return false } + } + if p.VBool != other.VBool { + if p.VBool == nil || other.VBool == nil { + return false + } + if (*p.VBool) != (*other.VBool) { return false } + } + if p.VLong != other.VLong { + if p.VLong == nil || other.VLong == nil { + return false + } + if (*p.VLong) != (*other.VLong) { return false } + } + if bytes.Compare(p.VBinary, other.VBinary) != 0 { return false } + return true +} + +func (p *Tag) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Tag(%+v)", *p) +} + +// Attributes: +// - Timestamp +// - Fields +type Log struct { + Timestamp int64 `thrift:"timestamp,1,required" db:"timestamp" json:"timestamp"` + Fields []*Tag `thrift:"fields,2,required" db:"fields" json:"fields"` +} + +func NewLog() *Log { + return &Log{} +} + + +func (p *Log) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Log) GetFields() []*Tag { + return p.Fields +} +func (p *Log) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTimestamp bool = false; + var issetFields bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I64 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetTimestamp = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.LIST { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + issetFields = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTimestamp{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")); + } + if !issetFields{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Fields is not set")); + } + return nil +} + +func (p *Log) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Timestamp = v +} + return nil +} + +func (p *Log) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Fields = tSlice + for i := 0; i < size; i ++ { + _elem0 := &Tag{} + if err := _elem0.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Fields = append(p.Fields, _elem0) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Log) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Log"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Log) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) } + return err +} + +func (p *Log) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "fields", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:fields: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Fields)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Fields { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:fields: ", p), err) } + return err +} + +func (p *Log) Equals(other *Log) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Timestamp != other.Timestamp { return false } + if len(p.Fields) != len(other.Fields) { return false } + for i, _tgt := range p.Fields { + _src1 := other.Fields[i] + if !_tgt.Equals(_src1) { return false } + } + return true +} + +func (p *Log) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Log(%+v)", *p) +} + +// Attributes: +// - RefType +// - TraceIdLow +// - TraceIdHigh +// - SpanId +type SpanRef struct { + RefType SpanRefType `thrift:"refType,1,required" db:"refType" json:"refType"` + TraceIdLow int64 `thrift:"traceIdLow,2,required" db:"traceIdLow" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,3,required" db:"traceIdHigh" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,4,required" db:"spanId" json:"spanId"` +} + +func NewSpanRef() *SpanRef { + return &SpanRef{} +} + + +func (p *SpanRef) GetRefType() SpanRefType { + return p.RefType +} + +func (p *SpanRef) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *SpanRef) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *SpanRef) GetSpanId() int64 { + return p.SpanId +} +func (p *SpanRef) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetRefType bool = false; + var issetTraceIdLow bool = false; + var issetTraceIdHigh bool = false; + var issetSpanId bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I32 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetRefType = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.I64 { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + issetTraceIdLow = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.I64 { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + issetTraceIdHigh = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.I64 { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + issetSpanId = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetRefType{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field RefType is not set")); + } + if !issetTraceIdLow{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")); + } + if !issetTraceIdHigh{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")); + } + if !issetSpanId{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")); + } + return nil +} + +func (p *SpanRef) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + temp := SpanRefType(v) + p.RefType = temp +} + return nil +} + +func (p *SpanRef) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + p.TraceIdLow = v +} + return nil +} + +func (p *SpanRef) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.TraceIdHigh = v +} + return nil +} + +func (p *SpanRef) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 4: ", err) +} else { + p.SpanId = v +} + return nil +} + +func (p *SpanRef) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "SpanRef"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *SpanRef) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "refType", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:refType: ", p), err) } + if err := oprot.WriteI32(ctx, int32(p.RefType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.refType (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:refType: ", p), err) } + return err +} + +func (p *SpanRef) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "traceIdLow", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdLow: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdLow: ", p), err) } + return err +} + +func (p *SpanRef) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "traceIdHigh", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:traceIdHigh: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:traceIdHigh: ", p), err) } + return err +} + +func (p *SpanRef) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "spanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:spanId: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (4) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:spanId: ", p), err) } + return err +} + +func (p *SpanRef) Equals(other *SpanRef) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.RefType != other.RefType { return false } + if p.TraceIdLow != other.TraceIdLow { return false } + if p.TraceIdHigh != other.TraceIdHigh { return false } + if p.SpanId != other.SpanId { return false } + return true +} + +func (p *SpanRef) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("SpanRef(%+v)", *p) +} + +// Attributes: +// - TraceIdLow +// - TraceIdHigh +// - SpanId +// - ParentSpanId +// - OperationName +// - References +// - Flags +// - StartTime +// - Duration +// - Tags +// - Logs +type Span struct { + TraceIdLow int64 `thrift:"traceIdLow,1,required" db:"traceIdLow" json:"traceIdLow"` + TraceIdHigh int64 `thrift:"traceIdHigh,2,required" db:"traceIdHigh" json:"traceIdHigh"` + SpanId int64 `thrift:"spanId,3,required" db:"spanId" json:"spanId"` + ParentSpanId int64 `thrift:"parentSpanId,4,required" db:"parentSpanId" json:"parentSpanId"` + OperationName string `thrift:"operationName,5,required" db:"operationName" json:"operationName"` + References []*SpanRef `thrift:"references,6" db:"references" json:"references,omitempty"` + Flags int32 `thrift:"flags,7,required" db:"flags" json:"flags"` + StartTime int64 `thrift:"startTime,8,required" db:"startTime" json:"startTime"` + Duration int64 `thrift:"duration,9,required" db:"duration" json:"duration"` + Tags []*Tag `thrift:"tags,10" db:"tags" json:"tags,omitempty"` + Logs []*Log `thrift:"logs,11" db:"logs" json:"logs,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + + +func (p *Span) GetTraceIdLow() int64 { + return p.TraceIdLow +} + +func (p *Span) GetTraceIdHigh() int64 { + return p.TraceIdHigh +} + +func (p *Span) GetSpanId() int64 { + return p.SpanId +} + +func (p *Span) GetParentSpanId() int64 { + return p.ParentSpanId +} + +func (p *Span) GetOperationName() string { + return p.OperationName +} +var Span_References_DEFAULT []*SpanRef + +func (p *Span) GetReferences() []*SpanRef { + return p.References +} + +func (p *Span) GetFlags() int32 { + return p.Flags +} + +func (p *Span) GetStartTime() int64 { + return p.StartTime +} + +func (p *Span) GetDuration() int64 { + return p.Duration +} +var Span_Tags_DEFAULT []*Tag + +func (p *Span) GetTags() []*Tag { + return p.Tags +} +var Span_Logs_DEFAULT []*Log + +func (p *Span) GetLogs() []*Log { + return p.Logs +} +func (p *Span) IsSetReferences() bool { + return p.References != nil +} + +func (p *Span) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Span) IsSetLogs() bool { + return p.Logs != nil +} + +func (p *Span) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetTraceIdLow bool = false; + var issetTraceIdHigh bool = false; + var issetSpanId bool = false; + var issetParentSpanId bool = false; + var issetOperationName bool = false; + var issetFlags bool = false; + var issetStartTime bool = false; + var issetDuration bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I64 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetTraceIdLow = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.I64 { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + issetTraceIdHigh = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.I64 { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + issetSpanId = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.I64 { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + issetParentSpanId = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 5: + if fieldTypeId == thrift.STRING { + if err := p.ReadField5(ctx, iprot); err != nil { + return err + } + issetOperationName = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 6: + if fieldTypeId == thrift.LIST { + if err := p.ReadField6(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 7: + if fieldTypeId == thrift.I32 { + if err := p.ReadField7(ctx, iprot); err != nil { + return err + } + issetFlags = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 8: + if fieldTypeId == thrift.I64 { + if err := p.ReadField8(ctx, iprot); err != nil { + return err + } + issetStartTime = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 9: + if fieldTypeId == thrift.I64 { + if err := p.ReadField9(ctx, iprot); err != nil { + return err + } + issetDuration = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 10: + if fieldTypeId == thrift.LIST { + if err := p.ReadField10(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 11: + if fieldTypeId == thrift.LIST { + if err := p.ReadField11(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetTraceIdLow{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")); + } + if !issetTraceIdHigh{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")); + } + if !issetSpanId{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")); + } + if !issetParentSpanId{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentSpanId is not set")); + } + if !issetOperationName{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field OperationName is not set")); + } + if !issetFlags{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Flags is not set")); + } + if !issetStartTime{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StartTime is not set")); + } + if !issetDuration{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Duration is not set")); + } + return nil +} + +func (p *Span) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.TraceIdLow = v +} + return nil +} + +func (p *Span) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + p.TraceIdHigh = v +} + return nil +} + +func (p *Span) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.SpanId = v +} + return nil +} + +func (p *Span) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 4: ", err) +} else { + p.ParentSpanId = v +} + return nil +} + +func (p *Span) ReadField5(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 5: ", err) +} else { + p.OperationName = v +} + return nil +} + +func (p *Span) ReadField6(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*SpanRef, 0, size) + p.References = tSlice + for i := 0; i < size; i ++ { + _elem2 := &SpanRef{} + if err := _elem2.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) + } + p.References = append(p.References, _elem2) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) ReadField7(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { + return thrift.PrependError("error reading field 7: ", err) +} else { + p.Flags = v +} + return nil +} + +func (p *Span) ReadField8(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 8: ", err) +} else { + p.StartTime = v +} + return nil +} + +func (p *Span) ReadField9(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 9: ", err) +} else { + p.Duration = v +} + return nil +} + +func (p *Span) ReadField10(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i ++ { + _elem3 := &Tag{} + if err := _elem3.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem3), err) + } + p.Tags = append(p.Tags, _elem3) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) ReadField11(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Log, 0, size) + p.Logs = tSlice + for i := 0; i < size; i ++ { + _elem4 := &Log{} + if err := _elem4.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) + } + p.Logs = append(p.Logs, _elem4) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Span"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + if err := p.writeField5(ctx, oprot); err != nil { return err } + if err := p.writeField6(ctx, oprot); err != nil { return err } + if err := p.writeField7(ctx, oprot); err != nil { return err } + if err := p.writeField8(ctx, oprot); err != nil { return err } + if err := p.writeField9(ctx, oprot); err != nil { return err } + if err := p.writeField10(ctx, oprot); err != nil { return err } + if err := p.writeField11(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Span) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "traceIdLow", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceIdLow: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.TraceIdLow)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceIdLow: ", p), err) } + return err +} + +func (p *Span) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "traceIdHigh", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdHigh: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.TraceIdHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdHigh: ", p), err) } + return err +} + +func (p *Span) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "spanId", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:spanId: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.SpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.spanId (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:spanId: ", p), err) } + return err +} + +func (p *Span) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "parentSpanId", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:parentSpanId: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.ParentSpanId)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parentSpanId (4) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:parentSpanId: ", p), err) } + return err +} + +func (p *Span) writeField5(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "operationName", thrift.STRING, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:operationName: ", p), err) } + if err := oprot.WriteString(ctx, string(p.OperationName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.operationName (5) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:operationName: ", p), err) } + return err +} + +func (p *Span) writeField6(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetReferences() { + if err := oprot.WriteFieldBegin(ctx, "references", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:references: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.References)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.References { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:references: ", p), err) } + } + return err +} + +func (p *Span) writeField7(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "flags", thrift.I32, 7); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:flags: ", p), err) } + if err := oprot.WriteI32(ctx, int32(p.Flags)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.flags (7) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 7:flags: ", p), err) } + return err +} + +func (p *Span) writeField8(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "startTime", thrift.I64, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:startTime: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.StartTime)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.startTime (8) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:startTime: ", p), err) } + return err +} + +func (p *Span) writeField9(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "duration", thrift.I64, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:duration: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (9) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:duration: ", p), err) } + return err +} + +func (p *Span) writeField10(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin(ctx, "tags", thrift.LIST, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:tags: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:tags: ", p), err) } + } + return err +} + +func (p *Span) writeField11(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetLogs() { + if err := oprot.WriteFieldBegin(ctx, "logs", thrift.LIST, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:logs: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Logs)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Logs { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:logs: ", p), err) } + } + return err +} + +func (p *Span) Equals(other *Span) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.TraceIdLow != other.TraceIdLow { return false } + if p.TraceIdHigh != other.TraceIdHigh { return false } + if p.SpanId != other.SpanId { return false } + if p.ParentSpanId != other.ParentSpanId { return false } + if p.OperationName != other.OperationName { return false } + if len(p.References) != len(other.References) { return false } + for i, _tgt := range p.References { + _src5 := other.References[i] + if !_tgt.Equals(_src5) { return false } + } + if p.Flags != other.Flags { return false } + if p.StartTime != other.StartTime { return false } + if p.Duration != other.Duration { return false } + if len(p.Tags) != len(other.Tags) { return false } + for i, _tgt := range p.Tags { + _src6 := other.Tags[i] + if !_tgt.Equals(_src6) { return false } + } + if len(p.Logs) != len(other.Logs) { return false } + for i, _tgt := range p.Logs { + _src7 := other.Logs[i] + if !_tgt.Equals(_src7) { return false } + } + return true +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - ServiceName +// - Tags +type Process struct { + ServiceName string `thrift:"serviceName,1,required" db:"serviceName" json:"serviceName"` + Tags []*Tag `thrift:"tags,2" db:"tags" json:"tags,omitempty"` +} + +func NewProcess() *Process { + return &Process{} +} + + +func (p *Process) GetServiceName() string { + return p.ServiceName +} +var Process_Tags_DEFAULT []*Tag + +func (p *Process) GetTags() []*Tag { + return p.Tags +} +func (p *Process) IsSetTags() bool { + return p.Tags != nil +} + +func (p *Process) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetServiceName bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.STRING { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetServiceName = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.LIST { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetServiceName{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")); + } + return nil +} + +func (p *Process) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.ServiceName = v +} + return nil +} + +func (p *Process) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Tag, 0, size) + p.Tags = tSlice + for i := 0; i < size; i ++ { + _elem8 := &Tag{} + if err := _elem8.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem8), err) + } + p.Tags = append(p.Tags, _elem8) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Process) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Process"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Process) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "serviceName", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) } + if err := oprot.WriteString(ctx, string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) } + return err +} + +func (p *Process) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetTags() { + if err := oprot.WriteFieldBegin(ctx, "tags", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tags: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Tags)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Tags { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tags: ", p), err) } + } + return err +} + +func (p *Process) Equals(other *Process) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.ServiceName != other.ServiceName { return false } + if len(p.Tags) != len(other.Tags) { return false } + for i, _tgt := range p.Tags { + _src9 := other.Tags[i] + if !_tgt.Equals(_src9) { return false } + } + return true +} + +func (p *Process) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Process(%+v)", *p) +} + +// Attributes: +// - FullQueueDroppedSpans +// - TooLargeDroppedSpans +// - FailedToEmitSpans +type ClientStats struct { + FullQueueDroppedSpans int64 `thrift:"fullQueueDroppedSpans,1,required" db:"fullQueueDroppedSpans" json:"fullQueueDroppedSpans"` + TooLargeDroppedSpans int64 `thrift:"tooLargeDroppedSpans,2,required" db:"tooLargeDroppedSpans" json:"tooLargeDroppedSpans"` + FailedToEmitSpans int64 `thrift:"failedToEmitSpans,3,required" db:"failedToEmitSpans" json:"failedToEmitSpans"` +} + +func NewClientStats() *ClientStats { + return &ClientStats{} +} + + +func (p *ClientStats) GetFullQueueDroppedSpans() int64 { + return p.FullQueueDroppedSpans +} + +func (p *ClientStats) GetTooLargeDroppedSpans() int64 { + return p.TooLargeDroppedSpans +} + +func (p *ClientStats) GetFailedToEmitSpans() int64 { + return p.FailedToEmitSpans +} +func (p *ClientStats) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetFullQueueDroppedSpans bool = false; + var issetTooLargeDroppedSpans bool = false; + var issetFailedToEmitSpans bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I64 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetFullQueueDroppedSpans = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.I64 { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + issetTooLargeDroppedSpans = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.I64 { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + issetFailedToEmitSpans = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetFullQueueDroppedSpans{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field FullQueueDroppedSpans is not set")); + } + if !issetTooLargeDroppedSpans{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TooLargeDroppedSpans is not set")); + } + if !issetFailedToEmitSpans{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field FailedToEmitSpans is not set")); + } + return nil +} + +func (p *ClientStats) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.FullQueueDroppedSpans = v +} + return nil +} + +func (p *ClientStats) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + p.TooLargeDroppedSpans = v +} + return nil +} + +func (p *ClientStats) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.FailedToEmitSpans = v +} + return nil +} + +func (p *ClientStats) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "ClientStats"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *ClientStats) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "fullQueueDroppedSpans", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:fullQueueDroppedSpans: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.FullQueueDroppedSpans)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.fullQueueDroppedSpans (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:fullQueueDroppedSpans: ", p), err) } + return err +} + +func (p *ClientStats) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "tooLargeDroppedSpans", thrift.I64, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tooLargeDroppedSpans: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.TooLargeDroppedSpans)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.tooLargeDroppedSpans (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tooLargeDroppedSpans: ", p), err) } + return err +} + +func (p *ClientStats) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "failedToEmitSpans", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:failedToEmitSpans: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.FailedToEmitSpans)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.failedToEmitSpans (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:failedToEmitSpans: ", p), err) } + return err +} + +func (p *ClientStats) Equals(other *ClientStats) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.FullQueueDroppedSpans != other.FullQueueDroppedSpans { return false } + if p.TooLargeDroppedSpans != other.TooLargeDroppedSpans { return false } + if p.FailedToEmitSpans != other.FailedToEmitSpans { return false } + return true +} + +func (p *ClientStats) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ClientStats(%+v)", *p) +} + +// Attributes: +// - Process +// - Spans +// - SeqNo +// - Stats +type Batch struct { + Process *Process `thrift:"process,1,required" db:"process" json:"process"` + Spans []*Span `thrift:"spans,2,required" db:"spans" json:"spans"` + SeqNo *int64 `thrift:"seqNo,3" db:"seqNo" json:"seqNo,omitempty"` + Stats *ClientStats `thrift:"stats,4" db:"stats" json:"stats,omitempty"` +} + +func NewBatch() *Batch { + return &Batch{} +} + +var Batch_Process_DEFAULT *Process +func (p *Batch) GetProcess() *Process { + if !p.IsSetProcess() { + return Batch_Process_DEFAULT + } +return p.Process +} + +func (p *Batch) GetSpans() []*Span { + return p.Spans +} +var Batch_SeqNo_DEFAULT int64 +func (p *Batch) GetSeqNo() int64 { + if !p.IsSetSeqNo() { + return Batch_SeqNo_DEFAULT + } +return *p.SeqNo +} +var Batch_Stats_DEFAULT *ClientStats +func (p *Batch) GetStats() *ClientStats { + if !p.IsSetStats() { + return Batch_Stats_DEFAULT + } +return p.Stats +} +func (p *Batch) IsSetProcess() bool { + return p.Process != nil +} + +func (p *Batch) IsSetSeqNo() bool { + return p.SeqNo != nil +} + +func (p *Batch) IsSetStats() bool { + return p.Stats != nil +} + +func (p *Batch) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetProcess bool = false; + var issetSpans bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.STRUCT { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetProcess = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.LIST { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + issetSpans = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.I64 { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.STRUCT { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetProcess{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Process is not set")); + } + if !issetSpans{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Spans is not set")); + } + return nil +} + +func (p *Batch) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + p.Process = &Process{} + if err := p.Process.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Process), err) + } + return nil +} + +func (p *Batch) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i ++ { + _elem10 := &Span{} + if err := _elem10.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem10), err) + } + p.Spans = append(p.Spans, _elem10) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Batch) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.SeqNo = &v +} + return nil +} + +func (p *Batch) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + p.Stats = &ClientStats{} + if err := p.Stats.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Stats), err) + } + return nil +} + +func (p *Batch) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Batch"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Batch) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "process", thrift.STRUCT, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:process: ", p), err) } + if err := p.Process.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Process), err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:process: ", p), err) } + return err +} + +func (p *Batch) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:spans: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:spans: ", p), err) } + return err +} + +func (p *Batch) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetSeqNo() { + if err := oprot.WriteFieldBegin(ctx, "seqNo", thrift.I64, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:seqNo: ", p), err) } + if err := oprot.WriteI64(ctx, int64(*p.SeqNo)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.seqNo (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:seqNo: ", p), err) } + } + return err +} + +func (p *Batch) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetStats() { + if err := oprot.WriteFieldBegin(ctx, "stats", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:stats: ", p), err) } + if err := p.Stats.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Stats), err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:stats: ", p), err) } + } + return err +} + +func (p *Batch) Equals(other *Batch) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if !p.Process.Equals(other.Process) { return false } + if len(p.Spans) != len(other.Spans) { return false } + for i, _tgt := range p.Spans { + _src11 := other.Spans[i] + if !_tgt.Equals(_src11) { return false } + } + if p.SeqNo != other.SeqNo { + if p.SeqNo == nil || other.SeqNo == nil { + return false + } + if (*p.SeqNo) != (*other.SeqNo) { return false } + } + if !p.Stats.Equals(other.Stats) { return false } + return true +} + +func (p *Batch) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Batch(%+v)", *p) +} + +// Attributes: +// - Ok +type BatchSubmitResponse struct { + Ok bool `thrift:"ok,1,required" db:"ok" json:"ok"` +} + +func NewBatchSubmitResponse() *BatchSubmitResponse { + return &BatchSubmitResponse{} +} + + +func (p *BatchSubmitResponse) GetOk() bool { + return p.Ok +} +func (p *BatchSubmitResponse) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.BOOL { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetOk = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")); + } + return nil +} + +func (p *BatchSubmitResponse) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Ok = v +} + return nil +} + +func (p *BatchSubmitResponse) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "BatchSubmitResponse"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *BatchSubmitResponse) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) } + if err := oprot.WriteBool(ctx, bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) } + return err +} + +func (p *BatchSubmitResponse) Equals(other *BatchSubmitResponse) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Ok != other.Ok { return false } + return true +} + +func (p *BatchSubmitResponse) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BatchSubmitResponse(%+v)", *p) +} + +type Collector interface { + // Parameters: + // - Batches + SubmitBatches(ctx context.Context, batches []*Batch) (_r []*BatchSubmitResponse, _err error) +} + +type CollectorClient struct { + c thrift.TClient + meta thrift.ResponseMeta +} + +func NewCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *CollectorClient { + return &CollectorClient{ + c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)), + } +} + +func NewCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *CollectorClient { + return &CollectorClient{ + c: thrift.NewTStandardClient(iprot, oprot), + } +} + +func NewCollectorClient(c thrift.TClient) *CollectorClient { + return &CollectorClient{ + c: c, + } +} + +func (p *CollectorClient) Client_() thrift.TClient { + return p.c +} + +func (p *CollectorClient) LastResponseMeta_() thrift.ResponseMeta { + return p.meta +} + +func (p *CollectorClient) SetLastResponseMeta_(meta thrift.ResponseMeta) { + p.meta = meta +} + +// Parameters: +// - Batches +func (p *CollectorClient) SubmitBatches(ctx context.Context, batches []*Batch) (_r []*BatchSubmitResponse, _err error) { + var _args12 CollectorSubmitBatchesArgs + _args12.Batches = batches + var _result14 CollectorSubmitBatchesResult + var _meta13 thrift.ResponseMeta + _meta13, _err = p.Client_().Call(ctx, "submitBatches", &_args12, &_result14) + p.SetLastResponseMeta_(_meta13) + if _err != nil { + return + } + return _result14.GetSuccess(), nil +} + +type CollectorProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler Collector +} + +func (p *CollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *CollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *CollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewCollectorProcessor(handler Collector) *CollectorProcessor { + + self15 := &CollectorProcessor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)} + self15.processorMap["submitBatches"] = &collectorProcessorSubmitBatches{handler:handler} +return self15 +} + +func (p *CollectorProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err2 := iprot.ReadMessageBegin(ctx) + if err2 != nil { return false, thrift.WrapTException(err2) } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(ctx, seqId, iprot, oprot) + } + iprot.Skip(ctx, thrift.STRUCT) + iprot.ReadMessageEnd(ctx) + x16 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function " + name) + oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId) + x16.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return false, x16 + +} + +type collectorProcessorSubmitBatches struct { + handler Collector +} + +func (p *collectorProcessorSubmitBatches) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := CollectorSubmitBatchesArgs{} + var err2 error + if err2 = args.Read(ctx, iprot); err2 != nil { + iprot.ReadMessageEnd(ctx) + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error()) + oprot.WriteMessageBegin(ctx, "submitBatches", thrift.EXCEPTION, seqId) + x.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return false, thrift.WrapTException(err2) + } + iprot.ReadMessageEnd(ctx) + + tickerCancel := func() {} + // Start a goroutine to do server side connectivity check. + if thrift.ServerConnectivityCheckInterval > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithCancel(ctx) + defer cancel() + var tickerCtx context.Context + tickerCtx, tickerCancel = context.WithCancel(context.Background()) + defer tickerCancel() + go func(ctx context.Context, cancel context.CancelFunc) { + ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if !iprot.Transport().IsOpen() { + cancel() + return + } + } + } + }(tickerCtx, cancel) + } + + result := CollectorSubmitBatchesResult{} + var retval []*BatchSubmitResponse + if retval, err2 = p.handler.SubmitBatches(ctx, args.Batches); err2 != nil { + tickerCancel() + if err2 == thrift.ErrAbandonRequest { + return false, thrift.WrapTException(err2) + } + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitBatches: " + err2.Error()) + oprot.WriteMessageBegin(ctx, "submitBatches", thrift.EXCEPTION, seqId) + x.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return true, thrift.WrapTException(err2) + } else { + result.Success = retval + } + tickerCancel() + if err2 = oprot.WriteMessageBegin(ctx, "submitBatches", thrift.REPLY, seqId); err2 != nil { + err = thrift.WrapTException(err2) + } + if err2 = result.Write(ctx, oprot); err == nil && err2 != nil { + err = thrift.WrapTException(err2) + } + if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil { + err = thrift.WrapTException(err2) + } + if err2 = oprot.Flush(ctx); err == nil && err2 != nil { + err = thrift.WrapTException(err2) + } + if err != nil { + return + } + return true, err +} + + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Batches +type CollectorSubmitBatchesArgs struct { + Batches []*Batch `thrift:"batches,1" db:"batches" json:"batches"` +} + +func NewCollectorSubmitBatchesArgs() *CollectorSubmitBatchesArgs { + return &CollectorSubmitBatchesArgs{} +} + + +func (p *CollectorSubmitBatchesArgs) GetBatches() []*Batch { + return p.Batches +} +func (p *CollectorSubmitBatchesArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.LIST { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *CollectorSubmitBatchesArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Batch, 0, size) + p.Batches = tSlice + for i := 0; i < size; i ++ { + _elem17 := &Batch{} + if err := _elem17.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem17), err) + } + p.Batches = append(p.Batches, _elem17) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *CollectorSubmitBatchesArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "submitBatches_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *CollectorSubmitBatchesArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "batches", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:batches: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Batches)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Batches { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:batches: ", p), err) } + return err +} + +func (p *CollectorSubmitBatchesArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("CollectorSubmitBatchesArgs(%+v)", *p) +} + +// Attributes: +// - Success +type CollectorSubmitBatchesResult struct { + Success []*BatchSubmitResponse `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewCollectorSubmitBatchesResult() *CollectorSubmitBatchesResult { + return &CollectorSubmitBatchesResult{} +} + +var CollectorSubmitBatchesResult_Success_DEFAULT []*BatchSubmitResponse + +func (p *CollectorSubmitBatchesResult) GetSuccess() []*BatchSubmitResponse { + return p.Success +} +func (p *CollectorSubmitBatchesResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *CollectorSubmitBatchesResult) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 0: + if fieldTypeId == thrift.LIST { + if err := p.ReadField0(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *CollectorSubmitBatchesResult) ReadField0(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BatchSubmitResponse, 0, size) + p.Success = tSlice + for i := 0; i < size; i ++ { + _elem18 := &BatchSubmitResponse{} + if err := _elem18.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem18), err) + } + p.Success = append(p.Success, _elem18) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *CollectorSubmitBatchesResult) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "submitBatches_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField0(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *CollectorSubmitBatchesResult) writeField0(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin(ctx, "success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) } + } + return err +} + +func (p *CollectorSubmitBatchesResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("CollectorSubmitBatchesResult(%+v)", *p) +} + + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/jaeger/ttypes.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,2106 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package jaeger - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -var GoUnusedProtection__ int - -type TagType int64 - -const ( - TagType_STRING TagType = 0 - TagType_DOUBLE TagType = 1 - TagType_BOOL TagType = 2 - TagType_LONG TagType = 3 - TagType_BINARY TagType = 4 -) - -func (p TagType) String() string { - switch p { - case TagType_STRING: - return "STRING" - case TagType_DOUBLE: - return "DOUBLE" - case TagType_BOOL: - return "BOOL" - case TagType_LONG: - return "LONG" - case TagType_BINARY: - return "BINARY" - } - return "" -} - -func TagTypeFromString(s string) (TagType, error) { - switch s { - case "STRING": - return TagType_STRING, nil - case "DOUBLE": - return TagType_DOUBLE, nil - case "BOOL": - return TagType_BOOL, nil - case "LONG": - return TagType_LONG, nil - case "BINARY": - return TagType_BINARY, nil - } - return TagType(0), fmt.Errorf("not a valid TagType string") -} - -func TagTypePtr(v TagType) *TagType { return &v } - -func (p TagType) MarshalText() ([]byte, error) { - return []byte(p.String()), nil -} - -func (p *TagType) UnmarshalText(text []byte) error { - q, err := TagTypeFromString(string(text)) - if err != nil { - return err - } - *p = q - return nil -} - -type SpanRefType int64 - -const ( - SpanRefType_CHILD_OF SpanRefType = 0 - SpanRefType_FOLLOWS_FROM SpanRefType = 1 -) - -func (p SpanRefType) String() string { - switch p { - case SpanRefType_CHILD_OF: - return "CHILD_OF" - case SpanRefType_FOLLOWS_FROM: - return "FOLLOWS_FROM" - } - return "" -} - -func SpanRefTypeFromString(s string) (SpanRefType, error) { - switch s { - case "CHILD_OF": - return SpanRefType_CHILD_OF, nil - case "FOLLOWS_FROM": - return SpanRefType_FOLLOWS_FROM, nil - } - return SpanRefType(0), fmt.Errorf("not a valid SpanRefType string") -} - -func SpanRefTypePtr(v SpanRefType) *SpanRefType { return &v } - -func (p SpanRefType) MarshalText() ([]byte, error) { - return []byte(p.String()), nil -} - -func (p *SpanRefType) UnmarshalText(text []byte) error { - q, err := SpanRefTypeFromString(string(text)) - if err != nil { - return err - } - *p = q - return nil -} - -// Attributes: -// - Key -// - VType -// - VStr -// - VDouble -// - VBool -// - VLong -// - VBinary -type Tag struct { - Key string `thrift:"key,1,required" json:"key"` - VType TagType `thrift:"vType,2,required" json:"vType"` - VStr *string `thrift:"vStr,3" json:"vStr,omitempty"` - VDouble *float64 `thrift:"vDouble,4" json:"vDouble,omitempty"` - VBool *bool `thrift:"vBool,5" json:"vBool,omitempty"` - VLong *int64 `thrift:"vLong,6" json:"vLong,omitempty"` - VBinary []byte `thrift:"vBinary,7" json:"vBinary,omitempty"` -} - -func NewTag() *Tag { - return &Tag{} -} - -func (p *Tag) GetKey() string { - return p.Key -} - -func (p *Tag) GetVType() TagType { - return p.VType -} - -var Tag_VStr_DEFAULT string - -func (p *Tag) GetVStr() string { - if !p.IsSetVStr() { - return Tag_VStr_DEFAULT - } - return *p.VStr -} - -var Tag_VDouble_DEFAULT float64 - -func (p *Tag) GetVDouble() float64 { - if !p.IsSetVDouble() { - return Tag_VDouble_DEFAULT - } - return *p.VDouble -} - -var Tag_VBool_DEFAULT bool - -func (p *Tag) GetVBool() bool { - if !p.IsSetVBool() { - return Tag_VBool_DEFAULT - } - return *p.VBool -} - -var Tag_VLong_DEFAULT int64 - -func (p *Tag) GetVLong() int64 { - if !p.IsSetVLong() { - return Tag_VLong_DEFAULT - } - return *p.VLong -} - -var Tag_VBinary_DEFAULT []byte - -func (p *Tag) GetVBinary() []byte { - return p.VBinary -} -func (p *Tag) IsSetVStr() bool { - return p.VStr != nil -} - -func (p *Tag) IsSetVDouble() bool { - return p.VDouble != nil -} - -func (p *Tag) IsSetVBool() bool { - return p.VBool != nil -} - -func (p *Tag) IsSetVLong() bool { - return p.VLong != nil -} - -func (p *Tag) IsSetVBinary() bool { - return p.VBinary != nil -} - -func (p *Tag) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetKey bool = false - var issetVType bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetKey = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetVType = true - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - case 5: - if err := p.readField5(iprot); err != nil { - return err - } - case 6: - if err := p.readField6(iprot); err != nil { - return err - } - case 7: - if err := p.readField7(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetKey { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Key is not set")) - } - if !issetVType { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field VType is not set")) - } - return nil -} - -func (p *Tag) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Key = v - } - return nil -} - -func (p *Tag) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - temp := TagType(v) - p.VType = temp - } - return nil -} - -func (p *Tag) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.VStr = &v - } - return nil -} - -func (p *Tag) readField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadDouble(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.VDouble = &v - } - return nil -} - -func (p *Tag) readField5(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 5: ", err) - } else { - p.VBool = &v - } - return nil -} - -func (p *Tag) readField6(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 6: ", err) - } else { - p.VLong = &v - } - return nil -} - -func (p *Tag) readField7(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 7: ", err) - } else { - p.VBinary = v - } - return nil -} - -func (p *Tag) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Tag"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := p.writeField5(oprot); err != nil { - return err - } - if err := p.writeField6(oprot); err != nil { - return err - } - if err := p.writeField7(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Tag) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) - } - if err := oprot.WriteString(string(p.Key)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) - } - return err -} - -func (p *Tag) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("vType", thrift.I32, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:vType: ", p), err) - } - if err := oprot.WriteI32(int32(p.VType)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.vType (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:vType: ", p), err) - } - return err -} - -func (p *Tag) writeField3(oprot thrift.TProtocol) (err error) { - if p.IsSetVStr() { - if err := oprot.WriteFieldBegin("vStr", thrift.STRING, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:vStr: ", p), err) - } - if err := oprot.WriteString(string(*p.VStr)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.vStr (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:vStr: ", p), err) - } - } - return err -} - -func (p *Tag) writeField4(oprot thrift.TProtocol) (err error) { - if p.IsSetVDouble() { - if err := oprot.WriteFieldBegin("vDouble", thrift.DOUBLE, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:vDouble: ", p), err) - } - if err := oprot.WriteDouble(float64(*p.VDouble)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.vDouble (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:vDouble: ", p), err) - } - } - return err -} - -func (p *Tag) writeField5(oprot thrift.TProtocol) (err error) { - if p.IsSetVBool() { - if err := oprot.WriteFieldBegin("vBool", thrift.BOOL, 5); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:vBool: ", p), err) - } - if err := oprot.WriteBool(bool(*p.VBool)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.vBool (5) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 5:vBool: ", p), err) - } - } - return err -} - -func (p *Tag) writeField6(oprot thrift.TProtocol) (err error) { - if p.IsSetVLong() { - if err := oprot.WriteFieldBegin("vLong", thrift.I64, 6); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:vLong: ", p), err) - } - if err := oprot.WriteI64(int64(*p.VLong)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.vLong (6) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 6:vLong: ", p), err) - } - } - return err -} - -func (p *Tag) writeField7(oprot thrift.TProtocol) (err error) { - if p.IsSetVBinary() { - if err := oprot.WriteFieldBegin("vBinary", thrift.STRING, 7); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:vBinary: ", p), err) - } - if err := oprot.WriteBinary(p.VBinary); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.vBinary (7) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 7:vBinary: ", p), err) - } - } - return err -} - -func (p *Tag) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Tag(%+v)", *p) -} - -// Attributes: -// - Timestamp -// - Fields -type Log struct { - Timestamp int64 `thrift:"timestamp,1,required" json:"timestamp"` - Fields []*Tag `thrift:"fields,2,required" json:"fields"` -} - -func NewLog() *Log { - return &Log{} -} - -func (p *Log) GetTimestamp() int64 { - return p.Timestamp -} - -func (p *Log) GetFields() []*Tag { - return p.Fields -} -func (p *Log) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetTimestamp bool = false - var issetFields bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetTimestamp = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetFields = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetTimestamp { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Timestamp is not set")) - } - if !issetFields { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Fields is not set")) - } - return nil -} - -func (p *Log) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Timestamp = v - } - return nil -} - -func (p *Log) readField2(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Tag, 0, size) - p.Fields = tSlice - for i := 0; i < size; i++ { - _elem0 := &Tag{} - if err := _elem0.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) - } - p.Fields = append(p.Fields, _elem0) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Log) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Log"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Log) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) - } - if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) - } - return err -} - -func (p *Log) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("fields", thrift.LIST, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:fields: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Fields)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Fields { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:fields: ", p), err) - } - return err -} - -func (p *Log) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Log(%+v)", *p) -} - -// Attributes: -// - RefType -// - TraceIdLow -// - TraceIdHigh -// - SpanId -type SpanRef struct { - RefType SpanRefType `thrift:"refType,1,required" json:"refType"` - TraceIdLow int64 `thrift:"traceIdLow,2,required" json:"traceIdLow"` - TraceIdHigh int64 `thrift:"traceIdHigh,3,required" json:"traceIdHigh"` - SpanId int64 `thrift:"spanId,4,required" json:"spanId"` -} - -func NewSpanRef() *SpanRef { - return &SpanRef{} -} - -func (p *SpanRef) GetRefType() SpanRefType { - return p.RefType -} - -func (p *SpanRef) GetTraceIdLow() int64 { - return p.TraceIdLow -} - -func (p *SpanRef) GetTraceIdHigh() int64 { - return p.TraceIdHigh -} - -func (p *SpanRef) GetSpanId() int64 { - return p.SpanId -} -func (p *SpanRef) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetRefType bool = false - var issetTraceIdLow bool = false - var issetTraceIdHigh bool = false - var issetSpanId bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetRefType = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetTraceIdLow = true - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - issetTraceIdHigh = true - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - issetSpanId = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetRefType { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field RefType is not set")) - } - if !issetTraceIdLow { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) - } - if !issetTraceIdHigh { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) - } - if !issetSpanId { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) - } - return nil -} - -func (p *SpanRef) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - temp := SpanRefType(v) - p.RefType = temp - } - return nil -} - -func (p *SpanRef) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.TraceIdLow = v - } - return nil -} - -func (p *SpanRef) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.TraceIdHigh = v - } - return nil -} - -func (p *SpanRef) readField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.SpanId = v - } - return nil -} - -func (p *SpanRef) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("SpanRef"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *SpanRef) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("refType", thrift.I32, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:refType: ", p), err) - } - if err := oprot.WriteI32(int32(p.RefType)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.refType (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:refType: ", p), err) - } - return err -} - -func (p *SpanRef) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdLow: ", p), err) - } - if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdLow: ", p), err) - } - return err -} - -func (p *SpanRef) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:traceIdHigh: ", p), err) - } - if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:traceIdHigh: ", p), err) - } - return err -} - -func (p *SpanRef) writeField4(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("spanId", thrift.I64, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:spanId: ", p), err) - } - if err := oprot.WriteI64(int64(p.SpanId)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.spanId (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:spanId: ", p), err) - } - return err -} - -func (p *SpanRef) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("SpanRef(%+v)", *p) -} - -// Attributes: -// - TraceIdLow -// - TraceIdHigh -// - SpanId -// - ParentSpanId -// - OperationName -// - References -// - Flags -// - StartTime -// - Duration -// - Tags -// - Logs -type Span struct { - TraceIdLow int64 `thrift:"traceIdLow,1,required" json:"traceIdLow"` - TraceIdHigh int64 `thrift:"traceIdHigh,2,required" json:"traceIdHigh"` - SpanId int64 `thrift:"spanId,3,required" json:"spanId"` - ParentSpanId int64 `thrift:"parentSpanId,4,required" json:"parentSpanId"` - OperationName string `thrift:"operationName,5,required" json:"operationName"` - References []*SpanRef `thrift:"references,6" json:"references,omitempty"` - Flags int32 `thrift:"flags,7,required" json:"flags"` - StartTime int64 `thrift:"startTime,8,required" json:"startTime"` - Duration int64 `thrift:"duration,9,required" json:"duration"` - Tags []*Tag `thrift:"tags,10" json:"tags,omitempty"` - Logs []*Log `thrift:"logs,11" json:"logs,omitempty"` -} - -func NewSpan() *Span { - return &Span{} -} - -func (p *Span) GetTraceIdLow() int64 { - return p.TraceIdLow -} - -func (p *Span) GetTraceIdHigh() int64 { - return p.TraceIdHigh -} - -func (p *Span) GetSpanId() int64 { - return p.SpanId -} - -func (p *Span) GetParentSpanId() int64 { - return p.ParentSpanId -} - -func (p *Span) GetOperationName() string { - return p.OperationName -} - -var Span_References_DEFAULT []*SpanRef - -func (p *Span) GetReferences() []*SpanRef { - return p.References -} - -func (p *Span) GetFlags() int32 { - return p.Flags -} - -func (p *Span) GetStartTime() int64 { - return p.StartTime -} - -func (p *Span) GetDuration() int64 { - return p.Duration -} - -var Span_Tags_DEFAULT []*Tag - -func (p *Span) GetTags() []*Tag { - return p.Tags -} - -var Span_Logs_DEFAULT []*Log - -func (p *Span) GetLogs() []*Log { - return p.Logs -} -func (p *Span) IsSetReferences() bool { - return p.References != nil -} - -func (p *Span) IsSetTags() bool { - return p.Tags != nil -} - -func (p *Span) IsSetLogs() bool { - return p.Logs != nil -} - -func (p *Span) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetTraceIdLow bool = false - var issetTraceIdHigh bool = false - var issetSpanId bool = false - var issetParentSpanId bool = false - var issetOperationName bool = false - var issetFlags bool = false - var issetStartTime bool = false - var issetDuration bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetTraceIdLow = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetTraceIdHigh = true - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - issetSpanId = true - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - issetParentSpanId = true - case 5: - if err := p.readField5(iprot); err != nil { - return err - } - issetOperationName = true - case 6: - if err := p.readField6(iprot); err != nil { - return err - } - case 7: - if err := p.readField7(iprot); err != nil { - return err - } - issetFlags = true - case 8: - if err := p.readField8(iprot); err != nil { - return err - } - issetStartTime = true - case 9: - if err := p.readField9(iprot); err != nil { - return err - } - issetDuration = true - case 10: - if err := p.readField10(iprot); err != nil { - return err - } - case 11: - if err := p.readField11(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetTraceIdLow { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdLow is not set")) - } - if !issetTraceIdHigh { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TraceIdHigh is not set")) - } - if !issetSpanId { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field SpanId is not set")) - } - if !issetParentSpanId { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ParentSpanId is not set")) - } - if !issetOperationName { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field OperationName is not set")) - } - if !issetFlags { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Flags is not set")) - } - if !issetStartTime { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field StartTime is not set")) - } - if !issetDuration { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Duration is not set")) - } - return nil -} - -func (p *Span) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.TraceIdLow = v - } - return nil -} - -func (p *Span) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.TraceIdHigh = v - } - return nil -} - -func (p *Span) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.SpanId = v - } - return nil -} - -func (p *Span) readField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.ParentSpanId = v - } - return nil -} - -func (p *Span) readField5(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 5: ", err) - } else { - p.OperationName = v - } - return nil -} - -func (p *Span) readField6(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*SpanRef, 0, size) - p.References = tSlice - for i := 0; i < size; i++ { - _elem1 := &SpanRef{} - if err := _elem1.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) - } - p.References = append(p.References, _elem1) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) readField7(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 7: ", err) - } else { - p.Flags = v - } - return nil -} - -func (p *Span) readField8(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 8: ", err) - } else { - p.StartTime = v - } - return nil -} - -func (p *Span) readField9(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 9: ", err) - } else { - p.Duration = v - } - return nil -} - -func (p *Span) readField10(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Tag, 0, size) - p.Tags = tSlice - for i := 0; i < size; i++ { - _elem2 := &Tag{} - if err := _elem2.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem2), err) - } - p.Tags = append(p.Tags, _elem2) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) readField11(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Log, 0, size) - p.Logs = tSlice - for i := 0; i < size; i++ { - _elem3 := &Log{} - if err := _elem3.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem3), err) - } - p.Logs = append(p.Logs, _elem3) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Span"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := p.writeField5(oprot); err != nil { - return err - } - if err := p.writeField6(oprot); err != nil { - return err - } - if err := p.writeField7(oprot); err != nil { - return err - } - if err := p.writeField8(oprot); err != nil { - return err - } - if err := p.writeField9(oprot); err != nil { - return err - } - if err := p.writeField10(oprot); err != nil { - return err - } - if err := p.writeField11(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("traceIdLow", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:traceIdLow: ", p), err) - } - if err := oprot.WriteI64(int64(p.TraceIdLow)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.traceIdLow (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:traceIdLow: ", p), err) - } - return err -} - -func (p *Span) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("traceIdHigh", thrift.I64, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:traceIdHigh: ", p), err) - } - if err := oprot.WriteI64(int64(p.TraceIdHigh)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.traceIdHigh (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:traceIdHigh: ", p), err) - } - return err -} - -func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("spanId", thrift.I64, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:spanId: ", p), err) - } - if err := oprot.WriteI64(int64(p.SpanId)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.spanId (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:spanId: ", p), err) - } - return err -} - -func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("parentSpanId", thrift.I64, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:parentSpanId: ", p), err) - } - if err := oprot.WriteI64(int64(p.ParentSpanId)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.parentSpanId (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:parentSpanId: ", p), err) - } - return err -} - -func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("operationName", thrift.STRING, 5); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:operationName: ", p), err) - } - if err := oprot.WriteString(string(p.OperationName)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.operationName (5) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 5:operationName: ", p), err) - } - return err -} - -func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { - if p.IsSetReferences() { - if err := oprot.WriteFieldBegin("references", thrift.LIST, 6); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:references: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.References)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.References { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 6:references: ", p), err) - } - } - return err -} - -func (p *Span) writeField7(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("flags", thrift.I32, 7); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 7:flags: ", p), err) - } - if err := oprot.WriteI32(int32(p.Flags)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.flags (7) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 7:flags: ", p), err) - } - return err -} - -func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("startTime", thrift.I64, 8); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:startTime: ", p), err) - } - if err := oprot.WriteI64(int64(p.StartTime)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.startTime (8) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 8:startTime: ", p), err) - } - return err -} - -func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("duration", thrift.I64, 9); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:duration: ", p), err) - } - if err := oprot.WriteI64(int64(p.Duration)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.duration (9) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 9:duration: ", p), err) - } - return err -} - -func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { - if p.IsSetTags() { - if err := oprot.WriteFieldBegin("tags", thrift.LIST, 10); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:tags: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Tags { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 10:tags: ", p), err) - } - } - return err -} - -func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { - if p.IsSetLogs() { - if err := oprot.WriteFieldBegin("logs", thrift.LIST, 11); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:logs: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Logs)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Logs { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 11:logs: ", p), err) - } - } - return err -} - -func (p *Span) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Span(%+v)", *p) -} - -// Attributes: -// - ServiceName -// - Tags -type Process struct { - ServiceName string `thrift:"serviceName,1,required" json:"serviceName"` - Tags []*Tag `thrift:"tags,2" json:"tags,omitempty"` -} - -func NewProcess() *Process { - return &Process{} -} - -func (p *Process) GetServiceName() string { - return p.ServiceName -} - -var Process_Tags_DEFAULT []*Tag - -func (p *Process) GetTags() []*Tag { - return p.Tags -} -func (p *Process) IsSetTags() bool { - return p.Tags != nil -} - -func (p *Process) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetServiceName bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetServiceName = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetServiceName { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field ServiceName is not set")) - } - return nil -} - -func (p *Process) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.ServiceName = v - } - return nil -} - -func (p *Process) readField2(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Tag, 0, size) - p.Tags = tSlice - for i := 0; i < size; i++ { - _elem4 := &Tag{} - if err := _elem4.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem4), err) - } - p.Tags = append(p.Tags, _elem4) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Process) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Process"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Process) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("serviceName", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:serviceName: ", p), err) - } - if err := oprot.WriteString(string(p.ServiceName)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.serviceName (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:serviceName: ", p), err) - } - return err -} - -func (p *Process) writeField2(oprot thrift.TProtocol) (err error) { - if p.IsSetTags() { - if err := oprot.WriteFieldBegin("tags", thrift.LIST, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tags: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Tags)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Tags { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tags: ", p), err) - } - } - return err -} - -func (p *Process) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Process(%+v)", *p) -} - -// Attributes: -// - FullQueueDroppedSpans -// - TooLargeDroppedSpans -// - FailedToEmitSpans -type ClientStats struct { - FullQueueDroppedSpans int64 `thrift:"fullQueueDroppedSpans,1,required" json:"fullQueueDroppedSpans"` - TooLargeDroppedSpans int64 `thrift:"tooLargeDroppedSpans,2,required" json:"tooLargeDroppedSpans"` - FailedToEmitSpans int64 `thrift:"failedToEmitSpans,3,required" json:"failedToEmitSpans"` -} - -func NewClientStats() *ClientStats { - return &ClientStats{} -} - -func (p *ClientStats) GetFullQueueDroppedSpans() int64 { - return p.FullQueueDroppedSpans -} - -func (p *ClientStats) GetTooLargeDroppedSpans() int64 { - return p.TooLargeDroppedSpans -} - -func (p *ClientStats) GetFailedToEmitSpans() int64 { - return p.FailedToEmitSpans -} -func (p *ClientStats) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetFullQueueDroppedSpans bool = false - var issetTooLargeDroppedSpans bool = false - var issetFailedToEmitSpans bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetFullQueueDroppedSpans = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetTooLargeDroppedSpans = true - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - issetFailedToEmitSpans = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetFullQueueDroppedSpans { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field FullQueueDroppedSpans is not set")) - } - if !issetTooLargeDroppedSpans { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field TooLargeDroppedSpans is not set")) - } - if !issetFailedToEmitSpans { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field FailedToEmitSpans is not set")) - } - return nil -} - -func (p *ClientStats) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.FullQueueDroppedSpans = v - } - return nil -} - -func (p *ClientStats) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.TooLargeDroppedSpans = v - } - return nil -} - -func (p *ClientStats) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.FailedToEmitSpans = v - } - return nil -} - -func (p *ClientStats) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("ClientStats"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *ClientStats) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("fullQueueDroppedSpans", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:fullQueueDroppedSpans: ", p), err) - } - if err := oprot.WriteI64(int64(p.FullQueueDroppedSpans)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.fullQueueDroppedSpans (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:fullQueueDroppedSpans: ", p), err) - } - return err -} - -func (p *ClientStats) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("tooLargeDroppedSpans", thrift.I64, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:tooLargeDroppedSpans: ", p), err) - } - if err := oprot.WriteI64(int64(p.TooLargeDroppedSpans)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.tooLargeDroppedSpans (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:tooLargeDroppedSpans: ", p), err) - } - return err -} - -func (p *ClientStats) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("failedToEmitSpans", thrift.I64, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:failedToEmitSpans: ", p), err) - } - if err := oprot.WriteI64(int64(p.FailedToEmitSpans)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.failedToEmitSpans (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:failedToEmitSpans: ", p), err) - } - return err -} - -func (p *ClientStats) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("ClientStats(%+v)", *p) -} - -// Attributes: -// - Process -// - Spans -// - SeqNo -// - Stats -type Batch struct { - Process *Process `thrift:"process,1,required" json:"process"` - Spans []*Span `thrift:"spans,2,required" json:"spans"` - SeqNo *int64 `thrift:"seqNo,3" json:"seqNo,omitempty"` - Stats *ClientStats `thrift:"stats,4" json:"stats,omitempty"` -} - -func NewBatch() *Batch { - return &Batch{} -} - -var Batch_Process_DEFAULT *Process - -func (p *Batch) GetProcess() *Process { - if !p.IsSetProcess() { - return Batch_Process_DEFAULT - } - return p.Process -} - -func (p *Batch) GetSpans() []*Span { - return p.Spans -} - -var Batch_SeqNo_DEFAULT int64 - -func (p *Batch) GetSeqNo() int64 { - if !p.IsSetSeqNo() { - return Batch_SeqNo_DEFAULT - } - return *p.SeqNo -} - -var Batch_Stats_DEFAULT *ClientStats - -func (p *Batch) GetStats() *ClientStats { - if !p.IsSetStats() { - return Batch_Stats_DEFAULT - } - return p.Stats -} -func (p *Batch) IsSetProcess() bool { - return p.Process != nil -} - -func (p *Batch) IsSetSeqNo() bool { - return p.SeqNo != nil -} - -func (p *Batch) IsSetStats() bool { - return p.Stats != nil -} - -func (p *Batch) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetProcess bool = false - var issetSpans bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetProcess = true - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - issetSpans = true - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetProcess { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Process is not set")) - } - if !issetSpans { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Spans is not set")) - } - return nil -} - -func (p *Batch) readField1(iprot thrift.TProtocol) error { - p.Process = &Process{} - if err := p.Process.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Process), err) - } - return nil -} - -func (p *Batch) readField2(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Span, 0, size) - p.Spans = tSlice - for i := 0; i < size; i++ { - _elem5 := &Span{} - if err := _elem5.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem5), err) - } - p.Spans = append(p.Spans, _elem5) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Batch) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.SeqNo = &v - } - return nil -} - -func (p *Batch) readField4(iprot thrift.TProtocol) error { - p.Stats = &ClientStats{} - if err := p.Stats.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Stats), err) - } - return nil -} - -func (p *Batch) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Batch"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Batch) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("process", thrift.STRUCT, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:process: ", p), err) - } - if err := p.Process.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Process), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:process: ", p), err) - } - return err -} - -func (p *Batch) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("spans", thrift.LIST, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:spans: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Spans { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:spans: ", p), err) - } - return err -} - -func (p *Batch) writeField3(oprot thrift.TProtocol) (err error) { - if p.IsSetSeqNo() { - if err := oprot.WriteFieldBegin("seqNo", thrift.I64, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:seqNo: ", p), err) - } - if err := oprot.WriteI64(int64(*p.SeqNo)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.seqNo (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:seqNo: ", p), err) - } - } - return err -} - -func (p *Batch) writeField4(oprot thrift.TProtocol) (err error) { - if p.IsSetStats() { - if err := oprot.WriteFieldBegin("stats", thrift.STRUCT, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:stats: ", p), err) - } - if err := p.Stats.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Stats), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:stats: ", p), err) - } - } - return err -} - -func (p *Batch) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Batch(%+v)", *p) -} - -// Attributes: -// - Ok -type BatchSubmitResponse struct { - Ok bool `thrift:"ok,1,required" json:"ok"` -} - -func NewBatchSubmitResponse() *BatchSubmitResponse { - return &BatchSubmitResponse{} -} - -func (p *BatchSubmitResponse) GetOk() bool { - return p.Ok -} -func (p *BatchSubmitResponse) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetOk bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetOk = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetOk { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) - } - return nil -} - -func (p *BatchSubmitResponse) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Ok = v - } - return nil -} - -func (p *BatchSubmitResponse) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("BatchSubmitResponse"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *BatchSubmitResponse) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) - } - if err := oprot.WriteBool(bool(p.Ok)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) - } - return err -} - -func (p *BatchSubmitResponse) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("BatchSubmitResponse(%+v)", *p) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/constants.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package zipkincore - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -const CLIENT_SEND = "cs" -const CLIENT_RECV = "cr" -const SERVER_SEND = "ss" -const SERVER_RECV = "sr" -const MESSAGE_SEND = "ms" -const MESSAGE_RECV = "mr" -const WIRE_SEND = "ws" -const WIRE_RECV = "wr" -const CLIENT_SEND_FRAGMENT = "csf" -const CLIENT_RECV_FRAGMENT = "crf" -const SERVER_SEND_FRAGMENT = "ssf" -const SERVER_RECV_FRAGMENT = "srf" -const LOCAL_COMPONENT = "lc" -const CLIENT_ADDR = "ca" -const SERVER_ADDR = "sa" -const MESSAGE_ADDR = "ma" - -func init() { -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/GoUnusedProtection__.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/GoUnusedProtection__.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/GoUnusedProtection__.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/GoUnusedProtection__.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,6 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package zipkincore + +var GoUnusedProtection__ int; + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/ttypes.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,1337 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package zipkincore - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -var GoUnusedProtection__ int - -type AnnotationType int64 - -const ( - AnnotationType_BOOL AnnotationType = 0 - AnnotationType_BYTES AnnotationType = 1 - AnnotationType_I16 AnnotationType = 2 - AnnotationType_I32 AnnotationType = 3 - AnnotationType_I64 AnnotationType = 4 - AnnotationType_DOUBLE AnnotationType = 5 - AnnotationType_STRING AnnotationType = 6 -) - -func (p AnnotationType) String() string { - switch p { - case AnnotationType_BOOL: - return "BOOL" - case AnnotationType_BYTES: - return "BYTES" - case AnnotationType_I16: - return "I16" - case AnnotationType_I32: - return "I32" - case AnnotationType_I64: - return "I64" - case AnnotationType_DOUBLE: - return "DOUBLE" - case AnnotationType_STRING: - return "STRING" - } - return "" -} - -func AnnotationTypeFromString(s string) (AnnotationType, error) { - switch s { - case "BOOL": - return AnnotationType_BOOL, nil - case "BYTES": - return AnnotationType_BYTES, nil - case "I16": - return AnnotationType_I16, nil - case "I32": - return AnnotationType_I32, nil - case "I64": - return AnnotationType_I64, nil - case "DOUBLE": - return AnnotationType_DOUBLE, nil - case "STRING": - return AnnotationType_STRING, nil - } - return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") -} - -func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } - -func (p AnnotationType) MarshalText() ([]byte, error) { - return []byte(p.String()), nil -} - -func (p *AnnotationType) UnmarshalText(text []byte) error { - q, err := AnnotationTypeFromString(string(text)) - if err != nil { - return err - } - *p = q - return nil -} - -// Indicates the network context of a service recording an annotation with two -// exceptions. -// -// When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, -// the endpoint indicates the source or destination of an RPC. This exception -// allows zipkin to display network context of uninstrumented services, or -// clients such as web browsers. -// -// Attributes: -// - Ipv4: IPv4 host address packed into 4 bytes. -// -// Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 -// - Port: IPv4 port -// -// Note: this is to be treated as an unsigned integer, so watch for negatives. -// -// Conventionally, when the port isn't known, port = 0. -// - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web" -// -// Conventionally, when the service name isn't known, service_name = "unknown". -// - Ipv6: IPv6 host address packed into 16 bytes. Ex Inet6Address.getBytes() -type Endpoint struct { - Ipv4 int32 `thrift:"ipv4,1" json:"ipv4"` - Port int16 `thrift:"port,2" json:"port"` - ServiceName string `thrift:"service_name,3" json:"service_name"` - Ipv6 []byte `thrift:"ipv6,4" json:"ipv6,omitempty"` -} - -func NewEndpoint() *Endpoint { - return &Endpoint{} -} - -func (p *Endpoint) GetIpv4() int32 { - return p.Ipv4 -} - -func (p *Endpoint) GetPort() int16 { - return p.Port -} - -func (p *Endpoint) GetServiceName() string { - return p.ServiceName -} - -var Endpoint_Ipv6_DEFAULT []byte - -func (p *Endpoint) GetIpv6() []byte { - return p.Ipv6 -} -func (p *Endpoint) IsSetIpv6() bool { - return p.Ipv6 != nil -} - -func (p *Endpoint) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *Endpoint) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Ipv4 = v - } - return nil -} - -func (p *Endpoint) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI16(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.Port = v - } - return nil -} - -func (p *Endpoint) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.ServiceName = v - } - return nil -} - -func (p *Endpoint) readField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.Ipv6 = v - } - return nil -} - -func (p *Endpoint) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Endpoint"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Endpoint) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("ipv4", thrift.I32, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) - } - if err := oprot.WriteI32(int32(p.Ipv4)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) - } - return err -} - -func (p *Endpoint) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("port", thrift.I16, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) - } - if err := oprot.WriteI16(int16(p.Port)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) - } - return err -} - -func (p *Endpoint) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("service_name", thrift.STRING, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:service_name: ", p), err) - } - if err := oprot.WriteString(string(p.ServiceName)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.service_name (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:service_name: ", p), err) - } - return err -} - -func (p *Endpoint) writeField4(oprot thrift.TProtocol) (err error) { - if p.IsSetIpv6() { - if err := oprot.WriteFieldBegin("ipv6", thrift.STRING, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:ipv6: ", p), err) - } - if err := oprot.WriteBinary(p.Ipv6); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.ipv6 (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:ipv6: ", p), err) - } - } - return err -} - -func (p *Endpoint) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Endpoint(%+v)", *p) -} - -// An annotation is similar to a log statement. It includes a host field which -// allows these events to be attributed properly, and also aggregatable. -// -// Attributes: -// - Timestamp: Microseconds from epoch. -// -// This value should use the most precise value possible. For example, -// gettimeofday or syncing nanoTime against a tick of currentTimeMillis. -// - Value -// - Host: Always the host that recorded the event. By specifying the host you allow -// rollup of all events (such as client requests to a service) by IP address. -type Annotation struct { - Timestamp int64 `thrift:"timestamp,1" json:"timestamp"` - Value string `thrift:"value,2" json:"value"` - Host *Endpoint `thrift:"host,3" json:"host,omitempty"` -} - -func NewAnnotation() *Annotation { - return &Annotation{} -} - -func (p *Annotation) GetTimestamp() int64 { - return p.Timestamp -} - -func (p *Annotation) GetValue() string { - return p.Value -} - -var Annotation_Host_DEFAULT *Endpoint - -func (p *Annotation) GetHost() *Endpoint { - if !p.IsSetHost() { - return Annotation_Host_DEFAULT - } - return p.Host -} -func (p *Annotation) IsSetHost() bool { - return p.Host != nil -} - -func (p *Annotation) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *Annotation) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Timestamp = v - } - return nil -} - -func (p *Annotation) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.Value = v - } - return nil -} - -func (p *Annotation) readField3(iprot thrift.TProtocol) error { - p.Host = &Endpoint{} - if err := p.Host.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) - } - return nil -} - -func (p *Annotation) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Annotation"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Annotation) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) - } - if err := oprot.WriteI64(int64(p.Timestamp)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) - } - return err -} - -func (p *Annotation) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) - } - if err := oprot.WriteString(string(p.Value)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) - } - return err -} - -func (p *Annotation) writeField3(oprot thrift.TProtocol) (err error) { - if p.IsSetHost() { - if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) - } - if err := p.Host.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) - } - } - return err -} - -func (p *Annotation) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Annotation(%+v)", *p) -} - -// Binary annotations are tags applied to a Span to give it context. For -// example, a binary annotation of "http.uri" could the path to a resource in a -// RPC call. -// -// Binary annotations of type STRING are always queryable, though more a -// historical implementation detail than a structural concern. -// -// Binary annotations can repeat, and vary on the host. Similar to Annotation, -// the host indicates who logged the event. This allows you to tell the -// difference between the client and server side of the same key. For example, -// the key "http.uri" might be different on the client and server side due to -// rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, -// you can see the different points of view, which often help in debugging. -// -// Attributes: -// - Key -// - Value -// - AnnotationType -// - Host: The host that recorded tag, which allows you to differentiate between -// multiple tags with the same key. There are two exceptions to this. -// -// When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or -// destination of an RPC. This exception allows zipkin to display network -// context of uninstrumented services, or clients such as web browsers. -type BinaryAnnotation struct { - Key string `thrift:"key,1" json:"key"` - Value []byte `thrift:"value,2" json:"value"` - AnnotationType AnnotationType `thrift:"annotation_type,3" json:"annotation_type"` - Host *Endpoint `thrift:"host,4" json:"host,omitempty"` -} - -func NewBinaryAnnotation() *BinaryAnnotation { - return &BinaryAnnotation{} -} - -func (p *BinaryAnnotation) GetKey() string { - return p.Key -} - -func (p *BinaryAnnotation) GetValue() []byte { - return p.Value -} - -func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { - return p.AnnotationType -} - -var BinaryAnnotation_Host_DEFAULT *Endpoint - -func (p *BinaryAnnotation) GetHost() *Endpoint { - if !p.IsSetHost() { - return BinaryAnnotation_Host_DEFAULT - } - return p.Host -} -func (p *BinaryAnnotation) IsSetHost() bool { - return p.Host != nil -} - -func (p *BinaryAnnotation) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - case 2: - if err := p.readField2(iprot); err != nil { - return err - } - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *BinaryAnnotation) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Key = v - } - return nil -} - -func (p *BinaryAnnotation) readField2(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBinary(); err != nil { - return thrift.PrependError("error reading field 2: ", err) - } else { - p.Value = v - } - return nil -} - -func (p *BinaryAnnotation) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI32(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - temp := AnnotationType(v) - p.AnnotationType = temp - } - return nil -} - -func (p *BinaryAnnotation) readField4(iprot thrift.TProtocol) error { - p.Host = &Endpoint{} - if err := p.Host.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) - } - return nil -} - -func (p *BinaryAnnotation) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("BinaryAnnotation"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField2(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *BinaryAnnotation) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("key", thrift.STRING, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) - } - if err := oprot.WriteString(string(p.Key)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) - } - return err -} - -func (p *BinaryAnnotation) writeField2(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("value", thrift.STRING, 2); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) - } - if err := oprot.WriteBinary(p.Value); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) - } - return err -} - -func (p *BinaryAnnotation) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("annotation_type", thrift.I32, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:annotation_type: ", p), err) - } - if err := oprot.WriteI32(int32(p.AnnotationType)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.annotation_type (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:annotation_type: ", p), err) - } - return err -} - -func (p *BinaryAnnotation) writeField4(oprot thrift.TProtocol) (err error) { - if p.IsSetHost() { - if err := oprot.WriteFieldBegin("host", thrift.STRUCT, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:host: ", p), err) - } - if err := p.Host.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:host: ", p), err) - } - } - return err -} - -func (p *BinaryAnnotation) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("BinaryAnnotation(%+v)", *p) -} - -// A trace is a series of spans (often RPC calls) which form a latency tree. -// -// The root span is where trace_id = id and parent_id = Nil. The root span is -// usually the longest interval in the trace, starting with a SERVER_RECV -// annotation and ending with a SERVER_SEND. -// -// Attributes: -// - TraceID -// - Name: Span name in lowercase, rpc method for example -// -// Conventionally, when the span name isn't known, name = "unknown". -// - ID -// - ParentID -// - Annotations -// - BinaryAnnotations -// - Debug -// - Timestamp: Microseconds from epoch of the creation of this span. -// -// This value should be set directly by instrumentation, using the most -// precise value possible. For example, gettimeofday or syncing nanoTime -// against a tick of currentTimeMillis. -// -// For compatibility with instrumentation that precede this field, collectors -// or span stores can derive this via Annotation.timestamp. -// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. -// -// This field is optional for compatibility with old data: first-party span -// stores are expected to support this at time of introduction. -// - Duration: Measurement of duration in microseconds, used to support queries. -// -// This value should be set directly, where possible. Doing so encourages -// precise measurement decoupled from problems of clocks, such as skew or NTP -// updates causing time to move backwards. -// -// For compatibility with instrumentation that precede this field, collectors -// or span stores can derive this by subtracting Annotation.timestamp. -// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. -// -// If this field is persisted as unset, zipkin will continue to work, except -// duration query support will be implementation-specific. Similarly, setting -// this field non-atomically is implementation-specific. -// -// This field is i64 vs i32 to support spans longer than 35 minutes. -// - TraceIDHigh: Optional unique 8-byte additional identifier for a trace. If non zero, this -// means the trace uses 128 bit traceIds instead of 64 bit. -type Span struct { - TraceID int64 `thrift:"trace_id,1" json:"trace_id"` - // unused field # 2 - Name string `thrift:"name,3" json:"name"` - ID int64 `thrift:"id,4" json:"id"` - ParentID *int64 `thrift:"parent_id,5" json:"parent_id,omitempty"` - Annotations []*Annotation `thrift:"annotations,6" json:"annotations"` - // unused field # 7 - BinaryAnnotations []*BinaryAnnotation `thrift:"binary_annotations,8" json:"binary_annotations"` - Debug bool `thrift:"debug,9" json:"debug,omitempty"` - Timestamp *int64 `thrift:"timestamp,10" json:"timestamp,omitempty"` - Duration *int64 `thrift:"duration,11" json:"duration,omitempty"` - TraceIDHigh *int64 `thrift:"trace_id_high,12" json:"trace_id_high,omitempty"` -} - -func NewSpan() *Span { - return &Span{} -} - -func (p *Span) GetTraceID() int64 { - return p.TraceID -} - -func (p *Span) GetName() string { - return p.Name -} - -func (p *Span) GetID() int64 { - return p.ID -} - -var Span_ParentID_DEFAULT int64 - -func (p *Span) GetParentID() int64 { - if !p.IsSetParentID() { - return Span_ParentID_DEFAULT - } - return *p.ParentID -} - -func (p *Span) GetAnnotations() []*Annotation { - return p.Annotations -} - -func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { - return p.BinaryAnnotations -} - -var Span_Debug_DEFAULT bool = false - -func (p *Span) GetDebug() bool { - return p.Debug -} - -var Span_Timestamp_DEFAULT int64 - -func (p *Span) GetTimestamp() int64 { - if !p.IsSetTimestamp() { - return Span_Timestamp_DEFAULT - } - return *p.Timestamp -} - -var Span_Duration_DEFAULT int64 - -func (p *Span) GetDuration() int64 { - if !p.IsSetDuration() { - return Span_Duration_DEFAULT - } - return *p.Duration -} - -var Span_TraceIDHigh_DEFAULT int64 - -func (p *Span) GetTraceIDHigh() int64 { - if !p.IsSetTraceIDHigh() { - return Span_TraceIDHigh_DEFAULT - } - return *p.TraceIDHigh -} -func (p *Span) IsSetParentID() bool { - return p.ParentID != nil -} - -func (p *Span) IsSetDebug() bool { - return p.Debug != Span_Debug_DEFAULT -} - -func (p *Span) IsSetTimestamp() bool { - return p.Timestamp != nil -} - -func (p *Span) IsSetDuration() bool { - return p.Duration != nil -} - -func (p *Span) IsSetTraceIDHigh() bool { - return p.TraceIDHigh != nil -} - -func (p *Span) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - case 3: - if err := p.readField3(iprot); err != nil { - return err - } - case 4: - if err := p.readField4(iprot); err != nil { - return err - } - case 5: - if err := p.readField5(iprot); err != nil { - return err - } - case 6: - if err := p.readField6(iprot); err != nil { - return err - } - case 8: - if err := p.readField8(iprot); err != nil { - return err - } - case 9: - if err := p.readField9(iprot); err != nil { - return err - } - case 10: - if err := p.readField10(iprot); err != nil { - return err - } - case 11: - if err := p.readField11(iprot); err != nil { - return err - } - case 12: - if err := p.readField12(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *Span) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.TraceID = v - } - return nil -} - -func (p *Span) readField3(iprot thrift.TProtocol) error { - if v, err := iprot.ReadString(); err != nil { - return thrift.PrependError("error reading field 3: ", err) - } else { - p.Name = v - } - return nil -} - -func (p *Span) readField4(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 4: ", err) - } else { - p.ID = v - } - return nil -} - -func (p *Span) readField5(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 5: ", err) - } else { - p.ParentID = &v - } - return nil -} - -func (p *Span) readField6(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Annotation, 0, size) - p.Annotations = tSlice - for i := 0; i < size; i++ { - _elem0 := &Annotation{} - if err := _elem0.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) - } - p.Annotations = append(p.Annotations, _elem0) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) readField8(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*BinaryAnnotation, 0, size) - p.BinaryAnnotations = tSlice - for i := 0; i < size; i++ { - _elem1 := &BinaryAnnotation{} - if err := _elem1.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) - } - p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *Span) readField9(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 9: ", err) - } else { - p.Debug = v - } - return nil -} - -func (p *Span) readField10(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 10: ", err) - } else { - p.Timestamp = &v - } - return nil -} - -func (p *Span) readField11(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 11: ", err) - } else { - p.Duration = &v - } - return nil -} - -func (p *Span) readField12(iprot thrift.TProtocol) error { - if v, err := iprot.ReadI64(); err != nil { - return thrift.PrependError("error reading field 12: ", err) - } else { - p.TraceIDHigh = &v - } - return nil -} - -func (p *Span) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Span"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := p.writeField3(oprot); err != nil { - return err - } - if err := p.writeField4(oprot); err != nil { - return err - } - if err := p.writeField5(oprot); err != nil { - return err - } - if err := p.writeField6(oprot); err != nil { - return err - } - if err := p.writeField8(oprot); err != nil { - return err - } - if err := p.writeField9(oprot); err != nil { - return err - } - if err := p.writeField10(oprot); err != nil { - return err - } - if err := p.writeField11(oprot); err != nil { - return err - } - if err := p.writeField12(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Span) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("trace_id", thrift.I64, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:trace_id: ", p), err) - } - if err := oprot.WriteI64(int64(p.TraceID)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.trace_id (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:trace_id: ", p), err) - } - return err -} - -func (p *Span) writeField3(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("name", thrift.STRING, 3); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) - } - if err := oprot.WriteString(string(p.Name)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) - } - return err -} - -func (p *Span) writeField4(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("id", thrift.I64, 4); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) - } - if err := oprot.WriteI64(int64(p.ID)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) - } - return err -} - -func (p *Span) writeField5(oprot thrift.TProtocol) (err error) { - if p.IsSetParentID() { - if err := oprot.WriteFieldBegin("parent_id", thrift.I64, 5); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parent_id: ", p), err) - } - if err := oprot.WriteI64(int64(*p.ParentID)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.parent_id (5) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parent_id: ", p), err) - } - } - return err -} - -func (p *Span) writeField6(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("annotations", thrift.LIST, 6); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Annotations)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Annotations { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) - } - return err -} - -func (p *Span) writeField8(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("binary_annotations", thrift.LIST, 8); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:binary_annotations: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.BinaryAnnotations { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 8:binary_annotations: ", p), err) - } - return err -} - -func (p *Span) writeField9(oprot thrift.TProtocol) (err error) { - if p.IsSetDebug() { - if err := oprot.WriteFieldBegin("debug", thrift.BOOL, 9); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:debug: ", p), err) - } - if err := oprot.WriteBool(bool(p.Debug)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.debug (9) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 9:debug: ", p), err) - } - } - return err -} - -func (p *Span) writeField10(oprot thrift.TProtocol) (err error) { - if p.IsSetTimestamp() { - if err := oprot.WriteFieldBegin("timestamp", thrift.I64, 10); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:timestamp: ", p), err) - } - if err := oprot.WriteI64(int64(*p.Timestamp)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.timestamp (10) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 10:timestamp: ", p), err) - } - } - return err -} - -func (p *Span) writeField11(oprot thrift.TProtocol) (err error) { - if p.IsSetDuration() { - if err := oprot.WriteFieldBegin("duration", thrift.I64, 11); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:duration: ", p), err) - } - if err := oprot.WriteI64(int64(*p.Duration)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.duration (11) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 11:duration: ", p), err) - } - } - return err -} - -func (p *Span) writeField12(oprot thrift.TProtocol) (err error) { - if p.IsSetTraceIDHigh() { - if err := oprot.WriteFieldBegin("trace_id_high", thrift.I64, 12); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 12:trace_id_high: ", p), err) - } - if err := oprot.WriteI64(int64(*p.TraceIDHigh)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.trace_id_high (12) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 12:trace_id_high: ", p), err) - } - } - return err -} - -func (p *Span) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Span(%+v)", *p) -} - -// Attributes: -// - Ok -type Response struct { - Ok bool `thrift:"ok,1,required" json:"ok"` -} - -func NewResponse() *Response { - return &Response{} -} - -func (p *Response) GetOk() bool { - return p.Ok -} -func (p *Response) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - var issetOk bool = false - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - issetOk = true - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - if !issetOk { - return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")) - } - return nil -} - -func (p *Response) readField1(iprot thrift.TProtocol) error { - if v, err := iprot.ReadBool(); err != nil { - return thrift.PrependError("error reading field 1: ", err) - } else { - p.Ok = v - } - return nil -} - -func (p *Response) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("Response"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *Response) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("ok", thrift.BOOL, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) - } - if err := oprot.WriteBool(bool(p.Ok)); err != nil { - return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) - } - return err -} - -func (p *Response) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("Response(%+v)", *p) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincollector.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,446 +0,0 @@ -// Autogenerated by Thrift Compiler (0.9.3) -// DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING - -package zipkincore - -import ( - "bytes" - "fmt" - "github.com/uber/jaeger-client-go/thrift" -) - -// (needed to ensure safety because of naive import list construction.) -var _ = thrift.ZERO -var _ = fmt.Printf -var _ = bytes.Equal - -type ZipkinCollector interface { - // Parameters: - // - Spans - SubmitZipkinBatch(spans []*Span) (r []*Response, err error) -} - -type ZipkinCollectorClient struct { - Transport thrift.TTransport - ProtocolFactory thrift.TProtocolFactory - InputProtocol thrift.TProtocol - OutputProtocol thrift.TProtocol - SeqId int32 -} - -func NewZipkinCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *ZipkinCollectorClient { - return &ZipkinCollectorClient{Transport: t, - ProtocolFactory: f, - InputProtocol: f.GetProtocol(t), - OutputProtocol: f.GetProtocol(t), - SeqId: 0, - } -} - -func NewZipkinCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *ZipkinCollectorClient { - return &ZipkinCollectorClient{Transport: t, - ProtocolFactory: nil, - InputProtocol: iprot, - OutputProtocol: oprot, - SeqId: 0, - } -} - -// Parameters: -// - Spans -func (p *ZipkinCollectorClient) SubmitZipkinBatch(spans []*Span) (r []*Response, err error) { - if err = p.sendSubmitZipkinBatch(spans); err != nil { - return - } - return p.recvSubmitZipkinBatch() -} - -func (p *ZipkinCollectorClient) sendSubmitZipkinBatch(spans []*Span) (err error) { - oprot := p.OutputProtocol - if oprot == nil { - oprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.OutputProtocol = oprot - } - p.SeqId++ - if err = oprot.WriteMessageBegin("submitZipkinBatch", thrift.CALL, p.SeqId); err != nil { - return - } - args := ZipkinCollectorSubmitZipkinBatchArgs{ - Spans: spans, - } - if err = args.Write(oprot); err != nil { - return - } - if err = oprot.WriteMessageEnd(); err != nil { - return - } - return oprot.Flush() -} - -func (p *ZipkinCollectorClient) recvSubmitZipkinBatch() (value []*Response, err error) { - iprot := p.InputProtocol - if iprot == nil { - iprot = p.ProtocolFactory.GetProtocol(p.Transport) - p.InputProtocol = iprot - } - method, mTypeId, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return - } - if method != "submitZipkinBatch" { - err = thrift.NewTApplicationException(thrift.WRONG_METHOD_NAME, "submitZipkinBatch failed: wrong method name") - return - } - if p.SeqId != seqId { - err = thrift.NewTApplicationException(thrift.BAD_SEQUENCE_ID, "submitZipkinBatch failed: out of sequence response") - return - } - if mTypeId == thrift.EXCEPTION { - error2 := thrift.NewTApplicationException(thrift.UNKNOWN_APPLICATION_EXCEPTION, "Unknown Exception") - var error3 error - error3, err = error2.Read(iprot) - if err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - err = error3 - return - } - if mTypeId != thrift.REPLY { - err = thrift.NewTApplicationException(thrift.INVALID_MESSAGE_TYPE_EXCEPTION, "submitZipkinBatch failed: invalid message type") - return - } - result := ZipkinCollectorSubmitZipkinBatchResult{} - if err = result.Read(iprot); err != nil { - return - } - if err = iprot.ReadMessageEnd(); err != nil { - return - } - value = result.GetSuccess() - return -} - -type ZipkinCollectorProcessor struct { - processorMap map[string]thrift.TProcessorFunction - handler ZipkinCollector -} - -func (p *ZipkinCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { - p.processorMap[key] = processor -} - -func (p *ZipkinCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { - processor, ok = p.processorMap[key] - return processor, ok -} - -func (p *ZipkinCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { - return p.processorMap -} - -func NewZipkinCollectorProcessor(handler ZipkinCollector) *ZipkinCollectorProcessor { - - self4 := &ZipkinCollectorProcessor{handler: handler, processorMap: make(map[string]thrift.TProcessorFunction)} - self4.processorMap["submitZipkinBatch"] = &zipkinCollectorProcessorSubmitZipkinBatch{handler: handler} - return self4 -} - -func (p *ZipkinCollectorProcessor) Process(iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - name, _, seqId, err := iprot.ReadMessageBegin() - if err != nil { - return false, err - } - if processor, ok := p.GetProcessorFunction(name); ok { - return processor.Process(seqId, iprot, oprot) - } - iprot.Skip(thrift.STRUCT) - iprot.ReadMessageEnd() - x5 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function "+name) - oprot.WriteMessageBegin(name, thrift.EXCEPTION, seqId) - x5.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, x5 - -} - -type zipkinCollectorProcessorSubmitZipkinBatch struct { - handler ZipkinCollector -} - -func (p *zipkinCollectorProcessorSubmitZipkinBatch) Process(seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { - args := ZipkinCollectorSubmitZipkinBatchArgs{} - if err = args.Read(iprot); err != nil { - iprot.ReadMessageEnd() - x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err.Error()) - oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return false, err - } - - iprot.ReadMessageEnd() - result := ZipkinCollectorSubmitZipkinBatchResult{} - var retval []*Response - var err2 error - if retval, err2 = p.handler.SubmitZipkinBatch(args.Spans); err2 != nil { - x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitZipkinBatch: "+err2.Error()) - oprot.WriteMessageBegin("submitZipkinBatch", thrift.EXCEPTION, seqId) - x.Write(oprot) - oprot.WriteMessageEnd() - oprot.Flush() - return true, err2 - } else { - result.Success = retval - } - if err2 = oprot.WriteMessageBegin("submitZipkinBatch", thrift.REPLY, seqId); err2 != nil { - err = err2 - } - if err2 = result.Write(oprot); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.WriteMessageEnd(); err == nil && err2 != nil { - err = err2 - } - if err2 = oprot.Flush(); err == nil && err2 != nil { - err = err2 - } - if err != nil { - return - } - return true, err -} - -// HELPER FUNCTIONS AND STRUCTURES - -// Attributes: -// - Spans -type ZipkinCollectorSubmitZipkinBatchArgs struct { - Spans []*Span `thrift:"spans,1" json:"spans"` -} - -func NewZipkinCollectorSubmitZipkinBatchArgs() *ZipkinCollectorSubmitZipkinBatchArgs { - return &ZipkinCollectorSubmitZipkinBatchArgs{} -} - -func (p *ZipkinCollectorSubmitZipkinBatchArgs) GetSpans() []*Span { - return p.Spans -} -func (p *ZipkinCollectorSubmitZipkinBatchArgs) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 1: - if err := p.readField1(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchArgs) readField1(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Span, 0, size) - p.Spans = tSlice - for i := 0; i < size; i++ { - _elem6 := &Span{} - if err := _elem6.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem6), err) - } - p.Spans = append(p.Spans, _elem6) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchArgs) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("submitZipkinBatch_args"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField1(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchArgs) writeField1(oprot thrift.TProtocol) (err error) { - if err := oprot.WriteFieldBegin("spans", thrift.LIST, 1); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Spans)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Spans { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) - } - return err -} - -func (p *ZipkinCollectorSubmitZipkinBatchArgs) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchArgs(%+v)", *p) -} - -// Attributes: -// - Success -type ZipkinCollectorSubmitZipkinBatchResult struct { - Success []*Response `thrift:"success,0" json:"success,omitempty"` -} - -func NewZipkinCollectorSubmitZipkinBatchResult() *ZipkinCollectorSubmitZipkinBatchResult { - return &ZipkinCollectorSubmitZipkinBatchResult{} -} - -var ZipkinCollectorSubmitZipkinBatchResult_Success_DEFAULT []*Response - -func (p *ZipkinCollectorSubmitZipkinBatchResult) GetSuccess() []*Response { - return p.Success -} -func (p *ZipkinCollectorSubmitZipkinBatchResult) IsSetSuccess() bool { - return p.Success != nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchResult) Read(iprot thrift.TProtocol) error { - if _, err := iprot.ReadStructBegin(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) - } - - for { - _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin() - if err != nil { - return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) - } - if fieldTypeId == thrift.STOP { - break - } - switch fieldId { - case 0: - if err := p.readField0(iprot); err != nil { - return err - } - default: - if err := iprot.Skip(fieldTypeId); err != nil { - return err - } - } - if err := iprot.ReadFieldEnd(); err != nil { - return err - } - } - if err := iprot.ReadStructEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) - } - return nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchResult) readField0(iprot thrift.TProtocol) error { - _, size, err := iprot.ReadListBegin() - if err != nil { - return thrift.PrependError("error reading list begin: ", err) - } - tSlice := make([]*Response, 0, size) - p.Success = tSlice - for i := 0; i < size; i++ { - _elem7 := &Response{} - if err := _elem7.Read(iprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem7), err) - } - p.Success = append(p.Success, _elem7) - } - if err := iprot.ReadListEnd(); err != nil { - return thrift.PrependError("error reading list end: ", err) - } - return nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchResult) Write(oprot thrift.TProtocol) error { - if err := oprot.WriteStructBegin("submitZipkinBatch_result"); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) - } - if err := p.writeField0(oprot); err != nil { - return err - } - if err := oprot.WriteFieldStop(); err != nil { - return thrift.PrependError("write field stop error: ", err) - } - if err := oprot.WriteStructEnd(); err != nil { - return thrift.PrependError("write struct stop error: ", err) - } - return nil -} - -func (p *ZipkinCollectorSubmitZipkinBatchResult) writeField0(oprot thrift.TProtocol) (err error) { - if p.IsSetSuccess() { - if err := oprot.WriteFieldBegin("success", thrift.LIST, 0); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) - } - if err := oprot.WriteListBegin(thrift.STRUCT, len(p.Success)); err != nil { - return thrift.PrependError("error writing list begin: ", err) - } - for _, v := range p.Success { - if err := v.Write(oprot); err != nil { - return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) - } - } - if err := oprot.WriteListEnd(); err != nil { - return thrift.PrependError("error writing list end: ", err) - } - if err := oprot.WriteFieldEnd(); err != nil { - return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) - } - } - return err -} - -func (p *ZipkinCollectorSubmitZipkinBatchResult) String() string { - if p == nil { - return "" - } - return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchResult(%+v)", *p) -} diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore-consts.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore-consts.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore-consts.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore-consts.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,39 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package zipkincore + +import( + "bytes" + "context" + "fmt" + "time" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = context.Background +var _ = time.Now +var _ = bytes.Equal + +const CLIENT_SEND = "cs" +const CLIENT_RECV = "cr" +const SERVER_SEND = "ss" +const SERVER_RECV = "sr" +const MESSAGE_SEND = "ms" +const MESSAGE_RECV = "mr" +const WIRE_SEND = "ws" +const WIRE_RECV = "wr" +const CLIENT_SEND_FRAGMENT = "csf" +const CLIENT_RECV_FRAGMENT = "crf" +const SERVER_SEND_FRAGMENT = "ssf" +const SERVER_RECV_FRAGMENT = "srf" +const LOCAL_COMPONENT = "lc" +const CLIENT_ADDR = "ca" +const SERVER_ADDR = "sa" +const MESSAGE_ADDR = "ma" + +func init() { +} + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore.go 1970-01-01 00:00:00.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/thrift-gen/zipkincore/zipkincore.go 2021-12-26 00:45:51.000000000 +0000 @@ -0,0 +1,1853 @@ +// Code generated by Thrift Compiler (0.14.1). DO NOT EDIT. + +package zipkincore + +import( + "bytes" + "context" + "database/sql/driver" + "errors" + "fmt" + "time" + "github.com/uber/jaeger-client-go/thrift" +) + +// (needed to ensure safety because of naive import list construction.) +var _ = thrift.ZERO +var _ = fmt.Printf +var _ = context.Background +var _ = time.Now +var _ = bytes.Equal + +type AnnotationType int64 +const ( + AnnotationType_BOOL AnnotationType = 0 + AnnotationType_BYTES AnnotationType = 1 + AnnotationType_I16 AnnotationType = 2 + AnnotationType_I32 AnnotationType = 3 + AnnotationType_I64 AnnotationType = 4 + AnnotationType_DOUBLE AnnotationType = 5 + AnnotationType_STRING AnnotationType = 6 +) + +func (p AnnotationType) String() string { + switch p { + case AnnotationType_BOOL: return "BOOL" + case AnnotationType_BYTES: return "BYTES" + case AnnotationType_I16: return "I16" + case AnnotationType_I32: return "I32" + case AnnotationType_I64: return "I64" + case AnnotationType_DOUBLE: return "DOUBLE" + case AnnotationType_STRING: return "STRING" + } + return "" +} + +func AnnotationTypeFromString(s string) (AnnotationType, error) { + switch s { + case "BOOL": return AnnotationType_BOOL, nil + case "BYTES": return AnnotationType_BYTES, nil + case "I16": return AnnotationType_I16, nil + case "I32": return AnnotationType_I32, nil + case "I64": return AnnotationType_I64, nil + case "DOUBLE": return AnnotationType_DOUBLE, nil + case "STRING": return AnnotationType_STRING, nil + } + return AnnotationType(0), fmt.Errorf("not a valid AnnotationType string") +} + + +func AnnotationTypePtr(v AnnotationType) *AnnotationType { return &v } + +func (p AnnotationType) MarshalText() ([]byte, error) { +return []byte(p.String()), nil +} + +func (p *AnnotationType) UnmarshalText(text []byte) error { +q, err := AnnotationTypeFromString(string(text)) +if (err != nil) { +return err +} +*p = q +return nil +} + +func (p *AnnotationType) Scan(value interface{}) error { +v, ok := value.(int64) +if !ok { +return errors.New("Scan value is not int64") +} +*p = AnnotationType(v) +return nil +} + +func (p * AnnotationType) Value() (driver.Value, error) { + if p == nil { + return nil, nil + } +return int64(*p), nil +} +// Indicates the network context of a service recording an annotation with two +// exceptions. +// +// When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, +// the endpoint indicates the source or destination of an RPC. This exception +// allows zipkin to display network context of uninstrumented services, or +// clients such as web browsers. +// +// Attributes: +// - Ipv4: IPv4 host address packed into 4 bytes. +// +// Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 +// - Port: IPv4 port +// +// Note: this is to be treated as an unsigned integer, so watch for negatives. +// +// Conventionally, when the port isn't known, port = 0. +// - ServiceName: Service name in lowercase, such as "memcache" or "zipkin-web" +// +// Conventionally, when the service name isn't known, service_name = "unknown". +// - Ipv6: IPv6 host address packed into 16 bytes. Ex Inet6Address.getBytes() +type Endpoint struct { + Ipv4 int32 `thrift:"ipv4,1" db:"ipv4" json:"ipv4"` + Port int16 `thrift:"port,2" db:"port" json:"port"` + ServiceName string `thrift:"service_name,3" db:"service_name" json:"service_name"` + Ipv6 []byte `thrift:"ipv6,4" db:"ipv6" json:"ipv6,omitempty"` +} + +func NewEndpoint() *Endpoint { + return &Endpoint{} +} + + +func (p *Endpoint) GetIpv4() int32 { + return p.Ipv4 +} + +func (p *Endpoint) GetPort() int16 { + return p.Port +} + +func (p *Endpoint) GetServiceName() string { + return p.ServiceName +} +var Endpoint_Ipv6_DEFAULT []byte + +func (p *Endpoint) GetIpv6() []byte { + return p.Ipv6 +} +func (p *Endpoint) IsSetIpv6() bool { + return p.Ipv6 != nil +} + +func (p *Endpoint) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I32 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.I16 { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.STRING { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.STRING { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Endpoint) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Ipv4 = v +} + return nil +} + +func (p *Endpoint) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI16(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + p.Port = v +} + return nil +} + +func (p *Endpoint) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.ServiceName = v +} + return nil +} + +func (p *Endpoint) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(ctx); err != nil { + return thrift.PrependError("error reading field 4: ", err) +} else { + p.Ipv6 = v +} + return nil +} + +func (p *Endpoint) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Endpoint"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Endpoint) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "ipv4", thrift.I32, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ipv4: ", p), err) } + if err := oprot.WriteI32(ctx, int32(p.Ipv4)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ipv4 (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ipv4: ", p), err) } + return err +} + +func (p *Endpoint) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "port", thrift.I16, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:port: ", p), err) } + if err := oprot.WriteI16(ctx, int16(p.Port)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.port (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:port: ", p), err) } + return err +} + +func (p *Endpoint) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "service_name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:service_name: ", p), err) } + if err := oprot.WriteString(ctx, string(p.ServiceName)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.service_name (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:service_name: ", p), err) } + return err +} + +func (p *Endpoint) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetIpv6() { + if err := oprot.WriteFieldBegin(ctx, "ipv6", thrift.STRING, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:ipv6: ", p), err) } + if err := oprot.WriteBinary(ctx, p.Ipv6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ipv6 (4) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:ipv6: ", p), err) } + } + return err +} + +func (p *Endpoint) Equals(other *Endpoint) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Ipv4 != other.Ipv4 { return false } + if p.Port != other.Port { return false } + if p.ServiceName != other.ServiceName { return false } + if bytes.Compare(p.Ipv6, other.Ipv6) != 0 { return false } + return true +} + +func (p *Endpoint) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Endpoint(%+v)", *p) +} + +// An annotation is similar to a log statement. It includes a host field which +// allows these events to be attributed properly, and also aggregatable. +// +// Attributes: +// - Timestamp: Microseconds from epoch. +// +// This value should use the most precise value possible. For example, +// gettimeofday or syncing nanoTime against a tick of currentTimeMillis. +// - Value +// - Host: Always the host that recorded the event. By specifying the host you allow +// rollup of all events (such as client requests to a service) by IP address. +type Annotation struct { + Timestamp int64 `thrift:"timestamp,1" db:"timestamp" json:"timestamp"` + Value string `thrift:"value,2" db:"value" json:"value"` + Host *Endpoint `thrift:"host,3" db:"host" json:"host,omitempty"` +} + +func NewAnnotation() *Annotation { + return &Annotation{} +} + + +func (p *Annotation) GetTimestamp() int64 { + return p.Timestamp +} + +func (p *Annotation) GetValue() string { + return p.Value +} +var Annotation_Host_DEFAULT *Endpoint +func (p *Annotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return Annotation_Host_DEFAULT + } +return p.Host +} +func (p *Annotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *Annotation) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I64 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.STRING { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.STRUCT { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Annotation) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Timestamp = v +} + return nil +} + +func (p *Annotation) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + p.Value = v +} + return nil +} + +func (p *Annotation) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *Annotation) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Annotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Annotation) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "timestamp", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:timestamp: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:timestamp: ", p), err) } + return err +} + +func (p *Annotation) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) } + if err := oprot.WriteString(ctx, string(p.Value)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) } + return err +} + +func (p *Annotation) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin(ctx, "host", thrift.STRUCT, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:host: ", p), err) } + if err := p.Host.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:host: ", p), err) } + } + return err +} + +func (p *Annotation) Equals(other *Annotation) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Timestamp != other.Timestamp { return false } + if p.Value != other.Value { return false } + if !p.Host.Equals(other.Host) { return false } + return true +} + +func (p *Annotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Annotation(%+v)", *p) +} + +// Binary annotations are tags applied to a Span to give it context. For +// example, a binary annotation of "http.uri" could the path to a resource in a +// RPC call. +// +// Binary annotations of type STRING are always queryable, though more a +// historical implementation detail than a structural concern. +// +// Binary annotations can repeat, and vary on the host. Similar to Annotation, +// the host indicates who logged the event. This allows you to tell the +// difference between the client and server side of the same key. For example, +// the key "http.uri" might be different on the client and server side due to +// rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, +// you can see the different points of view, which often help in debugging. +// +// Attributes: +// - Key +// - Value +// - AnnotationType +// - Host: The host that recorded tag, which allows you to differentiate between +// multiple tags with the same key. There are two exceptions to this. +// +// When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or +// destination of an RPC. This exception allows zipkin to display network +// context of uninstrumented services, or clients such as web browsers. +type BinaryAnnotation struct { + Key string `thrift:"key,1" db:"key" json:"key"` + Value []byte `thrift:"value,2" db:"value" json:"value"` + AnnotationType AnnotationType `thrift:"annotation_type,3" db:"annotation_type" json:"annotation_type"` + Host *Endpoint `thrift:"host,4" db:"host" json:"host,omitempty"` +} + +func NewBinaryAnnotation() *BinaryAnnotation { + return &BinaryAnnotation{} +} + + +func (p *BinaryAnnotation) GetKey() string { + return p.Key +} + +func (p *BinaryAnnotation) GetValue() []byte { + return p.Value +} + +func (p *BinaryAnnotation) GetAnnotationType() AnnotationType { + return p.AnnotationType +} +var BinaryAnnotation_Host_DEFAULT *Endpoint +func (p *BinaryAnnotation) GetHost() *Endpoint { + if !p.IsSetHost() { + return BinaryAnnotation_Host_DEFAULT + } +return p.Host +} +func (p *BinaryAnnotation) IsSetHost() bool { + return p.Host != nil +} + +func (p *BinaryAnnotation) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.STRING { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 2: + if fieldTypeId == thrift.STRING { + if err := p.ReadField2(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.I32 { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.STRUCT { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *BinaryAnnotation) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Key = v +} + return nil +} + +func (p *BinaryAnnotation) ReadField2(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBinary(ctx); err != nil { + return thrift.PrependError("error reading field 2: ", err) +} else { + p.Value = v +} + return nil +} + +func (p *BinaryAnnotation) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI32(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + temp := AnnotationType(v) + p.AnnotationType = temp +} + return nil +} + +func (p *BinaryAnnotation) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + p.Host = &Endpoint{} + if err := p.Host.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", p.Host), err) + } + return nil +} + +func (p *BinaryAnnotation) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "BinaryAnnotation"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField2(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *BinaryAnnotation) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "key", thrift.STRING, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:key: ", p), err) } + if err := oprot.WriteString(ctx, string(p.Key)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.key (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:key: ", p), err) } + return err +} + +func (p *BinaryAnnotation) writeField2(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "value", thrift.STRING, 2); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 2:value: ", p), err) } + if err := oprot.WriteBinary(ctx, p.Value); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.value (2) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 2:value: ", p), err) } + return err +} + +func (p *BinaryAnnotation) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "annotation_type", thrift.I32, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:annotation_type: ", p), err) } + if err := oprot.WriteI32(ctx, int32(p.AnnotationType)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.annotation_type (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:annotation_type: ", p), err) } + return err +} + +func (p *BinaryAnnotation) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetHost() { + if err := oprot.WriteFieldBegin(ctx, "host", thrift.STRUCT, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:host: ", p), err) } + if err := p.Host.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", p.Host), err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:host: ", p), err) } + } + return err +} + +func (p *BinaryAnnotation) Equals(other *BinaryAnnotation) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Key != other.Key { return false } + if bytes.Compare(p.Value, other.Value) != 0 { return false } + if p.AnnotationType != other.AnnotationType { return false } + if !p.Host.Equals(other.Host) { return false } + return true +} + +func (p *BinaryAnnotation) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("BinaryAnnotation(%+v)", *p) +} + +// A trace is a series of spans (often RPC calls) which form a latency tree. +// +// The root span is where trace_id = id and parent_id = Nil. The root span is +// usually the longest interval in the trace, starting with a SERVER_RECV +// annotation and ending with a SERVER_SEND. +// +// Attributes: +// - TraceID +// - Name: Span name in lowercase, rpc method for example +// +// Conventionally, when the span name isn't known, name = "unknown". +// - ID +// - ParentID +// - Annotations +// - BinaryAnnotations +// - Debug +// - Timestamp: Microseconds from epoch of the creation of this span. +// +// This value should be set directly by instrumentation, using the most +// precise value possible. For example, gettimeofday or syncing nanoTime +// against a tick of currentTimeMillis. +// +// For compatibility with instrumentation that precede this field, collectors +// or span stores can derive this via Annotation.timestamp. +// For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. +// +// This field is optional for compatibility with old data: first-party span +// stores are expected to support this at time of introduction. +// - Duration: Measurement of duration in microseconds, used to support queries. +// +// This value should be set directly, where possible. Doing so encourages +// precise measurement decoupled from problems of clocks, such as skew or NTP +// updates causing time to move backwards. +// +// For compatibility with instrumentation that precede this field, collectors +// or span stores can derive this by subtracting Annotation.timestamp. +// For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. +// +// If this field is persisted as unset, zipkin will continue to work, except +// duration query support will be implementation-specific. Similarly, setting +// this field non-atomically is implementation-specific. +// +// This field is i64 vs i32 to support spans longer than 35 minutes. +// - TraceIDHigh: Optional unique 8-byte additional identifier for a trace. If non zero, this +// means the trace uses 128 bit traceIds instead of 64 bit. +type Span struct { + TraceID int64 `thrift:"trace_id,1" db:"trace_id" json:"trace_id"` + // unused field # 2 + Name string `thrift:"name,3" db:"name" json:"name"` + ID int64 `thrift:"id,4" db:"id" json:"id"` + ParentID *int64 `thrift:"parent_id,5" db:"parent_id" json:"parent_id,omitempty"` + Annotations []*Annotation `thrift:"annotations,6" db:"annotations" json:"annotations"` + // unused field # 7 + BinaryAnnotations []*BinaryAnnotation `thrift:"binary_annotations,8" db:"binary_annotations" json:"binary_annotations"` + Debug bool `thrift:"debug,9" db:"debug" json:"debug"` + Timestamp *int64 `thrift:"timestamp,10" db:"timestamp" json:"timestamp,omitempty"` + Duration *int64 `thrift:"duration,11" db:"duration" json:"duration,omitempty"` + TraceIDHigh *int64 `thrift:"trace_id_high,12" db:"trace_id_high" json:"trace_id_high,omitempty"` +} + +func NewSpan() *Span { + return &Span{} +} + + +func (p *Span) GetTraceID() int64 { + return p.TraceID +} + +func (p *Span) GetName() string { + return p.Name +} + +func (p *Span) GetID() int64 { + return p.ID +} +var Span_ParentID_DEFAULT int64 +func (p *Span) GetParentID() int64 { + if !p.IsSetParentID() { + return Span_ParentID_DEFAULT + } +return *p.ParentID +} + +func (p *Span) GetAnnotations() []*Annotation { + return p.Annotations +} + +func (p *Span) GetBinaryAnnotations() []*BinaryAnnotation { + return p.BinaryAnnotations +} +var Span_Debug_DEFAULT bool = false + +func (p *Span) GetDebug() bool { + return p.Debug +} +var Span_Timestamp_DEFAULT int64 +func (p *Span) GetTimestamp() int64 { + if !p.IsSetTimestamp() { + return Span_Timestamp_DEFAULT + } +return *p.Timestamp +} +var Span_Duration_DEFAULT int64 +func (p *Span) GetDuration() int64 { + if !p.IsSetDuration() { + return Span_Duration_DEFAULT + } +return *p.Duration +} +var Span_TraceIDHigh_DEFAULT int64 +func (p *Span) GetTraceIDHigh() int64 { + if !p.IsSetTraceIDHigh() { + return Span_TraceIDHigh_DEFAULT + } +return *p.TraceIDHigh +} +func (p *Span) IsSetParentID() bool { + return p.ParentID != nil +} + +func (p *Span) IsSetDebug() bool { + return p.Debug != Span_Debug_DEFAULT +} + +func (p *Span) IsSetTimestamp() bool { + return p.Timestamp != nil +} + +func (p *Span) IsSetDuration() bool { + return p.Duration != nil +} + +func (p *Span) IsSetTraceIDHigh() bool { + return p.TraceIDHigh != nil +} + +func (p *Span) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.I64 { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 3: + if fieldTypeId == thrift.STRING { + if err := p.ReadField3(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 4: + if fieldTypeId == thrift.I64 { + if err := p.ReadField4(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 5: + if fieldTypeId == thrift.I64 { + if err := p.ReadField5(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 6: + if fieldTypeId == thrift.LIST { + if err := p.ReadField6(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 8: + if fieldTypeId == thrift.LIST { + if err := p.ReadField8(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 9: + if fieldTypeId == thrift.BOOL { + if err := p.ReadField9(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 10: + if fieldTypeId == thrift.I64 { + if err := p.ReadField10(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 11: + if fieldTypeId == thrift.I64 { + if err := p.ReadField11(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + case 12: + if fieldTypeId == thrift.I64 { + if err := p.ReadField12(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *Span) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.TraceID = v +} + return nil +} + +func (p *Span) ReadField3(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadString(ctx); err != nil { + return thrift.PrependError("error reading field 3: ", err) +} else { + p.Name = v +} + return nil +} + +func (p *Span) ReadField4(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 4: ", err) +} else { + p.ID = v +} + return nil +} + +func (p *Span) ReadField5(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 5: ", err) +} else { + p.ParentID = &v +} + return nil +} + +func (p *Span) ReadField6(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Annotation, 0, size) + p.Annotations = tSlice + for i := 0; i < size; i ++ { + _elem0 := &Annotation{} + if err := _elem0.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem0), err) + } + p.Annotations = append(p.Annotations, _elem0) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) ReadField8(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*BinaryAnnotation, 0, size) + p.BinaryAnnotations = tSlice + for i := 0; i < size; i ++ { + _elem1 := &BinaryAnnotation{} + if err := _elem1.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem1), err) + } + p.BinaryAnnotations = append(p.BinaryAnnotations, _elem1) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *Span) ReadField9(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(ctx); err != nil { + return thrift.PrependError("error reading field 9: ", err) +} else { + p.Debug = v +} + return nil +} + +func (p *Span) ReadField10(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 10: ", err) +} else { + p.Timestamp = &v +} + return nil +} + +func (p *Span) ReadField11(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 11: ", err) +} else { + p.Duration = &v +} + return nil +} + +func (p *Span) ReadField12(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadI64(ctx); err != nil { + return thrift.PrependError("error reading field 12: ", err) +} else { + p.TraceIDHigh = &v +} + return nil +} + +func (p *Span) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Span"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + if err := p.writeField3(ctx, oprot); err != nil { return err } + if err := p.writeField4(ctx, oprot); err != nil { return err } + if err := p.writeField5(ctx, oprot); err != nil { return err } + if err := p.writeField6(ctx, oprot); err != nil { return err } + if err := p.writeField8(ctx, oprot); err != nil { return err } + if err := p.writeField9(ctx, oprot); err != nil { return err } + if err := p.writeField10(ctx, oprot); err != nil { return err } + if err := p.writeField11(ctx, oprot); err != nil { return err } + if err := p.writeField12(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Span) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "trace_id", thrift.I64, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:trace_id: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.TraceID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.trace_id (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:trace_id: ", p), err) } + return err +} + +func (p *Span) writeField3(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "name", thrift.STRING, 3); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 3:name: ", p), err) } + if err := oprot.WriteString(ctx, string(p.Name)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.name (3) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 3:name: ", p), err) } + return err +} + +func (p *Span) writeField4(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "id", thrift.I64, 4); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 4:id: ", p), err) } + if err := oprot.WriteI64(ctx, int64(p.ID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.id (4) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 4:id: ", p), err) } + return err +} + +func (p *Span) writeField5(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetParentID() { + if err := oprot.WriteFieldBegin(ctx, "parent_id", thrift.I64, 5); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 5:parent_id: ", p), err) } + if err := oprot.WriteI64(ctx, int64(*p.ParentID)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.parent_id (5) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 5:parent_id: ", p), err) } + } + return err +} + +func (p *Span) writeField6(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "annotations", thrift.LIST, 6); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 6:annotations: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Annotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Annotations { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 6:annotations: ", p), err) } + return err +} + +func (p *Span) writeField8(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "binary_annotations", thrift.LIST, 8); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 8:binary_annotations: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.BinaryAnnotations)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.BinaryAnnotations { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 8:binary_annotations: ", p), err) } + return err +} + +func (p *Span) writeField9(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetDebug() { + if err := oprot.WriteFieldBegin(ctx, "debug", thrift.BOOL, 9); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 9:debug: ", p), err) } + if err := oprot.WriteBool(ctx, bool(p.Debug)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.debug (9) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 9:debug: ", p), err) } + } + return err +} + +func (p *Span) writeField10(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetTimestamp() { + if err := oprot.WriteFieldBegin(ctx, "timestamp", thrift.I64, 10); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 10:timestamp: ", p), err) } + if err := oprot.WriteI64(ctx, int64(*p.Timestamp)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.timestamp (10) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 10:timestamp: ", p), err) } + } + return err +} + +func (p *Span) writeField11(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetDuration() { + if err := oprot.WriteFieldBegin(ctx, "duration", thrift.I64, 11); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 11:duration: ", p), err) } + if err := oprot.WriteI64(ctx, int64(*p.Duration)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.duration (11) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 11:duration: ", p), err) } + } + return err +} + +func (p *Span) writeField12(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetTraceIDHigh() { + if err := oprot.WriteFieldBegin(ctx, "trace_id_high", thrift.I64, 12); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 12:trace_id_high: ", p), err) } + if err := oprot.WriteI64(ctx, int64(*p.TraceIDHigh)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.trace_id_high (12) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 12:trace_id_high: ", p), err) } + } + return err +} + +func (p *Span) Equals(other *Span) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.TraceID != other.TraceID { return false } + if p.Name != other.Name { return false } + if p.ID != other.ID { return false } + if p.ParentID != other.ParentID { + if p.ParentID == nil || other.ParentID == nil { + return false + } + if (*p.ParentID) != (*other.ParentID) { return false } + } + if len(p.Annotations) != len(other.Annotations) { return false } + for i, _tgt := range p.Annotations { + _src2 := other.Annotations[i] + if !_tgt.Equals(_src2) { return false } + } + if len(p.BinaryAnnotations) != len(other.BinaryAnnotations) { return false } + for i, _tgt := range p.BinaryAnnotations { + _src3 := other.BinaryAnnotations[i] + if !_tgt.Equals(_src3) { return false } + } + if p.Debug != other.Debug { return false } + if p.Timestamp != other.Timestamp { + if p.Timestamp == nil || other.Timestamp == nil { + return false + } + if (*p.Timestamp) != (*other.Timestamp) { return false } + } + if p.Duration != other.Duration { + if p.Duration == nil || other.Duration == nil { + return false + } + if (*p.Duration) != (*other.Duration) { return false } + } + if p.TraceIDHigh != other.TraceIDHigh { + if p.TraceIDHigh == nil || other.TraceIDHigh == nil { + return false + } + if (*p.TraceIDHigh) != (*other.TraceIDHigh) { return false } + } + return true +} + +func (p *Span) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Span(%+v)", *p) +} + +// Attributes: +// - Ok +type Response struct { + Ok bool `thrift:"ok,1,required" db:"ok" json:"ok"` +} + +func NewResponse() *Response { + return &Response{} +} + + +func (p *Response) GetOk() bool { + return p.Ok +} +func (p *Response) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + var issetOk bool = false; + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.BOOL { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + issetOk = true + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + if !issetOk{ + return thrift.NewTProtocolExceptionWithType(thrift.INVALID_DATA, fmt.Errorf("Required field Ok is not set")); + } + return nil +} + +func (p *Response) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + if v, err := iprot.ReadBool(ctx); err != nil { + return thrift.PrependError("error reading field 1: ", err) +} else { + p.Ok = v +} + return nil +} + +func (p *Response) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "Response"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *Response) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "ok", thrift.BOOL, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:ok: ", p), err) } + if err := oprot.WriteBool(ctx, bool(p.Ok)); err != nil { + return thrift.PrependError(fmt.Sprintf("%T.ok (1) field write error: ", p), err) } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:ok: ", p), err) } + return err +} + +func (p *Response) Equals(other *Response) bool { + if p == other { + return true + } else if p == nil || other == nil { + return false + } + if p.Ok != other.Ok { return false } + return true +} + +func (p *Response) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("Response(%+v)", *p) +} + +type ZipkinCollector interface { + // Parameters: + // - Spans + SubmitZipkinBatch(ctx context.Context, spans []*Span) (_r []*Response, _err error) +} + +type ZipkinCollectorClient struct { + c thrift.TClient + meta thrift.ResponseMeta +} + +func NewZipkinCollectorClientFactory(t thrift.TTransport, f thrift.TProtocolFactory) *ZipkinCollectorClient { + return &ZipkinCollectorClient{ + c: thrift.NewTStandardClient(f.GetProtocol(t), f.GetProtocol(t)), + } +} + +func NewZipkinCollectorClientProtocol(t thrift.TTransport, iprot thrift.TProtocol, oprot thrift.TProtocol) *ZipkinCollectorClient { + return &ZipkinCollectorClient{ + c: thrift.NewTStandardClient(iprot, oprot), + } +} + +func NewZipkinCollectorClient(c thrift.TClient) *ZipkinCollectorClient { + return &ZipkinCollectorClient{ + c: c, + } +} + +func (p *ZipkinCollectorClient) Client_() thrift.TClient { + return p.c +} + +func (p *ZipkinCollectorClient) LastResponseMeta_() thrift.ResponseMeta { + return p.meta +} + +func (p *ZipkinCollectorClient) SetLastResponseMeta_(meta thrift.ResponseMeta) { + p.meta = meta +} + +// Parameters: +// - Spans +func (p *ZipkinCollectorClient) SubmitZipkinBatch(ctx context.Context, spans []*Span) (_r []*Response, _err error) { + var _args4 ZipkinCollectorSubmitZipkinBatchArgs + _args4.Spans = spans + var _result6 ZipkinCollectorSubmitZipkinBatchResult + var _meta5 thrift.ResponseMeta + _meta5, _err = p.Client_().Call(ctx, "submitZipkinBatch", &_args4, &_result6) + p.SetLastResponseMeta_(_meta5) + if _err != nil { + return + } + return _result6.GetSuccess(), nil +} + +type ZipkinCollectorProcessor struct { + processorMap map[string]thrift.TProcessorFunction + handler ZipkinCollector +} + +func (p *ZipkinCollectorProcessor) AddToProcessorMap(key string, processor thrift.TProcessorFunction) { + p.processorMap[key] = processor +} + +func (p *ZipkinCollectorProcessor) GetProcessorFunction(key string) (processor thrift.TProcessorFunction, ok bool) { + processor, ok = p.processorMap[key] + return processor, ok +} + +func (p *ZipkinCollectorProcessor) ProcessorMap() map[string]thrift.TProcessorFunction { + return p.processorMap +} + +func NewZipkinCollectorProcessor(handler ZipkinCollector) *ZipkinCollectorProcessor { + + self7 := &ZipkinCollectorProcessor{handler:handler, processorMap:make(map[string]thrift.TProcessorFunction)} + self7.processorMap["submitZipkinBatch"] = &zipkinCollectorProcessorSubmitZipkinBatch{handler:handler} +return self7 +} + +func (p *ZipkinCollectorProcessor) Process(ctx context.Context, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + name, _, seqId, err2 := iprot.ReadMessageBegin(ctx) + if err2 != nil { return false, thrift.WrapTException(err2) } + if processor, ok := p.GetProcessorFunction(name); ok { + return processor.Process(ctx, seqId, iprot, oprot) + } + iprot.Skip(ctx, thrift.STRUCT) + iprot.ReadMessageEnd(ctx) + x8 := thrift.NewTApplicationException(thrift.UNKNOWN_METHOD, "Unknown function " + name) + oprot.WriteMessageBegin(ctx, name, thrift.EXCEPTION, seqId) + x8.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return false, x8 + +} + +type zipkinCollectorProcessorSubmitZipkinBatch struct { + handler ZipkinCollector +} + +func (p *zipkinCollectorProcessorSubmitZipkinBatch) Process(ctx context.Context, seqId int32, iprot, oprot thrift.TProtocol) (success bool, err thrift.TException) { + args := ZipkinCollectorSubmitZipkinBatchArgs{} + var err2 error + if err2 = args.Read(ctx, iprot); err2 != nil { + iprot.ReadMessageEnd(ctx) + x := thrift.NewTApplicationException(thrift.PROTOCOL_ERROR, err2.Error()) + oprot.WriteMessageBegin(ctx, "submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return false, thrift.WrapTException(err2) + } + iprot.ReadMessageEnd(ctx) + + tickerCancel := func() {} + // Start a goroutine to do server side connectivity check. + if thrift.ServerConnectivityCheckInterval > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithCancel(ctx) + defer cancel() + var tickerCtx context.Context + tickerCtx, tickerCancel = context.WithCancel(context.Background()) + defer tickerCancel() + go func(ctx context.Context, cancel context.CancelFunc) { + ticker := time.NewTicker(thrift.ServerConnectivityCheckInterval) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if !iprot.Transport().IsOpen() { + cancel() + return + } + } + } + }(tickerCtx, cancel) + } + + result := ZipkinCollectorSubmitZipkinBatchResult{} + var retval []*Response + if retval, err2 = p.handler.SubmitZipkinBatch(ctx, args.Spans); err2 != nil { + tickerCancel() + if err2 == thrift.ErrAbandonRequest { + return false, thrift.WrapTException(err2) + } + x := thrift.NewTApplicationException(thrift.INTERNAL_ERROR, "Internal error processing submitZipkinBatch: " + err2.Error()) + oprot.WriteMessageBegin(ctx, "submitZipkinBatch", thrift.EXCEPTION, seqId) + x.Write(ctx, oprot) + oprot.WriteMessageEnd(ctx) + oprot.Flush(ctx) + return true, thrift.WrapTException(err2) + } else { + result.Success = retval + } + tickerCancel() + if err2 = oprot.WriteMessageBegin(ctx, "submitZipkinBatch", thrift.REPLY, seqId); err2 != nil { + err = thrift.WrapTException(err2) + } + if err2 = result.Write(ctx, oprot); err == nil && err2 != nil { + err = thrift.WrapTException(err2) + } + if err2 = oprot.WriteMessageEnd(ctx); err == nil && err2 != nil { + err = thrift.WrapTException(err2) + } + if err2 = oprot.Flush(ctx); err == nil && err2 != nil { + err = thrift.WrapTException(err2) + } + if err != nil { + return + } + return true, err +} + + +// HELPER FUNCTIONS AND STRUCTURES + +// Attributes: +// - Spans +type ZipkinCollectorSubmitZipkinBatchArgs struct { + Spans []*Span `thrift:"spans,1" db:"spans" json:"spans"` +} + +func NewZipkinCollectorSubmitZipkinBatchArgs() *ZipkinCollectorSubmitZipkinBatchArgs { + return &ZipkinCollectorSubmitZipkinBatchArgs{} +} + + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) GetSpans() []*Span { + return p.Spans +} +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 1: + if fieldTypeId == thrift.LIST { + if err := p.ReadField1(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) ReadField1(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Span, 0, size) + p.Spans = tSlice + for i := 0; i < size; i ++ { + _elem9 := &Span{} + if err := _elem9.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem9), err) + } + p.Spans = append(p.Spans, _elem9) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "submitZipkinBatch_args"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField1(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) writeField1(ctx context.Context, oprot thrift.TProtocol) (err error) { + if err := oprot.WriteFieldBegin(ctx, "spans", thrift.LIST, 1); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 1:spans: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Spans)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Spans { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 1:spans: ", p), err) } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchArgs) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchArgs(%+v)", *p) +} + +// Attributes: +// - Success +type ZipkinCollectorSubmitZipkinBatchResult struct { + Success []*Response `thrift:"success,0" db:"success" json:"success,omitempty"` +} + +func NewZipkinCollectorSubmitZipkinBatchResult() *ZipkinCollectorSubmitZipkinBatchResult { + return &ZipkinCollectorSubmitZipkinBatchResult{} +} + +var ZipkinCollectorSubmitZipkinBatchResult_Success_DEFAULT []*Response + +func (p *ZipkinCollectorSubmitZipkinBatchResult) GetSuccess() []*Response { + return p.Success +} +func (p *ZipkinCollectorSubmitZipkinBatchResult) IsSetSuccess() bool { + return p.Success != nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Read(ctx context.Context, iprot thrift.TProtocol) error { + if _, err := iprot.ReadStructBegin(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read error: ", p), err) + } + + + for { + _, fieldTypeId, fieldId, err := iprot.ReadFieldBegin(ctx) + if err != nil { + return thrift.PrependError(fmt.Sprintf("%T field %d read error: ", p, fieldId), err) + } + if fieldTypeId == thrift.STOP { break; } + switch fieldId { + case 0: + if fieldTypeId == thrift.LIST { + if err := p.ReadField0(ctx, iprot); err != nil { + return err + } + } else { + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + default: + if err := iprot.Skip(ctx, fieldTypeId); err != nil { + return err + } + } + if err := iprot.ReadFieldEnd(ctx); err != nil { + return err + } + } + if err := iprot.ReadStructEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T read struct end error: ", p), err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) ReadField0(ctx context.Context, iprot thrift.TProtocol) error { + _, size, err := iprot.ReadListBegin(ctx) + if err != nil { + return thrift.PrependError("error reading list begin: ", err) + } + tSlice := make([]*Response, 0, size) + p.Success = tSlice + for i := 0; i < size; i ++ { + _elem10 := &Response{} + if err := _elem10.Read(ctx, iprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error reading struct: ", _elem10), err) + } + p.Success = append(p.Success, _elem10) + } + if err := iprot.ReadListEnd(ctx); err != nil { + return thrift.PrependError("error reading list end: ", err) + } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) Write(ctx context.Context, oprot thrift.TProtocol) error { + if err := oprot.WriteStructBegin(ctx, "submitZipkinBatch_result"); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write struct begin error: ", p), err) } + if p != nil { + if err := p.writeField0(ctx, oprot); err != nil { return err } + } + if err := oprot.WriteFieldStop(ctx); err != nil { + return thrift.PrependError("write field stop error: ", err) } + if err := oprot.WriteStructEnd(ctx); err != nil { + return thrift.PrependError("write struct stop error: ", err) } + return nil +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) writeField0(ctx context.Context, oprot thrift.TProtocol) (err error) { + if p.IsSetSuccess() { + if err := oprot.WriteFieldBegin(ctx, "success", thrift.LIST, 0); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field begin error 0:success: ", p), err) } + if err := oprot.WriteListBegin(ctx, thrift.STRUCT, len(p.Success)); err != nil { + return thrift.PrependError("error writing list begin: ", err) + } + for _, v := range p.Success { + if err := v.Write(ctx, oprot); err != nil { + return thrift.PrependError(fmt.Sprintf("%T error writing struct: ", v), err) + } + } + if err := oprot.WriteListEnd(ctx); err != nil { + return thrift.PrependError("error writing list end: ", err) + } + if err := oprot.WriteFieldEnd(ctx); err != nil { + return thrift.PrependError(fmt.Sprintf("%T write field end error 0:success: ", p), err) } + } + return err +} + +func (p *ZipkinCollectorSubmitZipkinBatchResult) String() string { + if p == nil { + return "" + } + return fmt.Sprintf("ZipkinCollectorSubmitZipkinBatchResult(%+v)", *p) +} + + diff -Nru libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go --- libpod-3.2.1+ds1/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/github.com/uber/jaeger-client-go/utils/udp_client.go 2021-12-26 00:45:51.000000000 +0000 @@ -15,6 +15,7 @@ package utils import ( + "context" "errors" "fmt" "io" @@ -124,15 +125,14 @@ } // EmitZipkinBatch implements EmitZipkinBatch() of Agent interface -func (a *AgentClientUDP) EmitZipkinBatch(spans []*zipkincore.Span) error { +func (a *AgentClientUDP) EmitZipkinBatch(context.Context, []*zipkincore.Span) error { return errors.New("Not implemented") } // EmitBatch implements EmitBatch() of Agent interface -func (a *AgentClientUDP) EmitBatch(batch *jaeger.Batch) error { +func (a *AgentClientUDP) EmitBatch(ctx context.Context, batch *jaeger.Batch) error { a.thriftBuffer.Reset() - a.client.SeqId = 0 // we have no need for distinct SeqIds for our one-way UDP messages - if err := a.client.EmitBatch(batch); err != nil { + if err := a.client.EmitBatch(ctx, batch); err != nil { return err } if a.thriftBuffer.Len() > a.maxPacketSize { diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/generated.pb.go libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/generated.pb.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/generated.pb.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/generated.pb.go 2021-12-26 00:45:51.000000000 +0000 @@ -868,134 +868,135 @@ } var fileDescriptor_e1014cab6f31e43b = []byte{ - // 2031 bytes of a gzipped FileDescriptorProto + // 2047 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcd, 0x6f, 0x24, 0x47, 0x15, 0x77, 0xcf, 0x87, 0x3d, 0x2e, 0xaf, 0xed, 0xdd, 0xb2, 0xb1, 0x27, 0xbb, 0x64, 0x66, 0x19, 0x60, 0xe3, 0x64, 0xb3, 0x3d, 0xec, 0x66, 0x13, 0xa1, 0x2c, 0x02, 0x79, 0xc6, 0x21, 0x84, 0x78, 0x6c, 0x53, 0x5e, 0xef, 0x61, 0x09, 0x12, 0xe5, 0xe9, 0xda, 0x71, 0xc7, 0xfd, 0xa5, 0xee, 0xea, - 0x61, 0x47, 0x5c, 0x10, 0x12, 0x37, 0x0e, 0xfc, 0x27, 0x08, 0x21, 0xb8, 0xa1, 0x08, 0x71, 0xd9, - 0x0b, 0x52, 0xc4, 0x85, 0x9c, 0x2c, 0x76, 0x72, 0x42, 0x28, 0x47, 0x2e, 0xb9, 0x80, 0xaa, 0xba, - 0xfa, 0xbb, 0xda, 0x33, 0xf6, 0x26, 0xce, 0x87, 0x72, 0xf3, 0x54, 0xfd, 0xde, 0xaf, 0xde, 0xab, - 0x7a, 0x55, 0xef, 0xd7, 0x55, 0x06, 0xf7, 0x8e, 0xbf, 0xeb, 0xa9, 0xba, 0xdd, 0x3e, 0xf6, 0x0f, - 0x89, 0x6b, 0x11, 0x4a, 0xbc, 0xf6, 0x90, 0x58, 0x9a, 0xed, 0xb6, 0x45, 0x07, 0x76, 0xf4, 0x36, - 0x76, 0x1c, 0xaf, 0x3d, 0xbc, 0xdd, 0x1e, 0x10, 0x8b, 0xb8, 0x98, 0x12, 0x4d, 0x75, 0x5c, 0x9b, - 0xda, 0x10, 0x06, 0x18, 0x15, 0x3b, 0xba, 0xca, 0x30, 0xea, 0xf0, 0xf6, 0xd5, 0x5b, 0x03, 0x9d, - 0x1e, 0xf9, 0x87, 0x6a, 0xdf, 0x36, 0xdb, 0x03, 0x7b, 0x60, 0xb7, 0x39, 0xf4, 0xd0, 0x7f, 0xc4, - 0x7f, 0xf1, 0x1f, 0xfc, 0xaf, 0x80, 0xe2, 0x6a, 0x2b, 0x31, 0x4c, 0xdf, 0x76, 0x89, 0x64, 0x98, - 0xab, 0x77, 0x63, 0x8c, 0x89, 0xfb, 0x47, 0xba, 0x45, 0xdc, 0x51, 0xdb, 0x39, 0x1e, 0xb0, 0x06, - 0xaf, 0x6d, 0x12, 0x8a, 0x65, 0x56, 0xed, 0x22, 0x2b, 0xd7, 0xb7, 0xa8, 0x6e, 0x92, 0x9c, 0xc1, - 0x6b, 0x93, 0x0c, 0xbc, 0xfe, 0x11, 0x31, 0x71, 0xce, 0xee, 0x95, 0x22, 0x3b, 0x9f, 0xea, 0x46, - 0x5b, 0xb7, 0xa8, 0x47, 0xdd, 0xac, 0x51, 0xeb, 0xbf, 0x0a, 0x80, 0x5d, 0xdb, 0xa2, 0xae, 0x6d, - 0x18, 0xc4, 0x45, 0x64, 0xa8, 0x7b, 0xba, 0x6d, 0xc1, 0x9f, 0x83, 0x1a, 0x8b, 0x47, 0xc3, 0x14, - 0xd7, 0x95, 0xeb, 0xca, 0xc6, 0xc2, 0x9d, 0xef, 0xa8, 0xf1, 0x24, 0x47, 0xf4, 0xaa, 0x73, 0x3c, - 0x60, 0x0d, 0x9e, 0xca, 0xd0, 0xea, 0xf0, 0xb6, 0xba, 0x7b, 0xf8, 0x2e, 0xe9, 0xd3, 0x1e, 0xa1, - 0xb8, 0x03, 0x9f, 0x9c, 0x34, 0x67, 0xc6, 0x27, 0x4d, 0x10, 0xb7, 0xa1, 0x88, 0x15, 0xee, 0x82, - 0x0a, 0x67, 0x2f, 0x71, 0xf6, 0x5b, 0x85, 0xec, 0x22, 0x68, 0x15, 0xe1, 0x5f, 0xbc, 0xf1, 0x98, - 0x12, 0x8b, 0xb9, 0xd7, 0xb9, 0x24, 0xa8, 0x2b, 0x5b, 0x98, 0x62, 0xc4, 0x89, 0xe0, 0xcb, 0xa0, - 0xe6, 0x0a, 0xf7, 0xeb, 0xe5, 0xeb, 0xca, 0x46, 0xb9, 0x73, 0x59, 0xa0, 0x6a, 0x61, 0x58, 0x28, - 0x42, 0xb4, 0xfe, 0xa6, 0x80, 0xb5, 0x7c, 0xdc, 0xdb, 0xba, 0x47, 0xe1, 0x3b, 0xb9, 0xd8, 0xd5, - 0xe9, 0x62, 0x67, 0xd6, 0x3c, 0xf2, 0x68, 0xe0, 0xb0, 0x25, 0x11, 0xf7, 0xdb, 0xa0, 0xaa, 0x53, - 0x62, 0x7a, 0xf5, 0xd2, 0xf5, 0xf2, 0xc6, 0xc2, 0x9d, 0x1b, 0x6a, 0x3e, 0x77, 0xd5, 0xbc, 0x63, - 0x9d, 0x45, 0x41, 0x59, 0x7d, 0x8b, 0x19, 0xa3, 0x80, 0xa3, 0xf5, 0x3f, 0x05, 0xcc, 0x6f, 0x61, - 0x62, 0xda, 0xd6, 0x3e, 0xa1, 0x17, 0xb0, 0x68, 0x5d, 0x50, 0xf1, 0x1c, 0xd2, 0x17, 0x8b, 0xf6, - 0x0d, 0x99, 0xef, 0x91, 0x3b, 0xfb, 0x0e, 0xe9, 0xc7, 0x0b, 0xc5, 0x7e, 0x21, 0x6e, 0x0c, 0xdf, - 0x06, 0xb3, 0x1e, 0xc5, 0xd4, 0xf7, 0xf8, 0x32, 0x2d, 0xdc, 0xf9, 0xe6, 0xe9, 0x34, 0x1c, 0xda, - 0x59, 0x12, 0x44, 0xb3, 0xc1, 0x6f, 0x24, 0x28, 0x5a, 0xff, 0x2e, 0x01, 0x18, 0x61, 0xbb, 0xb6, - 0xa5, 0xe9, 0x94, 0xe5, 0xef, 0xeb, 0xa0, 0x42, 0x47, 0x0e, 0xe1, 0xd3, 0x30, 0xdf, 0xb9, 0x11, - 0x7a, 0x71, 0x7f, 0xe4, 0x90, 0x8f, 0x4f, 0x9a, 0x6b, 0x79, 0x0b, 0xd6, 0x83, 0xb8, 0x0d, 0xdc, - 0x8e, 0xfc, 0x2b, 0x71, 0xeb, 0xbb, 0xe9, 0xa1, 0x3f, 0x3e, 0x69, 0x4a, 0x0e, 0x0b, 0x35, 0x62, - 0x4a, 0x3b, 0x08, 0x87, 0x00, 0x1a, 0xd8, 0xa3, 0xf7, 0x5d, 0x6c, 0x79, 0xc1, 0x48, 0xba, 0x49, - 0x44, 0xe4, 0x2f, 0x4d, 0xb7, 0x3c, 0xcc, 0xa2, 0x73, 0x55, 0x78, 0x01, 0xb7, 0x73, 0x6c, 0x48, - 0x32, 0x02, 0xbc, 0x01, 0x66, 0x5d, 0x82, 0x3d, 0xdb, 0xaa, 0x57, 0x78, 0x14, 0xd1, 0x04, 0x22, - 0xde, 0x8a, 0x44, 0x2f, 0x7c, 0x11, 0xcc, 0x99, 0xc4, 0xf3, 0xf0, 0x80, 0xd4, 0xab, 0x1c, 0xb8, - 0x2c, 0x80, 0x73, 0xbd, 0xa0, 0x19, 0x85, 0xfd, 0xad, 0x3f, 0x28, 0x60, 0x31, 0x9a, 0xb9, 0x0b, - 0xd8, 0x2a, 0x9d, 0xf4, 0x56, 0x79, 0xfe, 0xd4, 0x3c, 0x29, 0xd8, 0x21, 0xef, 0x95, 0x13, 0x3e, - 0xb3, 0x24, 0x84, 0x3f, 0x03, 0x35, 0x8f, 0x18, 0xa4, 0x4f, 0x6d, 0x57, 0xf8, 0xfc, 0xca, 0x94, - 0x3e, 0xe3, 0x43, 0x62, 0xec, 0x0b, 0xd3, 0xce, 0x25, 0xe6, 0x74, 0xf8, 0x0b, 0x45, 0x94, 0xf0, - 0x27, 0xa0, 0x46, 0x89, 0xe9, 0x18, 0x98, 0x12, 0xb1, 0x4d, 0x52, 0xf9, 0xcd, 0xd2, 0x85, 0x91, - 0xed, 0xd9, 0xda, 0x7d, 0x01, 0xe3, 0x1b, 0x25, 0x9a, 0x87, 0xb0, 0x15, 0x45, 0x34, 0xf0, 0x18, - 0x2c, 0xf9, 0x8e, 0xc6, 0x90, 0x94, 0x1d, 0xdd, 0x83, 0x91, 0x48, 0x9f, 0x9b, 0xa7, 0x4e, 0xc8, - 0x41, 0xca, 0xa4, 0xb3, 0x26, 0x06, 0x58, 0x4a, 0xb7, 0xa3, 0x0c, 0x35, 0xdc, 0x04, 0xcb, 0xa6, - 0x6e, 0x21, 0x82, 0xb5, 0xd1, 0x3e, 0xe9, 0xdb, 0x96, 0xe6, 0xf1, 0x04, 0xaa, 0x76, 0xd6, 0x05, - 0xc1, 0x72, 0x2f, 0xdd, 0x8d, 0xb2, 0x78, 0xb8, 0x0d, 0x56, 0xc3, 0x73, 0xf6, 0x47, 0xba, 0x47, - 0x6d, 0x77, 0xb4, 0xad, 0x9b, 0x3a, 0xad, 0xcf, 0x72, 0x9e, 0xfa, 0xf8, 0xa4, 0xb9, 0x8a, 0x24, - 0xfd, 0x48, 0x6a, 0xd5, 0xfa, 0xed, 0x2c, 0x58, 0xce, 0x9c, 0x06, 0xf0, 0x01, 0x58, 0xeb, 0xfb, - 0xae, 0x4b, 0x2c, 0xba, 0xe3, 0x9b, 0x87, 0xc4, 0xdd, 0xef, 0x1f, 0x11, 0xcd, 0x37, 0x88, 0xc6, - 0x57, 0xb4, 0xda, 0x69, 0x08, 0x5f, 0xd7, 0xba, 0x52, 0x14, 0x2a, 0xb0, 0x86, 0x3f, 0x06, 0xd0, - 0xe2, 0x4d, 0x3d, 0xdd, 0xf3, 0x22, 0xce, 0x12, 0xe7, 0x8c, 0x36, 0xe0, 0x4e, 0x0e, 0x81, 0x24, - 0x56, 0xcc, 0x47, 0x8d, 0x78, 0xba, 0x4b, 0xb4, 0xac, 0x8f, 0xe5, 0xb4, 0x8f, 0x5b, 0x52, 0x14, - 0x2a, 0xb0, 0x86, 0xaf, 0x82, 0x85, 0x60, 0x34, 0x3e, 0xe7, 0x62, 0x71, 0x56, 0x04, 0xd9, 0xc2, - 0x4e, 0xdc, 0x85, 0x92, 0x38, 0x16, 0x9a, 0x7d, 0xe8, 0x11, 0x77, 0x48, 0xb4, 0x37, 0x03, 0x0d, - 0xc0, 0x0a, 0x65, 0x95, 0x17, 0xca, 0x28, 0xb4, 0xdd, 0x1c, 0x02, 0x49, 0xac, 0x58, 0x68, 0x41, - 0xd6, 0xe4, 0x42, 0x9b, 0x4d, 0x87, 0x76, 0x20, 0x45, 0xa1, 0x02, 0x6b, 0x96, 0x7b, 0x81, 0xcb, - 0x9b, 0x43, 0xac, 0x1b, 0xf8, 0xd0, 0x20, 0xf5, 0xb9, 0x74, 0xee, 0xed, 0xa4, 0xbb, 0x51, 0x16, - 0x0f, 0xdf, 0x04, 0x57, 0x82, 0xa6, 0x03, 0x0b, 0x47, 0x24, 0x35, 0x4e, 0xf2, 0x9c, 0x20, 0xb9, - 0xb2, 0x93, 0x05, 0xa0, 0xbc, 0x0d, 0x7c, 0x1d, 0x2c, 0xf5, 0x6d, 0xc3, 0xe0, 0xf9, 0xd8, 0xb5, - 0x7d, 0x8b, 0xd6, 0xe7, 0x39, 0x0b, 0x64, 0x7b, 0xa8, 0x9b, 0xea, 0x41, 0x19, 0x24, 0x7c, 0x08, - 0x40, 0x3f, 0x2c, 0x07, 0x5e, 0x1d, 0x14, 0x17, 0xfa, 0x7c, 0x1d, 0x8a, 0x0b, 0x70, 0xd4, 0xe4, - 0xa1, 0x04, 0x5b, 0xeb, 0x3d, 0x05, 0xac, 0x17, 0xec, 0x71, 0xf8, 0x83, 0x54, 0xd5, 0xbb, 0x99, - 0xa9, 0x7a, 0xd7, 0x0a, 0xcc, 0x12, 0xa5, 0xaf, 0x0f, 0x16, 0x99, 0xee, 0xd0, 0xad, 0x41, 0x00, - 0x11, 0x27, 0xd8, 0x4b, 0x32, 0xdf, 0x51, 0x12, 0x18, 0x1f, 0xc3, 0x57, 0xc6, 0x27, 0xcd, 0xc5, - 0x54, 0x1f, 0x4a, 0x73, 0xb6, 0x7e, 0x5d, 0x02, 0x60, 0x8b, 0x38, 0x86, 0x3d, 0x32, 0x89, 0x75, - 0x11, 0xaa, 0x65, 0x2b, 0xa5, 0x5a, 0x5a, 0xd2, 0x85, 0x88, 0xfc, 0x29, 0x94, 0x2d, 0xdb, 0x19, - 0xd9, 0xf2, 0xad, 0x09, 0x3c, 0xa7, 0xeb, 0x96, 0x7f, 0x96, 0xc1, 0x4a, 0x0c, 0x8e, 0x85, 0xcb, - 0xbd, 0xd4, 0x12, 0xbe, 0x90, 0x59, 0xc2, 0x75, 0x89, 0xc9, 0xa7, 0xa6, 0x5c, 0xde, 0x05, 0x4b, - 0x4c, 0x57, 0x04, 0xab, 0xc6, 0x55, 0xcb, 0xec, 0x99, 0x55, 0x4b, 0x54, 0x75, 0xb6, 0x53, 0x4c, - 0x28, 0xc3, 0x5c, 0xa0, 0x92, 0xe6, 0xbe, 0x88, 0x2a, 0xe9, 0x8f, 0x0a, 0x58, 0x8a, 0x97, 0xe9, - 0x02, 0x64, 0x52, 0x37, 0x2d, 0x93, 0x1a, 0xa7, 0xe7, 0x65, 0x81, 0x4e, 0xfa, 0x47, 0x25, 0xe9, - 0x35, 0x17, 0x4a, 0x1b, 0xec, 0x83, 0xca, 0x31, 0xf4, 0x3e, 0xf6, 0x44, 0x59, 0xbd, 0x14, 0x7c, - 0x4c, 0x05, 0x6d, 0x28, 0xea, 0x4d, 0x49, 0xaa, 0xd2, 0xa7, 0x2b, 0xa9, 0xca, 0x9f, 0x8c, 0xa4, - 0xba, 0x0f, 0x6a, 0x5e, 0x28, 0xa6, 0x2a, 0x9c, 0xf2, 0xc6, 0xa4, 0xed, 0x2c, 0x74, 0x54, 0xc4, - 0x1a, 0x29, 0xa8, 0x88, 0x49, 0xa6, 0x9d, 0xaa, 0x9f, 0xa5, 0x76, 0x62, 0xe9, 0xed, 0x60, 0xdf, - 0x23, 0x1a, 0xdf, 0x4a, 0xb5, 0x38, 0xbd, 0xf7, 0x78, 0x2b, 0x12, 0xbd, 0xf0, 0x00, 0xac, 0x3b, - 0xae, 0x3d, 0x70, 0x89, 0xe7, 0x6d, 0x11, 0xac, 0x19, 0xba, 0x45, 0xc2, 0x00, 0x82, 0xaa, 0x77, - 0x6d, 0x7c, 0xd2, 0x5c, 0xdf, 0x93, 0x43, 0x50, 0x91, 0x6d, 0xeb, 0x2f, 0x15, 0x70, 0x39, 0x7b, - 0x22, 0x16, 0x08, 0x11, 0xe5, 0x5c, 0x42, 0xe4, 0xe5, 0x44, 0x8a, 0x06, 0x2a, 0x2d, 0xf1, 0xcd, - 0x9f, 0x4b, 0xd3, 0x4d, 0xb0, 0x2c, 0x84, 0x47, 0xd8, 0x29, 0xa4, 0x58, 0xb4, 0x3c, 0x07, 0xe9, - 0x6e, 0x94, 0xc5, 0xc3, 0x7b, 0x60, 0xd1, 0xe5, 0xda, 0x2a, 0x24, 0x08, 0xf4, 0xc9, 0xd7, 0x04, - 0xc1, 0x22, 0x4a, 0x76, 0xa2, 0x34, 0x96, 0x69, 0x93, 0x58, 0x72, 0x84, 0x04, 0x95, 0xb4, 0x36, - 0xd9, 0xcc, 0x02, 0x50, 0xde, 0x06, 0xf6, 0xc0, 0x8a, 0x6f, 0xe5, 0xa9, 0x82, 0x5c, 0xbb, 0x26, - 0xa8, 0x56, 0x0e, 0xf2, 0x10, 0x24, 0xb3, 0x83, 0x3f, 0x4d, 0xc9, 0x95, 0x59, 0x7e, 0x8a, 0xbc, - 0x70, 0xfa, 0x76, 0x98, 0x5a, 0xaf, 0x48, 0x74, 0x54, 0x6d, 0x5a, 0x1d, 0xd5, 0xfa, 0xb3, 0x02, - 0x60, 0x7e, 0x0b, 0x4e, 0xfc, 0xb8, 0xcf, 0x59, 0x24, 0x4a, 0xa4, 0x26, 0x57, 0x38, 0x37, 0x27, - 0x2b, 0x9c, 0xf8, 0x04, 0x9d, 0x4e, 0xe2, 0x88, 0xe9, 0xbd, 0x98, 0x8b, 0x99, 0x29, 0x24, 0x4e, - 0xec, 0xcf, 0xb3, 0x49, 0x9c, 0x04, 0xcf, 0xe9, 0x12, 0xe7, 0x3f, 0x25, 0xb0, 0x12, 0x83, 0xa7, - 0x96, 0x38, 0x12, 0x93, 0xaf, 0x2e, 0x67, 0xa6, 0x93, 0x1d, 0xf1, 0xd4, 0x7d, 0x4e, 0x64, 0x47, - 0xec, 0x50, 0x81, 0xec, 0xf8, 0x7d, 0x29, 0xe9, 0xf5, 0x19, 0x65, 0xc7, 0x27, 0x70, 0x55, 0xf1, - 0x85, 0x53, 0x2e, 0xad, 0xbf, 0x96, 0xc1, 0xe5, 0xec, 0x16, 0x4c, 0xd5, 0x41, 0x65, 0x62, 0x1d, - 0xdc, 0x03, 0xab, 0x8f, 0x7c, 0xc3, 0x18, 0xf1, 0x18, 0x12, 0xc5, 0x30, 0xa8, 0xa0, 0x5f, 0x17, - 0x96, 0xab, 0x3f, 0x94, 0x60, 0x90, 0xd4, 0x32, 0x5f, 0x16, 0x2b, 0xcf, 0x5a, 0x16, 0xab, 0xe7, - 0x28, 0x8b, 0x72, 0x65, 0x51, 0x3e, 0x97, 0xb2, 0x98, 0xba, 0x26, 0x4a, 0x8e, 0xab, 0x89, 0xdf, - 0xf0, 0x63, 0x05, 0xac, 0xc9, 0x3f, 0x9f, 0xa1, 0x01, 0x96, 0x4c, 0xfc, 0x38, 0x79, 0x79, 0x31, - 0xa9, 0x60, 0xf8, 0x54, 0x37, 0xd4, 0xe0, 0x75, 0x47, 0x7d, 0xcb, 0xa2, 0xbb, 0xee, 0x3e, 0x75, - 0x75, 0x6b, 0x10, 0x14, 0xd8, 0x5e, 0x8a, 0x0b, 0x65, 0xb8, 0xe1, 0x43, 0x50, 0x33, 0xf1, 0xe3, - 0x7d, 0xdf, 0x1d, 0x84, 0x85, 0xf0, 0xec, 0xe3, 0xf0, 0xdc, 0xef, 0x09, 0x16, 0x14, 0xf1, 0xb5, - 0x3e, 0x54, 0xc0, 0x7a, 0x41, 0x05, 0xfd, 0x12, 0x45, 0xb9, 0x0b, 0xae, 0xa7, 0x82, 0x64, 0x1b, - 0x92, 0x3c, 0xf2, 0x0d, 0xbe, 0x37, 0x85, 0x5e, 0xb9, 0x09, 0xe6, 0x1d, 0xec, 0x52, 0x3d, 0x12, - 0xba, 0xd5, 0xce, 0xe2, 0xf8, 0xa4, 0x39, 0xbf, 0x17, 0x36, 0xa2, 0xb8, 0xbf, 0xf5, 0x9b, 0x12, - 0x58, 0x48, 0x90, 0x5c, 0x80, 0x76, 0x78, 0x23, 0xa5, 0x1d, 0xa4, 0xaf, 0x31, 0xc9, 0xa8, 0x8a, - 0xc4, 0x43, 0x2f, 0x23, 0x1e, 0xbe, 0x3d, 0x89, 0xe8, 0x74, 0xf5, 0xf0, 0x51, 0x09, 0xac, 0x26, - 0xd0, 0xb1, 0x7c, 0xf8, 0x5e, 0x4a, 0x3e, 0x6c, 0x64, 0xe4, 0x43, 0x5d, 0x66, 0xf3, 0x95, 0x7e, - 0x98, 0xac, 0x1f, 0xfe, 0xa4, 0x80, 0xe5, 0xc4, 0xdc, 0x5d, 0x80, 0x80, 0xd8, 0x4a, 0x0b, 0x88, - 0xe6, 0x84, 0x7c, 0x29, 0x50, 0x10, 0x4f, 0xaa, 0x29, 0xbf, 0xbf, 0xf4, 0x37, 0x17, 0xbf, 0x04, - 0xab, 0x43, 0xdb, 0xf0, 0x4d, 0xd2, 0x35, 0xb0, 0x6e, 0x86, 0x00, 0x56, 0x71, 0xd9, 0x24, 0xbe, - 0x28, 0xa5, 0x27, 0xae, 0xa7, 0x7b, 0x94, 0x58, 0xf4, 0x41, 0x6c, 0x19, 0xd7, 0xf9, 0x07, 0x12, - 0x3a, 0x24, 0x1d, 0x04, 0xbe, 0x0a, 0x16, 0x58, 0xa5, 0xd4, 0xfb, 0x64, 0x07, 0x9b, 0x61, 0x4e, - 0x45, 0x6f, 0x0f, 0xfb, 0x71, 0x17, 0x4a, 0xe2, 0xe0, 0x11, 0x58, 0x71, 0x6c, 0xad, 0x87, 0x2d, - 0x3c, 0x20, 0xec, 0xfc, 0xdf, 0xb3, 0x0d, 0xbd, 0x3f, 0xe2, 0x77, 0x1a, 0xf3, 0x9d, 0xd7, 0xc2, - 0xef, 0xd5, 0xbd, 0x3c, 0x84, 0x7d, 0x0f, 0x48, 0x9a, 0xf9, 0x7e, 0x96, 0x51, 0x42, 0x33, 0xf7, - 0x54, 0x36, 0x97, 0xfb, 0xff, 0x02, 0x59, 0x72, 0x9d, 0xf3, 0xb1, 0xac, 0xe8, 0xb6, 0xa6, 0x76, - 0xae, 0x97, 0xae, 0x8f, 0x2a, 0xe0, 0x4a, 0xee, 0x80, 0xfc, 0x0c, 0xef, 0x4b, 0x72, 0xaa, 0xae, - 0x7c, 0x06, 0x55, 0xb7, 0x09, 0x96, 0xc5, 0x23, 0x5b, 0x46, 0x14, 0x46, 0xe2, 0xbc, 0x9b, 0xee, - 0x46, 0x59, 0xbc, 0xec, 0xbe, 0xa6, 0x7a, 0xc6, 0xfb, 0x9a, 0xa4, 0x17, 0xe2, 0x7f, 0x43, 0x82, - 0xac, 0xcb, 0x7b, 0x21, 0xfe, 0x45, 0x24, 0x8b, 0x87, 0xdf, 0x0f, 0x53, 0x2a, 0x62, 0x98, 0xe3, - 0x0c, 0x99, 0x1c, 0x89, 0x08, 0x32, 0xe8, 0x67, 0x7a, 0x48, 0x7a, 0x47, 0xf2, 0x90, 0xb4, 0x31, - 0x21, 0x95, 0xa7, 0x97, 0xa1, 0x7f, 0x57, 0xc0, 0x73, 0x85, 0x7b, 0x00, 0x6e, 0xa6, 0xea, 0xec, - 0xad, 0x4c, 0x9d, 0x7d, 0xbe, 0xd0, 0x30, 0x51, 0x6c, 0x4d, 0xf9, 0x65, 0xcb, 0xdd, 0x89, 0x97, - 0x2d, 0x12, 0x15, 0x35, 0xf9, 0xd6, 0xa5, 0xb3, 0xf1, 0xe4, 0x69, 0x63, 0xe6, 0xfd, 0xa7, 0x8d, - 0x99, 0x0f, 0x9e, 0x36, 0x66, 0x7e, 0x35, 0x6e, 0x28, 0x4f, 0xc6, 0x0d, 0xe5, 0xfd, 0x71, 0x43, - 0xf9, 0x60, 0xdc, 0x50, 0xfe, 0x35, 0x6e, 0x28, 0xbf, 0xfb, 0xb0, 0x31, 0xf3, 0xb0, 0x34, 0xbc, - 0xfd, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe6, 0xc9, 0x16, 0xd9, 0x6e, 0x26, 0x00, 0x00, + 0x61, 0x47, 0x5c, 0x10, 0x12, 0x27, 0x38, 0xf0, 0x9f, 0x20, 0x84, 0xc8, 0x0d, 0x45, 0x88, 0xcb, + 0x5e, 0x90, 0x22, 0x2e, 0xe4, 0x64, 0xb1, 0x93, 0x13, 0x42, 0x1c, 0xb9, 0xe4, 0x02, 0xaa, 0xea, + 0xea, 0xef, 0x6a, 0xcf, 0xd8, 0x9b, 0x38, 0x24, 0xca, 0x6d, 0xba, 0xea, 0xf7, 0x7e, 0xf5, 0x5e, + 0xd5, 0xab, 0x7a, 0xbf, 0xae, 0x1e, 0x70, 0xef, 0xf8, 0xdb, 0x9e, 0xaa, 0xdb, 0xed, 0x63, 0xff, + 0x90, 0xb8, 0x16, 0xa1, 0xc4, 0x6b, 0x0f, 0x89, 0xa5, 0xd9, 0x6e, 0x5b, 0x74, 0x60, 0x47, 0x6f, + 0x63, 0xc7, 0xf1, 0xda, 0xc3, 0xdb, 0xed, 0x01, 0xb1, 0x88, 0x8b, 0x29, 0xd1, 0x54, 0xc7, 0xb5, + 0xa9, 0x0d, 0x61, 0x80, 0x51, 0xb1, 0xa3, 0xab, 0x0c, 0xa3, 0x0e, 0x6f, 0x5f, 0xbd, 0x35, 0xd0, + 0xe9, 0x91, 0x7f, 0xa8, 0xf6, 0x6d, 0xb3, 0x3d, 0xb0, 0x07, 0x76, 0x9b, 0x43, 0x0f, 0xfd, 0x47, + 0xfc, 0x89, 0x3f, 0xf0, 0x5f, 0x01, 0xc5, 0xd5, 0x56, 0x62, 0x98, 0xbe, 0xed, 0x12, 0xc9, 0x30, + 0x57, 0xef, 0xc6, 0x18, 0x13, 0xf7, 0x8f, 0x74, 0x8b, 0xb8, 0xa3, 0xb6, 0x73, 0x3c, 0x60, 0x0d, + 0x5e, 0xdb, 0x24, 0x14, 0xcb, 0xac, 0xda, 0x45, 0x56, 0xae, 0x6f, 0x51, 0xdd, 0x24, 0x39, 0x83, + 0xd7, 0x26, 0x19, 0x78, 0xfd, 0x23, 0x62, 0xe2, 0x9c, 0xdd, 0x2b, 0x45, 0x76, 0x3e, 0xd5, 0x8d, + 0xb6, 0x6e, 0x51, 0x8f, 0xba, 0x59, 0xa3, 0xd6, 0x7f, 0x14, 0x00, 0xbb, 0xb6, 0x45, 0x5d, 0xdb, + 0x30, 0x88, 0x8b, 0xc8, 0x50, 0xf7, 0x74, 0xdb, 0x82, 0x3f, 0x05, 0x35, 0x16, 0x8f, 0x86, 0x29, + 0xae, 0x2b, 0xd7, 0x95, 0x8d, 0x85, 0x3b, 0xdf, 0x52, 0xe3, 0x49, 0x8e, 0xe8, 0x55, 0xe7, 0x78, + 0xc0, 0x1a, 0x3c, 0x95, 0xa1, 0xd5, 0xe1, 0x6d, 0x75, 0xf7, 0xf0, 0x5d, 0xd2, 0xa7, 0x3d, 0x42, + 0x71, 0x07, 0x3e, 0x39, 0x69, 0xce, 0x8c, 0x4f, 0x9a, 0x20, 0x6e, 0x43, 0x11, 0x2b, 0xdc, 0x05, + 0x15, 0xce, 0x5e, 0xe2, 0xec, 0xb7, 0x0a, 0xd9, 0x45, 0xd0, 0x2a, 0xc2, 0x3f, 0x7b, 0xe3, 0x31, + 0x25, 0x16, 0x73, 0xaf, 0x73, 0x49, 0x50, 0x57, 0xb6, 0x30, 0xc5, 0x88, 0x13, 0xc1, 0x97, 0x41, + 0xcd, 0x15, 0xee, 0xd7, 0xcb, 0xd7, 0x95, 0x8d, 0x72, 0xe7, 0xb2, 0x40, 0xd5, 0xc2, 0xb0, 0x50, + 0x84, 0x68, 0xfd, 0x45, 0x01, 0x6b, 0xf9, 0xb8, 0xb7, 0x75, 0x8f, 0xc2, 0x77, 0x72, 0xb1, 0xab, + 0xd3, 0xc5, 0xce, 0xac, 0x79, 0xe4, 0xd1, 0xc0, 0x61, 0x4b, 0x22, 0xee, 0xb7, 0x41, 0x55, 0xa7, + 0xc4, 0xf4, 0xea, 0xa5, 0xeb, 0xe5, 0x8d, 0x85, 0x3b, 0x37, 0xd4, 0x7c, 0xee, 0xaa, 0x79, 0xc7, + 0x3a, 0x8b, 0x82, 0xb2, 0xfa, 0x16, 0x33, 0x46, 0x01, 0x47, 0xeb, 0xbf, 0x0a, 0x98, 0xdf, 0xc2, + 0xc4, 0xb4, 0xad, 0x7d, 0x42, 0x2f, 0x60, 0xd1, 0xba, 0xa0, 0xe2, 0x39, 0xa4, 0x2f, 0x16, 0xed, + 0x6b, 0x32, 0xdf, 0x23, 0x77, 0xf6, 0x1d, 0xd2, 0x8f, 0x17, 0x8a, 0x3d, 0x21, 0x6e, 0x0c, 0xdf, + 0x06, 0xb3, 0x1e, 0xc5, 0xd4, 0xf7, 0xf8, 0x32, 0x2d, 0xdc, 0xf9, 0xfa, 0xe9, 0x34, 0x1c, 0xda, + 0x59, 0x12, 0x44, 0xb3, 0xc1, 0x33, 0x12, 0x14, 0xad, 0x7f, 0x96, 0x00, 0x8c, 0xb0, 0x5d, 0xdb, + 0xd2, 0x74, 0xca, 0xf2, 0xf7, 0x75, 0x50, 0xa1, 0x23, 0x87, 0xf0, 0x69, 0x98, 0xef, 0xdc, 0x08, + 0xbd, 0xb8, 0x3f, 0x72, 0xc8, 0xc7, 0x27, 0xcd, 0xb5, 0xbc, 0x05, 0xeb, 0x41, 0xdc, 0x06, 0x6e, + 0x47, 0xfe, 0x95, 0xb8, 0xf5, 0xdd, 0xf4, 0xd0, 0x1f, 0x9f, 0x34, 0x25, 0x87, 0x85, 0x1a, 0x31, + 0xa5, 0x1d, 0x84, 0x43, 0x00, 0x0d, 0xec, 0xd1, 0xfb, 0x2e, 0xb6, 0xbc, 0x60, 0x24, 0xdd, 0x24, + 0x22, 0xf2, 0x97, 0xa6, 0x5b, 0x1e, 0x66, 0xd1, 0xb9, 0x2a, 0xbc, 0x80, 0xdb, 0x39, 0x36, 0x24, + 0x19, 0x01, 0xde, 0x00, 0xb3, 0x2e, 0xc1, 0x9e, 0x6d, 0xd5, 0x2b, 0x3c, 0x8a, 0x68, 0x02, 0x11, + 0x6f, 0x45, 0xa2, 0x17, 0xbe, 0x08, 0xe6, 0x4c, 0xe2, 0x79, 0x78, 0x40, 0xea, 0x55, 0x0e, 0x5c, + 0x16, 0xc0, 0xb9, 0x5e, 0xd0, 0x8c, 0xc2, 0xfe, 0xd6, 0xef, 0x15, 0xb0, 0x18, 0xcd, 0xdc, 0x05, + 0x6c, 0x95, 0x4e, 0x7a, 0xab, 0x3c, 0x7f, 0x6a, 0x9e, 0x14, 0xec, 0x90, 0xf7, 0xcb, 0x09, 0x9f, + 0x59, 0x12, 0xc2, 0x9f, 0x80, 0x9a, 0x47, 0x0c, 0xd2, 0xa7, 0xb6, 0x2b, 0x7c, 0x7e, 0x65, 0x4a, + 0x9f, 0xf1, 0x21, 0x31, 0xf6, 0x85, 0x69, 0xe7, 0x12, 0x73, 0x3a, 0x7c, 0x42, 0x11, 0x25, 0xfc, + 0x11, 0xa8, 0x51, 0x62, 0x3a, 0x06, 0xa6, 0x44, 0x6c, 0x93, 0x54, 0x7e, 0xb3, 0x74, 0x61, 0x64, + 0x7b, 0xb6, 0x76, 0x5f, 0xc0, 0xf8, 0x46, 0x89, 0xe6, 0x21, 0x6c, 0x45, 0x11, 0x0d, 0x3c, 0x06, + 0x4b, 0xbe, 0xa3, 0x31, 0x24, 0x65, 0x47, 0xf7, 0x60, 0x24, 0xd2, 0xe7, 0xe6, 0xa9, 0x13, 0x72, + 0x90, 0x32, 0xe9, 0xac, 0x89, 0x01, 0x96, 0xd2, 0xed, 0x28, 0x43, 0x0d, 0x37, 0xc1, 0xb2, 0xa9, + 0x5b, 0x88, 0x60, 0x6d, 0xb4, 0x4f, 0xfa, 0xb6, 0xa5, 0x79, 0x3c, 0x81, 0xaa, 0x9d, 0x75, 0x41, + 0xb0, 0xdc, 0x4b, 0x77, 0xa3, 0x2c, 0x1e, 0x6e, 0x83, 0xd5, 0xf0, 0x9c, 0xfd, 0x81, 0xee, 0x51, + 0xdb, 0x1d, 0x6d, 0xeb, 0xa6, 0x4e, 0xeb, 0xb3, 0x9c, 0xa7, 0x3e, 0x3e, 0x69, 0xae, 0x22, 0x49, + 0x3f, 0x92, 0x5a, 0xb5, 0x7e, 0x33, 0x0b, 0x96, 0x33, 0xa7, 0x01, 0x7c, 0x00, 0xd6, 0xfa, 0xbe, + 0xeb, 0x12, 0x8b, 0xee, 0xf8, 0xe6, 0x21, 0x71, 0xf7, 0xfb, 0x47, 0x44, 0xf3, 0x0d, 0xa2, 0xf1, + 0x15, 0xad, 0x76, 0x1a, 0xc2, 0xd7, 0xb5, 0xae, 0x14, 0x85, 0x0a, 0xac, 0xe1, 0x0f, 0x01, 0xb4, + 0x78, 0x53, 0x4f, 0xf7, 0xbc, 0x88, 0xb3, 0xc4, 0x39, 0xa3, 0x0d, 0xb8, 0x93, 0x43, 0x20, 0x89, + 0x15, 0xf3, 0x51, 0x23, 0x9e, 0xee, 0x12, 0x2d, 0xeb, 0x63, 0x39, 0xed, 0xe3, 0x96, 0x14, 0x85, + 0x0a, 0xac, 0xe1, 0xab, 0x60, 0x21, 0x18, 0x8d, 0xcf, 0xb9, 0x58, 0x9c, 0x15, 0x41, 0xb6, 0xb0, + 0x13, 0x77, 0xa1, 0x24, 0x8e, 0x85, 0x66, 0x1f, 0x7a, 0xc4, 0x1d, 0x12, 0xed, 0xcd, 0x40, 0x03, + 0xb0, 0x42, 0x59, 0xe5, 0x85, 0x32, 0x0a, 0x6d, 0x37, 0x87, 0x40, 0x12, 0x2b, 0x16, 0x5a, 0x90, + 0x35, 0xb9, 0xd0, 0x66, 0xd3, 0xa1, 0x1d, 0x48, 0x51, 0xa8, 0xc0, 0x9a, 0xe5, 0x5e, 0xe0, 0xf2, + 0xe6, 0x10, 0xeb, 0x06, 0x3e, 0x34, 0x48, 0x7d, 0x2e, 0x9d, 0x7b, 0x3b, 0xe9, 0x6e, 0x94, 0xc5, + 0xc3, 0x37, 0xc1, 0x95, 0xa0, 0xe9, 0xc0, 0xc2, 0x11, 0x49, 0x8d, 0x93, 0x3c, 0x27, 0x48, 0xae, + 0xec, 0x64, 0x01, 0x28, 0x6f, 0x03, 0x5f, 0x07, 0x4b, 0x7d, 0xdb, 0x30, 0x78, 0x3e, 0x76, 0x6d, + 0xdf, 0xa2, 0xf5, 0x79, 0xce, 0x02, 0xd9, 0x1e, 0xea, 0xa6, 0x7a, 0x50, 0x06, 0x09, 0x1f, 0x02, + 0xd0, 0x0f, 0xcb, 0x81, 0x57, 0x07, 0xc5, 0x85, 0x3e, 0x5f, 0x87, 0xe2, 0x02, 0x1c, 0x35, 0x79, + 0x28, 0xc1, 0xd6, 0x7a, 0x5f, 0x01, 0xeb, 0x05, 0x7b, 0x1c, 0x7e, 0x2f, 0x55, 0xf5, 0x6e, 0x66, + 0xaa, 0xde, 0xb5, 0x02, 0xb3, 0x44, 0xe9, 0xeb, 0x83, 0x45, 0xa6, 0x3b, 0x74, 0x6b, 0x10, 0x40, + 0xc4, 0x09, 0xf6, 0x92, 0xcc, 0x77, 0x94, 0x04, 0xc6, 0xc7, 0xf0, 0x95, 0xf1, 0x49, 0x73, 0x31, + 0xd5, 0x87, 0xd2, 0x9c, 0xad, 0x5f, 0x96, 0x00, 0xd8, 0x22, 0x8e, 0x61, 0x8f, 0x4c, 0x62, 0x5d, + 0x84, 0x6a, 0xd9, 0x4a, 0xa9, 0x96, 0x96, 0x74, 0x21, 0x22, 0x7f, 0x0a, 0x65, 0xcb, 0x76, 0x46, + 0xb6, 0x7c, 0x63, 0x02, 0xcf, 0xe9, 0xba, 0xe5, 0xef, 0x65, 0xb0, 0x12, 0x83, 0x63, 0xe1, 0x72, + 0x2f, 0xb5, 0x84, 0x2f, 0x64, 0x96, 0x70, 0x5d, 0x62, 0xf2, 0xa9, 0x29, 0x97, 0x77, 0xc1, 0x12, + 0xd3, 0x15, 0xc1, 0xaa, 0x71, 0xd5, 0x32, 0x7b, 0x66, 0xd5, 0x12, 0x55, 0x9d, 0xed, 0x14, 0x13, + 0xca, 0x30, 0x17, 0xa8, 0xa4, 0xb9, 0xcf, 0xa3, 0x4a, 0xfa, 0x83, 0x02, 0x96, 0xe2, 0x65, 0xba, + 0x00, 0x99, 0xd4, 0x4d, 0xcb, 0xa4, 0xc6, 0xe9, 0x79, 0x59, 0xa0, 0x93, 0xfe, 0x56, 0x49, 0x7a, + 0xcd, 0x85, 0xd2, 0x06, 0x7b, 0xa1, 0x72, 0x0c, 0xbd, 0x8f, 0x3d, 0x51, 0x56, 0x2f, 0x05, 0x2f, + 0x53, 0x41, 0x1b, 0x8a, 0x7a, 0x53, 0x92, 0xaa, 0xf4, 0xe9, 0x4a, 0xaa, 0xf2, 0x27, 0x23, 0xa9, + 0xee, 0x83, 0x9a, 0x17, 0x8a, 0xa9, 0x0a, 0xa7, 0xbc, 0x31, 0x69, 0x3b, 0x0b, 0x1d, 0x15, 0xb1, + 0x46, 0x0a, 0x2a, 0x62, 0x92, 0x69, 0xa7, 0xea, 0x67, 0xa9, 0x9d, 0x58, 0x7a, 0x3b, 0xd8, 0xf7, + 0x88, 0xc6, 0xb7, 0x52, 0x2d, 0x4e, 0xef, 0x3d, 0xde, 0x8a, 0x44, 0x2f, 0x3c, 0x00, 0xeb, 0x8e, + 0x6b, 0x0f, 0x5c, 0xe2, 0x79, 0x5b, 0x04, 0x6b, 0x86, 0x6e, 0x91, 0x30, 0x80, 0xa0, 0xea, 0x5d, + 0x1b, 0x9f, 0x34, 0xd7, 0xf7, 0xe4, 0x10, 0x54, 0x64, 0xdb, 0xfa, 0x53, 0x05, 0x5c, 0xce, 0x9e, + 0x88, 0x05, 0x42, 0x44, 0x39, 0x97, 0x10, 0x79, 0x39, 0x91, 0xa2, 0x81, 0x4a, 0x4b, 0xbc, 0xf3, + 0xe7, 0xd2, 0x74, 0x13, 0x2c, 0x0b, 0xe1, 0x11, 0x76, 0x0a, 0x29, 0x16, 0x2d, 0xcf, 0x41, 0xba, + 0x1b, 0x65, 0xf1, 0xf0, 0x1e, 0x58, 0x74, 0xb9, 0xb6, 0x0a, 0x09, 0x02, 0x7d, 0xf2, 0x15, 0x41, + 0xb0, 0x88, 0x92, 0x9d, 0x28, 0x8d, 0x65, 0xda, 0x24, 0x96, 0x1c, 0x21, 0x41, 0x25, 0xad, 0x4d, + 0x36, 0xb3, 0x00, 0x94, 0xb7, 0x81, 0x3d, 0xb0, 0xe2, 0x5b, 0x79, 0xaa, 0x20, 0xd7, 0xae, 0x09, + 0xaa, 0x95, 0x83, 0x3c, 0x04, 0xc9, 0xec, 0xe0, 0x8f, 0x53, 0x72, 0x65, 0x96, 0x9f, 0x22, 0x2f, + 0x9c, 0xbe, 0x1d, 0xa6, 0xd6, 0x2b, 0x12, 0x1d, 0x55, 0x9b, 0x56, 0x47, 0xb5, 0xde, 0x53, 0x00, + 0xcc, 0x6f, 0xc1, 0x89, 0x2f, 0xf7, 0x39, 0x8b, 0x44, 0x89, 0xd4, 0xe4, 0x0a, 0xe7, 0xe6, 0x64, + 0x85, 0x13, 0x9f, 0xa0, 0xd3, 0x49, 0x1c, 0x31, 0xbd, 0x17, 0x73, 0x31, 0x33, 0x85, 0xc4, 0x89, + 0xfd, 0x79, 0x36, 0x89, 0x93, 0xe0, 0x39, 0x5d, 0xe2, 0xfc, 0xab, 0x04, 0x56, 0x62, 0xf0, 0xd4, + 0x12, 0x47, 0x62, 0xf2, 0xe5, 0xe5, 0xcc, 0x74, 0xb2, 0x23, 0x9e, 0xba, 0xff, 0x13, 0xd9, 0x11, + 0x3b, 0x54, 0x20, 0x3b, 0x7e, 0x57, 0x4a, 0x7a, 0x7d, 0x46, 0xd9, 0xf1, 0x09, 0x5c, 0x55, 0x7c, + 0xee, 0x94, 0x4b, 0xeb, 0xcf, 0x65, 0x70, 0x39, 0xbb, 0x05, 0x53, 0x75, 0x50, 0x99, 0x58, 0x07, + 0xf7, 0xc0, 0xea, 0x23, 0xdf, 0x30, 0x46, 0x3c, 0x86, 0x44, 0x31, 0x0c, 0x2a, 0xe8, 0x57, 0x85, + 0xe5, 0xea, 0xf7, 0x25, 0x18, 0x24, 0xb5, 0xcc, 0x97, 0xc5, 0xca, 0xb3, 0x96, 0xc5, 0xea, 0x39, + 0xca, 0xa2, 0x5c, 0x59, 0x94, 0xcf, 0xa5, 0x2c, 0xa6, 0xae, 0x89, 0x92, 0xe3, 0x6a, 0xe2, 0x3b, + 0xfc, 0x58, 0x01, 0x6b, 0xf2, 0xd7, 0x67, 0x68, 0x80, 0x25, 0x13, 0x3f, 0x4e, 0x5e, 0x5e, 0x4c, + 0x2a, 0x18, 0x3e, 0xd5, 0x0d, 0x35, 0xf8, 0xba, 0xa3, 0xbe, 0x65, 0xd1, 0x5d, 0x77, 0x9f, 0xba, + 0xba, 0x35, 0x08, 0x0a, 0x6c, 0x2f, 0xc5, 0x85, 0x32, 0xdc, 0xf0, 0x21, 0xa8, 0x99, 0xf8, 0xf1, + 0xbe, 0xef, 0x0e, 0xc2, 0x42, 0x78, 0xf6, 0x71, 0x78, 0xee, 0xf7, 0x04, 0x0b, 0x8a, 0xf8, 0x5a, + 0x1f, 0x29, 0x60, 0xbd, 0xa0, 0x82, 0x7e, 0x81, 0xa2, 0xdc, 0x05, 0xd7, 0x53, 0x41, 0xb2, 0x0d, + 0x49, 0x1e, 0xf9, 0x06, 0xdf, 0x9b, 0x42, 0xaf, 0xdc, 0x04, 0xf3, 0x0e, 0x76, 0xa9, 0x1e, 0x09, + 0xdd, 0x6a, 0x67, 0x71, 0x7c, 0xd2, 0x9c, 0xdf, 0x0b, 0x1b, 0x51, 0xdc, 0xdf, 0xfa, 0x55, 0x09, + 0x2c, 0x24, 0x48, 0x2e, 0x40, 0x3b, 0xbc, 0x91, 0xd2, 0x0e, 0xd2, 0xaf, 0x31, 0xc9, 0xa8, 0x8a, + 0xc4, 0x43, 0x2f, 0x23, 0x1e, 0xbe, 0x39, 0x89, 0xe8, 0x74, 0xf5, 0xf0, 0xef, 0x12, 0x58, 0x4d, + 0xa0, 0x63, 0xf9, 0xf0, 0x9d, 0x94, 0x7c, 0xd8, 0xc8, 0xc8, 0x87, 0xba, 0xcc, 0xe6, 0x4b, 0xfd, + 0x30, 0x59, 0x3f, 0xfc, 0x51, 0x01, 0xcb, 0x89, 0xb9, 0xbb, 0x00, 0x01, 0xb1, 0x95, 0x16, 0x10, + 0xcd, 0x09, 0xf9, 0x52, 0xa0, 0x20, 0x7e, 0x3d, 0x9b, 0xf2, 0xfb, 0x0b, 0x7f, 0x73, 0xf1, 0x73, + 0xb0, 0x3a, 0xb4, 0x0d, 0xdf, 0x24, 0x5d, 0x03, 0xeb, 0x66, 0x08, 0x60, 0x15, 0x97, 0x4d, 0xe2, + 0x8b, 0x52, 0x7a, 0xe2, 0x7a, 0xba, 0x47, 0x89, 0x45, 0x1f, 0xc4, 0x96, 0x71, 0x9d, 0x7f, 0x20, + 0xa1, 0x43, 0xd2, 0x41, 0xe0, 0xab, 0x60, 0x81, 0x55, 0x4a, 0xbd, 0x4f, 0x76, 0xb0, 0x19, 0xe6, + 0x54, 0xf4, 0xed, 0x61, 0x3f, 0xee, 0x42, 0x49, 0x1c, 0x3c, 0x02, 0x2b, 0x8e, 0xad, 0xf5, 0xb0, + 0x85, 0x07, 0x84, 0x9d, 0xff, 0x7b, 0xb6, 0xa1, 0xf7, 0x47, 0xfc, 0x4e, 0x63, 0xbe, 0xf3, 0x5a, + 0xf8, 0xbe, 0xba, 0x97, 0x87, 0xb0, 0xf7, 0x01, 0x49, 0x33, 0xdf, 0xcf, 0x32, 0x4a, 0x68, 0xe6, + 0x3e, 0x95, 0xcd, 0xe5, 0xfe, 0x5f, 0x20, 0x4b, 0xae, 0x73, 0x7e, 0x2c, 0x2b, 0xba, 0xad, 0xa9, + 0x9d, 0xeb, 0xb6, 0x46, 0xa2, 0x67, 0xe7, 0xcf, 0xa6, 0x67, 0x5b, 0xef, 0x55, 0xc1, 0x95, 0xdc, + 0x19, 0xfb, 0x19, 0x5e, 0xb9, 0xe4, 0x84, 0x61, 0xf9, 0x0c, 0xc2, 0x70, 0x13, 0x2c, 0x8b, 0xef, + 0x74, 0x19, 0x5d, 0x19, 0xcd, 0x47, 0x37, 0xdd, 0x8d, 0xb2, 0x78, 0xd9, 0x95, 0x4f, 0xf5, 0x8c, + 0x57, 0x3e, 0x49, 0x2f, 0xc4, 0xdf, 0x4b, 0x82, 0xc4, 0xcd, 0x7b, 0x21, 0xfe, 0x65, 0x92, 0xc5, + 0xc3, 0xef, 0x86, 0x59, 0x19, 0x31, 0xcc, 0x71, 0x86, 0x4c, 0x9a, 0x45, 0x04, 0x19, 0xf4, 0x33, + 0x7d, 0x8b, 0x7a, 0x47, 0xf2, 0x2d, 0x6a, 0x63, 0xc2, 0x6e, 0x98, 0xfe, 0x76, 0x47, 0xaa, 0xdd, + 0x17, 0xce, 0xae, 0xdd, 0x5b, 0x7f, 0x55, 0xc0, 0x73, 0x85, 0xfb, 0x11, 0x6e, 0xa6, 0x6a, 0xfe, + 0xad, 0x4c, 0xcd, 0x7f, 0xbe, 0xd0, 0x30, 0x51, 0xf8, 0x4d, 0xf9, 0xc5, 0xcf, 0xdd, 0x89, 0x17, + 0x3f, 0x12, 0x45, 0x37, 0xf9, 0x06, 0xa8, 0xb3, 0xf1, 0xe4, 0x69, 0x63, 0xe6, 0x83, 0xa7, 0x8d, + 0x99, 0x0f, 0x9f, 0x36, 0x66, 0x7e, 0x31, 0x6e, 0x28, 0x4f, 0xc6, 0x0d, 0xe5, 0x83, 0x71, 0x43, + 0xf9, 0x70, 0xdc, 0x50, 0xfe, 0x31, 0x6e, 0x28, 0xbf, 0xfd, 0xa8, 0x31, 0xf3, 0xb0, 0x34, 0xbc, + 0xfd, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8a, 0x69, 0x8a, 0x39, 0xfa, 0x26, 0x00, 0x00, } func (m *ControllerRevision) Marshal() (dAtA []byte, err error) { @@ -2310,6 +2311,9 @@ _ = i var l int _ = l + i = encodeVarintGenerated(dAtA, i, uint64(m.MinReadySeconds)) + i-- + dAtA[i] = 0x48 if m.RevisionHistoryLimit != nil { i = encodeVarintGenerated(dAtA, i, uint64(*m.RevisionHistoryLimit)) i-- @@ -2399,6 +2403,9 @@ _ = i var l int _ = l + i = encodeVarintGenerated(dAtA, i, uint64(m.AvailableReplicas)) + i-- + dAtA[i] = 0x58 if len(m.Conditions) > 0 { for iNdEx := len(m.Conditions) - 1; iNdEx >= 0; iNdEx-- { { @@ -2978,6 +2985,7 @@ if m.RevisionHistoryLimit != nil { n += 1 + sovGenerated(uint64(*m.RevisionHistoryLimit)) } + n += 1 + sovGenerated(uint64(m.MinReadySeconds)) return n } @@ -3005,6 +3013,7 @@ n += 1 + l + sovGenerated(uint64(l)) } } + n += 1 + sovGenerated(uint64(m.AvailableReplicas)) return n } @@ -3408,6 +3417,7 @@ `PodManagementPolicy:` + fmt.Sprintf("%v", this.PodManagementPolicy) + `,`, `UpdateStrategy:` + strings.Replace(strings.Replace(this.UpdateStrategy.String(), "StatefulSetUpdateStrategy", "StatefulSetUpdateStrategy", 1), `&`, ``, 1) + `,`, `RevisionHistoryLimit:` + valueToStringGenerated(this.RevisionHistoryLimit) + `,`, + `MinReadySeconds:` + fmt.Sprintf("%v", this.MinReadySeconds) + `,`, `}`, }, "") return s @@ -3431,6 +3441,7 @@ `UpdateRevision:` + fmt.Sprintf("%v", this.UpdateRevision) + `,`, `CollisionCount:` + valueToStringGenerated(this.CollisionCount) + `,`, `Conditions:` + repeatedStringForConditions + `,`, + `AvailableReplicas:` + fmt.Sprintf("%v", this.AvailableReplicas) + `,`, `}`, }, "") return s @@ -7719,6 +7730,25 @@ } } m.RevisionHistoryLimit = &v + case 9: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MinReadySeconds", wireType) + } + m.MinReadySeconds = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MinReadySeconds |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -7982,6 +8012,25 @@ return err } iNdEx = postIndex + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AvailableReplicas", wireType) + } + m.AvailableReplicas = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.AvailableReplicas |= int32(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/generated.proto libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/generated.proto --- libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/generated.proto 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/generated.proto 2021-12-26 00:45:51.000000000 +0000 @@ -219,7 +219,8 @@ // Deployment enables declarative updates for Pods and ReplicaSets. message Deployment { - // Standard object metadata. + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; @@ -366,7 +367,8 @@ message ReplicaSet { // If the Labels of a ReplicaSet are empty, they are defaulted to // be the same as the Pod(s) that the ReplicaSet manages. - // Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; @@ -479,7 +481,7 @@ // The maximum number of DaemonSet pods that can be unavailable during the // update. Value can be an absolute number (ex: 5) or a percentage of total // number of DaemonSet pods at the start of the update (ex: 10%). Absolute - // number is calculated from percentage by rounding down to a minimum of one. + // number is calculated from percentage by rounding up. // This cannot be 0 if MaxSurge is 0 // Default value is 1. // Example: when this is set to 30%, at most 30% of the total number of nodes @@ -511,7 +513,7 @@ // daemonset on any given node can double if the readiness check fails, and // so resource intensive daemonsets should take into account that they may // cause evictions during disruption. - // This is an alpha field and requires enabling DaemonSetUpdateSurge feature gate. + // This is beta field and enabled/disabled by DaemonSetUpdateSurge feature gate. // +optional optional k8s.io.apimachinery.pkg.util.intstr.IntOrString maxSurge = 2; } @@ -562,6 +564,8 @@ // The StatefulSet guarantees that a given network identity will always // map to the same storage identity. message StatefulSet { + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; @@ -598,9 +602,12 @@ // StatefulSetList is a collection of StatefulSets. message StatefulSetList { + // Standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.ListMeta metadata = 1; + // Items is the list of stateful sets. repeated StatefulSet items = 2; } @@ -663,6 +670,13 @@ // consists of all revisions not represented by a currently applied // StatefulSetSpec version. The default value is 10. optional int32 revisionHistoryLimit = 8; + + // Minimum number of seconds for which a newly created pod should be ready + // without any of its container crashing for it to be considered available. + // Defaults to 0 (pod will be considered available as soon as it is ready) + // This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate. + // +optional + optional int32 minReadySeconds = 9; } // StatefulSetStatus represents the current state of a StatefulSet. @@ -705,6 +719,12 @@ // +patchMergeKey=type // +patchStrategy=merge repeated StatefulSetCondition conditions = 10; + + // Total number of available pods (ready for at least minReadySeconds) targeted by this statefulset. + // This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate. + // Remove omitempty when graduating to beta + // +optional + optional int32 availableReplicas = 11; } // StatefulSetUpdateStrategy indicates the strategy that the StatefulSet diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/types.go libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/types.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -34,6 +34,7 @@ // +genclient // +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale // +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale +// +genclient:method=ApplyScale,verb=apply,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // StatefulSet represents a set of pods with consistent identities. @@ -44,6 +45,8 @@ // map to the same storage identity. type StatefulSet struct { metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` @@ -172,6 +175,13 @@ // consists of all revisions not represented by a currently applied // StatefulSetSpec version. The default value is 10. RevisionHistoryLimit *int32 `json:"revisionHistoryLimit,omitempty" protobuf:"varint,8,opt,name=revisionHistoryLimit"` + + // Minimum number of seconds for which a newly created pod should be ready + // without any of its container crashing for it to be considered available. + // Defaults to 0 (pod will be considered available as soon as it is ready) + // This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate. + // +optional + MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,9,opt,name=minReadySeconds"` } // StatefulSetStatus represents the current state of a StatefulSet. @@ -214,6 +224,12 @@ // +patchMergeKey=type // +patchStrategy=merge Conditions []StatefulSetCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,10,rep,name=conditions"` + + // Total number of available pods (ready for at least minReadySeconds) targeted by this statefulset. + // This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate. + // Remove omitempty when graduating to beta + // +optional + AvailableReplicas int32 `json:"availableReplicas,omitempty" protobuf:"varint,11,opt,name=availableReplicas"` } type StatefulSetConditionType string @@ -240,20 +256,26 @@ // StatefulSetList is a collection of StatefulSets. type StatefulSetList struct { metav1.TypeMeta `json:",inline"` + // Standard list's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - Items []StatefulSet `json:"items" protobuf:"bytes,2,rep,name=items"` + + // Items is the list of stateful sets. + Items []StatefulSet `json:"items" protobuf:"bytes,2,rep,name=items"` } // +genclient // +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale // +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale +// +genclient:method=ApplyScale,verb=apply,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Deployment enables declarative updates for Pods and ReplicaSets. type Deployment struct { metav1.TypeMeta `json:",inline"` - // Standard object metadata. + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` @@ -488,7 +510,7 @@ // The maximum number of DaemonSet pods that can be unavailable during the // update. Value can be an absolute number (ex: 5) or a percentage of total // number of DaemonSet pods at the start of the update (ex: 10%). Absolute - // number is calculated from percentage by rounding down to a minimum of one. + // number is calculated from percentage by rounding up. // This cannot be 0 if MaxSurge is 0 // Default value is 1. // Example: when this is set to 30%, at most 30% of the total number of nodes @@ -520,7 +542,7 @@ // daemonset on any given node can double if the readiness check fails, and // so resource intensive daemonsets should take into account that they may // cause evictions during disruption. - // This is an alpha field and requires enabling DaemonSetUpdateSurge feature gate. + // This is beta field and enabled/disabled by DaemonSetUpdateSurge feature gate. // +optional MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty" protobuf:"bytes,2,opt,name=maxSurge"` } @@ -682,6 +704,7 @@ // +genclient // +genclient:method=GetScale,verb=get,subresource=scale,result=k8s.io/api/autoscaling/v1.Scale // +genclient:method=UpdateScale,verb=update,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale +// +genclient:method=ApplyScale,verb=apply,subresource=scale,input=k8s.io/api/autoscaling/v1.Scale,result=k8s.io/api/autoscaling/v1.Scale // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // ReplicaSet ensures that a specified number of pod replicas are running at any given time. @@ -690,7 +713,8 @@ // If the Labels of a ReplicaSet are empty, they are defaulted to // be the same as the Pod(s) that the ReplicaSet manages. - // Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata // +optional metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/types_swagger_doc_generated.go libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/types_swagger_doc_generated.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/apps/v1/types_swagger_doc_generated.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/apps/v1/types_swagger_doc_generated.go 2021-12-26 00:45:51.000000000 +0000 @@ -125,7 +125,7 @@ var map_Deployment = map[string]string{ "": "Deployment enables declarative updates for Pods and ReplicaSets.", - "metadata": "Standard object metadata.", + "metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", "spec": "Specification of the desired behavior of the Deployment.", "status": "Most recently observed status of the Deployment.", } @@ -262,8 +262,8 @@ var map_RollingUpdateDaemonSet = map[string]string{ "": "Spec to control the desired behavior of daemon set rolling update.", - "maxUnavailable": "The maximum number of DaemonSet pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total number of DaemonSet pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding down to a minimum of one. This cannot be 0 if MaxSurge is 0 Default value is 1. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their pods stopped for an update at any given time. The update starts by stopping at most 30% of those DaemonSet pods and then brings up new DaemonSet pods in their place. Once the new pods are available, it then proceeds onto other DaemonSet pods, thus ensuring that at least 70% of original number of DaemonSet pods are available at all times during the update.", - "maxSurge": "The maximum number of nodes with an existing available DaemonSet pod that can have an updated DaemonSet pod during during an update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up to a minimum of 1. Default value is 0. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their a new pod created before the old pod is marked as deleted. The update starts by launching new pods on 30% of nodes. Once an updated pod is available (Ready for at least minReadySeconds) the old DaemonSet pod on that node is marked deleted. If the old pod becomes unavailable for any reason (Ready transitions to false, is evicted, or is drained) an updated pod is immediatedly created on that node without considering surge limits. Allowing surge implies the possibility that the resources consumed by the daemonset on any given node can double if the readiness check fails, and so resource intensive daemonsets should take into account that they may cause evictions during disruption. This is an alpha field and requires enabling DaemonSetUpdateSurge feature gate.", + "maxUnavailable": "The maximum number of DaemonSet pods that can be unavailable during the update. Value can be an absolute number (ex: 5) or a percentage of total number of DaemonSet pods at the start of the update (ex: 10%). Absolute number is calculated from percentage by rounding up. This cannot be 0 if MaxSurge is 0 Default value is 1. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their pods stopped for an update at any given time. The update starts by stopping at most 30% of those DaemonSet pods and then brings up new DaemonSet pods in their place. Once the new pods are available, it then proceeds onto other DaemonSet pods, thus ensuring that at least 70% of original number of DaemonSet pods are available at all times during the update.", + "maxSurge": "The maximum number of nodes with an existing available DaemonSet pod that can have an updated DaemonSet pod during during an update. Value can be an absolute number (ex: 5) or a percentage of desired pods (ex: 10%). This can not be 0 if MaxUnavailable is 0. Absolute number is calculated from percentage by rounding up to a minimum of 1. Default value is 0. Example: when this is set to 30%, at most 30% of the total number of nodes that should be running the daemon pod (i.e. status.desiredNumberScheduled) can have their a new pod created before the old pod is marked as deleted. The update starts by launching new pods on 30% of nodes. Once an updated pod is available (Ready for at least minReadySeconds) the old DaemonSet pod on that node is marked deleted. If the old pod becomes unavailable for any reason (Ready transitions to false, is evicted, or is drained) an updated pod is immediatedly created on that node without considering surge limits. Allowing surge implies the possibility that the resources consumed by the daemonset on any given node can double if the readiness check fails, and so resource intensive daemonsets should take into account that they may cause evictions during disruption. This is beta field and enabled/disabled by DaemonSetUpdateSurge feature gate.", } func (RollingUpdateDaemonSet) SwaggerDoc() map[string]string { @@ -290,9 +290,10 @@ } var map_StatefulSet = map[string]string{ - "": "StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", - "spec": "Spec defines the desired identities of pods in this set.", - "status": "Status is the current status of Pods in this StatefulSet. This data may be out of date by some window of time.", + "": "StatefulSet represents a set of pods with consistent identities. Identities are defined as:\n - Network: A single stable DNS and hostname.\n - Storage: As many VolumeClaims as requested.\nThe StatefulSet guarantees that a given network identity will always map to the same storage identity.", + "metadata": "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "spec": "Spec defines the desired identities of pods in this set.", + "status": "Status is the current status of Pods in this StatefulSet. This data may be out of date by some window of time.", } func (StatefulSet) SwaggerDoc() map[string]string { @@ -313,7 +314,9 @@ } var map_StatefulSetList = map[string]string{ - "": "StatefulSetList is a collection of StatefulSets.", + "": "StatefulSetList is a collection of StatefulSets.", + "metadata": "Standard list's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata", + "items": "Items is the list of stateful sets.", } func (StatefulSetList) SwaggerDoc() map[string]string { @@ -330,6 +333,7 @@ "podManagementPolicy": "podManagementPolicy controls how pods are created during initial scale up, when replacing pods on nodes, or when scaling down. The default policy is `OrderedReady`, where pods are created in increasing order (pod-0, then pod-1, etc) and the controller will wait until each pod is ready before continuing. When scaling down, the pods are removed in the opposite order. The alternative policy is `Parallel` which will create pods in parallel to match the desired scale without waiting, and on scale down will delete all pods at once.", "updateStrategy": "updateStrategy indicates the StatefulSetUpdateStrategy that will be employed to update Pods in the StatefulSet when a revision is made to Template.", "revisionHistoryLimit": "revisionHistoryLimit is the maximum number of revisions that will be maintained in the StatefulSet's revision history. The revision history consists of all revisions not represented by a currently applied StatefulSetSpec version. The default value is 10.", + "minReadySeconds": "Minimum number of seconds for which a newly created pod should be ready without any of its container crashing for it to be considered available. Defaults to 0 (pod will be considered available as soon as it is ready) This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate.", } func (StatefulSetSpec) SwaggerDoc() map[string]string { @@ -347,6 +351,7 @@ "updateRevision": "updateRevision, if not empty, indicates the version of the StatefulSet used to generate Pods in the sequence [replicas-updatedReplicas,replicas)", "collisionCount": "collisionCount is the count of hash collisions for the StatefulSet. The StatefulSet controller uses this field as a collision avoidance mechanism when it needs to create the name for the newest ControllerRevision.", "conditions": "Represents the latest available observations of a statefulset's current state.", + "availableReplicas": "Total number of available pods (ready for at least minReadySeconds) targeted by this statefulset. This is an alpha field and requires enabling StatefulSetMinReadySeconds feature gate. Remove omitempty when graduating to beta", } func (StatefulSetStatus) SwaggerDoc() map[string]string { diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/annotation_key_constants.go libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/annotation_key_constants.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/annotation_key_constants.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/annotation_key_constants.go 2021-12-26 00:45:51.000000000 +0000 @@ -23,9 +23,6 @@ // webhook backend fails. ImagePolicyFailedOpenKey string = "alpha.image-policy.k8s.io/failed-open" - // PodPresetOptOutAnnotationKey represents the annotation key for a pod to exempt itself from pod preset manipulation - PodPresetOptOutAnnotationKey string = "podpreset.admission.kubernetes.io/exclude" - // MirrorAnnotationKey represents the annotation key set by kubelets when creating mirror pods MirrorPodAnnotationKey string = "kubernetes.io/config.mirror" @@ -144,11 +141,11 @@ // pod deletion order. // The implicit deletion cost for pods that don't set the annotation is 0, negative values are permitted. // - // This annotation is alpha-level and is only honored when PodDeletionCost feature is enabled. + // This annotation is beta-level and is only honored when PodDeletionCost feature is enabled. PodDeletionCost = "controller.kubernetes.io/pod-deletion-cost" // AnnotationTopologyAwareHints can be used to enable or disable Topology - // Aware Hints for a Service. This may be set to "auto" or "disabled". Any - // other value is treated as "disabled". + // Aware Hints for a Service. This may be set to "Auto" or "Disabled". Any + // other value is treated as "Disabled". AnnotationTopologyAwareHints = "service.kubernetes.io/topology-aware-hints" ) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/generated.pb.go libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/generated.pb.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/generated.pb.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/generated.pb.go 2021-12-26 00:45:51.000000000 +0000 @@ -1421,38 +1421,10 @@ var xxx_messageInfo_EphemeralContainerCommon proto.InternalMessageInfo -func (m *EphemeralContainers) Reset() { *m = EphemeralContainers{} } -func (*EphemeralContainers) ProtoMessage() {} -func (*EphemeralContainers) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{49} -} -func (m *EphemeralContainers) XXX_Unmarshal(b []byte) error { - return m.Unmarshal(b) -} -func (m *EphemeralContainers) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - b = b[:cap(b)] - n, err := m.MarshalToSizedBuffer(b) - if err != nil { - return nil, err - } - return b[:n], nil -} -func (m *EphemeralContainers) XXX_Merge(src proto.Message) { - xxx_messageInfo_EphemeralContainers.Merge(m, src) -} -func (m *EphemeralContainers) XXX_Size() int { - return m.Size() -} -func (m *EphemeralContainers) XXX_DiscardUnknown() { - xxx_messageInfo_EphemeralContainers.DiscardUnknown(m) -} - -var xxx_messageInfo_EphemeralContainers proto.InternalMessageInfo - func (m *EphemeralVolumeSource) Reset() { *m = EphemeralVolumeSource{} } func (*EphemeralVolumeSource) ProtoMessage() {} func (*EphemeralVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{50} + return fileDescriptor_83c10c24ec417dc9, []int{49} } func (m *EphemeralVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1480,7 +1452,7 @@ func (m *Event) Reset() { *m = Event{} } func (*Event) ProtoMessage() {} func (*Event) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{51} + return fileDescriptor_83c10c24ec417dc9, []int{50} } func (m *Event) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1508,7 +1480,7 @@ func (m *EventList) Reset() { *m = EventList{} } func (*EventList) ProtoMessage() {} func (*EventList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{52} + return fileDescriptor_83c10c24ec417dc9, []int{51} } func (m *EventList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1536,7 +1508,7 @@ func (m *EventSeries) Reset() { *m = EventSeries{} } func (*EventSeries) ProtoMessage() {} func (*EventSeries) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{53} + return fileDescriptor_83c10c24ec417dc9, []int{52} } func (m *EventSeries) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1564,7 +1536,7 @@ func (m *EventSource) Reset() { *m = EventSource{} } func (*EventSource) ProtoMessage() {} func (*EventSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{54} + return fileDescriptor_83c10c24ec417dc9, []int{53} } func (m *EventSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1592,7 +1564,7 @@ func (m *ExecAction) Reset() { *m = ExecAction{} } func (*ExecAction) ProtoMessage() {} func (*ExecAction) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{55} + return fileDescriptor_83c10c24ec417dc9, []int{54} } func (m *ExecAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1620,7 +1592,7 @@ func (m *FCVolumeSource) Reset() { *m = FCVolumeSource{} } func (*FCVolumeSource) ProtoMessage() {} func (*FCVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{56} + return fileDescriptor_83c10c24ec417dc9, []int{55} } func (m *FCVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1620,7 @@ func (m *FlexPersistentVolumeSource) Reset() { *m = FlexPersistentVolumeSource{} } func (*FlexPersistentVolumeSource) ProtoMessage() {} func (*FlexPersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{57} + return fileDescriptor_83c10c24ec417dc9, []int{56} } func (m *FlexPersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1676,7 +1648,7 @@ func (m *FlexVolumeSource) Reset() { *m = FlexVolumeSource{} } func (*FlexVolumeSource) ProtoMessage() {} func (*FlexVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{58} + return fileDescriptor_83c10c24ec417dc9, []int{57} } func (m *FlexVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1704,7 +1676,7 @@ func (m *FlockerVolumeSource) Reset() { *m = FlockerVolumeSource{} } func (*FlockerVolumeSource) ProtoMessage() {} func (*FlockerVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{59} + return fileDescriptor_83c10c24ec417dc9, []int{58} } func (m *FlockerVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1732,7 +1704,7 @@ func (m *GCEPersistentDiskVolumeSource) Reset() { *m = GCEPersistentDiskVolumeSource{} } func (*GCEPersistentDiskVolumeSource) ProtoMessage() {} func (*GCEPersistentDiskVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{60} + return fileDescriptor_83c10c24ec417dc9, []int{59} } func (m *GCEPersistentDiskVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1760,7 +1732,7 @@ func (m *GitRepoVolumeSource) Reset() { *m = GitRepoVolumeSource{} } func (*GitRepoVolumeSource) ProtoMessage() {} func (*GitRepoVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{61} + return fileDescriptor_83c10c24ec417dc9, []int{60} } func (m *GitRepoVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1788,7 +1760,7 @@ func (m *GlusterfsPersistentVolumeSource) Reset() { *m = GlusterfsPersistentVolumeSource{} } func (*GlusterfsPersistentVolumeSource) ProtoMessage() {} func (*GlusterfsPersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{62} + return fileDescriptor_83c10c24ec417dc9, []int{61} } func (m *GlusterfsPersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1816,7 +1788,7 @@ func (m *GlusterfsVolumeSource) Reset() { *m = GlusterfsVolumeSource{} } func (*GlusterfsVolumeSource) ProtoMessage() {} func (*GlusterfsVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{63} + return fileDescriptor_83c10c24ec417dc9, []int{62} } func (m *GlusterfsVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1844,7 +1816,7 @@ func (m *HTTPGetAction) Reset() { *m = HTTPGetAction{} } func (*HTTPGetAction) ProtoMessage() {} func (*HTTPGetAction) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{64} + return fileDescriptor_83c10c24ec417dc9, []int{63} } func (m *HTTPGetAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1872,7 +1844,7 @@ func (m *HTTPHeader) Reset() { *m = HTTPHeader{} } func (*HTTPHeader) ProtoMessage() {} func (*HTTPHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{65} + return fileDescriptor_83c10c24ec417dc9, []int{64} } func (m *HTTPHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1900,7 +1872,7 @@ func (m *Handler) Reset() { *m = Handler{} } func (*Handler) ProtoMessage() {} func (*Handler) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{66} + return fileDescriptor_83c10c24ec417dc9, []int{65} } func (m *Handler) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1928,7 +1900,7 @@ func (m *HostAlias) Reset() { *m = HostAlias{} } func (*HostAlias) ProtoMessage() {} func (*HostAlias) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{67} + return fileDescriptor_83c10c24ec417dc9, []int{66} } func (m *HostAlias) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1956,7 +1928,7 @@ func (m *HostPathVolumeSource) Reset() { *m = HostPathVolumeSource{} } func (*HostPathVolumeSource) ProtoMessage() {} func (*HostPathVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{68} + return fileDescriptor_83c10c24ec417dc9, []int{67} } func (m *HostPathVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1984,7 +1956,7 @@ func (m *ISCSIPersistentVolumeSource) Reset() { *m = ISCSIPersistentVolumeSource{} } func (*ISCSIPersistentVolumeSource) ProtoMessage() {} func (*ISCSIPersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{69} + return fileDescriptor_83c10c24ec417dc9, []int{68} } func (m *ISCSIPersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2012,7 +1984,7 @@ func (m *ISCSIVolumeSource) Reset() { *m = ISCSIVolumeSource{} } func (*ISCSIVolumeSource) ProtoMessage() {} func (*ISCSIVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{70} + return fileDescriptor_83c10c24ec417dc9, []int{69} } func (m *ISCSIVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2040,7 +2012,7 @@ func (m *KeyToPath) Reset() { *m = KeyToPath{} } func (*KeyToPath) ProtoMessage() {} func (*KeyToPath) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{71} + return fileDescriptor_83c10c24ec417dc9, []int{70} } func (m *KeyToPath) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2068,7 +2040,7 @@ func (m *Lifecycle) Reset() { *m = Lifecycle{} } func (*Lifecycle) ProtoMessage() {} func (*Lifecycle) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{72} + return fileDescriptor_83c10c24ec417dc9, []int{71} } func (m *Lifecycle) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2096,7 +2068,7 @@ func (m *LimitRange) Reset() { *m = LimitRange{} } func (*LimitRange) ProtoMessage() {} func (*LimitRange) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{73} + return fileDescriptor_83c10c24ec417dc9, []int{72} } func (m *LimitRange) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2124,7 +2096,7 @@ func (m *LimitRangeItem) Reset() { *m = LimitRangeItem{} } func (*LimitRangeItem) ProtoMessage() {} func (*LimitRangeItem) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{74} + return fileDescriptor_83c10c24ec417dc9, []int{73} } func (m *LimitRangeItem) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2152,7 +2124,7 @@ func (m *LimitRangeList) Reset() { *m = LimitRangeList{} } func (*LimitRangeList) ProtoMessage() {} func (*LimitRangeList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{75} + return fileDescriptor_83c10c24ec417dc9, []int{74} } func (m *LimitRangeList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2180,7 +2152,7 @@ func (m *LimitRangeSpec) Reset() { *m = LimitRangeSpec{} } func (*LimitRangeSpec) ProtoMessage() {} func (*LimitRangeSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{76} + return fileDescriptor_83c10c24ec417dc9, []int{75} } func (m *LimitRangeSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2208,7 +2180,7 @@ func (m *List) Reset() { *m = List{} } func (*List) ProtoMessage() {} func (*List) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{77} + return fileDescriptor_83c10c24ec417dc9, []int{76} } func (m *List) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2236,7 +2208,7 @@ func (m *LoadBalancerIngress) Reset() { *m = LoadBalancerIngress{} } func (*LoadBalancerIngress) ProtoMessage() {} func (*LoadBalancerIngress) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{78} + return fileDescriptor_83c10c24ec417dc9, []int{77} } func (m *LoadBalancerIngress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2264,7 +2236,7 @@ func (m *LoadBalancerStatus) Reset() { *m = LoadBalancerStatus{} } func (*LoadBalancerStatus) ProtoMessage() {} func (*LoadBalancerStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{79} + return fileDescriptor_83c10c24ec417dc9, []int{78} } func (m *LoadBalancerStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2292,7 +2264,7 @@ func (m *LocalObjectReference) Reset() { *m = LocalObjectReference{} } func (*LocalObjectReference) ProtoMessage() {} func (*LocalObjectReference) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{80} + return fileDescriptor_83c10c24ec417dc9, []int{79} } func (m *LocalObjectReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2320,7 +2292,7 @@ func (m *LocalVolumeSource) Reset() { *m = LocalVolumeSource{} } func (*LocalVolumeSource) ProtoMessage() {} func (*LocalVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{81} + return fileDescriptor_83c10c24ec417dc9, []int{80} } func (m *LocalVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2348,7 +2320,7 @@ func (m *NFSVolumeSource) Reset() { *m = NFSVolumeSource{} } func (*NFSVolumeSource) ProtoMessage() {} func (*NFSVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{82} + return fileDescriptor_83c10c24ec417dc9, []int{81} } func (m *NFSVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2376,7 +2348,7 @@ func (m *Namespace) Reset() { *m = Namespace{} } func (*Namespace) ProtoMessage() {} func (*Namespace) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{83} + return fileDescriptor_83c10c24ec417dc9, []int{82} } func (m *Namespace) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2404,7 +2376,7 @@ func (m *NamespaceCondition) Reset() { *m = NamespaceCondition{} } func (*NamespaceCondition) ProtoMessage() {} func (*NamespaceCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{84} + return fileDescriptor_83c10c24ec417dc9, []int{83} } func (m *NamespaceCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2432,7 +2404,7 @@ func (m *NamespaceList) Reset() { *m = NamespaceList{} } func (*NamespaceList) ProtoMessage() {} func (*NamespaceList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{85} + return fileDescriptor_83c10c24ec417dc9, []int{84} } func (m *NamespaceList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2460,7 +2432,7 @@ func (m *NamespaceSpec) Reset() { *m = NamespaceSpec{} } func (*NamespaceSpec) ProtoMessage() {} func (*NamespaceSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{86} + return fileDescriptor_83c10c24ec417dc9, []int{85} } func (m *NamespaceSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2488,7 +2460,7 @@ func (m *NamespaceStatus) Reset() { *m = NamespaceStatus{} } func (*NamespaceStatus) ProtoMessage() {} func (*NamespaceStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{87} + return fileDescriptor_83c10c24ec417dc9, []int{86} } func (m *NamespaceStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2516,7 +2488,7 @@ func (m *Node) Reset() { *m = Node{} } func (*Node) ProtoMessage() {} func (*Node) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{88} + return fileDescriptor_83c10c24ec417dc9, []int{87} } func (m *Node) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2544,7 +2516,7 @@ func (m *NodeAddress) Reset() { *m = NodeAddress{} } func (*NodeAddress) ProtoMessage() {} func (*NodeAddress) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{89} + return fileDescriptor_83c10c24ec417dc9, []int{88} } func (m *NodeAddress) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2572,7 +2544,7 @@ func (m *NodeAffinity) Reset() { *m = NodeAffinity{} } func (*NodeAffinity) ProtoMessage() {} func (*NodeAffinity) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{90} + return fileDescriptor_83c10c24ec417dc9, []int{89} } func (m *NodeAffinity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2600,7 +2572,7 @@ func (m *NodeCondition) Reset() { *m = NodeCondition{} } func (*NodeCondition) ProtoMessage() {} func (*NodeCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{91} + return fileDescriptor_83c10c24ec417dc9, []int{90} } func (m *NodeCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2628,7 +2600,7 @@ func (m *NodeConfigSource) Reset() { *m = NodeConfigSource{} } func (*NodeConfigSource) ProtoMessage() {} func (*NodeConfigSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{92} + return fileDescriptor_83c10c24ec417dc9, []int{91} } func (m *NodeConfigSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2656,7 +2628,7 @@ func (m *NodeConfigStatus) Reset() { *m = NodeConfigStatus{} } func (*NodeConfigStatus) ProtoMessage() {} func (*NodeConfigStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{93} + return fileDescriptor_83c10c24ec417dc9, []int{92} } func (m *NodeConfigStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2684,7 +2656,7 @@ func (m *NodeDaemonEndpoints) Reset() { *m = NodeDaemonEndpoints{} } func (*NodeDaemonEndpoints) ProtoMessage() {} func (*NodeDaemonEndpoints) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{94} + return fileDescriptor_83c10c24ec417dc9, []int{93} } func (m *NodeDaemonEndpoints) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2712,7 +2684,7 @@ func (m *NodeList) Reset() { *m = NodeList{} } func (*NodeList) ProtoMessage() {} func (*NodeList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{95} + return fileDescriptor_83c10c24ec417dc9, []int{94} } func (m *NodeList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2740,7 +2712,7 @@ func (m *NodeProxyOptions) Reset() { *m = NodeProxyOptions{} } func (*NodeProxyOptions) ProtoMessage() {} func (*NodeProxyOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{96} + return fileDescriptor_83c10c24ec417dc9, []int{95} } func (m *NodeProxyOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2768,7 +2740,7 @@ func (m *NodeResources) Reset() { *m = NodeResources{} } func (*NodeResources) ProtoMessage() {} func (*NodeResources) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{97} + return fileDescriptor_83c10c24ec417dc9, []int{96} } func (m *NodeResources) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2796,7 +2768,7 @@ func (m *NodeSelector) Reset() { *m = NodeSelector{} } func (*NodeSelector) ProtoMessage() {} func (*NodeSelector) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{98} + return fileDescriptor_83c10c24ec417dc9, []int{97} } func (m *NodeSelector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2824,7 +2796,7 @@ func (m *NodeSelectorRequirement) Reset() { *m = NodeSelectorRequirement{} } func (*NodeSelectorRequirement) ProtoMessage() {} func (*NodeSelectorRequirement) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{99} + return fileDescriptor_83c10c24ec417dc9, []int{98} } func (m *NodeSelectorRequirement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2852,7 +2824,7 @@ func (m *NodeSelectorTerm) Reset() { *m = NodeSelectorTerm{} } func (*NodeSelectorTerm) ProtoMessage() {} func (*NodeSelectorTerm) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{100} + return fileDescriptor_83c10c24ec417dc9, []int{99} } func (m *NodeSelectorTerm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2880,7 +2852,7 @@ func (m *NodeSpec) Reset() { *m = NodeSpec{} } func (*NodeSpec) ProtoMessage() {} func (*NodeSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{101} + return fileDescriptor_83c10c24ec417dc9, []int{100} } func (m *NodeSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2908,7 +2880,7 @@ func (m *NodeStatus) Reset() { *m = NodeStatus{} } func (*NodeStatus) ProtoMessage() {} func (*NodeStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{102} + return fileDescriptor_83c10c24ec417dc9, []int{101} } func (m *NodeStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2936,7 +2908,7 @@ func (m *NodeSystemInfo) Reset() { *m = NodeSystemInfo{} } func (*NodeSystemInfo) ProtoMessage() {} func (*NodeSystemInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{103} + return fileDescriptor_83c10c24ec417dc9, []int{102} } func (m *NodeSystemInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2964,7 +2936,7 @@ func (m *ObjectFieldSelector) Reset() { *m = ObjectFieldSelector{} } func (*ObjectFieldSelector) ProtoMessage() {} func (*ObjectFieldSelector) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{104} + return fileDescriptor_83c10c24ec417dc9, []int{103} } func (m *ObjectFieldSelector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2992,7 +2964,7 @@ func (m *ObjectReference) Reset() { *m = ObjectReference{} } func (*ObjectReference) ProtoMessage() {} func (*ObjectReference) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{105} + return fileDescriptor_83c10c24ec417dc9, []int{104} } func (m *ObjectReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3020,7 +2992,7 @@ func (m *PersistentVolume) Reset() { *m = PersistentVolume{} } func (*PersistentVolume) ProtoMessage() {} func (*PersistentVolume) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{106} + return fileDescriptor_83c10c24ec417dc9, []int{105} } func (m *PersistentVolume) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3048,7 +3020,7 @@ func (m *PersistentVolumeClaim) Reset() { *m = PersistentVolumeClaim{} } func (*PersistentVolumeClaim) ProtoMessage() {} func (*PersistentVolumeClaim) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{107} + return fileDescriptor_83c10c24ec417dc9, []int{106} } func (m *PersistentVolumeClaim) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3076,7 +3048,7 @@ func (m *PersistentVolumeClaimCondition) Reset() { *m = PersistentVolumeClaimCondition{} } func (*PersistentVolumeClaimCondition) ProtoMessage() {} func (*PersistentVolumeClaimCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{108} + return fileDescriptor_83c10c24ec417dc9, []int{107} } func (m *PersistentVolumeClaimCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3104,7 +3076,7 @@ func (m *PersistentVolumeClaimList) Reset() { *m = PersistentVolumeClaimList{} } func (*PersistentVolumeClaimList) ProtoMessage() {} func (*PersistentVolumeClaimList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{109} + return fileDescriptor_83c10c24ec417dc9, []int{108} } func (m *PersistentVolumeClaimList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3132,7 +3104,7 @@ func (m *PersistentVolumeClaimSpec) Reset() { *m = PersistentVolumeClaimSpec{} } func (*PersistentVolumeClaimSpec) ProtoMessage() {} func (*PersistentVolumeClaimSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{110} + return fileDescriptor_83c10c24ec417dc9, []int{109} } func (m *PersistentVolumeClaimSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3160,7 +3132,7 @@ func (m *PersistentVolumeClaimStatus) Reset() { *m = PersistentVolumeClaimStatus{} } func (*PersistentVolumeClaimStatus) ProtoMessage() {} func (*PersistentVolumeClaimStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{111} + return fileDescriptor_83c10c24ec417dc9, []int{110} } func (m *PersistentVolumeClaimStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3188,7 +3160,7 @@ func (m *PersistentVolumeClaimTemplate) Reset() { *m = PersistentVolumeClaimTemplate{} } func (*PersistentVolumeClaimTemplate) ProtoMessage() {} func (*PersistentVolumeClaimTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{112} + return fileDescriptor_83c10c24ec417dc9, []int{111} } func (m *PersistentVolumeClaimTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3216,7 +3188,7 @@ func (m *PersistentVolumeClaimVolumeSource) Reset() { *m = PersistentVolumeClaimVolumeSource{} } func (*PersistentVolumeClaimVolumeSource) ProtoMessage() {} func (*PersistentVolumeClaimVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{113} + return fileDescriptor_83c10c24ec417dc9, []int{112} } func (m *PersistentVolumeClaimVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3244,7 +3216,7 @@ func (m *PersistentVolumeList) Reset() { *m = PersistentVolumeList{} } func (*PersistentVolumeList) ProtoMessage() {} func (*PersistentVolumeList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{114} + return fileDescriptor_83c10c24ec417dc9, []int{113} } func (m *PersistentVolumeList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3272,7 +3244,7 @@ func (m *PersistentVolumeSource) Reset() { *m = PersistentVolumeSource{} } func (*PersistentVolumeSource) ProtoMessage() {} func (*PersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{115} + return fileDescriptor_83c10c24ec417dc9, []int{114} } func (m *PersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3300,7 +3272,7 @@ func (m *PersistentVolumeSpec) Reset() { *m = PersistentVolumeSpec{} } func (*PersistentVolumeSpec) ProtoMessage() {} func (*PersistentVolumeSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{116} + return fileDescriptor_83c10c24ec417dc9, []int{115} } func (m *PersistentVolumeSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3328,7 +3300,7 @@ func (m *PersistentVolumeStatus) Reset() { *m = PersistentVolumeStatus{} } func (*PersistentVolumeStatus) ProtoMessage() {} func (*PersistentVolumeStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{117} + return fileDescriptor_83c10c24ec417dc9, []int{116} } func (m *PersistentVolumeStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3356,7 +3328,7 @@ func (m *PhotonPersistentDiskVolumeSource) Reset() { *m = PhotonPersistentDiskVolumeSource{} } func (*PhotonPersistentDiskVolumeSource) ProtoMessage() {} func (*PhotonPersistentDiskVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{118} + return fileDescriptor_83c10c24ec417dc9, []int{117} } func (m *PhotonPersistentDiskVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3384,7 +3356,7 @@ func (m *Pod) Reset() { *m = Pod{} } func (*Pod) ProtoMessage() {} func (*Pod) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{119} + return fileDescriptor_83c10c24ec417dc9, []int{118} } func (m *Pod) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3412,7 +3384,7 @@ func (m *PodAffinity) Reset() { *m = PodAffinity{} } func (*PodAffinity) ProtoMessage() {} func (*PodAffinity) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{120} + return fileDescriptor_83c10c24ec417dc9, []int{119} } func (m *PodAffinity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3440,7 +3412,7 @@ func (m *PodAffinityTerm) Reset() { *m = PodAffinityTerm{} } func (*PodAffinityTerm) ProtoMessage() {} func (*PodAffinityTerm) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{121} + return fileDescriptor_83c10c24ec417dc9, []int{120} } func (m *PodAffinityTerm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3468,7 +3440,7 @@ func (m *PodAntiAffinity) Reset() { *m = PodAntiAffinity{} } func (*PodAntiAffinity) ProtoMessage() {} func (*PodAntiAffinity) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{122} + return fileDescriptor_83c10c24ec417dc9, []int{121} } func (m *PodAntiAffinity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3496,7 +3468,7 @@ func (m *PodAttachOptions) Reset() { *m = PodAttachOptions{} } func (*PodAttachOptions) ProtoMessage() {} func (*PodAttachOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{123} + return fileDescriptor_83c10c24ec417dc9, []int{122} } func (m *PodAttachOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3524,7 +3496,7 @@ func (m *PodCondition) Reset() { *m = PodCondition{} } func (*PodCondition) ProtoMessage() {} func (*PodCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{124} + return fileDescriptor_83c10c24ec417dc9, []int{123} } func (m *PodCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3552,7 +3524,7 @@ func (m *PodDNSConfig) Reset() { *m = PodDNSConfig{} } func (*PodDNSConfig) ProtoMessage() {} func (*PodDNSConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{125} + return fileDescriptor_83c10c24ec417dc9, []int{124} } func (m *PodDNSConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3580,7 +3552,7 @@ func (m *PodDNSConfigOption) Reset() { *m = PodDNSConfigOption{} } func (*PodDNSConfigOption) ProtoMessage() {} func (*PodDNSConfigOption) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{126} + return fileDescriptor_83c10c24ec417dc9, []int{125} } func (m *PodDNSConfigOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3608,7 +3580,7 @@ func (m *PodExecOptions) Reset() { *m = PodExecOptions{} } func (*PodExecOptions) ProtoMessage() {} func (*PodExecOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{127} + return fileDescriptor_83c10c24ec417dc9, []int{126} } func (m *PodExecOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3636,7 +3608,7 @@ func (m *PodIP) Reset() { *m = PodIP{} } func (*PodIP) ProtoMessage() {} func (*PodIP) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{128} + return fileDescriptor_83c10c24ec417dc9, []int{127} } func (m *PodIP) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3664,7 +3636,7 @@ func (m *PodList) Reset() { *m = PodList{} } func (*PodList) ProtoMessage() {} func (*PodList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{129} + return fileDescriptor_83c10c24ec417dc9, []int{128} } func (m *PodList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3692,7 +3664,7 @@ func (m *PodLogOptions) Reset() { *m = PodLogOptions{} } func (*PodLogOptions) ProtoMessage() {} func (*PodLogOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{130} + return fileDescriptor_83c10c24ec417dc9, []int{129} } func (m *PodLogOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3720,7 +3692,7 @@ func (m *PodPortForwardOptions) Reset() { *m = PodPortForwardOptions{} } func (*PodPortForwardOptions) ProtoMessage() {} func (*PodPortForwardOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{131} + return fileDescriptor_83c10c24ec417dc9, []int{130} } func (m *PodPortForwardOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3748,7 +3720,7 @@ func (m *PodProxyOptions) Reset() { *m = PodProxyOptions{} } func (*PodProxyOptions) ProtoMessage() {} func (*PodProxyOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{132} + return fileDescriptor_83c10c24ec417dc9, []int{131} } func (m *PodProxyOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3776,7 +3748,7 @@ func (m *PodReadinessGate) Reset() { *m = PodReadinessGate{} } func (*PodReadinessGate) ProtoMessage() {} func (*PodReadinessGate) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{133} + return fileDescriptor_83c10c24ec417dc9, []int{132} } func (m *PodReadinessGate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3804,7 +3776,7 @@ func (m *PodSecurityContext) Reset() { *m = PodSecurityContext{} } func (*PodSecurityContext) ProtoMessage() {} func (*PodSecurityContext) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{134} + return fileDescriptor_83c10c24ec417dc9, []int{133} } func (m *PodSecurityContext) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3832,7 +3804,7 @@ func (m *PodSignature) Reset() { *m = PodSignature{} } func (*PodSignature) ProtoMessage() {} func (*PodSignature) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{135} + return fileDescriptor_83c10c24ec417dc9, []int{134} } func (m *PodSignature) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3860,7 +3832,7 @@ func (m *PodSpec) Reset() { *m = PodSpec{} } func (*PodSpec) ProtoMessage() {} func (*PodSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{136} + return fileDescriptor_83c10c24ec417dc9, []int{135} } func (m *PodSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3888,7 +3860,7 @@ func (m *PodStatus) Reset() { *m = PodStatus{} } func (*PodStatus) ProtoMessage() {} func (*PodStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{137} + return fileDescriptor_83c10c24ec417dc9, []int{136} } func (m *PodStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3916,7 +3888,7 @@ func (m *PodStatusResult) Reset() { *m = PodStatusResult{} } func (*PodStatusResult) ProtoMessage() {} func (*PodStatusResult) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{138} + return fileDescriptor_83c10c24ec417dc9, []int{137} } func (m *PodStatusResult) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3944,7 +3916,7 @@ func (m *PodTemplate) Reset() { *m = PodTemplate{} } func (*PodTemplate) ProtoMessage() {} func (*PodTemplate) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{139} + return fileDescriptor_83c10c24ec417dc9, []int{138} } func (m *PodTemplate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3972,7 +3944,7 @@ func (m *PodTemplateList) Reset() { *m = PodTemplateList{} } func (*PodTemplateList) ProtoMessage() {} func (*PodTemplateList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{140} + return fileDescriptor_83c10c24ec417dc9, []int{139} } func (m *PodTemplateList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4000,7 +3972,7 @@ func (m *PodTemplateSpec) Reset() { *m = PodTemplateSpec{} } func (*PodTemplateSpec) ProtoMessage() {} func (*PodTemplateSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{141} + return fileDescriptor_83c10c24ec417dc9, []int{140} } func (m *PodTemplateSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4028,7 +4000,7 @@ func (m *PortStatus) Reset() { *m = PortStatus{} } func (*PortStatus) ProtoMessage() {} func (*PortStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{142} + return fileDescriptor_83c10c24ec417dc9, []int{141} } func (m *PortStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4056,7 +4028,7 @@ func (m *PortworxVolumeSource) Reset() { *m = PortworxVolumeSource{} } func (*PortworxVolumeSource) ProtoMessage() {} func (*PortworxVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{143} + return fileDescriptor_83c10c24ec417dc9, []int{142} } func (m *PortworxVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4084,7 +4056,7 @@ func (m *Preconditions) Reset() { *m = Preconditions{} } func (*Preconditions) ProtoMessage() {} func (*Preconditions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{144} + return fileDescriptor_83c10c24ec417dc9, []int{143} } func (m *Preconditions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4112,7 +4084,7 @@ func (m *PreferAvoidPodsEntry) Reset() { *m = PreferAvoidPodsEntry{} } func (*PreferAvoidPodsEntry) ProtoMessage() {} func (*PreferAvoidPodsEntry) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{145} + return fileDescriptor_83c10c24ec417dc9, []int{144} } func (m *PreferAvoidPodsEntry) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4140,7 +4112,7 @@ func (m *PreferredSchedulingTerm) Reset() { *m = PreferredSchedulingTerm{} } func (*PreferredSchedulingTerm) ProtoMessage() {} func (*PreferredSchedulingTerm) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{146} + return fileDescriptor_83c10c24ec417dc9, []int{145} } func (m *PreferredSchedulingTerm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4168,7 +4140,7 @@ func (m *Probe) Reset() { *m = Probe{} } func (*Probe) ProtoMessage() {} func (*Probe) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{147} + return fileDescriptor_83c10c24ec417dc9, []int{146} } func (m *Probe) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4196,7 +4168,7 @@ func (m *ProjectedVolumeSource) Reset() { *m = ProjectedVolumeSource{} } func (*ProjectedVolumeSource) ProtoMessage() {} func (*ProjectedVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{148} + return fileDescriptor_83c10c24ec417dc9, []int{147} } func (m *ProjectedVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4224,7 +4196,7 @@ func (m *QuobyteVolumeSource) Reset() { *m = QuobyteVolumeSource{} } func (*QuobyteVolumeSource) ProtoMessage() {} func (*QuobyteVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{149} + return fileDescriptor_83c10c24ec417dc9, []int{148} } func (m *QuobyteVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4252,7 +4224,7 @@ func (m *RBDPersistentVolumeSource) Reset() { *m = RBDPersistentVolumeSource{} } func (*RBDPersistentVolumeSource) ProtoMessage() {} func (*RBDPersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{150} + return fileDescriptor_83c10c24ec417dc9, []int{149} } func (m *RBDPersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4280,7 +4252,7 @@ func (m *RBDVolumeSource) Reset() { *m = RBDVolumeSource{} } func (*RBDVolumeSource) ProtoMessage() {} func (*RBDVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{151} + return fileDescriptor_83c10c24ec417dc9, []int{150} } func (m *RBDVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4308,7 +4280,7 @@ func (m *RangeAllocation) Reset() { *m = RangeAllocation{} } func (*RangeAllocation) ProtoMessage() {} func (*RangeAllocation) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{152} + return fileDescriptor_83c10c24ec417dc9, []int{151} } func (m *RangeAllocation) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4336,7 +4308,7 @@ func (m *ReplicationController) Reset() { *m = ReplicationController{} } func (*ReplicationController) ProtoMessage() {} func (*ReplicationController) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{153} + return fileDescriptor_83c10c24ec417dc9, []int{152} } func (m *ReplicationController) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4364,7 +4336,7 @@ func (m *ReplicationControllerCondition) Reset() { *m = ReplicationControllerCondition{} } func (*ReplicationControllerCondition) ProtoMessage() {} func (*ReplicationControllerCondition) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{154} + return fileDescriptor_83c10c24ec417dc9, []int{153} } func (m *ReplicationControllerCondition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4392,7 +4364,7 @@ func (m *ReplicationControllerList) Reset() { *m = ReplicationControllerList{} } func (*ReplicationControllerList) ProtoMessage() {} func (*ReplicationControllerList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{155} + return fileDescriptor_83c10c24ec417dc9, []int{154} } func (m *ReplicationControllerList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4420,7 +4392,7 @@ func (m *ReplicationControllerSpec) Reset() { *m = ReplicationControllerSpec{} } func (*ReplicationControllerSpec) ProtoMessage() {} func (*ReplicationControllerSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{156} + return fileDescriptor_83c10c24ec417dc9, []int{155} } func (m *ReplicationControllerSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4448,7 +4420,7 @@ func (m *ReplicationControllerStatus) Reset() { *m = ReplicationControllerStatus{} } func (*ReplicationControllerStatus) ProtoMessage() {} func (*ReplicationControllerStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{157} + return fileDescriptor_83c10c24ec417dc9, []int{156} } func (m *ReplicationControllerStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4476,7 +4448,7 @@ func (m *ResourceFieldSelector) Reset() { *m = ResourceFieldSelector{} } func (*ResourceFieldSelector) ProtoMessage() {} func (*ResourceFieldSelector) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{158} + return fileDescriptor_83c10c24ec417dc9, []int{157} } func (m *ResourceFieldSelector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4504,7 +4476,7 @@ func (m *ResourceQuota) Reset() { *m = ResourceQuota{} } func (*ResourceQuota) ProtoMessage() {} func (*ResourceQuota) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{159} + return fileDescriptor_83c10c24ec417dc9, []int{158} } func (m *ResourceQuota) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4532,7 +4504,7 @@ func (m *ResourceQuotaList) Reset() { *m = ResourceQuotaList{} } func (*ResourceQuotaList) ProtoMessage() {} func (*ResourceQuotaList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{160} + return fileDescriptor_83c10c24ec417dc9, []int{159} } func (m *ResourceQuotaList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4560,7 +4532,7 @@ func (m *ResourceQuotaSpec) Reset() { *m = ResourceQuotaSpec{} } func (*ResourceQuotaSpec) ProtoMessage() {} func (*ResourceQuotaSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{161} + return fileDescriptor_83c10c24ec417dc9, []int{160} } func (m *ResourceQuotaSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4588,7 +4560,7 @@ func (m *ResourceQuotaStatus) Reset() { *m = ResourceQuotaStatus{} } func (*ResourceQuotaStatus) ProtoMessage() {} func (*ResourceQuotaStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{162} + return fileDescriptor_83c10c24ec417dc9, []int{161} } func (m *ResourceQuotaStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4616,7 +4588,7 @@ func (m *ResourceRequirements) Reset() { *m = ResourceRequirements{} } func (*ResourceRequirements) ProtoMessage() {} func (*ResourceRequirements) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{163} + return fileDescriptor_83c10c24ec417dc9, []int{162} } func (m *ResourceRequirements) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4644,7 +4616,7 @@ func (m *SELinuxOptions) Reset() { *m = SELinuxOptions{} } func (*SELinuxOptions) ProtoMessage() {} func (*SELinuxOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{164} + return fileDescriptor_83c10c24ec417dc9, []int{163} } func (m *SELinuxOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4672,7 +4644,7 @@ func (m *ScaleIOPersistentVolumeSource) Reset() { *m = ScaleIOPersistentVolumeSource{} } func (*ScaleIOPersistentVolumeSource) ProtoMessage() {} func (*ScaleIOPersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{165} + return fileDescriptor_83c10c24ec417dc9, []int{164} } func (m *ScaleIOPersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4700,7 +4672,7 @@ func (m *ScaleIOVolumeSource) Reset() { *m = ScaleIOVolumeSource{} } func (*ScaleIOVolumeSource) ProtoMessage() {} func (*ScaleIOVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{166} + return fileDescriptor_83c10c24ec417dc9, []int{165} } func (m *ScaleIOVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4728,7 +4700,7 @@ func (m *ScopeSelector) Reset() { *m = ScopeSelector{} } func (*ScopeSelector) ProtoMessage() {} func (*ScopeSelector) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{167} + return fileDescriptor_83c10c24ec417dc9, []int{166} } func (m *ScopeSelector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4756,7 +4728,7 @@ func (m *ScopedResourceSelectorRequirement) Reset() { *m = ScopedResourceSelectorRequirement{} } func (*ScopedResourceSelectorRequirement) ProtoMessage() {} func (*ScopedResourceSelectorRequirement) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{168} + return fileDescriptor_83c10c24ec417dc9, []int{167} } func (m *ScopedResourceSelectorRequirement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4784,7 +4756,7 @@ func (m *SeccompProfile) Reset() { *m = SeccompProfile{} } func (*SeccompProfile) ProtoMessage() {} func (*SeccompProfile) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{169} + return fileDescriptor_83c10c24ec417dc9, []int{168} } func (m *SeccompProfile) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4812,7 +4784,7 @@ func (m *Secret) Reset() { *m = Secret{} } func (*Secret) ProtoMessage() {} func (*Secret) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{170} + return fileDescriptor_83c10c24ec417dc9, []int{169} } func (m *Secret) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4840,7 +4812,7 @@ func (m *SecretEnvSource) Reset() { *m = SecretEnvSource{} } func (*SecretEnvSource) ProtoMessage() {} func (*SecretEnvSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{171} + return fileDescriptor_83c10c24ec417dc9, []int{170} } func (m *SecretEnvSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4868,7 +4840,7 @@ func (m *SecretKeySelector) Reset() { *m = SecretKeySelector{} } func (*SecretKeySelector) ProtoMessage() {} func (*SecretKeySelector) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{172} + return fileDescriptor_83c10c24ec417dc9, []int{171} } func (m *SecretKeySelector) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4896,7 +4868,7 @@ func (m *SecretList) Reset() { *m = SecretList{} } func (*SecretList) ProtoMessage() {} func (*SecretList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{173} + return fileDescriptor_83c10c24ec417dc9, []int{172} } func (m *SecretList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4924,7 +4896,7 @@ func (m *SecretProjection) Reset() { *m = SecretProjection{} } func (*SecretProjection) ProtoMessage() {} func (*SecretProjection) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{174} + return fileDescriptor_83c10c24ec417dc9, []int{173} } func (m *SecretProjection) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4952,7 +4924,7 @@ func (m *SecretReference) Reset() { *m = SecretReference{} } func (*SecretReference) ProtoMessage() {} func (*SecretReference) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{175} + return fileDescriptor_83c10c24ec417dc9, []int{174} } func (m *SecretReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4980,7 +4952,7 @@ func (m *SecretVolumeSource) Reset() { *m = SecretVolumeSource{} } func (*SecretVolumeSource) ProtoMessage() {} func (*SecretVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{176} + return fileDescriptor_83c10c24ec417dc9, []int{175} } func (m *SecretVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5008,7 +4980,7 @@ func (m *SecurityContext) Reset() { *m = SecurityContext{} } func (*SecurityContext) ProtoMessage() {} func (*SecurityContext) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{177} + return fileDescriptor_83c10c24ec417dc9, []int{176} } func (m *SecurityContext) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5036,7 +5008,7 @@ func (m *SerializedReference) Reset() { *m = SerializedReference{} } func (*SerializedReference) ProtoMessage() {} func (*SerializedReference) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{178} + return fileDescriptor_83c10c24ec417dc9, []int{177} } func (m *SerializedReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5064,7 +5036,7 @@ func (m *Service) Reset() { *m = Service{} } func (*Service) ProtoMessage() {} func (*Service) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{179} + return fileDescriptor_83c10c24ec417dc9, []int{178} } func (m *Service) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5092,7 +5064,7 @@ func (m *ServiceAccount) Reset() { *m = ServiceAccount{} } func (*ServiceAccount) ProtoMessage() {} func (*ServiceAccount) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{180} + return fileDescriptor_83c10c24ec417dc9, []int{179} } func (m *ServiceAccount) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5120,7 +5092,7 @@ func (m *ServiceAccountList) Reset() { *m = ServiceAccountList{} } func (*ServiceAccountList) ProtoMessage() {} func (*ServiceAccountList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{181} + return fileDescriptor_83c10c24ec417dc9, []int{180} } func (m *ServiceAccountList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5148,7 +5120,7 @@ func (m *ServiceAccountTokenProjection) Reset() { *m = ServiceAccountTokenProjection{} } func (*ServiceAccountTokenProjection) ProtoMessage() {} func (*ServiceAccountTokenProjection) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{182} + return fileDescriptor_83c10c24ec417dc9, []int{181} } func (m *ServiceAccountTokenProjection) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5176,7 +5148,7 @@ func (m *ServiceList) Reset() { *m = ServiceList{} } func (*ServiceList) ProtoMessage() {} func (*ServiceList) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{183} + return fileDescriptor_83c10c24ec417dc9, []int{182} } func (m *ServiceList) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5204,7 +5176,7 @@ func (m *ServicePort) Reset() { *m = ServicePort{} } func (*ServicePort) ProtoMessage() {} func (*ServicePort) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{184} + return fileDescriptor_83c10c24ec417dc9, []int{183} } func (m *ServicePort) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5232,7 +5204,7 @@ func (m *ServiceProxyOptions) Reset() { *m = ServiceProxyOptions{} } func (*ServiceProxyOptions) ProtoMessage() {} func (*ServiceProxyOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{185} + return fileDescriptor_83c10c24ec417dc9, []int{184} } func (m *ServiceProxyOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5260,7 +5232,7 @@ func (m *ServiceSpec) Reset() { *m = ServiceSpec{} } func (*ServiceSpec) ProtoMessage() {} func (*ServiceSpec) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{186} + return fileDescriptor_83c10c24ec417dc9, []int{185} } func (m *ServiceSpec) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5288,7 +5260,7 @@ func (m *ServiceStatus) Reset() { *m = ServiceStatus{} } func (*ServiceStatus) ProtoMessage() {} func (*ServiceStatus) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{187} + return fileDescriptor_83c10c24ec417dc9, []int{186} } func (m *ServiceStatus) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5316,7 +5288,7 @@ func (m *SessionAffinityConfig) Reset() { *m = SessionAffinityConfig{} } func (*SessionAffinityConfig) ProtoMessage() {} func (*SessionAffinityConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{188} + return fileDescriptor_83c10c24ec417dc9, []int{187} } func (m *SessionAffinityConfig) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5344,7 +5316,7 @@ func (m *StorageOSPersistentVolumeSource) Reset() { *m = StorageOSPersistentVolumeSource{} } func (*StorageOSPersistentVolumeSource) ProtoMessage() {} func (*StorageOSPersistentVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{189} + return fileDescriptor_83c10c24ec417dc9, []int{188} } func (m *StorageOSPersistentVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5372,7 +5344,7 @@ func (m *StorageOSVolumeSource) Reset() { *m = StorageOSVolumeSource{} } func (*StorageOSVolumeSource) ProtoMessage() {} func (*StorageOSVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{190} + return fileDescriptor_83c10c24ec417dc9, []int{189} } func (m *StorageOSVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5400,7 +5372,7 @@ func (m *Sysctl) Reset() { *m = Sysctl{} } func (*Sysctl) ProtoMessage() {} func (*Sysctl) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{191} + return fileDescriptor_83c10c24ec417dc9, []int{190} } func (m *Sysctl) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5428,7 +5400,7 @@ func (m *TCPSocketAction) Reset() { *m = TCPSocketAction{} } func (*TCPSocketAction) ProtoMessage() {} func (*TCPSocketAction) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{192} + return fileDescriptor_83c10c24ec417dc9, []int{191} } func (m *TCPSocketAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5456,7 +5428,7 @@ func (m *Taint) Reset() { *m = Taint{} } func (*Taint) ProtoMessage() {} func (*Taint) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{193} + return fileDescriptor_83c10c24ec417dc9, []int{192} } func (m *Taint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5484,7 +5456,7 @@ func (m *Toleration) Reset() { *m = Toleration{} } func (*Toleration) ProtoMessage() {} func (*Toleration) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{194} + return fileDescriptor_83c10c24ec417dc9, []int{193} } func (m *Toleration) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5512,7 +5484,7 @@ func (m *TopologySelectorLabelRequirement) Reset() { *m = TopologySelectorLabelRequirement{} } func (*TopologySelectorLabelRequirement) ProtoMessage() {} func (*TopologySelectorLabelRequirement) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{195} + return fileDescriptor_83c10c24ec417dc9, []int{194} } func (m *TopologySelectorLabelRequirement) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5540,7 +5512,7 @@ func (m *TopologySelectorTerm) Reset() { *m = TopologySelectorTerm{} } func (*TopologySelectorTerm) ProtoMessage() {} func (*TopologySelectorTerm) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{196} + return fileDescriptor_83c10c24ec417dc9, []int{195} } func (m *TopologySelectorTerm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5568,7 +5540,7 @@ func (m *TopologySpreadConstraint) Reset() { *m = TopologySpreadConstraint{} } func (*TopologySpreadConstraint) ProtoMessage() {} func (*TopologySpreadConstraint) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{197} + return fileDescriptor_83c10c24ec417dc9, []int{196} } func (m *TopologySpreadConstraint) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5596,7 +5568,7 @@ func (m *TypedLocalObjectReference) Reset() { *m = TypedLocalObjectReference{} } func (*TypedLocalObjectReference) ProtoMessage() {} func (*TypedLocalObjectReference) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{198} + return fileDescriptor_83c10c24ec417dc9, []int{197} } func (m *TypedLocalObjectReference) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5624,7 +5596,7 @@ func (m *Volume) Reset() { *m = Volume{} } func (*Volume) ProtoMessage() {} func (*Volume) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{199} + return fileDescriptor_83c10c24ec417dc9, []int{198} } func (m *Volume) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5652,7 +5624,7 @@ func (m *VolumeDevice) Reset() { *m = VolumeDevice{} } func (*VolumeDevice) ProtoMessage() {} func (*VolumeDevice) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{200} + return fileDescriptor_83c10c24ec417dc9, []int{199} } func (m *VolumeDevice) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5680,7 +5652,7 @@ func (m *VolumeMount) Reset() { *m = VolumeMount{} } func (*VolumeMount) ProtoMessage() {} func (*VolumeMount) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{201} + return fileDescriptor_83c10c24ec417dc9, []int{200} } func (m *VolumeMount) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5708,7 +5680,7 @@ func (m *VolumeNodeAffinity) Reset() { *m = VolumeNodeAffinity{} } func (*VolumeNodeAffinity) ProtoMessage() {} func (*VolumeNodeAffinity) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{202} + return fileDescriptor_83c10c24ec417dc9, []int{201} } func (m *VolumeNodeAffinity) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5736,7 +5708,7 @@ func (m *VolumeProjection) Reset() { *m = VolumeProjection{} } func (*VolumeProjection) ProtoMessage() {} func (*VolumeProjection) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{203} + return fileDescriptor_83c10c24ec417dc9, []int{202} } func (m *VolumeProjection) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5764,7 +5736,7 @@ func (m *VolumeSource) Reset() { *m = VolumeSource{} } func (*VolumeSource) ProtoMessage() {} func (*VolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{204} + return fileDescriptor_83c10c24ec417dc9, []int{203} } func (m *VolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5792,7 +5764,7 @@ func (m *VsphereVirtualDiskVolumeSource) Reset() { *m = VsphereVirtualDiskVolumeSource{} } func (*VsphereVirtualDiskVolumeSource) ProtoMessage() {} func (*VsphereVirtualDiskVolumeSource) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{205} + return fileDescriptor_83c10c24ec417dc9, []int{204} } func (m *VsphereVirtualDiskVolumeSource) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5820,7 +5792,7 @@ func (m *WeightedPodAffinityTerm) Reset() { *m = WeightedPodAffinityTerm{} } func (*WeightedPodAffinityTerm) ProtoMessage() {} func (*WeightedPodAffinityTerm) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{206} + return fileDescriptor_83c10c24ec417dc9, []int{205} } func (m *WeightedPodAffinityTerm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5848,7 +5820,7 @@ func (m *WindowsSecurityContextOptions) Reset() { *m = WindowsSecurityContextOptions{} } func (*WindowsSecurityContextOptions) ProtoMessage() {} func (*WindowsSecurityContextOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_83c10c24ec417dc9, []int{207} + return fileDescriptor_83c10c24ec417dc9, []int{206} } func (m *WindowsSecurityContextOptions) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -5927,7 +5899,6 @@ proto.RegisterType((*EnvVarSource)(nil), "k8s.io.api.core.v1.EnvVarSource") proto.RegisterType((*EphemeralContainer)(nil), "k8s.io.api.core.v1.EphemeralContainer") proto.RegisterType((*EphemeralContainerCommon)(nil), "k8s.io.api.core.v1.EphemeralContainerCommon") - proto.RegisterType((*EphemeralContainers)(nil), "k8s.io.api.core.v1.EphemeralContainers") proto.RegisterType((*EphemeralVolumeSource)(nil), "k8s.io.api.core.v1.EphemeralVolumeSource") proto.RegisterType((*Event)(nil), "k8s.io.api.core.v1.Event") proto.RegisterType((*EventList)(nil), "k8s.io.api.core.v1.EventList") @@ -6116,887 +6087,887 @@ } var fileDescriptor_83c10c24ec417dc9 = []byte{ - // 14066 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x6b, 0x70, 0x1c, 0xd9, - 0x75, 0x18, 0xac, 0x9e, 0xc1, 0x6b, 0x0e, 0xde, 0x17, 0x24, 0x17, 0xc4, 0x2e, 0x09, 0x6e, 0x53, - 0xe2, 0x72, 0xb5, 0xbb, 0xa0, 0xb8, 0x0f, 0x69, 0xbd, 0x2b, 0xad, 0x05, 0x60, 0x00, 0x72, 0x96, - 0x04, 0x38, 0x7b, 0x07, 0x24, 0x25, 0x79, 0xa5, 0x52, 0x63, 0xe6, 0x02, 0x68, 0x61, 0xa6, 0x7b, - 0xb6, 0xbb, 0x07, 0x24, 0xf6, 0x93, 0xeb, 0xf3, 0x27, 0x3f, 0xe5, 0xc7, 0x57, 0xaa, 0x94, 0xf3, - 0xb2, 0x5d, 0xae, 0x94, 0xe3, 0x54, 0xac, 0x38, 0x49, 0xc5, 0xb1, 0x63, 0x3b, 0x96, 0x13, 0x3b, - 0x71, 0x1e, 0x4e, 0x7e, 0x38, 0x8e, 0x2b, 0xb1, 0x5c, 0xe5, 0x0a, 0x62, 0xd3, 0x49, 0xb9, 0xf4, - 0x23, 0xb6, 0x13, 0x3b, 0x3f, 0x82, 0xb8, 0xe2, 0xd4, 0x7d, 0xf6, 0xbd, 0xfd, 0x98, 0x19, 0x70, - 0x41, 0x68, 0xa5, 0xda, 0x7f, 0x33, 0xf7, 0x9c, 0x7b, 0xee, 0xed, 0xfb, 0x3c, 0xf7, 0x3c, 0xe1, - 0xd5, 0xdd, 0x97, 0xc3, 0x05, 0xd7, 0xbf, 0xb2, 0xdb, 0xd9, 0x24, 0x81, 0x47, 0x22, 0x12, 0x5e, - 0xd9, 0x23, 0x5e, 0xc3, 0x0f, 0xae, 0x08, 0x80, 0xd3, 0x76, 0xaf, 0xd4, 0xfd, 0x80, 0x5c, 0xd9, - 0xbb, 0x7a, 0x65, 0x9b, 0x78, 0x24, 0x70, 0x22, 0xd2, 0x58, 0x68, 0x07, 0x7e, 0xe4, 0x23, 0xc4, - 0x71, 0x16, 0x9c, 0xb6, 0xbb, 0x40, 0x71, 0x16, 0xf6, 0xae, 0xce, 0x3d, 0xb7, 0xed, 0x46, 0x3b, - 0x9d, 0xcd, 0x85, 0xba, 0xdf, 0xba, 0xb2, 0xed, 0x6f, 0xfb, 0x57, 0x18, 0xea, 0x66, 0x67, 0x8b, - 0xfd, 0x63, 0x7f, 0xd8, 0x2f, 0x4e, 0x62, 0xee, 0xc5, 0xb8, 0x99, 0x96, 0x53, 0xdf, 0x71, 0x3d, - 0x12, 0xec, 0x5f, 0x69, 0xef, 0x6e, 0xb3, 0x76, 0x03, 0x12, 0xfa, 0x9d, 0xa0, 0x4e, 0x92, 0x0d, - 0x77, 0xad, 0x15, 0x5e, 0x69, 0x91, 0xc8, 0xc9, 0xe8, 0xee, 0xdc, 0x95, 0xbc, 0x5a, 0x41, 0xc7, - 0x8b, 0xdc, 0x56, 0xba, 0x99, 0x0f, 0xf7, 0xaa, 0x10, 0xd6, 0x77, 0x48, 0xcb, 0x49, 0xd5, 0x7b, - 0x21, 0xaf, 0x5e, 0x27, 0x72, 0x9b, 0x57, 0x5c, 0x2f, 0x0a, 0xa3, 0x20, 0x59, 0xc9, 0xfe, 0xaa, - 0x05, 0x17, 0x16, 0xef, 0xd6, 0x56, 0x9a, 0x4e, 0x18, 0xb9, 0xf5, 0xa5, 0xa6, 0x5f, 0xdf, 0xad, - 0x45, 0x7e, 0x40, 0xee, 0xf8, 0xcd, 0x4e, 0x8b, 0xd4, 0xd8, 0x40, 0xa0, 0x67, 0x61, 0x64, 0x8f, - 0xfd, 0xaf, 0x94, 0x67, 0xad, 0x0b, 0xd6, 0xe5, 0xd2, 0xd2, 0xd4, 0xaf, 0x1f, 0xcc, 0xbf, 0xef, - 0xc1, 0xc1, 0xfc, 0xc8, 0x1d, 0x51, 0x8e, 0x15, 0x06, 0xba, 0x04, 0x43, 0x5b, 0xe1, 0xc6, 0x7e, - 0x9b, 0xcc, 0x16, 0x18, 0xee, 0x84, 0xc0, 0x1d, 0x5a, 0xad, 0xd1, 0x52, 0x2c, 0xa0, 0xe8, 0x0a, - 0x94, 0xda, 0x4e, 0x10, 0xb9, 0x91, 0xeb, 0x7b, 0xb3, 0xc5, 0x0b, 0xd6, 0xe5, 0xc1, 0xa5, 0x69, - 0x81, 0x5a, 0xaa, 0x4a, 0x00, 0x8e, 0x71, 0x68, 0x37, 0x02, 0xe2, 0x34, 0x6e, 0x79, 0xcd, 0xfd, - 0xd9, 0x81, 0x0b, 0xd6, 0xe5, 0x91, 0xb8, 0x1b, 0x58, 0x94, 0x63, 0x85, 0x61, 0xff, 0x48, 0x01, - 0x46, 0x16, 0xb7, 0xb6, 0x5c, 0xcf, 0x8d, 0xf6, 0xd1, 0x1d, 0x18, 0xf3, 0xfc, 0x06, 0x91, 0xff, - 0xd9, 0x57, 0x8c, 0x3e, 0x7f, 0x61, 0x21, 0xbd, 0x94, 0x16, 0xd6, 0x35, 0xbc, 0xa5, 0xa9, 0x07, - 0x07, 0xf3, 0x63, 0x7a, 0x09, 0x36, 0xe8, 0x20, 0x0c, 0xa3, 0x6d, 0xbf, 0xa1, 0xc8, 0x16, 0x18, - 0xd9, 0xf9, 0x2c, 0xb2, 0xd5, 0x18, 0x6d, 0x69, 0xf2, 0xc1, 0xc1, 0xfc, 0xa8, 0x56, 0x80, 0x75, - 0x22, 0x68, 0x13, 0x26, 0xe9, 0x5f, 0x2f, 0x72, 0x15, 0xdd, 0x22, 0xa3, 0x7b, 0x31, 0x8f, 0xae, - 0x86, 0xba, 0x34, 0xf3, 0xe0, 0x60, 0x7e, 0x32, 0x51, 0x88, 0x93, 0x04, 0xed, 0xb7, 0x61, 0x62, - 0x31, 0x8a, 0x9c, 0xfa, 0x0e, 0x69, 0xf0, 0x19, 0x44, 0x2f, 0xc2, 0x80, 0xe7, 0xb4, 0x88, 0x98, - 0xdf, 0x0b, 0x62, 0x60, 0x07, 0xd6, 0x9d, 0x16, 0x39, 0x3c, 0x98, 0x9f, 0xba, 0xed, 0xb9, 0x6f, - 0x75, 0xc4, 0xaa, 0xa0, 0x65, 0x98, 0x61, 0xa3, 0xe7, 0x01, 0x1a, 0x64, 0xcf, 0xad, 0x93, 0xaa, - 0x13, 0xed, 0x88, 0xf9, 0x46, 0xa2, 0x2e, 0x94, 0x15, 0x04, 0x6b, 0x58, 0xf6, 0x7d, 0x28, 0x2d, - 0xee, 0xf9, 0x6e, 0xa3, 0xea, 0x37, 0x42, 0xb4, 0x0b, 0x93, 0xed, 0x80, 0x6c, 0x91, 0x40, 0x15, - 0xcd, 0x5a, 0x17, 0x8a, 0x97, 0x47, 0x9f, 0xbf, 0x9c, 0xf9, 0xb1, 0x26, 0xea, 0x8a, 0x17, 0x05, - 0xfb, 0x4b, 0x8f, 0x89, 0xf6, 0x26, 0x13, 0x50, 0x9c, 0xa4, 0x6c, 0xff, 0x8b, 0x02, 0x9c, 0x5e, - 0x7c, 0xbb, 0x13, 0x90, 0xb2, 0x1b, 0xee, 0x26, 0x57, 0x78, 0xc3, 0x0d, 0x77, 0xd7, 0xe3, 0x11, - 0x50, 0x4b, 0xab, 0x2c, 0xca, 0xb1, 0xc2, 0x40, 0xcf, 0xc1, 0x30, 0xfd, 0x7d, 0x1b, 0x57, 0xc4, - 0x27, 0xcf, 0x08, 0xe4, 0xd1, 0xb2, 0x13, 0x39, 0x65, 0x0e, 0xc2, 0x12, 0x07, 0xad, 0xc1, 0x68, - 0x9d, 0x6d, 0xc8, 0xed, 0x35, 0xbf, 0x41, 0xd8, 0x64, 0x96, 0x96, 0x9e, 0xa1, 0xe8, 0xcb, 0x71, - 0xf1, 0xe1, 0xc1, 0xfc, 0x2c, 0xef, 0x9b, 0x20, 0xa1, 0xc1, 0xb0, 0x5e, 0x1f, 0xd9, 0x6a, 0x7f, - 0x0d, 0x30, 0x4a, 0x90, 0xb1, 0xb7, 0x2e, 0x6b, 0x5b, 0x65, 0x90, 0x6d, 0x95, 0xb1, 0xec, 0x6d, - 0x82, 0xae, 0xc2, 0xc0, 0xae, 0xeb, 0x35, 0x66, 0x87, 0x18, 0xad, 0x73, 0x74, 0xce, 0x6f, 0xb8, - 0x5e, 0xe3, 0xf0, 0x60, 0x7e, 0xda, 0xe8, 0x0e, 0x2d, 0xc4, 0x0c, 0xd5, 0xfe, 0x53, 0x0b, 0xe6, - 0x19, 0x6c, 0xd5, 0x6d, 0x92, 0x2a, 0x09, 0x42, 0x37, 0x8c, 0x88, 0x17, 0x19, 0x03, 0xfa, 0x3c, - 0x40, 0x48, 0xea, 0x01, 0x89, 0xb4, 0x21, 0x55, 0x0b, 0xa3, 0xa6, 0x20, 0x58, 0xc3, 0xa2, 0x07, - 0x42, 0xb8, 0xe3, 0x04, 0x6c, 0x7d, 0x89, 0x81, 0x55, 0x07, 0x42, 0x4d, 0x02, 0x70, 0x8c, 0x63, - 0x1c, 0x08, 0xc5, 0x5e, 0x07, 0x02, 0xfa, 0x18, 0x4c, 0xc6, 0x8d, 0x85, 0x6d, 0xa7, 0x2e, 0x07, - 0x90, 0x6d, 0x99, 0x9a, 0x09, 0xc2, 0x49, 0x5c, 0xfb, 0xef, 0x58, 0x62, 0xf1, 0xd0, 0xaf, 0x7e, - 0x97, 0x7f, 0xab, 0xfd, 0x8b, 0x16, 0x0c, 0x2f, 0xb9, 0x5e, 0xc3, 0xf5, 0xb6, 0xd1, 0x67, 0x61, - 0x84, 0xde, 0x4d, 0x0d, 0x27, 0x72, 0xc4, 0xb9, 0xf7, 0x21, 0x6d, 0x6f, 0xa9, 0xab, 0x62, 0xa1, - 0xbd, 0xbb, 0x4d, 0x0b, 0xc2, 0x05, 0x8a, 0x4d, 0x77, 0xdb, 0xad, 0xcd, 0xcf, 0x91, 0x7a, 0xb4, - 0x46, 0x22, 0x27, 0xfe, 0x9c, 0xb8, 0x0c, 0x2b, 0xaa, 0xe8, 0x06, 0x0c, 0x45, 0x4e, 0xb0, 0x4d, - 0x22, 0x71, 0x00, 0x66, 0x1e, 0x54, 0xbc, 0x26, 0xa6, 0x3b, 0x92, 0x78, 0x75, 0x12, 0x5f, 0x0b, - 0x1b, 0xac, 0x2a, 0x16, 0x24, 0xec, 0x1f, 0x1a, 0x86, 0xb3, 0xcb, 0xb5, 0x4a, 0xce, 0xba, 0xba, - 0x04, 0x43, 0x8d, 0xc0, 0xdd, 0x23, 0x81, 0x18, 0x67, 0x45, 0xa5, 0xcc, 0x4a, 0xb1, 0x80, 0xa2, - 0x97, 0x61, 0x8c, 0x5f, 0x48, 0xd7, 0x1d, 0xaf, 0xd1, 0x94, 0x43, 0x7c, 0x4a, 0x60, 0x8f, 0xdd, - 0xd1, 0x60, 0xd8, 0xc0, 0x3c, 0xe2, 0xa2, 0xba, 0x94, 0xd8, 0x8c, 0x79, 0x97, 0xdd, 0x17, 0x2d, - 0x98, 0xe2, 0xcd, 0x2c, 0x46, 0x51, 0xe0, 0x6e, 0x76, 0x22, 0x12, 0xce, 0x0e, 0xb2, 0x93, 0x6e, - 0x39, 0x6b, 0xb4, 0x72, 0x47, 0x60, 0xe1, 0x4e, 0x82, 0x0a, 0x3f, 0x04, 0x67, 0x45, 0xbb, 0x53, - 0x49, 0x30, 0x4e, 0x35, 0x8b, 0xbe, 0xd3, 0x82, 0xb9, 0xba, 0xef, 0x45, 0x81, 0xdf, 0x6c, 0x92, - 0xa0, 0xda, 0xd9, 0x6c, 0xba, 0xe1, 0x0e, 0x5f, 0xa7, 0x98, 0x6c, 0xb1, 0x93, 0x20, 0x67, 0x0e, - 0x15, 0x92, 0x98, 0xc3, 0xf3, 0x0f, 0x0e, 0xe6, 0xe7, 0x96, 0x73, 0x49, 0xe1, 0x2e, 0xcd, 0xa0, - 0x5d, 0x40, 0xf4, 0x2a, 0xad, 0x45, 0xce, 0x36, 0x89, 0x1b, 0x1f, 0xee, 0xbf, 0xf1, 0x33, 0x0f, - 0x0e, 0xe6, 0xd1, 0x7a, 0x8a, 0x04, 0xce, 0x20, 0x8b, 0xde, 0x82, 0x53, 0xb4, 0x34, 0xf5, 0xad, - 0x23, 0xfd, 0x37, 0x37, 0xfb, 0xe0, 0x60, 0xfe, 0xd4, 0x7a, 0x06, 0x11, 0x9c, 0x49, 0x1a, 0x7d, - 0x87, 0x05, 0x67, 0xe3, 0xcf, 0x5f, 0xb9, 0xdf, 0x76, 0xbc, 0x46, 0xdc, 0x70, 0xa9, 0xff, 0x86, - 0xe9, 0x99, 0x7c, 0x76, 0x39, 0x8f, 0x12, 0xce, 0x6f, 0x64, 0x6e, 0x19, 0x4e, 0x67, 0xae, 0x16, - 0x34, 0x05, 0xc5, 0x5d, 0xc2, 0xb9, 0xa0, 0x12, 0xa6, 0x3f, 0xd1, 0x29, 0x18, 0xdc, 0x73, 0x9a, - 0x1d, 0xb1, 0x51, 0x30, 0xff, 0xf3, 0x4a, 0xe1, 0x65, 0xcb, 0xfe, 0x97, 0x45, 0x98, 0x5c, 0xae, - 0x55, 0x1e, 0x6a, 0x17, 0xea, 0xd7, 0x50, 0xa1, 0xeb, 0x35, 0x14, 0x5f, 0x6a, 0xc5, 0xdc, 0x4b, - 0xed, 0xff, 0xcd, 0xd8, 0x42, 0x03, 0x6c, 0x0b, 0x7d, 0x4b, 0xce, 0x16, 0x3a, 0xe6, 0x8d, 0xb3, - 0x97, 0xb3, 0x8a, 0x06, 0xd9, 0x64, 0x66, 0x72, 0x2c, 0x37, 0xfd, 0xba, 0xd3, 0x4c, 0x1e, 0x7d, - 0x47, 0x5c, 0x4a, 0xc7, 0x33, 0x8f, 0x75, 0x18, 0x5b, 0x76, 0xda, 0xce, 0xa6, 0xdb, 0x74, 0x23, - 0x97, 0x84, 0xe8, 0x29, 0x28, 0x3a, 0x8d, 0x06, 0xe3, 0xb6, 0x4a, 0x4b, 0xa7, 0x1f, 0x1c, 0xcc, - 0x17, 0x17, 0x1b, 0xf4, 0xda, 0x07, 0x85, 0xb5, 0x8f, 0x29, 0x06, 0xfa, 0x20, 0x0c, 0x34, 0x02, - 0xbf, 0x3d, 0x5b, 0x60, 0x98, 0x74, 0xd7, 0x0d, 0x94, 0x03, 0xbf, 0x9d, 0x40, 0x65, 0x38, 0xf6, - 0xaf, 0x16, 0xe0, 0x89, 0x65, 0xd2, 0xde, 0x59, 0xad, 0xe5, 0x9c, 0xdf, 0x97, 0x61, 0xa4, 0xe5, - 0x7b, 0x6e, 0xe4, 0x07, 0xa1, 0x68, 0x9a, 0xad, 0x88, 0x35, 0x51, 0x86, 0x15, 0x14, 0x5d, 0x80, - 0x81, 0x76, 0xcc, 0x54, 0x8e, 0x49, 0x86, 0x94, 0xb1, 0x93, 0x0c, 0x42, 0x31, 0x3a, 0x21, 0x09, - 0xc4, 0x8a, 0x51, 0x18, 0xb7, 0x43, 0x12, 0x60, 0x06, 0x89, 0x6f, 0x66, 0x7a, 0x67, 0x8b, 0x13, - 0x3a, 0x71, 0x33, 0x53, 0x08, 0xd6, 0xb0, 0x50, 0x15, 0x4a, 0x61, 0x62, 0x66, 0xfb, 0xda, 0xa6, - 0xe3, 0xec, 0xea, 0x56, 0x33, 0x19, 0x13, 0x31, 0x6e, 0x94, 0xa1, 0x9e, 0x57, 0xf7, 0x57, 0x0a, - 0x80, 0xf8, 0x10, 0x7e, 0x83, 0x0d, 0xdc, 0xed, 0xf4, 0xc0, 0xf5, 0xbf, 0x25, 0x8e, 0x6b, 0xf4, - 0xfe, 0xcc, 0x82, 0x27, 0x96, 0x5d, 0xaf, 0x41, 0x82, 0x9c, 0x05, 0xf8, 0x68, 0xde, 0xb2, 0x47, - 0x63, 0x1a, 0x8c, 0x25, 0x36, 0x70, 0x0c, 0x4b, 0xcc, 0xfe, 0x63, 0x0b, 0x10, 0xff, 0xec, 0x77, - 0xdd, 0xc7, 0xde, 0x4e, 0x7f, 0xec, 0x31, 0x2c, 0x0b, 0xfb, 0x26, 0x4c, 0x2c, 0x37, 0x5d, 0xe2, - 0x45, 0x95, 0xea, 0xb2, 0xef, 0x6d, 0xb9, 0xdb, 0xe8, 0x15, 0x98, 0x88, 0xdc, 0x16, 0xf1, 0x3b, - 0x51, 0x8d, 0xd4, 0x7d, 0x8f, 0xbd, 0x24, 0xad, 0xcb, 0x83, 0x4b, 0xe8, 0xc1, 0xc1, 0xfc, 0xc4, - 0x86, 0x01, 0xc1, 0x09, 0x4c, 0xfb, 0x77, 0xe9, 0xf8, 0xf9, 0xad, 0xb6, 0xef, 0x11, 0x2f, 0x5a, - 0xf6, 0xbd, 0x06, 0x97, 0x38, 0xbc, 0x02, 0x03, 0x11, 0x1d, 0x0f, 0x3e, 0x76, 0x97, 0xe4, 0x46, - 0xa1, 0xa3, 0x70, 0x78, 0x30, 0x7f, 0x26, 0x5d, 0x83, 0x8d, 0x13, 0xab, 0x83, 0xbe, 0x05, 0x86, - 0xc2, 0xc8, 0x89, 0x3a, 0xa1, 0x18, 0xcd, 0x27, 0xe5, 0x68, 0xd6, 0x58, 0xe9, 0xe1, 0xc1, 0xfc, - 0xa4, 0xaa, 0xc6, 0x8b, 0xb0, 0xa8, 0x80, 0x9e, 0x86, 0xe1, 0x16, 0x09, 0x43, 0x67, 0x5b, 0xde, - 0x86, 0x93, 0xa2, 0xee, 0xf0, 0x1a, 0x2f, 0xc6, 0x12, 0x8e, 0x2e, 0xc2, 0x20, 0x09, 0x02, 0x3f, - 0x10, 0x7b, 0x74, 0x5c, 0x20, 0x0e, 0xae, 0xd0, 0x42, 0xcc, 0x61, 0xf6, 0xbf, 0xb3, 0x60, 0x52, - 0xf5, 0x95, 0xb7, 0x75, 0x02, 0xaf, 0x82, 0x4f, 0x01, 0xd4, 0xe5, 0x07, 0x86, 0xec, 0xf6, 0x18, - 0x7d, 0xfe, 0x52, 0xe6, 0x45, 0x9d, 0x1a, 0xc6, 0x98, 0xb2, 0x2a, 0x0a, 0xb1, 0x46, 0xcd, 0xfe, - 0x27, 0x16, 0xcc, 0x24, 0xbe, 0xe8, 0xa6, 0x1b, 0x46, 0xe8, 0xcd, 0xd4, 0x57, 0x2d, 0xf4, 0xf7, - 0x55, 0xb4, 0x36, 0xfb, 0x26, 0xb5, 0x94, 0x65, 0x89, 0xf6, 0x45, 0xd7, 0x61, 0xd0, 0x8d, 0x48, - 0x4b, 0x7e, 0xcc, 0xc5, 0xae, 0x1f, 0xc3, 0x7b, 0x15, 0xcf, 0x48, 0x85, 0xd6, 0xc4, 0x9c, 0x80, - 0xfd, 0xab, 0x45, 0x28, 0xf1, 0x65, 0xbb, 0xe6, 0xb4, 0x4f, 0x60, 0x2e, 0x9e, 0x81, 0x92, 0xdb, - 0x6a, 0x75, 0x22, 0x67, 0x53, 0x1c, 0xe7, 0x23, 0x7c, 0x6b, 0x55, 0x64, 0x21, 0x8e, 0xe1, 0xa8, - 0x02, 0x03, 0xac, 0x2b, 0xfc, 0x2b, 0x9f, 0xca, 0xfe, 0x4a, 0xd1, 0xf7, 0x85, 0xb2, 0x13, 0x39, - 0x9c, 0x93, 0x52, 0xf7, 0x08, 0x2d, 0xc2, 0x8c, 0x04, 0x72, 0x00, 0x36, 0x5d, 0xcf, 0x09, 0xf6, - 0x69, 0xd9, 0x6c, 0x91, 0x11, 0x7c, 0xae, 0x3b, 0xc1, 0x25, 0x85, 0xcf, 0xc9, 0xaa, 0x0f, 0x8b, - 0x01, 0x58, 0x23, 0x3a, 0xf7, 0x11, 0x28, 0x29, 0xe4, 0xa3, 0x30, 0x44, 0x73, 0x1f, 0x83, 0xc9, - 0x44, 0x5b, 0xbd, 0xaa, 0x8f, 0xe9, 0xfc, 0xd4, 0x2f, 0xb1, 0x23, 0x43, 0xf4, 0x7a, 0xc5, 0xdb, - 0x13, 0x47, 0xee, 0xdb, 0x70, 0xaa, 0x99, 0x71, 0x92, 0x89, 0x79, 0xed, 0xff, 0xe4, 0x7b, 0x42, - 0x7c, 0xf6, 0xa9, 0x2c, 0x28, 0xce, 0x6c, 0x83, 0xf2, 0x08, 0x7e, 0x9b, 0x6e, 0x10, 0xa7, 0xa9, - 0xb3, 0xdb, 0xb7, 0x44, 0x19, 0x56, 0x50, 0x7a, 0xde, 0x9d, 0x52, 0x9d, 0xbf, 0x41, 0xf6, 0x6b, - 0xa4, 0x49, 0xea, 0x91, 0x1f, 0x7c, 0x5d, 0xbb, 0x7f, 0x8e, 0x8f, 0x3e, 0x3f, 0x2e, 0x47, 0x05, - 0x81, 0xe2, 0x0d, 0xb2, 0xcf, 0xa7, 0x42, 0xff, 0xba, 0x62, 0xd7, 0xaf, 0xfb, 0x19, 0x0b, 0xc6, - 0xd5, 0xd7, 0x9d, 0xc0, 0xb9, 0xb0, 0x64, 0x9e, 0x0b, 0xe7, 0xba, 0x2e, 0xf0, 0x9c, 0x13, 0xe1, - 0x2b, 0x05, 0x38, 0xab, 0x70, 0xe8, 0xdb, 0x80, 0xff, 0x11, 0xab, 0xea, 0x0a, 0x94, 0x3c, 0x25, - 0xb5, 0xb2, 0x4c, 0x71, 0x51, 0x2c, 0xb3, 0x8a, 0x71, 0x28, 0x8b, 0xe7, 0xc5, 0xa2, 0xa5, 0x31, - 0x5d, 0x9c, 0x2b, 0x44, 0xb7, 0x4b, 0x50, 0xec, 0xb8, 0x0d, 0x71, 0xc1, 0x7c, 0x48, 0x8e, 0xf6, - 0xed, 0x4a, 0xf9, 0xf0, 0x60, 0xfe, 0xc9, 0x3c, 0x55, 0x02, 0xbd, 0xd9, 0xc2, 0x85, 0xdb, 0x95, - 0x32, 0xa6, 0x95, 0xd1, 0x22, 0x4c, 0x4a, 0x6d, 0xc9, 0x1d, 0xca, 0x6e, 0xf9, 0x9e, 0xb8, 0x87, - 0x94, 0x4c, 0x16, 0x9b, 0x60, 0x9c, 0xc4, 0x47, 0x65, 0x98, 0xda, 0xed, 0x6c, 0x92, 0x26, 0x89, - 0xf8, 0x07, 0xdf, 0x20, 0x5c, 0x62, 0x59, 0x8a, 0x5f, 0x66, 0x37, 0x12, 0x70, 0x9c, 0xaa, 0x61, - 0xff, 0x05, 0xbb, 0x0f, 0xc4, 0xe8, 0x55, 0x03, 0x9f, 0x2e, 0x2c, 0x4a, 0xfd, 0xeb, 0xb9, 0x9c, - 0xfb, 0x59, 0x15, 0x37, 0xc8, 0xfe, 0x86, 0x4f, 0x39, 0xf3, 0xec, 0x55, 0x61, 0xac, 0xf9, 0x81, - 0xae, 0x6b, 0xfe, 0xe7, 0x0a, 0x70, 0x5a, 0x8d, 0x80, 0xc1, 0x04, 0x7e, 0xa3, 0x8f, 0xc1, 0x55, - 0x18, 0x6d, 0x90, 0x2d, 0xa7, 0xd3, 0x8c, 0x94, 0xf8, 0x7c, 0x90, 0xab, 0x50, 0xca, 0x71, 0x31, - 0xd6, 0x71, 0x8e, 0x30, 0x6c, 0xff, 0x73, 0x94, 0x5d, 0xc4, 0x91, 0x43, 0xd7, 0xb8, 0xda, 0x35, - 0x56, 0xee, 0xae, 0xb9, 0x08, 0x83, 0x6e, 0x8b, 0x32, 0x66, 0x05, 0x93, 0xdf, 0xaa, 0xd0, 0x42, - 0xcc, 0x61, 0xe8, 0x03, 0x30, 0x5c, 0xf7, 0x5b, 0x2d, 0xc7, 0x6b, 0xb0, 0x2b, 0xaf, 0xb4, 0x34, - 0x4a, 0x79, 0xb7, 0x65, 0x5e, 0x84, 0x25, 0x0c, 0x3d, 0x01, 0x03, 0x4e, 0xb0, 0xcd, 0x65, 0x18, - 0xa5, 0xa5, 0x11, 0xda, 0xd2, 0x62, 0xb0, 0x1d, 0x62, 0x56, 0x4a, 0x9f, 0x60, 0xf7, 0xfc, 0x60, - 0xd7, 0xf5, 0xb6, 0xcb, 0x6e, 0x20, 0xb6, 0x84, 0xba, 0x0b, 0xef, 0x2a, 0x08, 0xd6, 0xb0, 0xd0, - 0x2a, 0x0c, 0xb6, 0xfd, 0x20, 0x0a, 0x67, 0x87, 0xd8, 0x70, 0x3f, 0x99, 0x73, 0x10, 0xf1, 0xaf, - 0xad, 0xfa, 0x41, 0x14, 0x7f, 0x00, 0xfd, 0x17, 0x62, 0x5e, 0x1d, 0xdd, 0x84, 0x61, 0xe2, 0xed, - 0xad, 0x06, 0x7e, 0x6b, 0x76, 0x26, 0x9f, 0xd2, 0x0a, 0x47, 0xe1, 0xcb, 0x2c, 0xe6, 0x51, 0x45, - 0x31, 0x96, 0x24, 0xd0, 0xb7, 0x40, 0x91, 0x78, 0x7b, 0xb3, 0xc3, 0x8c, 0xd2, 0x5c, 0x0e, 0xa5, - 0x3b, 0x4e, 0x10, 0x9f, 0xf9, 0x2b, 0xde, 0x1e, 0xa6, 0x75, 0xd0, 0x27, 0xa1, 0x24, 0x0f, 0x8c, - 0x50, 0x08, 0xeb, 0x32, 0x17, 0xac, 0x3c, 0x66, 0x30, 0x79, 0xab, 0xe3, 0x06, 0xa4, 0x45, 0xbc, - 0x28, 0x8c, 0x4f, 0x48, 0x09, 0x0d, 0x71, 0x4c, 0x0d, 0x7d, 0x52, 0x4a, 0x88, 0xd7, 0xfc, 0x8e, - 0x17, 0x85, 0xb3, 0x25, 0xd6, 0xbd, 0x4c, 0xdd, 0xdd, 0x9d, 0x18, 0x2f, 0x29, 0x42, 0xe6, 0x95, - 0xb1, 0x41, 0x0a, 0x7d, 0x1a, 0xc6, 0xf9, 0x7f, 0xae, 0x01, 0x0b, 0x67, 0x4f, 0x33, 0xda, 0x17, - 0xf2, 0x69, 0x73, 0xc4, 0xa5, 0xd3, 0x82, 0xf8, 0xb8, 0x5e, 0x1a, 0x62, 0x93, 0x1a, 0xc2, 0x30, - 0xde, 0x74, 0xf7, 0x88, 0x47, 0xc2, 0xb0, 0x1a, 0xf8, 0x9b, 0x64, 0x16, 0xd8, 0xc0, 0x9c, 0xcd, - 0xd6, 0x98, 0xf9, 0x9b, 0x64, 0x69, 0x9a, 0xd2, 0xbc, 0xa9, 0xd7, 0xc1, 0x26, 0x09, 0x74, 0x1b, - 0x26, 0xe8, 0x8b, 0xcd, 0x8d, 0x89, 0x8e, 0xf6, 0x22, 0xca, 0xde, 0x55, 0xd8, 0xa8, 0x84, 0x13, - 0x44, 0xd0, 0x2d, 0x18, 0x0b, 0x23, 0x27, 0x88, 0x3a, 0x6d, 0x4e, 0xf4, 0x4c, 0x2f, 0xa2, 0x4c, - 0xe1, 0x5a, 0xd3, 0xaa, 0x60, 0x83, 0x00, 0x7a, 0x1d, 0x4a, 0x4d, 0x77, 0x8b, 0xd4, 0xf7, 0xeb, - 0x4d, 0x32, 0x3b, 0xc6, 0xa8, 0x65, 0x1e, 0x2a, 0x37, 0x25, 0x12, 0xe7, 0x73, 0xd5, 0x5f, 0x1c, - 0x57, 0x47, 0x77, 0xe0, 0x4c, 0x44, 0x82, 0x96, 0xeb, 0x39, 0xf4, 0x30, 0x10, 0x4f, 0x2b, 0xa6, - 0xc8, 0x1c, 0x67, 0xbb, 0xed, 0xbc, 0x98, 0x8d, 0x33, 0x1b, 0x99, 0x58, 0x38, 0xa7, 0x36, 0xba, - 0x0f, 0xb3, 0x19, 0x10, 0xbf, 0xe9, 0xd6, 0xf7, 0x67, 0x4f, 0x31, 0xca, 0x1f, 0x15, 0x94, 0x67, - 0x37, 0x72, 0xf0, 0x0e, 0xbb, 0xc0, 0x70, 0x2e, 0x75, 0x74, 0x0b, 0x26, 0xd9, 0x09, 0x54, 0xed, - 0x34, 0x9b, 0xa2, 0xc1, 0x09, 0xd6, 0xe0, 0x07, 0xe4, 0x7d, 0x5c, 0x31, 0xc1, 0x87, 0x07, 0xf3, - 0x10, 0xff, 0xc3, 0xc9, 0xda, 0x68, 0x93, 0xe9, 0xcc, 0x3a, 0x81, 0x1b, 0xed, 0xd3, 0x73, 0x83, - 0xdc, 0x8f, 0x66, 0x27, 0xbb, 0xca, 0x2b, 0x74, 0x54, 0xa5, 0x58, 0xd3, 0x0b, 0x71, 0x92, 0x20, - 0x3d, 0x52, 0xc3, 0xa8, 0xe1, 0x7a, 0xb3, 0x53, 0xfc, 0x5d, 0x22, 0x4f, 0xa4, 0x1a, 0x2d, 0xc4, - 0x1c, 0xc6, 0xf4, 0x65, 0xf4, 0xc7, 0x2d, 0x7a, 0x73, 0x4d, 0x33, 0xc4, 0x58, 0x5f, 0x26, 0x01, - 0x38, 0xc6, 0xa1, 0xcc, 0x64, 0x14, 0xed, 0xcf, 0x22, 0x86, 0xaa, 0x0e, 0x96, 0x8d, 0x8d, 0x4f, - 0x62, 0x5a, 0x6e, 0x6f, 0xc2, 0x84, 0x3a, 0x08, 0xd9, 0x98, 0xa0, 0x79, 0x18, 0x64, 0xec, 0x93, - 0x90, 0xae, 0x95, 0x68, 0x17, 0x18, 0x6b, 0x85, 0x79, 0x39, 0xeb, 0x82, 0xfb, 0x36, 0x59, 0xda, - 0x8f, 0x08, 0x7f, 0xd3, 0x17, 0xb5, 0x2e, 0x48, 0x00, 0x8e, 0x71, 0xec, 0xff, 0xc3, 0xd9, 0xd0, - 0xf8, 0xb4, 0xed, 0xe3, 0x7e, 0x79, 0x16, 0x46, 0x76, 0xfc, 0x30, 0xa2, 0xd8, 0xac, 0x8d, 0xc1, - 0x98, 0xf1, 0xbc, 0x2e, 0xca, 0xb1, 0xc2, 0x40, 0xaf, 0xc2, 0x78, 0x5d, 0x6f, 0x40, 0x5c, 0x8e, - 0xea, 0x18, 0x31, 0x5a, 0xc7, 0x26, 0x2e, 0x7a, 0x19, 0x46, 0x98, 0x0d, 0x48, 0xdd, 0x6f, 0x0a, - 0xae, 0x4d, 0xde, 0xf0, 0x23, 0x55, 0x51, 0x7e, 0xa8, 0xfd, 0xc6, 0x0a, 0x1b, 0x5d, 0x82, 0x21, - 0xda, 0x85, 0x4a, 0x55, 0x5c, 0x4b, 0x4a, 0x50, 0x74, 0x9d, 0x95, 0x62, 0x01, 0xb5, 0xff, 0x52, - 0x41, 0x1b, 0x65, 0xfa, 0x1e, 0x26, 0xa8, 0x0a, 0xc3, 0xf7, 0x1c, 0x37, 0x72, 0xbd, 0x6d, 0xc1, - 0x7f, 0x3c, 0xdd, 0xf5, 0x8e, 0x62, 0x95, 0xee, 0xf2, 0x0a, 0xfc, 0x16, 0x15, 0x7f, 0xb0, 0x24, - 0x43, 0x29, 0x06, 0x1d, 0xcf, 0xa3, 0x14, 0x0b, 0xfd, 0x52, 0xc4, 0xbc, 0x02, 0xa7, 0x28, 0xfe, - 0x60, 0x49, 0x06, 0xbd, 0x09, 0x20, 0x77, 0x18, 0x69, 0x08, 0xdb, 0x8b, 0x67, 0x7b, 0x13, 0xdd, - 0x50, 0x75, 0x96, 0x26, 0xe8, 0x1d, 0x1d, 0xff, 0xc7, 0x1a, 0x3d, 0x3b, 0x62, 0x7c, 0x5a, 0xba, - 0x33, 0xe8, 0xdb, 0xe8, 0x12, 0x77, 0x82, 0x88, 0x34, 0x16, 0x23, 0x31, 0x38, 0x1f, 0xec, 0xef, - 0x91, 0xb2, 0xe1, 0xb6, 0x88, 0xbe, 0x1d, 0x04, 0x11, 0x1c, 0xd3, 0xb3, 0x7f, 0xa1, 0x08, 0xb3, - 0x79, 0xdd, 0xa5, 0x8b, 0x8e, 0xdc, 0x77, 0xa3, 0x65, 0xca, 0x5e, 0x59, 0xe6, 0xa2, 0x5b, 0x11, - 0xe5, 0x58, 0x61, 0xd0, 0xd9, 0x0f, 0xdd, 0x6d, 0xf9, 0xc6, 0x1c, 0x8c, 0x67, 0xbf, 0xc6, 0x4a, - 0xb1, 0x80, 0x52, 0xbc, 0x80, 0x38, 0xa1, 0x30, 0xee, 0xd1, 0x56, 0x09, 0x66, 0xa5, 0x58, 0x40, - 0x75, 0x69, 0xd7, 0x40, 0x0f, 0x69, 0x97, 0x31, 0x44, 0x83, 0xc7, 0x3b, 0x44, 0xe8, 0x33, 0x00, - 0x5b, 0xae, 0xe7, 0x86, 0x3b, 0x8c, 0xfa, 0xd0, 0x91, 0xa9, 0x2b, 0xe6, 0x6c, 0x55, 0x51, 0xc1, - 0x1a, 0x45, 0xf4, 0x12, 0x8c, 0xaa, 0x0d, 0x58, 0x29, 0x33, 0x4d, 0xa7, 0x66, 0x39, 0x12, 0x9f, - 0x46, 0x65, 0xac, 0xe3, 0xd9, 0x9f, 0x4b, 0xae, 0x17, 0xb1, 0x03, 0xb4, 0xf1, 0xb5, 0xfa, 0x1d, - 0xdf, 0x42, 0xf7, 0xf1, 0xb5, 0xbf, 0x56, 0x84, 0x49, 0xa3, 0xb1, 0x4e, 0xd8, 0xc7, 0x99, 0x75, - 0x8d, 0x1e, 0xe0, 0x4e, 0x44, 0xc4, 0xfe, 0xb3, 0x7b, 0x6f, 0x15, 0xfd, 0x90, 0xa7, 0x3b, 0x80, - 0xd7, 0x47, 0x9f, 0x81, 0x52, 0xd3, 0x09, 0x99, 0xe4, 0x8c, 0x88, 0x7d, 0xd7, 0x0f, 0xb1, 0xf8, - 0x61, 0xe2, 0x84, 0x91, 0x76, 0x6b, 0x72, 0xda, 0x31, 0x49, 0x7a, 0xd3, 0x50, 0xfe, 0x44, 0x5a, - 0x8f, 0xa9, 0x4e, 0x50, 0x26, 0x66, 0x1f, 0x73, 0x18, 0x7a, 0x19, 0xc6, 0x02, 0xc2, 0x56, 0xc5, - 0x32, 0xe5, 0xe6, 0xd8, 0x32, 0x1b, 0x8c, 0xd9, 0x3e, 0xac, 0xc1, 0xb0, 0x81, 0x19, 0xbf, 0x0d, - 0x86, 0xba, 0xbc, 0x0d, 0x9e, 0x86, 0x61, 0xf6, 0x43, 0xad, 0x00, 0x35, 0x1b, 0x15, 0x5e, 0x8c, - 0x25, 0x3c, 0xb9, 0x60, 0x46, 0xfa, 0x5b, 0x30, 0xf4, 0xf5, 0x21, 0x16, 0x35, 0xd3, 0x32, 0x8f, - 0xf0, 0x53, 0x4e, 0x2c, 0x79, 0x2c, 0x61, 0xf6, 0x07, 0x61, 0xa2, 0xec, 0x90, 0x96, 0xef, 0xad, - 0x78, 0x8d, 0xb6, 0xef, 0x7a, 0x11, 0x9a, 0x85, 0x01, 0x76, 0x89, 0xf0, 0x23, 0x60, 0x80, 0x36, - 0x84, 0x59, 0x89, 0xbd, 0x0d, 0xa7, 0xcb, 0xfe, 0x3d, 0xef, 0x9e, 0x13, 0x34, 0x16, 0xab, 0x15, - 0xed, 0x7d, 0xbd, 0x2e, 0xdf, 0x77, 0xdc, 0x68, 0x2b, 0xf3, 0xe8, 0xd5, 0x6a, 0x72, 0xb6, 0x76, - 0xd5, 0x6d, 0x92, 0x1c, 0x29, 0xc8, 0x5f, 0x2d, 0x18, 0x2d, 0xc5, 0xf8, 0x4a, 0xab, 0x65, 0xe5, - 0x6a, 0xb5, 0xde, 0x80, 0x91, 0x2d, 0x97, 0x34, 0x1b, 0x98, 0x6c, 0x89, 0x95, 0xf8, 0x54, 0xbe, - 0x1d, 0xca, 0x2a, 0xc5, 0x94, 0x52, 0x2f, 0xfe, 0x3a, 0x5c, 0x15, 0x95, 0xb1, 0x22, 0x83, 0x76, - 0x61, 0x4a, 0x3e, 0x18, 0x24, 0x54, 0xac, 0xcb, 0xa7, 0xbb, 0xbd, 0x42, 0x4c, 0xe2, 0xa7, 0x1e, - 0x1c, 0xcc, 0x4f, 0xe1, 0x04, 0x19, 0x9c, 0x22, 0x4c, 0x9f, 0x83, 0x2d, 0x7a, 0x02, 0x0f, 0xb0, - 0xe1, 0x67, 0xcf, 0x41, 0xf6, 0xb2, 0x65, 0xa5, 0xf6, 0x8f, 0x59, 0xf0, 0x58, 0x6a, 0x64, 0xc4, - 0x0b, 0xff, 0x98, 0x67, 0x21, 0xf9, 0xe2, 0x2e, 0xf4, 0x7e, 0x71, 0xdb, 0x7f, 0xd7, 0x82, 0x53, - 0x2b, 0xad, 0x76, 0xb4, 0x5f, 0x76, 0x4d, 0x15, 0xd4, 0x47, 0x60, 0xa8, 0x45, 0x1a, 0x6e, 0xa7, - 0x25, 0x66, 0x6e, 0x5e, 0x9e, 0x52, 0x6b, 0xac, 0xf4, 0xf0, 0x60, 0x7e, 0xbc, 0x16, 0xf9, 0x81, - 0xb3, 0x4d, 0x78, 0x01, 0x16, 0xe8, 0xec, 0xac, 0x77, 0xdf, 0x26, 0x37, 0xdd, 0x96, 0x2b, 0xed, - 0x8a, 0xba, 0xca, 0xec, 0x16, 0xe4, 0x80, 0x2e, 0xbc, 0xd1, 0x71, 0xbc, 0xc8, 0x8d, 0xf6, 0x85, - 0xf6, 0x48, 0x12, 0xc1, 0x31, 0x3d, 0xfb, 0xab, 0x16, 0x4c, 0xca, 0x75, 0xbf, 0xd8, 0x68, 0x04, - 0x24, 0x0c, 0xd1, 0x1c, 0x14, 0xdc, 0xb6, 0xe8, 0x25, 0x88, 0x5e, 0x16, 0x2a, 0x55, 0x5c, 0x70, - 0xdb, 0x92, 0x2d, 0x63, 0x07, 0x61, 0xd1, 0x54, 0xa4, 0x5d, 0x17, 0xe5, 0x58, 0x61, 0xa0, 0xcb, - 0x30, 0xe2, 0xf9, 0x0d, 0x6e, 0xdb, 0xc5, 0xaf, 0x34, 0xb6, 0xc0, 0xd6, 0x45, 0x19, 0x56, 0x50, - 0x54, 0x85, 0x12, 0x37, 0x7b, 0x8a, 0x17, 0x6d, 0x5f, 0xc6, 0x53, 0xec, 0xcb, 0x36, 0x64, 0x4d, - 0x1c, 0x13, 0xb1, 0x7f, 0xc5, 0x82, 0x31, 0xf9, 0x65, 0x7d, 0xf2, 0x9c, 0x74, 0x6b, 0xc5, 0xfc, - 0x66, 0xbc, 0xb5, 0x28, 0xcf, 0xc8, 0x20, 0x06, 0xab, 0x58, 0x3c, 0x12, 0xab, 0x78, 0x15, 0x46, - 0x9d, 0x76, 0xbb, 0x6a, 0xf2, 0x99, 0x6c, 0x29, 0x2d, 0xc6, 0xc5, 0x58, 0xc7, 0xb1, 0x7f, 0xb4, - 0x00, 0x13, 0xf2, 0x0b, 0x6a, 0x9d, 0xcd, 0x90, 0x44, 0x68, 0x03, 0x4a, 0x0e, 0x9f, 0x25, 0x22, - 0x17, 0xf9, 0xc5, 0x6c, 0x39, 0x82, 0x31, 0xa5, 0xf1, 0x85, 0xbf, 0x28, 0x6b, 0xe3, 0x98, 0x10, - 0x6a, 0xc2, 0xb4, 0xe7, 0x47, 0xec, 0xf0, 0x57, 0xf0, 0x6e, 0xaa, 0x9d, 0x24, 0xf5, 0xb3, 0x82, - 0xfa, 0xf4, 0x7a, 0x92, 0x0a, 0x4e, 0x13, 0x46, 0x2b, 0x52, 0x36, 0x53, 0xcc, 0x17, 0x06, 0xe8, - 0x13, 0x97, 0x2d, 0x9a, 0xb1, 0x7f, 0xd9, 0x82, 0x92, 0x44, 0x3b, 0x09, 0x2d, 0xde, 0x1a, 0x0c, - 0x87, 0x6c, 0x12, 0xe4, 0xd0, 0xd8, 0xdd, 0x3a, 0xce, 0xe7, 0x2b, 0xbe, 0xd3, 0xf8, 0xff, 0x10, - 0x4b, 0x1a, 0x4c, 0x34, 0xaf, 0xba, 0xff, 0x2e, 0x11, 0xcd, 0xab, 0xfe, 0xe4, 0x5c, 0x4a, 0x7f, - 0xc8, 0xfa, 0xac, 0xc9, 0xba, 0x28, 0xeb, 0xd5, 0x0e, 0xc8, 0x96, 0x7b, 0x3f, 0xc9, 0x7a, 0x55, - 0x59, 0x29, 0x16, 0x50, 0xf4, 0x26, 0x8c, 0xd5, 0xa5, 0x4c, 0x36, 0xde, 0xe1, 0x97, 0xba, 0xea, - 0x07, 0x94, 0x2a, 0x89, 0xcb, 0x42, 0x96, 0xb5, 0xfa, 0xd8, 0xa0, 0x66, 0x9a, 0x11, 0x14, 0x7b, - 0x99, 0x11, 0xc4, 0x74, 0xf3, 0x95, 0xea, 0x3f, 0x6e, 0xc1, 0x10, 0x97, 0xc5, 0xf5, 0x27, 0x0a, - 0xd5, 0x34, 0x6b, 0xf1, 0xd8, 0xdd, 0xa1, 0x85, 0x42, 0x53, 0x86, 0xd6, 0xa0, 0xc4, 0x7e, 0x30, - 0x59, 0x62, 0x31, 0xdf, 0xea, 0x9e, 0xb7, 0xaa, 0x77, 0xf0, 0x8e, 0xac, 0x86, 0x63, 0x0a, 0xf6, - 0x0f, 0x17, 0xe9, 0xe9, 0x16, 0xa3, 0x1a, 0x97, 0xbe, 0xf5, 0xe8, 0x2e, 0xfd, 0xc2, 0xa3, 0xba, - 0xf4, 0xb7, 0x61, 0xb2, 0xae, 0xe9, 0xe1, 0xe2, 0x99, 0xbc, 0xdc, 0x75, 0x91, 0x68, 0x2a, 0x3b, - 0x2e, 0x65, 0x59, 0x36, 0x89, 0xe0, 0x24, 0x55, 0xf4, 0x6d, 0x30, 0xc6, 0xe7, 0x59, 0xb4, 0xc2, - 0x2d, 0x31, 0x3e, 0x90, 0xbf, 0x5e, 0xf4, 0x26, 0xb8, 0x54, 0x4e, 0xab, 0x8e, 0x0d, 0x62, 0xf6, - 0x9f, 0x58, 0x80, 0x56, 0xda, 0x3b, 0xa4, 0x45, 0x02, 0xa7, 0x19, 0x8b, 0xd3, 0xbf, 0xdf, 0x82, - 0x59, 0x92, 0x2a, 0x5e, 0xf6, 0x5b, 0x2d, 0xf1, 0x68, 0xc9, 0x79, 0x57, 0xaf, 0xe4, 0xd4, 0x51, - 0x6e, 0x09, 0xb3, 0x79, 0x18, 0x38, 0xb7, 0x3d, 0xb4, 0x06, 0x33, 0xfc, 0x96, 0x54, 0x00, 0xcd, - 0xf6, 0xfa, 0x71, 0x41, 0x78, 0x66, 0x23, 0x8d, 0x82, 0xb3, 0xea, 0xd9, 0xdf, 0x35, 0x06, 0xb9, - 0xbd, 0x78, 0x4f, 0x8f, 0xf0, 0x9e, 0x1e, 0xe1, 0x3d, 0x3d, 0xc2, 0x7b, 0x7a, 0x84, 0xf7, 0xf4, - 0x08, 0xdf, 0xf4, 0x7a, 0x84, 0x3f, 0xb2, 0x60, 0x26, 0x7d, 0x0d, 0x9c, 0x04, 0x63, 0xde, 0x81, - 0x99, 0xf4, 0x5d, 0xd7, 0xd5, 0xce, 0x2e, 0xdd, 0xcf, 0xf8, 0xde, 0xcb, 0xf8, 0x06, 0x9c, 0x45, - 0xdf, 0xfe, 0xcb, 0x16, 0x9c, 0x56, 0xc8, 0xc6, 0x4b, 0xff, 0xf3, 0x30, 0xc3, 0xcf, 0x97, 0xe5, - 0xa6, 0xe3, 0xb6, 0x36, 0x48, 0xab, 0xdd, 0x74, 0x22, 0x69, 0x66, 0x70, 0x35, 0x73, 0xab, 0x26, - 0x4c, 0x74, 0x8d, 0x8a, 0x4b, 0x8f, 0xd1, 0x7e, 0x65, 0x00, 0x70, 0x56, 0x33, 0xf6, 0x2f, 0x8c, - 0xc0, 0xe0, 0xca, 0x1e, 0xf1, 0xa2, 0x13, 0x18, 0xfa, 0x3a, 0x4c, 0xb8, 0xde, 0x9e, 0xdf, 0xdc, - 0x23, 0x0d, 0x0e, 0x3f, 0xca, 0xd3, 0xfd, 0x8c, 0x20, 0x3d, 0x51, 0x31, 0x48, 0xe0, 0x04, 0xc9, - 0x47, 0x21, 0x3e, 0xbf, 0x06, 0x43, 0xfc, 0xd6, 0x12, 0xb2, 0xf3, 0xcc, 0x4b, 0x8a, 0x0d, 0xa2, - 0xb8, 0x8b, 0x63, 0xd1, 0x3e, 0xbf, 0x15, 0x45, 0x75, 0xf4, 0x39, 0x98, 0xd8, 0x72, 0x83, 0x30, - 0xda, 0x70, 0x5b, 0x24, 0x8c, 0x9c, 0x56, 0xfb, 0x21, 0xc4, 0xe5, 0x6a, 0x1c, 0x56, 0x0d, 0x4a, - 0x38, 0x41, 0x19, 0x6d, 0xc3, 0x78, 0xd3, 0xd1, 0x9b, 0x1a, 0x3e, 0x72, 0x53, 0xea, 0x3a, 0xbc, - 0xa9, 0x13, 0xc2, 0x26, 0x5d, 0x7a, 0x7e, 0xd4, 0x99, 0xc4, 0x77, 0x84, 0xc9, 0x41, 0xd4, 0xf9, - 0xc1, 0x45, 0xbd, 0x1c, 0x46, 0x39, 0x3b, 0x66, 0x11, 0x5c, 0x32, 0x39, 0x3b, 0xcd, 0xee, 0xf7, - 0xb3, 0x50, 0x22, 0x74, 0x08, 0x29, 0x61, 0x71, 0xa3, 0x5e, 0xe9, 0xaf, 0xaf, 0x6b, 0x6e, 0x3d, - 0xf0, 0x4d, 0x45, 0xc5, 0x8a, 0xa4, 0x84, 0x63, 0xa2, 0x68, 0x19, 0x86, 0x42, 0x12, 0xb8, 0x24, - 0x14, 0x77, 0x6b, 0x97, 0x69, 0x64, 0x68, 0xdc, 0x99, 0x86, 0xff, 0xc6, 0xa2, 0x2a, 0x5d, 0x5e, - 0x0e, 0x93, 0xe1, 0xb2, 0xdb, 0x4f, 0x5b, 0x5e, 0x8b, 0xac, 0x14, 0x0b, 0x28, 0x7a, 0x1d, 0x86, - 0x03, 0xd2, 0x64, 0x9a, 0xb0, 0xf1, 0xfe, 0x17, 0x39, 0x57, 0xac, 0xf1, 0x7a, 0x58, 0x12, 0x40, - 0x37, 0x00, 0x05, 0x84, 0x72, 0x86, 0xae, 0xb7, 0xad, 0xec, 0x64, 0xc5, 0xcd, 0xa2, 0x4e, 0x22, - 0x1c, 0x63, 0x48, 0xbf, 0x26, 0x9c, 0x51, 0x0d, 0x5d, 0x83, 0x69, 0x55, 0x5a, 0xf1, 0xc2, 0xc8, - 0xa1, 0x27, 0xfa, 0x24, 0xa3, 0xa5, 0x04, 0x33, 0x38, 0x89, 0x80, 0xd3, 0x75, 0xec, 0x2f, 0x5b, - 0xc0, 0xc7, 0xf9, 0x04, 0xc4, 0x11, 0xaf, 0x99, 0xe2, 0x88, 0xb3, 0xb9, 0x33, 0x97, 0x23, 0x8a, - 0xf8, 0xb2, 0x05, 0xa3, 0xda, 0xcc, 0xc6, 0x6b, 0xd6, 0xea, 0xb2, 0x66, 0x3b, 0x30, 0x45, 0x57, - 0xfa, 0xad, 0xcd, 0x90, 0x04, 0x7b, 0xa4, 0xc1, 0x16, 0x66, 0xe1, 0xe1, 0x16, 0xa6, 0xb2, 0xc9, - 0xbb, 0x99, 0x20, 0x88, 0x53, 0x4d, 0xd8, 0x9f, 0x95, 0x5d, 0x55, 0x26, 0x8c, 0x75, 0x35, 0xe7, - 0x09, 0x13, 0x46, 0x35, 0xab, 0x38, 0xc6, 0xa1, 0x5b, 0x6d, 0xc7, 0x0f, 0xa3, 0xa4, 0x09, 0xe3, - 0x75, 0x3f, 0x8c, 0x30, 0x83, 0xd8, 0x2f, 0x00, 0xac, 0xdc, 0x27, 0x75, 0xbe, 0x62, 0xf5, 0xd7, - 0x92, 0x95, 0xff, 0x5a, 0xb2, 0x7f, 0xcb, 0x82, 0x89, 0xd5, 0x65, 0xe3, 0xe6, 0x5a, 0x00, 0xe0, - 0x4f, 0xbc, 0xbb, 0x77, 0xd7, 0xa5, 0xfe, 0x9f, 0xab, 0x70, 0x55, 0x29, 0xd6, 0x30, 0xd0, 0x59, - 0x28, 0x36, 0x3b, 0x9e, 0x90, 0x97, 0x0e, 0x53, 0x7e, 0xe0, 0x66, 0xc7, 0xc3, 0xb4, 0x4c, 0xf3, - 0xa1, 0x28, 0xf6, 0xed, 0x43, 0xd1, 0x33, 0x96, 0x01, 0x9a, 0x87, 0xc1, 0x7b, 0xf7, 0xdc, 0x06, - 0xf7, 0x18, 0x15, 0xb6, 0x09, 0x77, 0xef, 0x56, 0xca, 0x21, 0xe6, 0xe5, 0xf6, 0x97, 0x8a, 0x30, - 0xb7, 0xda, 0x24, 0xf7, 0xdf, 0xa1, 0xd7, 0x6c, 0xbf, 0x1e, 0x20, 0x47, 0x93, 0x3c, 0x1d, 0xd5, - 0xcb, 0xa7, 0xf7, 0x78, 0x6c, 0xc1, 0x30, 0xb7, 0xe0, 0x93, 0x3e, 0xb4, 0xaf, 0x66, 0xb5, 0x9e, - 0x3f, 0x20, 0x0b, 0xdc, 0x12, 0x50, 0xb8, 0x00, 0xaa, 0x0b, 0x53, 0x94, 0x62, 0x49, 0x7c, 0xee, - 0x15, 0x18, 0xd3, 0x31, 0x8f, 0xe4, 0x6f, 0xf7, 0xff, 0x15, 0x61, 0x8a, 0xf6, 0xe0, 0x91, 0x4e, - 0xc4, 0xed, 0xf4, 0x44, 0x1c, 0xb7, 0xcf, 0x55, 0xef, 0xd9, 0x78, 0x33, 0x39, 0x1b, 0x57, 0xf3, - 0x66, 0xe3, 0xa4, 0xe7, 0xe0, 0x3b, 0x2d, 0x98, 0x59, 0x6d, 0xfa, 0xf5, 0xdd, 0x84, 0x5f, 0xd4, - 0x4b, 0x30, 0x4a, 0x8f, 0xe3, 0xd0, 0x70, 0xd9, 0x37, 0x82, 0x38, 0x08, 0x10, 0xd6, 0xf1, 0xb4, - 0x6a, 0xb7, 0x6f, 0x57, 0xca, 0x59, 0xb1, 0x1f, 0x04, 0x08, 0xeb, 0x78, 0xf6, 0x6f, 0x58, 0x70, - 0xee, 0xda, 0xf2, 0x4a, 0xbc, 0x14, 0x53, 0xe1, 0x27, 0x2e, 0xc1, 0x50, 0xbb, 0xa1, 0x75, 0x25, - 0x96, 0x27, 0x97, 0x59, 0x2f, 0x04, 0xf4, 0xdd, 0x12, 0x5a, 0xe5, 0xa7, 0x2c, 0x98, 0xb9, 0xe6, - 0x46, 0xf4, 0x76, 0x4d, 0x06, 0x42, 0xa0, 0xd7, 0x6b, 0xe8, 0x46, 0x7e, 0xb0, 0x9f, 0x0c, 0x84, - 0x80, 0x15, 0x04, 0x6b, 0x58, 0xbc, 0xe5, 0x3d, 0x97, 0xd9, 0x8e, 0x17, 0x4c, 0xcd, 0x1a, 0x16, - 0xe5, 0x58, 0x61, 0xd0, 0x0f, 0x6b, 0xb8, 0x01, 0x13, 0x4a, 0xee, 0x8b, 0x13, 0x56, 0x7d, 0x58, - 0x59, 0x02, 0x70, 0x8c, 0x43, 0xdf, 0x67, 0xf3, 0xd7, 0x9a, 0x9d, 0x30, 0x22, 0xc1, 0x56, 0x98, - 0x73, 0x3a, 0xbe, 0x00, 0x25, 0x22, 0x55, 0x00, 0xa2, 0xd7, 0x8a, 0x63, 0x54, 0xba, 0x01, 0x1e, - 0x8f, 0x41, 0xe1, 0xf5, 0xe1, 0x65, 0x79, 0x34, 0x37, 0xb9, 0x55, 0x40, 0x44, 0x6f, 0x4b, 0x0f, - 0x50, 0xc1, 0x3c, 0xdd, 0x57, 0x52, 0x50, 0x9c, 0x51, 0xc3, 0xfe, 0x31, 0x0b, 0x4e, 0xab, 0x0f, - 0x7e, 0xd7, 0x7d, 0xa6, 0xfd, 0xb3, 0x05, 0x18, 0xbf, 0xbe, 0xb1, 0x51, 0xbd, 0x46, 0x22, 0x71, - 0x6d, 0xf7, 0x56, 0xec, 0x63, 0x4d, 0x3f, 0xd9, 0xed, 0x31, 0xd7, 0x89, 0xdc, 0xe6, 0x02, 0x8f, - 0x73, 0xb4, 0x50, 0xf1, 0xa2, 0x5b, 0x41, 0x2d, 0x0a, 0x5c, 0x6f, 0x3b, 0x53, 0xa3, 0x29, 0x99, - 0x8b, 0x62, 0x1e, 0x73, 0x81, 0x5e, 0x80, 0x21, 0x16, 0x68, 0x49, 0x4e, 0xc2, 0xe3, 0xea, 0x2d, - 0xc4, 0x4a, 0x0f, 0x0f, 0xe6, 0x4b, 0xb7, 0x71, 0x85, 0xff, 0xc1, 0x02, 0x15, 0xdd, 0x86, 0xd1, - 0x9d, 0x28, 0x6a, 0x5f, 0x27, 0x4e, 0x83, 0x3e, 0xc6, 0xf9, 0x71, 0x78, 0x3e, 0xeb, 0x38, 0xa4, - 0x83, 0xc0, 0xd1, 0xe2, 0x13, 0x24, 0x2e, 0x0b, 0xb1, 0x4e, 0xc7, 0xae, 0x01, 0xc4, 0xb0, 0x63, - 0x52, 0xcd, 0xd8, 0x7f, 0x60, 0xc1, 0x30, 0x8f, 0x79, 0x11, 0xa0, 0x8f, 0xc2, 0x00, 0xb9, 0x4f, - 0xea, 0x82, 0xe3, 0xcd, 0xec, 0x70, 0xcc, 0x69, 0x71, 0x11, 0x33, 0xfd, 0x8f, 0x59, 0x2d, 0x74, - 0x1d, 0x86, 0x69, 0x6f, 0xaf, 0xa9, 0x00, 0x20, 0x4f, 0xe6, 0x7d, 0xb1, 0x9a, 0x76, 0xce, 0x9c, - 0x89, 0x22, 0x2c, 0xab, 0x33, 0x7d, 0x78, 0xbd, 0x5d, 0xa3, 0x27, 0x76, 0xd4, 0x8d, 0xb1, 0xd8, - 0x58, 0xae, 0x72, 0x24, 0x41, 0x8d, 0xeb, 0xc3, 0x65, 0x21, 0x8e, 0x89, 0xd8, 0x1b, 0x50, 0xa2, - 0x93, 0xba, 0xd8, 0x74, 0x9d, 0xee, 0x2a, 0xfe, 0x67, 0xa0, 0x24, 0x15, 0xf8, 0xa1, 0xf0, 0x75, - 0x67, 0x54, 0xa5, 0x7e, 0x3f, 0xc4, 0x31, 0xdc, 0xde, 0x82, 0x53, 0xcc, 0x1c, 0xd3, 0x89, 0x76, - 0x8c, 0x3d, 0xd6, 0x7b, 0x31, 0x3f, 0x2b, 0x1e, 0x90, 0x7c, 0x66, 0x66, 0x35, 0x77, 0xd2, 0x31, - 0x49, 0x31, 0x7e, 0x4c, 0xda, 0x5f, 0x1b, 0x80, 0xc7, 0x2b, 0xb5, 0xfc, 0x70, 0x28, 0x2f, 0xc3, - 0x18, 0xe7, 0x4b, 0xe9, 0xd2, 0x76, 0x9a, 0xa2, 0x5d, 0x25, 0x5b, 0xde, 0xd0, 0x60, 0xd8, 0xc0, - 0x44, 0xe7, 0xa0, 0xe8, 0xbe, 0xe5, 0x25, 0x9d, 0xad, 0x2a, 0x6f, 0xac, 0x63, 0x5a, 0x4e, 0xc1, - 0x94, 0xc5, 0xe5, 0x77, 0x87, 0x02, 0x2b, 0x36, 0xf7, 0x35, 0x98, 0x70, 0xc3, 0x7a, 0xe8, 0x56, - 0x3c, 0x7a, 0xce, 0x68, 0x27, 0x95, 0x12, 0x6e, 0xd0, 0x4e, 0x2b, 0x28, 0x4e, 0x60, 0x6b, 0x17, - 0xd9, 0x60, 0xdf, 0x6c, 0x72, 0x4f, 0xe7, 0x6f, 0xfa, 0x02, 0x68, 0xb3, 0xaf, 0x0b, 0x99, 0x92, - 0x40, 0xbc, 0x00, 0xf8, 0x07, 0x87, 0x58, 0xc2, 0xe8, 0xcb, 0xb1, 0xbe, 0xe3, 0xb4, 0x17, 0x3b, - 0xd1, 0x4e, 0xd9, 0x0d, 0xeb, 0xfe, 0x1e, 0x09, 0xf6, 0xd9, 0xa3, 0x7f, 0x24, 0x7e, 0x39, 0x2a, - 0xc0, 0xf2, 0xf5, 0xc5, 0x2a, 0xc5, 0xc4, 0xe9, 0x3a, 0x68, 0x11, 0x26, 0x65, 0x61, 0x8d, 0x84, - 0xec, 0x0a, 0x1b, 0x65, 0x64, 0x94, 0xfb, 0x93, 0x28, 0x56, 0x44, 0x92, 0xf8, 0x26, 0x27, 0x0d, - 0xc7, 0xc1, 0x49, 0x7f, 0x04, 0xc6, 0x5d, 0xcf, 0x8d, 0x5c, 0x27, 0xf2, 0xb9, 0x86, 0x8b, 0xbf, - 0xef, 0x99, 0xe8, 0xbe, 0xa2, 0x03, 0xb0, 0x89, 0x67, 0xff, 0x97, 0x01, 0x98, 0x66, 0xd3, 0xf6, - 0xde, 0x0a, 0xfb, 0x66, 0x5a, 0x61, 0xb7, 0xd3, 0x2b, 0xec, 0x38, 0x9e, 0x08, 0x0f, 0xbd, 0xcc, - 0x3e, 0x07, 0x25, 0xe5, 0xf1, 0x25, 0x5d, 0x3e, 0xad, 0x1c, 0x97, 0xcf, 0xde, 0xdc, 0x87, 0x34, - 0x9a, 0x2b, 0x66, 0x1a, 0xcd, 0xfd, 0x75, 0x0b, 0x62, 0x95, 0x0d, 0xba, 0x0e, 0xa5, 0xb6, 0xcf, - 0x6c, 0x41, 0x03, 0x69, 0x60, 0xfd, 0x78, 0xe6, 0x45, 0xc5, 0x2f, 0x45, 0xfe, 0xf1, 0x55, 0x59, - 0x03, 0xc7, 0x95, 0xd1, 0x12, 0x0c, 0xb7, 0x03, 0x52, 0x8b, 0x58, 0x54, 0x94, 0x9e, 0x74, 0xf8, - 0x1a, 0xe1, 0xf8, 0x58, 0x56, 0xb4, 0x7f, 0xce, 0x02, 0xe0, 0x76, 0x69, 0x8e, 0xb7, 0x4d, 0x4e, - 0x40, 0x6a, 0x5d, 0x86, 0x81, 0xb0, 0x4d, 0xea, 0xdd, 0xac, 0x74, 0xe3, 0xfe, 0xd4, 0xda, 0xa4, - 0x1e, 0x0f, 0x38, 0xfd, 0x87, 0x59, 0x6d, 0xfb, 0xbb, 0x01, 0x26, 0x62, 0xb4, 0x4a, 0x44, 0x5a, - 0xe8, 0x39, 0x23, 0x4a, 0xc2, 0xd9, 0x44, 0x94, 0x84, 0x12, 0xc3, 0xd6, 0x04, 0xa4, 0x9f, 0x83, - 0x62, 0xcb, 0xb9, 0x2f, 0x24, 0x60, 0xcf, 0x74, 0xef, 0x06, 0xa5, 0xbf, 0xb0, 0xe6, 0xdc, 0xe7, - 0x8f, 0xc4, 0x67, 0xe4, 0x02, 0x59, 0x73, 0xee, 0x1f, 0x72, 0x5b, 0x5c, 0x76, 0x48, 0xdd, 0x74, - 0xc3, 0xe8, 0x0b, 0xff, 0x39, 0xfe, 0xcf, 0x96, 0x1d, 0x6d, 0x84, 0xb5, 0xe5, 0x7a, 0xc2, 0xe4, - 0xaa, 0xaf, 0xb6, 0x5c, 0x2f, 0xd9, 0x96, 0xeb, 0xf5, 0xd1, 0x96, 0xeb, 0xa1, 0xb7, 0x61, 0x58, - 0x58, 0x44, 0x8a, 0xa8, 0x44, 0x57, 0xfa, 0x68, 0x4f, 0x18, 0x54, 0xf2, 0x36, 0xaf, 0xc8, 0x47, - 0xb0, 0x28, 0xed, 0xd9, 0xae, 0x6c, 0x10, 0xfd, 0x15, 0x0b, 0x26, 0xc4, 0x6f, 0x4c, 0xde, 0xea, - 0x90, 0x30, 0x12, 0xbc, 0xe7, 0x87, 0xfb, 0xef, 0x83, 0xa8, 0xc8, 0xbb, 0xf2, 0x61, 0x79, 0xcc, - 0x9a, 0xc0, 0x9e, 0x3d, 0x4a, 0xf4, 0x02, 0xfd, 0x7d, 0x0b, 0x4e, 0xb5, 0x9c, 0xfb, 0xbc, 0x45, - 0x5e, 0x86, 0x9d, 0xc8, 0xf5, 0x85, 0x65, 0xc1, 0x47, 0xfb, 0x9b, 0xfe, 0x54, 0x75, 0xde, 0x49, - 0xa9, 0xfe, 0x3c, 0x95, 0x85, 0xd2, 0xb3, 0xab, 0x99, 0xfd, 0x9a, 0xdb, 0x82, 0x11, 0xb9, 0xde, - 0x32, 0x44, 0x0d, 0x65, 0x9d, 0xb1, 0x3e, 0xb2, 0x41, 0xaa, 0x1e, 0x7d, 0x80, 0xb6, 0x23, 0xd6, - 0xda, 0x23, 0x6d, 0xe7, 0x73, 0x30, 0xa6, 0xaf, 0xb1, 0x47, 0xda, 0xd6, 0x5b, 0x30, 0x93, 0xb1, - 0x96, 0x1e, 0x69, 0x93, 0xf7, 0xe0, 0x6c, 0xee, 0xfa, 0x78, 0x94, 0x0d, 0xdb, 0x3f, 0x6b, 0xe9, - 0xe7, 0xe0, 0x09, 0xa8, 0x0e, 0x96, 0x4d, 0xd5, 0xc1, 0xf9, 0xee, 0x3b, 0x27, 0x47, 0x7f, 0xf0, - 0xa6, 0xde, 0x69, 0x7a, 0xaa, 0xa3, 0xd7, 0x61, 0xa8, 0x49, 0x4b, 0xa4, 0x5d, 0xad, 0xdd, 0x7b, - 0x47, 0xc6, 0xbc, 0x14, 0x2b, 0x0f, 0xb1, 0xa0, 0x60, 0xff, 0xa2, 0x05, 0x03, 0x27, 0x30, 0x12, - 0xd8, 0x1c, 0x89, 0xe7, 0x72, 0x49, 0x8b, 0x80, 0xc9, 0x0b, 0xd8, 0xb9, 0xb7, 0x72, 0x3f, 0x22, - 0x5e, 0xc8, 0x9e, 0x8a, 0x99, 0x03, 0xf3, 0x93, 0x16, 0xcc, 0xdc, 0xf4, 0x9d, 0xc6, 0x92, 0xd3, - 0x74, 0xbc, 0x3a, 0x09, 0x2a, 0xde, 0xf6, 0x91, 0x8c, 0xc2, 0x0b, 0x3d, 0x8d, 0xc2, 0x97, 0xa5, - 0x4d, 0xd5, 0x40, 0xfe, 0xfc, 0x51, 0x46, 0x32, 0x19, 0x37, 0xc6, 0xb0, 0xfe, 0xdd, 0x01, 0xa4, - 0xf7, 0x52, 0xb8, 0xe8, 0x60, 0x18, 0x76, 0x79, 0x7f, 0xc5, 0x24, 0x3e, 0x95, 0xcd, 0xe0, 0xa5, - 0x3e, 0x4f, 0x73, 0x3e, 0xe1, 0x05, 0x58, 0x12, 0xb2, 0x5f, 0x86, 0x4c, 0x3f, 0xff, 0xde, 0xc2, - 0x07, 0xfb, 0x93, 0x30, 0xcd, 0x6a, 0x1e, 0xf1, 0x61, 0x6c, 0x27, 0x64, 0x9b, 0x19, 0x11, 0x00, - 0xed, 0x2f, 0x5a, 0x30, 0xb9, 0x9e, 0x08, 0x8c, 0x76, 0x89, 0x69, 0x43, 0x33, 0x44, 0xea, 0x35, - 0x56, 0x8a, 0x05, 0xf4, 0xd8, 0x25, 0x59, 0x7f, 0x61, 0x41, 0x1c, 0x7a, 0xe3, 0x04, 0xd8, 0xb7, - 0x65, 0x83, 0x7d, 0xcb, 0x94, 0xb0, 0xa8, 0xee, 0xe4, 0x71, 0x6f, 0xe8, 0x86, 0x0a, 0x4a, 0xd5, - 0x45, 0xb8, 0x12, 0x93, 0xe1, 0x4b, 0x71, 0xc2, 0x8c, 0x5c, 0x25, 0xc3, 0x54, 0xd9, 0xbf, 0x5d, - 0x00, 0xa4, 0x70, 0xfb, 0x0e, 0x9a, 0x95, 0xae, 0x71, 0x3c, 0x41, 0xb3, 0xf6, 0x00, 0x31, 0x7d, - 0x7e, 0xe0, 0x78, 0x21, 0x27, 0xeb, 0x0a, 0xd9, 0xdd, 0xd1, 0x8c, 0x05, 0xe6, 0x44, 0x93, 0xe8, - 0x66, 0x8a, 0x1a, 0xce, 0x68, 0x41, 0xb3, 0xd3, 0x18, 0xec, 0xd7, 0x4e, 0x63, 0xa8, 0x87, 0x1b, - 0xde, 0xcf, 0x58, 0x30, 0xae, 0x86, 0xe9, 0x5d, 0x62, 0x24, 0xaf, 0xfa, 0x93, 0x73, 0x80, 0x56, - 0xb5, 0x2e, 0xb3, 0x8b, 0xe5, 0x5b, 0x99, 0x3b, 0xa5, 0xd3, 0x74, 0xdf, 0x26, 0x2a, 0x64, 0xe1, - 0xbc, 0x70, 0x8f, 0x14, 0xa5, 0x87, 0x07, 0xf3, 0xe3, 0xea, 0x1f, 0x0f, 0x91, 0x1c, 0x57, 0xa1, - 0x47, 0xf2, 0x64, 0x62, 0x29, 0xa2, 0x97, 0x60, 0xb0, 0xbd, 0xe3, 0x84, 0x24, 0xe1, 0x4c, 0x34, - 0x58, 0xa5, 0x85, 0x87, 0x07, 0xf3, 0x13, 0xaa, 0x02, 0x2b, 0xc1, 0x1c, 0xbb, 0xff, 0x50, 0x64, - 0xe9, 0xc5, 0xd9, 0x33, 0x14, 0xd9, 0x9f, 0x58, 0x30, 0xb0, 0xee, 0x37, 0x4e, 0xe2, 0x08, 0x78, - 0xcd, 0x38, 0x02, 0x9e, 0xc8, 0x8b, 0x5e, 0x9f, 0xbb, 0xfb, 0x57, 0x13, 0xbb, 0xff, 0x7c, 0x2e, - 0x85, 0xee, 0x1b, 0xbf, 0x05, 0xa3, 0x2c, 0x26, 0xbe, 0x70, 0x9c, 0x7a, 0xc1, 0xd8, 0xf0, 0xf3, - 0x89, 0x0d, 0x3f, 0xa9, 0xa1, 0x6a, 0x3b, 0xfd, 0x69, 0x18, 0x16, 0x9e, 0x38, 0x49, 0xaf, 0x54, - 0x81, 0x8b, 0x25, 0xdc, 0xfe, 0xf1, 0x22, 0x18, 0x31, 0xf8, 0xd1, 0x2f, 0x5b, 0xb0, 0x10, 0x70, - 0x0b, 0xdd, 0x46, 0xb9, 0x13, 0xb8, 0xde, 0x76, 0xad, 0xbe, 0x43, 0x1a, 0x9d, 0xa6, 0xeb, 0x6d, - 0x57, 0xb6, 0x3d, 0x5f, 0x15, 0xaf, 0xdc, 0x27, 0xf5, 0x0e, 0x53, 0x82, 0xf5, 0x08, 0xf8, 0xaf, - 0x2c, 0xdd, 0x9f, 0x7f, 0x70, 0x30, 0xbf, 0x80, 0x8f, 0x44, 0x1b, 0x1f, 0xb1, 0x2f, 0xe8, 0x37, - 0x2c, 0xb8, 0xc2, 0x43, 0xd3, 0xf7, 0xdf, 0xff, 0x2e, 0xaf, 0xe5, 0xaa, 0x24, 0x15, 0x13, 0xd9, - 0x20, 0x41, 0x6b, 0xe9, 0x23, 0x62, 0x40, 0xaf, 0x54, 0x8f, 0xd6, 0x16, 0x3e, 0x6a, 0xe7, 0xec, - 0x7f, 0x56, 0x84, 0x71, 0x11, 0xb2, 0x4a, 0xdc, 0x01, 0x2f, 0x19, 0x4b, 0xe2, 0xc9, 0xc4, 0x92, - 0x98, 0x36, 0x90, 0x8f, 0xe7, 0xf8, 0x0f, 0x61, 0x9a, 0x1e, 0xce, 0xd7, 0x89, 0x13, 0x44, 0x9b, - 0xc4, 0xe1, 0xe6, 0x57, 0xc5, 0x23, 0x9f, 0xfe, 0x4a, 0x3c, 0x77, 0x33, 0x49, 0x0c, 0xa7, 0xe9, - 0x7f, 0x33, 0xdd, 0x39, 0x1e, 0x4c, 0xa5, 0xa2, 0x8e, 0x7d, 0x0a, 0x4a, 0xca, 0x8d, 0x44, 0x1c, - 0x3a, 0xdd, 0x83, 0xf7, 0x25, 0x29, 0x70, 0x11, 0x5a, 0xec, 0xc2, 0x14, 0x93, 0xb3, 0xff, 0x41, - 0xc1, 0x68, 0x90, 0x4f, 0xe2, 0x3a, 0x8c, 0x38, 0x61, 0xe8, 0x6e, 0x7b, 0xa4, 0x21, 0x76, 0xec, - 0xfb, 0xf3, 0x76, 0xac, 0xd1, 0x0c, 0x73, 0xe5, 0x59, 0x14, 0x35, 0xb1, 0xa2, 0x81, 0xae, 0x73, - 0x23, 0xb7, 0x3d, 0xf9, 0xde, 0xeb, 0x8f, 0x1a, 0x48, 0x33, 0xb8, 0x3d, 0x82, 0x45, 0x7d, 0xf4, - 0x69, 0x6e, 0x85, 0x78, 0xc3, 0xf3, 0xef, 0x79, 0xd7, 0x7c, 0x5f, 0x86, 0x85, 0xe8, 0x8f, 0xe0, - 0xb4, 0xb4, 0x3d, 0x54, 0xd5, 0xb1, 0x49, 0xad, 0xbf, 0x30, 0x9e, 0x9f, 0x87, 0x19, 0x4a, 0xda, - 0xf4, 0xda, 0x0e, 0x11, 0x81, 0x49, 0x11, 0x0f, 0x4d, 0x96, 0x89, 0xb1, 0xcb, 0x7c, 0xca, 0x99, - 0xb5, 0x63, 0x39, 0xf2, 0x0d, 0x93, 0x04, 0x4e, 0xd2, 0xb4, 0xff, 0xb6, 0x05, 0xcc, 0x83, 0xf5, - 0x04, 0xf8, 0x91, 0x8f, 0x99, 0xfc, 0xc8, 0x6c, 0xde, 0x20, 0xe7, 0xb0, 0x22, 0x2f, 0xf2, 0x95, - 0x55, 0x0d, 0xfc, 0xfb, 0xfb, 0xc2, 0x74, 0xa4, 0xf7, 0xfb, 0xc3, 0xfe, 0xdf, 0x16, 0x3f, 0xc4, - 0x94, 0x93, 0x07, 0xfa, 0x76, 0x18, 0xa9, 0x3b, 0x6d, 0xa7, 0xce, 0x13, 0xc6, 0xe4, 0x4a, 0xf4, - 0x8c, 0x4a, 0x0b, 0xcb, 0xa2, 0x06, 0x97, 0x50, 0xc9, 0xb8, 0x7a, 0x23, 0xb2, 0xb8, 0xa7, 0x54, - 0x4a, 0x35, 0x39, 0xb7, 0x0b, 0xe3, 0x06, 0xb1, 0x47, 0x2a, 0xce, 0xf8, 0x76, 0x7e, 0xc5, 0xaa, - 0x38, 0x90, 0x2d, 0x98, 0xf6, 0xb4, 0xff, 0xf4, 0x42, 0x91, 0x8f, 0xcb, 0xf7, 0xf7, 0xba, 0x44, - 0xd9, 0xed, 0xa3, 0x39, 0xc7, 0x26, 0xc8, 0xe0, 0x34, 0x65, 0xfb, 0x27, 0x2c, 0x78, 0x4c, 0x47, - 0xd4, 0xfc, 0x6f, 0x7a, 0xe9, 0x08, 0xca, 0x30, 0xe2, 0xb7, 0x49, 0xe0, 0x44, 0x7e, 0x20, 0x6e, - 0x8d, 0xcb, 0x72, 0xd0, 0x6f, 0x89, 0xf2, 0x43, 0x11, 0x6e, 0x5d, 0x52, 0x97, 0xe5, 0x58, 0xd5, - 0xa4, 0xaf, 0x4f, 0x36, 0x18, 0xa1, 0xf0, 0xb4, 0x62, 0x67, 0x00, 0x53, 0x97, 0x87, 0x58, 0x40, - 0xec, 0xaf, 0x59, 0x7c, 0x61, 0xe9, 0x5d, 0x47, 0x6f, 0xc1, 0x54, 0xcb, 0x89, 0xea, 0x3b, 0x2b, - 0xf7, 0xdb, 0x01, 0xd7, 0xb8, 0xc8, 0x71, 0x7a, 0xa6, 0xd7, 0x38, 0x69, 0x1f, 0x19, 0x1b, 0x56, - 0xae, 0x25, 0x88, 0xe1, 0x14, 0x79, 0xb4, 0x09, 0xa3, 0xac, 0x8c, 0x39, 0x11, 0x86, 0xdd, 0x58, - 0x83, 0xbc, 0xd6, 0x94, 0xc5, 0xc1, 0x5a, 0x4c, 0x07, 0xeb, 0x44, 0xed, 0x9f, 0x2e, 0xf2, 0xdd, - 0xce, 0x58, 0xf9, 0xa7, 0x61, 0xb8, 0xed, 0x37, 0x96, 0x2b, 0x65, 0x2c, 0x66, 0x41, 0x5d, 0x23, - 0x55, 0x5e, 0x8c, 0x25, 0x1c, 0x5d, 0x86, 0x11, 0xf1, 0x53, 0x6a, 0xc8, 0xd8, 0xd9, 0x2c, 0xf0, - 0x42, 0xac, 0xa0, 0xe8, 0x79, 0x80, 0x76, 0xe0, 0xef, 0xb9, 0x0d, 0x16, 0xdc, 0xa2, 0x68, 0x1a, - 0x0b, 0x55, 0x15, 0x04, 0x6b, 0x58, 0xe8, 0x55, 0x18, 0xef, 0x78, 0x21, 0x67, 0x47, 0xb4, 0x50, - 0xb6, 0xca, 0x8c, 0xe5, 0xb6, 0x0e, 0xc4, 0x26, 0x2e, 0x5a, 0x84, 0xa1, 0xc8, 0x61, 0xc6, 0x2f, - 0x83, 0xf9, 0xc6, 0xb7, 0x1b, 0x14, 0x43, 0xcf, 0x4d, 0x42, 0x2b, 0x60, 0x51, 0x11, 0x7d, 0x4a, - 0xfa, 0xf3, 0xf2, 0x83, 0x5d, 0x58, 0xbd, 0xf7, 0x77, 0x09, 0x68, 0xde, 0xbc, 0xc2, 0x9a, 0xde, - 0xa0, 0x85, 0x5e, 0x01, 0x20, 0xf7, 0x23, 0x12, 0x78, 0x4e, 0x53, 0xd9, 0x96, 0x29, 0xbe, 0xa0, - 0xec, 0xaf, 0xfb, 0xd1, 0xed, 0x90, 0xac, 0x28, 0x0c, 0xac, 0x61, 0xdb, 0xbf, 0x51, 0x02, 0x88, - 0xf9, 0x76, 0xf4, 0x76, 0xea, 0xe0, 0x7a, 0xb6, 0x3b, 0xa7, 0x7f, 0x7c, 0xa7, 0x16, 0xfa, 0x1e, - 0x0b, 0x46, 0x9d, 0x66, 0xd3, 0xaf, 0x3b, 0x3c, 0xd8, 0x70, 0xa1, 0xfb, 0xc1, 0x29, 0xda, 0x5f, - 0x8c, 0x6b, 0xf0, 0x2e, 0xbc, 0x20, 0x57, 0xa8, 0x06, 0xe9, 0xd9, 0x0b, 0xbd, 0x61, 0xf4, 0x21, - 0xf9, 0x54, 0x2c, 0x1a, 0x43, 0xa9, 0x9e, 0x8a, 0x25, 0x76, 0x47, 0xe8, 0xaf, 0xc4, 0xdb, 0xc6, - 0x2b, 0x71, 0x20, 0xdf, 0x61, 0xd1, 0x60, 0x5f, 0x7b, 0x3d, 0x10, 0x51, 0x55, 0x0f, 0x5e, 0x30, - 0x98, 0xef, 0x1d, 0xa8, 0xbd, 0x93, 0x7a, 0x04, 0x2e, 0xf8, 0x1c, 0x4c, 0x36, 0x4c, 0x26, 0x40, - 0xac, 0xc4, 0xa7, 0xf2, 0xe8, 0x26, 0x78, 0x86, 0xf8, 0xda, 0x4f, 0x00, 0x70, 0x92, 0x30, 0xaa, - 0xf2, 0x58, 0x16, 0x15, 0x6f, 0xcb, 0x17, 0x9e, 0x17, 0x76, 0xee, 0x5c, 0xee, 0x87, 0x11, 0x69, - 0x51, 0xcc, 0xf8, 0x76, 0x5f, 0x17, 0x75, 0xb1, 0xa2, 0x82, 0x5e, 0x87, 0x21, 0xe6, 0x1e, 0x16, - 0xce, 0x8e, 0xe4, 0x4b, 0x9c, 0xcd, 0xe0, 0x6c, 0xf1, 0x86, 0x64, 0x7f, 0x43, 0x2c, 0x28, 0xa0, - 0xeb, 0xd2, 0xf9, 0x32, 0xac, 0x78, 0xb7, 0x43, 0xc2, 0x9c, 0x2f, 0x4b, 0x4b, 0xef, 0x8f, 0xfd, - 0x2a, 0x79, 0x79, 0x66, 0x06, 0x33, 0xa3, 0x26, 0xe5, 0xa2, 0xc4, 0x7f, 0x99, 0x18, 0x6d, 0x16, - 0xf2, 0xbb, 0x67, 0x26, 0x4f, 0x8b, 0x87, 0xf3, 0x8e, 0x49, 0x02, 0x27, 0x69, 0x52, 0x8e, 0x94, - 0xef, 0x7a, 0xe1, 0xbb, 0xd1, 0xeb, 0xec, 0xe0, 0x0f, 0x71, 0x76, 0x1b, 0xf1, 0x12, 0x2c, 0xea, - 0x9f, 0x28, 0x7b, 0x30, 0xe7, 0xc1, 0x54, 0x72, 0x8b, 0x3e, 0x52, 0x76, 0xe4, 0x0f, 0x06, 0x60, - 0xc2, 0x5c, 0x52, 0xe8, 0x0a, 0x94, 0x04, 0x11, 0x95, 0xcc, 0x40, 0xed, 0x92, 0x35, 0x09, 0xc0, - 0x31, 0x0e, 0xcb, 0x61, 0xc1, 0xaa, 0x6b, 0xc6, 0xba, 0x71, 0x0e, 0x0b, 0x05, 0xc1, 0x1a, 0x16, - 0x7d, 0x58, 0x6d, 0xfa, 0x7e, 0xa4, 0x2e, 0x24, 0xb5, 0xee, 0x96, 0x58, 0x29, 0x16, 0x50, 0x7a, - 0x11, 0xed, 0x92, 0xc0, 0x23, 0x4d, 0x33, 0xec, 0xb1, 0xba, 0x88, 0x6e, 0xe8, 0x40, 0x6c, 0xe2, - 0xd2, 0xeb, 0xd4, 0x0f, 0xd9, 0x42, 0x16, 0xcf, 0xb7, 0xd8, 0xf8, 0xb9, 0xc6, 0xfd, 0xbf, 0x25, - 0x1c, 0x7d, 0x12, 0x1e, 0x53, 0xa1, 0x9d, 0x30, 0xd7, 0x66, 0xc8, 0x16, 0x87, 0x0c, 0x69, 0xcb, - 0x63, 0xcb, 0xd9, 0x68, 0x38, 0xaf, 0x3e, 0x7a, 0x0d, 0x26, 0x04, 0x8b, 0x2f, 0x29, 0x0e, 0x9b, - 0x06, 0x36, 0x37, 0x0c, 0x28, 0x4e, 0x60, 0xcb, 0xc0, 0xcd, 0x8c, 0xcb, 0x96, 0x14, 0x46, 0xd2, - 0x81, 0x9b, 0x75, 0x38, 0x4e, 0xd5, 0x40, 0x8b, 0x30, 0xc9, 0x79, 0x30, 0xd7, 0xdb, 0xe6, 0x73, - 0x22, 0x5c, 0xab, 0xd4, 0x96, 0xba, 0x65, 0x82, 0x71, 0x12, 0x1f, 0xbd, 0x0c, 0x63, 0x4e, 0x50, - 0xdf, 0x71, 0x23, 0x52, 0x8f, 0x3a, 0x01, 0xf7, 0xb9, 0xd2, 0x2c, 0x94, 0x16, 0x35, 0x18, 0x36, - 0x30, 0xed, 0xb7, 0x61, 0x26, 0x23, 0x30, 0x04, 0x5d, 0x38, 0x4e, 0xdb, 0x95, 0xdf, 0x94, 0x30, - 0x63, 0x5e, 0xac, 0x56, 0xe4, 0xd7, 0x68, 0x58, 0x74, 0x75, 0xb2, 0x00, 0x12, 0x5a, 0x1e, 0x44, - 0xb5, 0x3a, 0x57, 0x25, 0x00, 0xc7, 0x38, 0xf6, 0xff, 0x28, 0xc0, 0x64, 0x86, 0x6e, 0x85, 0xe5, - 0xe2, 0x4b, 0x3c, 0x52, 0xe2, 0xd4, 0x7b, 0x66, 0x1c, 0xf0, 0xc2, 0x11, 0xe2, 0x80, 0x17, 0x7b, - 0xc5, 0x01, 0x1f, 0x78, 0x27, 0x71, 0xc0, 0xcd, 0x11, 0x1b, 0xec, 0x6b, 0xc4, 0x32, 0x62, 0x87, - 0x0f, 0x1d, 0x31, 0x76, 0xb8, 0x31, 0xe8, 0xc3, 0x7d, 0x0c, 0xfa, 0x0f, 0x17, 0x60, 0x2a, 0x69, - 0x49, 0x79, 0x02, 0x72, 0xdb, 0xd7, 0x0d, 0xb9, 0xed, 0xe5, 0x7e, 0x5c, 0x61, 0x73, 0x65, 0xb8, - 0x38, 0x21, 0xc3, 0xfd, 0x60, 0x5f, 0xd4, 0xba, 0xcb, 0x73, 0xff, 0x66, 0x01, 0x4e, 0x67, 0xfa, - 0xe2, 0x9e, 0xc0, 0xd8, 0xdc, 0x32, 0xc6, 0xe6, 0xb9, 0xbe, 0xdd, 0x84, 0x73, 0x07, 0xe8, 0x6e, - 0x62, 0x80, 0xae, 0xf4, 0x4f, 0xb2, 0xfb, 0x28, 0x7d, 0xb5, 0x08, 0xe7, 0x33, 0xeb, 0xc5, 0x62, - 0xcf, 0x55, 0x43, 0xec, 0xf9, 0x7c, 0x42, 0xec, 0x69, 0x77, 0xaf, 0x7d, 0x3c, 0x72, 0x50, 0xe1, - 0x2e, 0xcb, 0xa2, 0x1c, 0x3c, 0xa4, 0x0c, 0xd4, 0x70, 0x97, 0x55, 0x84, 0xb0, 0x49, 0xf7, 0x9b, - 0x49, 0xf6, 0xf9, 0x6f, 0x2c, 0x38, 0x9b, 0x39, 0x37, 0x27, 0x20, 0xeb, 0x5a, 0x37, 0x65, 0x5d, - 0x4f, 0xf7, 0xbd, 0x5a, 0x73, 0x84, 0x5f, 0xbf, 0x36, 0x90, 0xf3, 0x2d, 0xec, 0x25, 0x7f, 0x0b, - 0x46, 0x9d, 0x7a, 0x9d, 0x84, 0xe1, 0x9a, 0xdf, 0x50, 0xa1, 0x8e, 0x9f, 0x63, 0xef, 0xac, 0xb8, - 0xf8, 0xf0, 0x60, 0x7e, 0x2e, 0x49, 0x22, 0x06, 0x63, 0x9d, 0x02, 0xfa, 0x34, 0x8c, 0x84, 0xe2, - 0xde, 0x14, 0x73, 0xff, 0x42, 0x9f, 0x83, 0xe3, 0x6c, 0x92, 0xa6, 0x19, 0x8b, 0x49, 0x49, 0x2a, - 0x14, 0x49, 0x33, 0x6e, 0x4b, 0xe1, 0x58, 0xe3, 0xb6, 0x3c, 0x0f, 0xb0, 0xa7, 0x1e, 0x03, 0x49, - 0xf9, 0x83, 0xf6, 0x4c, 0xd0, 0xb0, 0xd0, 0xc7, 0x61, 0x2a, 0xe4, 0xc1, 0x0a, 0x97, 0x9b, 0x4e, - 0xc8, 0x9c, 0x65, 0xc4, 0x2a, 0x64, 0xf1, 0x9e, 0x6a, 0x09, 0x18, 0x4e, 0x61, 0xa3, 0x55, 0xd9, - 0x2a, 0x8b, 0xac, 0xc8, 0x17, 0xe6, 0xa5, 0xb8, 0x45, 0x91, 0x09, 0xf8, 0x54, 0x72, 0xf8, 0xd9, - 0xc0, 0x6b, 0x35, 0xd1, 0xa7, 0x01, 0xe8, 0xf2, 0x11, 0x72, 0x88, 0xe1, 0xfc, 0xc3, 0x93, 0x9e, - 0x2a, 0x8d, 0x4c, 0xdb, 0x5e, 0xe6, 0xe1, 0x5a, 0x56, 0x44, 0xb0, 0x46, 0xd0, 0xfe, 0xe1, 0x01, - 0x78, 0xbc, 0xcb, 0x19, 0x89, 0x16, 0x4d, 0x3d, 0xec, 0x33, 0xc9, 0xc7, 0xf5, 0x5c, 0x66, 0x65, - 0xe3, 0xb5, 0x9d, 0x58, 0x8a, 0x85, 0x77, 0xbc, 0x14, 0x7f, 0xc0, 0xd2, 0xc4, 0x1e, 0xdc, 0xe2, - 0xf3, 0x63, 0x47, 0x3c, 0xfb, 0x8f, 0x51, 0x0e, 0xb2, 0x95, 0x21, 0x4c, 0x78, 0xbe, 0xef, 0xee, - 0xf4, 0x2d, 0x5d, 0x38, 0x59, 0x29, 0xf1, 0x6f, 0x59, 0x70, 0xae, 0x6b, 0xd0, 0x8e, 0x6f, 0x40, - 0x86, 0xc1, 0xfe, 0x82, 0x05, 0x4f, 0x66, 0xd6, 0x30, 0xcc, 0x8c, 0xae, 0x40, 0xa9, 0x4e, 0x0b, - 0x35, 0x2f, 0xcd, 0xd8, 0x7d, 0x5d, 0x02, 0x70, 0x8c, 0x63, 0x58, 0x13, 0x15, 0x7a, 0x5a, 0x13, - 0xfd, 0x8a, 0x05, 0xa9, 0x4d, 0x7f, 0x02, 0xb7, 0x4f, 0xc5, 0xbc, 0x7d, 0xde, 0xdf, 0xcf, 0x68, - 0xe6, 0x5c, 0x3c, 0x7f, 0x3c, 0x09, 0x67, 0x72, 0xbc, 0x94, 0xf6, 0x60, 0x7a, 0xbb, 0x4e, 0x4c, - 0xff, 0xd7, 0x6e, 0x71, 0x61, 0xba, 0x3a, 0xcb, 0xb2, 0x5c, 0xa5, 0xd3, 0x29, 0x14, 0x9c, 0x6e, - 0x02, 0x7d, 0xc1, 0x82, 0x53, 0xce, 0xbd, 0x70, 0x85, 0x72, 0x11, 0x6e, 0x7d, 0xa9, 0xe9, 0xd7, - 0x77, 0xe9, 0x11, 0x2d, 0x37, 0xc2, 0x8b, 0x99, 0x92, 0x9d, 0xbb, 0xb5, 0x14, 0xbe, 0xd1, 0x3c, - 0x4b, 0xde, 0x9a, 0x85, 0x85, 0x33, 0xdb, 0x42, 0x58, 0x44, 0xf4, 0xa7, 0x6f, 0x94, 0x2e, 0x1e, - 0xda, 0x59, 0xee, 0x64, 0xfc, 0x5a, 0x94, 0x10, 0xac, 0xe8, 0xa0, 0xcf, 0x42, 0x69, 0x5b, 0xfa, - 0x78, 0x66, 0x5c, 0xbb, 0xf1, 0x40, 0x76, 0xf7, 0x7c, 0xe5, 0xea, 0x59, 0x85, 0x84, 0x63, 0xa2, - 0xe8, 0x35, 0x28, 0x7a, 0x5b, 0x61, 0xb7, 0xfc, 0xa7, 0x09, 0x3b, 0x3c, 0x1e, 0x07, 0x61, 0x7d, - 0xb5, 0x86, 0x69, 0x45, 0x74, 0x1d, 0x8a, 0xc1, 0x66, 0x43, 0x88, 0x25, 0x33, 0x37, 0x29, 0x5e, - 0x2a, 0xe7, 0xf4, 0x8a, 0x51, 0xc2, 0x4b, 0x65, 0x4c, 0x49, 0xa0, 0x2a, 0x0c, 0x32, 0xd7, 0x1e, - 0x71, 0xc9, 0x65, 0xb2, 0xf3, 0x5d, 0x5c, 0xe4, 0x78, 0xb0, 0x04, 0x86, 0x80, 0x39, 0x21, 0xb4, - 0x01, 0x43, 0x75, 0x96, 0x2b, 0x53, 0x44, 0x82, 0xfb, 0x50, 0xa6, 0x00, 0xb2, 0x4b, 0x12, 0x51, - 0x21, 0x8f, 0x63, 0x18, 0x58, 0xd0, 0x62, 0x54, 0x49, 0x7b, 0x67, 0x2b, 0x14, 0xb9, 0x9d, 0xb3, - 0xa9, 0x76, 0xc9, 0x8d, 0x2b, 0xa8, 0x32, 0x0c, 0x2c, 0x68, 0xa1, 0x57, 0xa0, 0xb0, 0x55, 0x17, - 0x6e, 0x3b, 0x99, 0x92, 0x48, 0x33, 0x94, 0xc5, 0xd2, 0xd0, 0x83, 0x83, 0xf9, 0xc2, 0xea, 0x32, - 0x2e, 0x6c, 0xd5, 0xd1, 0x3a, 0x0c, 0x6f, 0x71, 0xe7, 0x77, 0x21, 0x6c, 0x7c, 0x2a, 0xdb, 0x2f, - 0x3f, 0xe5, 0x1f, 0xcf, 0x3d, 0x56, 0x04, 0x00, 0x4b, 0x22, 0x2c, 0x40, 0xbe, 0x72, 0xe2, 0x17, - 0x41, 0xd3, 0x16, 0x8e, 0x16, 0x78, 0x81, 0x33, 0x1d, 0x71, 0x28, 0x00, 0xac, 0x51, 0xa4, 0xab, - 0xda, 0x91, 0x09, 0xf6, 0x45, 0xb0, 0x99, 0xcc, 0x55, 0xad, 0xb2, 0xf0, 0x77, 0x5b, 0xd5, 0x0a, - 0x09, 0xc7, 0x44, 0xd1, 0x2e, 0x8c, 0xef, 0x85, 0xed, 0x1d, 0x22, 0xb7, 0x34, 0x8b, 0x3d, 0x93, - 0x73, 0x2f, 0xdf, 0x11, 0x88, 0x6e, 0x10, 0x75, 0x9c, 0x66, 0xea, 0x14, 0x62, 0x3a, 0xfd, 0x3b, - 0x3a, 0x31, 0x6c, 0xd2, 0xa6, 0xc3, 0xff, 0x56, 0xc7, 0xdf, 0xdc, 0x8f, 0x88, 0x88, 0x75, 0x96, - 0x39, 0xfc, 0x6f, 0x70, 0x94, 0xf4, 0xf0, 0x0b, 0x00, 0x96, 0x44, 0xd0, 0x1d, 0x31, 0x3c, 0xec, - 0xf4, 0x9c, 0xca, 0x0f, 0x48, 0xba, 0x28, 0x91, 0x72, 0x06, 0x85, 0x9d, 0x96, 0x31, 0x29, 0x76, - 0x4a, 0xb6, 0x77, 0xfc, 0xc8, 0xf7, 0x12, 0x27, 0xf4, 0x74, 0xfe, 0x29, 0x59, 0xcd, 0xc0, 0x4f, - 0x9f, 0x92, 0x59, 0x58, 0x38, 0xb3, 0x2d, 0xd4, 0x80, 0x89, 0xb6, 0x1f, 0x44, 0xf7, 0xfc, 0x40, - 0xae, 0x2f, 0xd4, 0x45, 0x58, 0x62, 0x60, 0x8a, 0x16, 0x59, 0x18, 0x41, 0x13, 0x82, 0x13, 0x34, - 0xd1, 0x27, 0x60, 0x38, 0xac, 0x3b, 0x4d, 0x52, 0xb9, 0x35, 0x3b, 0x93, 0x7f, 0xfd, 0xd4, 0x38, - 0x4a, 0xce, 0xea, 0xe2, 0xd1, 0xf4, 0x39, 0x0a, 0x96, 0xe4, 0xd0, 0x2a, 0x0c, 0xb2, 0x04, 0x68, - 0x2c, 0x30, 0x5f, 0x4e, 0x5c, 0xd5, 0x94, 0x55, 0x34, 0x3f, 0x9b, 0x58, 0x31, 0xe6, 0xd5, 0xe9, - 0x1e, 0x10, 0x6f, 0x06, 0x3f, 0x9c, 0x3d, 0x9d, 0xbf, 0x07, 0xc4, 0x53, 0xe3, 0x56, 0xad, 0xdb, - 0x1e, 0x50, 0x48, 0x38, 0x26, 0x4a, 0x4f, 0x66, 0x7a, 0x9a, 0x9e, 0xe9, 0x62, 0xce, 0x93, 0x7b, - 0x96, 0xb2, 0x93, 0x99, 0x9e, 0xa4, 0x94, 0x84, 0xfd, 0x7b, 0xc3, 0x69, 0x9e, 0x85, 0xbd, 0x32, - 0xbf, 0xcb, 0x4a, 0x29, 0x20, 0x3f, 0xdc, 0xaf, 0xd0, 0xeb, 0x18, 0x59, 0xf0, 0x2f, 0x58, 0x70, - 0xa6, 0x9d, 0xf9, 0x21, 0x82, 0x01, 0xe8, 0x4f, 0x76, 0xc6, 0x3f, 0x5d, 0x05, 0x71, 0xcc, 0x86, - 0xe3, 0x9c, 0x96, 0x92, 0xcf, 0x9c, 0xe2, 0x3b, 0x7e, 0xe6, 0xac, 0xc1, 0x08, 0x63, 0x32, 0x7b, - 0xe4, 0x8e, 0x4e, 0xbe, 0xf6, 0x18, 0x2b, 0xb1, 0x2c, 0x2a, 0x62, 0x45, 0x02, 0xfd, 0xa0, 0x05, - 0xe7, 0x92, 0x5d, 0xc7, 0x84, 0x81, 0x45, 0xe4, 0x47, 0xfe, 0xc0, 0x5d, 0x15, 0xdf, 0x9f, 0xe2, - 0xff, 0x0d, 0xe4, 0xc3, 0x5e, 0x08, 0xb8, 0x7b, 0x63, 0xa8, 0x9c, 0xf1, 0xc2, 0x1e, 0x32, 0xb5, - 0x0a, 0x7d, 0xbc, 0xb2, 0x5f, 0x84, 0xb1, 0x96, 0xdf, 0xf1, 0x22, 0x61, 0xfd, 0x23, 0x2c, 0x11, - 0x98, 0x06, 0x7e, 0x4d, 0x2b, 0xc7, 0x06, 0x56, 0xe2, 0x6d, 0x3e, 0xf2, 0xd0, 0x6f, 0xf3, 0x37, - 0x61, 0xcc, 0xd3, 0xcc, 0x55, 0x05, 0x3f, 0x70, 0x29, 0x3f, 0x6a, 0xab, 0x6e, 0xdc, 0xca, 0x7b, - 0xa9, 0x97, 0x60, 0x83, 0xda, 0xc9, 0x3e, 0xf8, 0xbe, 0x6c, 0x65, 0x30, 0xf5, 0x5c, 0x04, 0xf0, - 0x51, 0x53, 0x04, 0x70, 0x29, 0x29, 0x02, 0x48, 0x49, 0x94, 0x8d, 0xd7, 0x7f, 0xff, 0x49, 0x69, - 0xfa, 0x0d, 0x84, 0x68, 0x37, 0xe1, 0x42, 0xaf, 0x6b, 0x89, 0x99, 0x81, 0x35, 0x94, 0xfe, 0x30, - 0x36, 0x03, 0x6b, 0x54, 0xca, 0x98, 0x41, 0xfa, 0x0d, 0xb1, 0x63, 0xff, 0x37, 0x0b, 0x8a, 0x55, - 0xbf, 0x71, 0x02, 0x0f, 0xde, 0x8f, 0x19, 0x0f, 0xde, 0xc7, 0xb3, 0x2f, 0xc4, 0x46, 0xae, 0x3c, - 0x7c, 0x25, 0x21, 0x0f, 0x3f, 0x97, 0x47, 0xa0, 0xbb, 0xf4, 0xfb, 0x27, 0x8b, 0x30, 0x5a, 0xf5, - 0x1b, 0xca, 0x06, 0xfb, 0xd7, 0x1e, 0xc6, 0x06, 0x3b, 0x37, 0xb5, 0x82, 0x46, 0x99, 0x59, 0x8f, - 0x49, 0xf7, 0xd3, 0x6f, 0x30, 0x53, 0xec, 0xbb, 0xc4, 0xdd, 0xde, 0x89, 0x48, 0x23, 0xf9, 0x39, - 0x27, 0x67, 0x8a, 0xfd, 0x7b, 0x05, 0x98, 0x4c, 0xb4, 0x8e, 0x9a, 0x30, 0xde, 0xd4, 0xa5, 0xad, - 0x62, 0x9d, 0x3e, 0x94, 0xa0, 0x56, 0x98, 0xb2, 0x6a, 0x45, 0xd8, 0x24, 0x8e, 0x16, 0x00, 0x94, - 0xfa, 0x51, 0x8a, 0xf5, 0x18, 0xd7, 0xaf, 0xf4, 0x93, 0x21, 0xd6, 0x30, 0xd0, 0x4b, 0x30, 0x1a, - 0xf9, 0x6d, 0xbf, 0xe9, 0x6f, 0xef, 0xdf, 0x20, 0x32, 0xa8, 0x93, 0x32, 0x50, 0xdb, 0x88, 0x41, - 0x58, 0xc7, 0x43, 0xf7, 0x61, 0x5a, 0x11, 0xa9, 0x1d, 0x83, 0x04, 0x9a, 0x49, 0x15, 0xd6, 0x93, - 0x14, 0x71, 0xba, 0x11, 0xfb, 0xa7, 0x8a, 0x7c, 0x88, 0xbd, 0xc8, 0x7d, 0x6f, 0x37, 0xbc, 0xbb, - 0x77, 0xc3, 0x57, 0x2d, 0x98, 0xa2, 0xad, 0x33, 0xeb, 0x1b, 0x79, 0xcd, 0xab, 0x38, 0xd1, 0x56, - 0x97, 0x38, 0xd1, 0x97, 0xe8, 0xa9, 0xd9, 0xf0, 0x3b, 0x91, 0x90, 0xdd, 0x69, 0xc7, 0x22, 0x2d, - 0xc5, 0x02, 0x2a, 0xf0, 0x48, 0x10, 0x08, 0x8f, 0x41, 0x1d, 0x8f, 0x04, 0x01, 0x16, 0x50, 0x19, - 0x46, 0x7a, 0x20, 0x3b, 0x8c, 0x34, 0x0f, 0x8e, 0x29, 0xec, 0x34, 0x04, 0xc3, 0xa5, 0x05, 0xc7, - 0x94, 0x06, 0x1c, 0x31, 0x8e, 0xfd, 0xb3, 0x45, 0x18, 0xab, 0xfa, 0x8d, 0x58, 0xf5, 0xf8, 0xa2, - 0xa1, 0x7a, 0xbc, 0x90, 0x50, 0x3d, 0x4e, 0xe9, 0xb8, 0xef, 0x29, 0x1a, 0xbf, 0x5e, 0x8a, 0xc6, - 0x7f, 0x6a, 0xb1, 0x59, 0x2b, 0xaf, 0xd7, 0xb8, 0x31, 0x17, 0xba, 0x0a, 0xa3, 0xec, 0x80, 0x61, - 0x2e, 0xaa, 0x52, 0x1f, 0xc7, 0xd2, 0x23, 0xad, 0xc7, 0xc5, 0x58, 0xc7, 0x41, 0x97, 0x61, 0x24, - 0x24, 0x4e, 0x50, 0xdf, 0x51, 0xa7, 0xab, 0x50, 0x9e, 0xf1, 0x32, 0xac, 0xa0, 0xe8, 0x8d, 0x38, - 0x2e, 0x63, 0x31, 0xdf, 0xe5, 0x4d, 0xef, 0x0f, 0xdf, 0x22, 0xf9, 0xc1, 0x18, 0xed, 0xbb, 0x80, - 0xd2, 0xf8, 0x7d, 0x04, 0x24, 0x9b, 0x37, 0x03, 0x92, 0x95, 0x52, 0xc1, 0xc8, 0xfe, 0xdc, 0x82, - 0x89, 0xaa, 0xdf, 0xa0, 0x5b, 0xf7, 0x9b, 0x69, 0x9f, 0xea, 0x41, 0x69, 0x87, 0xba, 0x04, 0xa5, - 0xbd, 0x08, 0x83, 0x55, 0xbf, 0x51, 0xa9, 0x76, 0xf3, 0x37, 0xb7, 0xff, 0x96, 0x05, 0xc3, 0x55, - 0xbf, 0x71, 0x02, 0x6a, 0x81, 0x8f, 0x9a, 0x6a, 0x81, 0xc7, 0x72, 0xd6, 0x4d, 0x8e, 0x26, 0xe0, - 0x6f, 0x0c, 0xc0, 0x38, 0xed, 0xa7, 0xbf, 0x2d, 0xa7, 0xd2, 0x18, 0x36, 0xab, 0x8f, 0x61, 0xa3, - 0x5c, 0xb8, 0xdf, 0x6c, 0xfa, 0xf7, 0x92, 0xd3, 0xba, 0xca, 0x4a, 0xb1, 0x80, 0xa2, 0x67, 0x61, - 0xa4, 0x1d, 0x90, 0x3d, 0xd7, 0x17, 0xec, 0xad, 0xa6, 0x64, 0xa9, 0x8a, 0x72, 0xac, 0x30, 0xe8, - 0xb3, 0x30, 0x74, 0x3d, 0x7a, 0x95, 0xd7, 0x7d, 0xaf, 0xc1, 0x25, 0xe7, 0x45, 0x91, 0x2a, 0x42, - 0x2b, 0xc7, 0x06, 0x16, 0xba, 0x0b, 0x25, 0xf6, 0x9f, 0x1d, 0x3b, 0x47, 0x4f, 0x3a, 0x2a, 0x92, - 0xd0, 0x09, 0x02, 0x38, 0xa6, 0x85, 0x9e, 0x07, 0x88, 0x64, 0xf4, 0xf1, 0x50, 0x04, 0x9f, 0x52, - 0x4f, 0x01, 0x15, 0x97, 0x3c, 0xc4, 0x1a, 0x16, 0x7a, 0x06, 0x4a, 0x91, 0xe3, 0x36, 0x6f, 0xba, - 0x1e, 0x09, 0x99, 0x44, 0xbc, 0x28, 0x73, 0xc1, 0x89, 0x42, 0x1c, 0xc3, 0x29, 0x2b, 0xc6, 0x22, - 0x33, 0xf0, 0x94, 0xc5, 0x23, 0x0c, 0x9b, 0xb1, 0x62, 0x37, 0x55, 0x29, 0xd6, 0x30, 0xd0, 0x0e, - 0x3c, 0xe1, 0x7a, 0x2c, 0xad, 0x02, 0xa9, 0xed, 0xba, 0xed, 0x8d, 0x9b, 0xb5, 0x3b, 0x24, 0x70, - 0xb7, 0xf6, 0x97, 0x9c, 0xfa, 0x2e, 0xf1, 0x64, 0x3a, 0xc9, 0xf7, 0x8b, 0x2e, 0x3e, 0x51, 0xe9, - 0x82, 0x8b, 0xbb, 0x52, 0xb2, 0x5f, 0x86, 0xd3, 0x55, 0xbf, 0x51, 0xf5, 0x83, 0x68, 0xd5, 0x0f, - 0xee, 0x39, 0x41, 0x43, 0xae, 0x94, 0x79, 0x19, 0x25, 0x81, 0x1e, 0x85, 0x83, 0xfc, 0xa0, 0x30, - 0x22, 0x20, 0xbc, 0xc0, 0x98, 0xaf, 0x23, 0xfa, 0xf6, 0xd4, 0x19, 0x1b, 0xa0, 0x72, 0x8c, 0x5c, - 0x73, 0x22, 0x82, 0x6e, 0xb1, 0xdc, 0xc9, 0xf1, 0x8d, 0x28, 0xaa, 0x3f, 0xad, 0xe5, 0x4e, 0x8e, - 0x81, 0x99, 0x57, 0xa8, 0x59, 0xdf, 0xfe, 0xef, 0x83, 0xec, 0x70, 0x4c, 0xe4, 0xa9, 0x40, 0x9f, - 0x81, 0x89, 0x90, 0xdc, 0x74, 0xbd, 0xce, 0x7d, 0x29, 0x8d, 0xe8, 0xe2, 0x9d, 0x55, 0x5b, 0xd1, - 0x31, 0xb9, 0x4c, 0xd3, 0x2c, 0xc3, 0x09, 0x6a, 0xa8, 0x05, 0x13, 0xf7, 0x5c, 0xaf, 0xe1, 0xdf, - 0x0b, 0x25, 0xfd, 0x91, 0x7c, 0xd1, 0xe6, 0x5d, 0x8e, 0x99, 0xe8, 0xa3, 0xd1, 0xdc, 0x5d, 0x83, - 0x18, 0x4e, 0x10, 0xa7, 0x0b, 0x30, 0xe8, 0x78, 0x8b, 0xe1, 0xed, 0x90, 0x04, 0x22, 0x0b, 0x36, - 0x5b, 0x80, 0x58, 0x16, 0xe2, 0x18, 0x4e, 0x17, 0x20, 0xfb, 0x73, 0x2d, 0xf0, 0x3b, 0x3c, 0x47, - 0x80, 0x58, 0x80, 0x58, 0x95, 0x62, 0x0d, 0x83, 0x6e, 0x50, 0xf6, 0x6f, 0xdd, 0xf7, 0xb0, 0xef, - 0x47, 0x72, 0x4b, 0xb3, 0xbc, 0xab, 0x5a, 0x39, 0x36, 0xb0, 0xd0, 0x2a, 0xa0, 0xb0, 0xd3, 0x6e, - 0x37, 0x99, 0xd9, 0x87, 0xd3, 0x64, 0xa4, 0xb8, 0xca, 0xbd, 0xc8, 0x43, 0xa7, 0xd6, 0x52, 0x50, - 0x9c, 0x51, 0x83, 0x9e, 0xd5, 0x5b, 0xa2, 0xab, 0x83, 0xac, 0xab, 0x5c, 0x0d, 0x52, 0xe3, 0xfd, - 0x94, 0x30, 0xb4, 0x02, 0xc3, 0xe1, 0x7e, 0x58, 0x8f, 0x44, 0x0c, 0xb8, 0x9c, 0x54, 0x44, 0x35, - 0x86, 0xa2, 0x65, 0xc2, 0xe3, 0x55, 0xb0, 0xac, 0x8b, 0xea, 0x30, 0x23, 0x28, 0x2e, 0xef, 0x38, - 0x9e, 0x4a, 0xec, 0xc2, 0xad, 0x5f, 0xaf, 0x3e, 0x38, 0x98, 0x9f, 0x11, 0x2d, 0xeb, 0xe0, 0xc3, - 0x83, 0xf9, 0x33, 0x55, 0xbf, 0x91, 0x01, 0xc1, 0x59, 0xd4, 0xf8, 0xe2, 0xab, 0xd7, 0xfd, 0x56, - 0xbb, 0x1a, 0xf8, 0x5b, 0x6e, 0x93, 0x74, 0x53, 0x25, 0xd5, 0x0c, 0x4c, 0xb1, 0xf8, 0x8c, 0x32, - 0x9c, 0xa0, 0x66, 0x7f, 0x3b, 0xe3, 0x67, 0x58, 0xe2, 0xe7, 0xa8, 0x13, 0x10, 0xd4, 0x82, 0xf1, - 0x36, 0xdb, 0x26, 0x22, 0x72, 0xbf, 0x58, 0xeb, 0x2f, 0xf6, 0x29, 0x12, 0xb9, 0x47, 0xaf, 0x01, - 0x25, 0xb2, 0x64, 0x6f, 0xcd, 0xaa, 0x4e, 0x0e, 0x9b, 0xd4, 0xed, 0x1f, 0x7b, 0x8c, 0xdd, 0x88, - 0x35, 0x2e, 0xe7, 0x18, 0x16, 0xc6, 0xf6, 0xe2, 0x69, 0x35, 0x97, 0x2f, 0x70, 0x8b, 0xa7, 0x45, - 0x18, 0xec, 0x63, 0x59, 0x17, 0x7d, 0x1a, 0x26, 0xe8, 0x4b, 0x45, 0xcb, 0xa8, 0x72, 0x2a, 0x3f, - 0x28, 0x42, 0x9c, 0x48, 0x45, 0xcb, 0xea, 0xa1, 0x57, 0xc6, 0x09, 0x62, 0xe8, 0x0d, 0x66, 0x16, - 0x62, 0x26, 0x6b, 0xe9, 0x41, 0x5a, 0xb7, 0x00, 0x91, 0x64, 0x35, 0x22, 0x79, 0x89, 0x60, 0xec, - 0x47, 0x9b, 0x08, 0x06, 0xdd, 0x84, 0x71, 0x91, 0xfd, 0x58, 0xac, 0xdc, 0xa2, 0x21, 0x07, 0x1c, - 0xc7, 0x3a, 0xf0, 0x30, 0x59, 0x80, 0xcd, 0xca, 0x68, 0x1b, 0xce, 0x69, 0xd9, 0x88, 0xae, 0x05, - 0x0e, 0x53, 0xe6, 0xbb, 0xec, 0x38, 0xd5, 0xee, 0xea, 0x27, 0x1f, 0x1c, 0xcc, 0x9f, 0xdb, 0xe8, - 0x86, 0x88, 0xbb, 0xd3, 0x41, 0xb7, 0xe0, 0x34, 0x77, 0xe9, 0x2d, 0x13, 0xa7, 0xd1, 0x74, 0x3d, - 0xc5, 0x0c, 0xf0, 0x2d, 0x7f, 0xf6, 0xc1, 0xc1, 0xfc, 0xe9, 0xc5, 0x2c, 0x04, 0x9c, 0x5d, 0x0f, - 0x7d, 0x14, 0x4a, 0x0d, 0x2f, 0x14, 0x63, 0x30, 0x64, 0x24, 0x7c, 0x2a, 0x95, 0xd7, 0x6b, 0xea, - 0xfb, 0xe3, 0x3f, 0x38, 0xae, 0x80, 0xb6, 0xb9, 0xac, 0x58, 0x49, 0x30, 0x86, 0x53, 0x21, 0x8d, - 0x92, 0x42, 0x3e, 0xc3, 0xa9, 0x8f, 0x2b, 0x49, 0x94, 0xad, 0xbb, 0xe1, 0xef, 0x67, 0x10, 0x46, - 0xaf, 0x03, 0xa2, 0x2f, 0x08, 0xb7, 0x4e, 0x16, 0xeb, 0x2c, 0x2d, 0x04, 0x13, 0xad, 0x8f, 0x98, - 0x6e, 0x66, 0xb5, 0x14, 0x06, 0xce, 0xa8, 0x85, 0xae, 0xd3, 0x53, 0x45, 0x2f, 0x15, 0xa7, 0x96, - 0x4a, 0xcf, 0x57, 0x26, 0xed, 0x80, 0xd4, 0x9d, 0x88, 0x34, 0x4c, 0x8a, 0x38, 0x51, 0x0f, 0x35, - 0xe0, 0x09, 0xa7, 0x13, 0xf9, 0x4c, 0x0c, 0x6f, 0xa2, 0x6e, 0xf8, 0xbb, 0xc4, 0x63, 0x1a, 0xb0, - 0x91, 0xa5, 0x0b, 0x94, 0xdb, 0x58, 0xec, 0x82, 0x87, 0xbb, 0x52, 0xa1, 0x5c, 0xa2, 0xca, 0xc7, - 0x0b, 0x66, 0xa0, 0xa6, 0x8c, 0x9c, 0xbc, 0x2f, 0xc1, 0xe8, 0x8e, 0x1f, 0x46, 0xeb, 0x24, 0xba, - 0xe7, 0x07, 0xbb, 0x22, 0xde, 0x66, 0x1c, 0xa3, 0x39, 0x06, 0x61, 0x1d, 0x8f, 0x3e, 0x03, 0x99, - 0x7d, 0x46, 0xa5, 0xcc, 0x54, 0xe3, 0x23, 0xf1, 0x19, 0x73, 0x9d, 0x17, 0x63, 0x09, 0x97, 0xa8, - 0x95, 0xea, 0x32, 0x53, 0x73, 0x27, 0x50, 0x2b, 0xd5, 0x65, 0x2c, 0xe1, 0x74, 0xb9, 0x86, 0x3b, - 0x4e, 0x40, 0xaa, 0x81, 0x5f, 0x27, 0xa1, 0x16, 0x19, 0xfc, 0x71, 0x1e, 0x4d, 0x94, 0x2e, 0xd7, - 0x5a, 0x16, 0x02, 0xce, 0xae, 0x87, 0x48, 0x3a, 0x13, 0xd7, 0x44, 0xbe, 0x7e, 0x22, 0xcd, 0xcf, - 0xf4, 0x99, 0x8c, 0xcb, 0x83, 0x29, 0x95, 0x03, 0x8c, 0xc7, 0x0f, 0x0d, 0x67, 0x27, 0xd9, 0xda, - 0xee, 0x3f, 0xf8, 0xa8, 0xd2, 0xf8, 0x54, 0x12, 0x94, 0x70, 0x8a, 0xb6, 0x11, 0x8b, 0x6b, 0xaa, - 0x67, 0x2c, 0xae, 0x2b, 0x50, 0x0a, 0x3b, 0x9b, 0x0d, 0xbf, 0xe5, 0xb8, 0x1e, 0x53, 0x73, 0x6b, - 0xef, 0x91, 0x9a, 0x04, 0xe0, 0x18, 0x07, 0xad, 0xc2, 0x88, 0x23, 0xd5, 0x39, 0x28, 0x3f, 0xfa, - 0x8a, 0x52, 0xe2, 0xf0, 0x80, 0x04, 0x52, 0x81, 0xa3, 0xea, 0xa2, 0x57, 0x61, 0x5c, 0xb8, 0xa4, - 0x8a, 0xf4, 0x93, 0x33, 0xa6, 0xdf, 0x50, 0x4d, 0x07, 0x62, 0x13, 0x17, 0xdd, 0x86, 0xd1, 0xc8, - 0x6f, 0x32, 0xe7, 0x17, 0xca, 0xe6, 0x9d, 0xc9, 0x8f, 0x23, 0xb6, 0xa1, 0xd0, 0x74, 0x49, 0xaa, - 0xaa, 0x8a, 0x75, 0x3a, 0x68, 0x83, 0xaf, 0x77, 0x16, 0x21, 0x9b, 0x84, 0xb3, 0x8f, 0xe5, 0xdf, - 0x49, 0x2a, 0x90, 0xb6, 0xb9, 0x1d, 0x44, 0x4d, 0xac, 0x93, 0x41, 0xd7, 0x60, 0xba, 0x1d, 0xb8, - 0x3e, 0x5b, 0x13, 0x4a, 0x93, 0x37, 0x6b, 0xa6, 0xe7, 0xa9, 0x26, 0x11, 0x70, 0xba, 0x0e, 0xf3, - 0x28, 0x16, 0x85, 0xb3, 0x67, 0x79, 0x86, 0x6a, 0xfe, 0xbc, 0xe3, 0x65, 0x58, 0x41, 0xd1, 0x1a, - 0x3b, 0x89, 0xb9, 0x64, 0x62, 0x76, 0x2e, 0x3f, 0xe0, 0x8b, 0x2e, 0xc1, 0xe0, 0xcc, 0xab, 0xfa, - 0x8b, 0x63, 0x0a, 0xa8, 0xa1, 0xa5, 0x32, 0xa4, 0x2f, 0x86, 0x70, 0xf6, 0x89, 0x2e, 0x46, 0x72, - 0x89, 0xe7, 0x45, 0xcc, 0x10, 0x18, 0xc5, 0x21, 0x4e, 0xd0, 0x44, 0x1f, 0x87, 0x29, 0x11, 0xa6, - 0x2e, 0x1e, 0xa6, 0x73, 0xb1, 0x49, 0x31, 0x4e, 0xc0, 0x70, 0x0a, 0x9b, 0x67, 0x0e, 0x70, 0x36, - 0x9b, 0x44, 0x1c, 0x7d, 0x37, 0x5d, 0x6f, 0x37, 0x9c, 0x3d, 0xcf, 0xce, 0x07, 0x91, 0x39, 0x20, - 0x09, 0xc5, 0x19, 0x35, 0xd0, 0x06, 0x4c, 0xb5, 0x03, 0x42, 0x5a, 0x8c, 0xd1, 0x17, 0xf7, 0xd9, - 0x3c, 0x77, 0xa8, 0xa7, 0x3d, 0xa9, 0x26, 0x60, 0x87, 0x19, 0x65, 0x38, 0x45, 0x01, 0xdd, 0x83, - 0x11, 0x7f, 0x8f, 0x04, 0x3b, 0xc4, 0x69, 0xcc, 0x5e, 0xe8, 0x62, 0xe2, 0x2e, 0x2e, 0xb7, 0x5b, - 0x02, 0x37, 0xa1, 0xfd, 0x97, 0xc5, 0xbd, 0xb5, 0xff, 0xb2, 0x31, 0xf4, 0x43, 0x16, 0x9c, 0x95, - 0x0a, 0x83, 0x5a, 0x9b, 0x8e, 0xfa, 0xb2, 0xef, 0x85, 0x51, 0xc0, 0x5d, 0xc0, 0x9f, 0xcc, 0x77, - 0x8b, 0xde, 0xc8, 0xa9, 0xa4, 0x84, 0xa3, 0x67, 0xf3, 0x30, 0x42, 0x9c, 0xdf, 0x22, 0x5a, 0x86, - 0xe9, 0x90, 0x44, 0xf2, 0x30, 0x5a, 0x0c, 0x57, 0xdf, 0x28, 0xaf, 0xcf, 0x5e, 0xe4, 0xfe, 0xeb, - 0x74, 0x33, 0xd4, 0x92, 0x40, 0x9c, 0xc6, 0x9f, 0xfb, 0x56, 0x98, 0x4e, 0x5d, 0xff, 0x47, 0xc9, - 0x88, 0x32, 0xb7, 0x0b, 0xe3, 0xc6, 0x10, 0x3f, 0x52, 0xed, 0xf1, 0xbf, 0x1a, 0x86, 0x92, 0xd2, - 0x2c, 0xa2, 0x2b, 0xa6, 0xc2, 0xf8, 0x6c, 0x52, 0x61, 0x3c, 0x42, 0xdf, 0xf5, 0xba, 0x8e, 0x78, - 0x23, 0x23, 0x6a, 0x57, 0xde, 0x86, 0xee, 0xdf, 0x1d, 0x5b, 0x13, 0xd7, 0x16, 0xfb, 0xd6, 0x3c, - 0x0f, 0x74, 0x95, 0x00, 0x5f, 0x83, 0x69, 0xcf, 0x67, 0x3c, 0x27, 0x69, 0x48, 0x86, 0x82, 0xf1, - 0x0d, 0x25, 0x3d, 0x0c, 0x46, 0x02, 0x01, 0xa7, 0xeb, 0xd0, 0x06, 0xf9, 0xc5, 0x9f, 0x14, 0x39, - 0x73, 0xbe, 0x00, 0x0b, 0x28, 0xba, 0x08, 0x83, 0x6d, 0xbf, 0x51, 0xa9, 0x0a, 0x7e, 0x53, 0x8b, - 0x15, 0xd9, 0xa8, 0x54, 0x31, 0x87, 0xa1, 0x45, 0x18, 0x62, 0x3f, 0xc2, 0xd9, 0xb1, 0xfc, 0x78, - 0x07, 0xac, 0x86, 0x96, 0x6f, 0x86, 0x55, 0xc0, 0xa2, 0x22, 0x13, 0x7d, 0x51, 0x26, 0x9d, 0x89, - 0xbe, 0x86, 0x1f, 0x52, 0xf4, 0x25, 0x09, 0xe0, 0x98, 0x16, 0xba, 0x0f, 0xa7, 0x8d, 0x87, 0x11, - 0x5f, 0x22, 0x24, 0x14, 0x3e, 0xd7, 0x17, 0xbb, 0xbe, 0x88, 0x84, 0xa6, 0xfa, 0x9c, 0xe8, 0xf4, - 0xe9, 0x4a, 0x16, 0x25, 0x9c, 0xdd, 0x00, 0x6a, 0xc2, 0x74, 0x3d, 0xd5, 0xea, 0x48, 0xff, 0xad, - 0xaa, 0x09, 0x4d, 0xb7, 0x98, 0x26, 0x8c, 0x5e, 0x85, 0x91, 0xb7, 0xfc, 0x90, 0x9d, 0xd5, 0x82, - 0x47, 0x96, 0x0e, 0xbb, 0x23, 0x6f, 0xdc, 0xaa, 0xb1, 0xf2, 0xc3, 0x83, 0xf9, 0xd1, 0xaa, 0xdf, - 0x90, 0x7f, 0xb1, 0xaa, 0x80, 0xbe, 0xd7, 0x82, 0xb9, 0xf4, 0xcb, 0x4b, 0x75, 0x7a, 0xbc, 0xff, - 0x4e, 0xdb, 0xa2, 0xd1, 0xb9, 0x95, 0x5c, 0x72, 0xb8, 0x4b, 0x53, 0xf6, 0x2f, 0x59, 0x4c, 0xea, - 0x26, 0x34, 0x40, 0x24, 0xec, 0x34, 0x4f, 0x22, 0xcd, 0xe6, 0x8a, 0xa1, 0x9c, 0x7a, 0x68, 0xcb, - 0x85, 0x7f, 0x6e, 0x31, 0xcb, 0x85, 0x13, 0x74, 0x51, 0x78, 0x03, 0x46, 0x22, 0x99, 0xfe, 0xb4, - 0x4b, 0x66, 0x50, 0xad, 0x53, 0xcc, 0x7a, 0x43, 0x71, 0xac, 0x2a, 0xd3, 0xa9, 0x22, 0x63, 0xff, - 0x23, 0x3e, 0x03, 0x12, 0x72, 0x02, 0x3a, 0x80, 0xb2, 0xa9, 0x03, 0x98, 0xef, 0xf1, 0x05, 0x39, - 0xba, 0x80, 0x7f, 0x68, 0xf6, 0x9b, 0x49, 0x6a, 0xde, 0xed, 0x26, 0x33, 0xf6, 0x17, 0x2d, 0x80, - 0x38, 0x14, 0x2f, 0x93, 0x2f, 0xfb, 0x81, 0xcc, 0xb1, 0x98, 0x95, 0x4d, 0xe8, 0x65, 0xca, 0xa3, - 0xfa, 0x91, 0x5f, 0xf7, 0x9b, 0x42, 0xc3, 0xf5, 0x44, 0xac, 0x86, 0xe0, 0xe5, 0x87, 0xda, 0x6f, - 0xac, 0xb0, 0xd1, 0xbc, 0x0c, 0xfc, 0x55, 0x8c, 0x15, 0x63, 0x46, 0xd0, 0xaf, 0x1f, 0xb1, 0xe0, - 0x54, 0x96, 0xbd, 0x2b, 0x7d, 0xf1, 0x70, 0x99, 0x95, 0x32, 0x67, 0x52, 0xb3, 0x79, 0x47, 0x94, - 0x63, 0x85, 0xd1, 0x77, 0xe6, 0xb0, 0xa3, 0xc5, 0xc0, 0xbd, 0x05, 0xe3, 0xd5, 0x80, 0x68, 0x97, - 0xeb, 0x6b, 0xdc, 0x99, 0x9c, 0xf7, 0xe7, 0xd9, 0x23, 0x3b, 0x92, 0xdb, 0x3f, 0x5d, 0x80, 0x53, - 0xdc, 0x2a, 0x60, 0x71, 0xcf, 0x77, 0x1b, 0x55, 0xbf, 0x21, 0xb2, 0xbe, 0x7d, 0x0a, 0xc6, 0xda, - 0x9a, 0xa0, 0xb1, 0x5b, 0x3c, 0x47, 0x5d, 0x20, 0x19, 0x8b, 0x46, 0xf4, 0x52, 0x6c, 0xd0, 0x42, - 0x0d, 0x18, 0x23, 0x7b, 0x6e, 0x5d, 0xa9, 0x96, 0x0b, 0x47, 0xbe, 0xe8, 0x54, 0x2b, 0x2b, 0x1a, - 0x1d, 0x6c, 0x50, 0x7d, 0x04, 0xf9, 0x7c, 0xed, 0x1f, 0xb5, 0xe0, 0xb1, 0x9c, 0xe8, 0x8f, 0xb4, - 0xb9, 0x7b, 0xcc, 0xfe, 0x42, 0x2c, 0x5b, 0xd5, 0x1c, 0xb7, 0xca, 0xc0, 0x02, 0x8a, 0x3e, 0x01, - 0xc0, 0xad, 0x2a, 0xe8, 0x93, 0xbb, 0x57, 0x98, 0x3c, 0x23, 0xc2, 0x97, 0x16, 0xac, 0x49, 0xd6, - 0xc7, 0x1a, 0x2d, 0xfb, 0x4b, 0x03, 0x30, 0xc8, 0x93, 0xad, 0xaf, 0xc2, 0xf0, 0x0e, 0xcf, 0x85, - 0xd1, 0x4f, 0xda, 0x8d, 0x58, 0x18, 0xc2, 0x0b, 0xb0, 0xac, 0x8c, 0xd6, 0x60, 0x86, 0xe7, 0x12, - 0x69, 0x96, 0x49, 0xd3, 0xd9, 0x97, 0x92, 0x3b, 0x9e, 0x87, 0x53, 0x49, 0x30, 0x2b, 0x69, 0x14, - 0x9c, 0x55, 0x0f, 0xbd, 0x06, 0x13, 0xf4, 0x25, 0xe5, 0x77, 0x22, 0x49, 0x89, 0x67, 0x11, 0x51, - 0x4f, 0xb7, 0x0d, 0x03, 0x8a, 0x13, 0xd8, 0xf4, 0x31, 0xdf, 0x4e, 0xc9, 0x28, 0x07, 0xe3, 0xc7, - 0xbc, 0x29, 0x97, 0x34, 0x71, 0x99, 0xa1, 0x6b, 0x87, 0x99, 0xf5, 0x6e, 0xec, 0x04, 0x24, 0xdc, - 0xf1, 0x9b, 0x0d, 0xc6, 0xf4, 0x0d, 0x6a, 0x86, 0xae, 0x09, 0x38, 0x4e, 0xd5, 0xa0, 0x54, 0xb6, - 0x1c, 0xb7, 0xd9, 0x09, 0x48, 0x4c, 0x65, 0xc8, 0xa4, 0xb2, 0x9a, 0x80, 0xe3, 0x54, 0x8d, 0xde, - 0xc2, 0xd7, 0xe1, 0xe3, 0x11, 0xbe, 0xd2, 0x05, 0x7b, 0xba, 0x1a, 0xf8, 0xf4, 0xc4, 0x96, 0xb1, - 0x73, 0x94, 0x99, 0xf4, 0xb0, 0x74, 0xf3, 0xed, 0x12, 0x65, 0x4e, 0x18, 0x92, 0x72, 0x0a, 0x86, - 0xa5, 0x42, 0x4d, 0x38, 0xf8, 0x4a, 0x2a, 0xe8, 0x2a, 0x8c, 0x8a, 0x54, 0x14, 0xcc, 0x9a, 0x97, - 0xaf, 0x11, 0x66, 0x59, 0x51, 0x8e, 0x8b, 0xb1, 0x8e, 0x63, 0x7f, 0x5f, 0x01, 0x66, 0x32, 0xdc, - 0x31, 0xf8, 0x99, 0xb8, 0xed, 0x86, 0x91, 0x4a, 0x6a, 0xa8, 0x9d, 0x89, 0xbc, 0x1c, 0x2b, 0x0c, - 0xba, 0xf1, 0xf8, 0xa9, 0x9b, 0x3c, 0x69, 0x85, 0xb9, 0xb3, 0x80, 0x1e, 0x31, 0x3d, 0xe0, 0x05, - 0x18, 0xe8, 0x84, 0x44, 0xc6, 0x87, 0x54, 0x77, 0x10, 0x53, 0xb8, 0x31, 0x08, 0x7d, 0x13, 0x6c, - 0x2b, 0xdd, 0x95, 0xf6, 0x26, 0xe0, 0xda, 0x2b, 0x0e, 0xa3, 0x9d, 0x8b, 0x88, 0xe7, 0x78, 0x91, - 0x78, 0x39, 0xc4, 0x81, 0xce, 0x58, 0x29, 0x16, 0x50, 0xfb, 0x4b, 0x45, 0x38, 0x9b, 0xeb, 0xa0, - 0x45, 0xbb, 0xde, 0xf2, 0x3d, 0x37, 0xf2, 0x95, 0xc9, 0x0a, 0x0f, 0x6e, 0x46, 0xda, 0x3b, 0x6b, - 0xa2, 0x1c, 0x2b, 0x0c, 0x74, 0x09, 0x06, 0x99, 0xb8, 0x2e, 0x95, 0xde, 0x71, 0xa9, 0xcc, 0xa3, - 0xdd, 0x70, 0x70, 0xdf, 0xa9, 0x73, 0x2f, 0xd2, 0xeb, 0xd8, 0x6f, 0x26, 0x4f, 0x47, 0xda, 0x5d, - 0xdf, 0x6f, 0x62, 0x06, 0x44, 0x1f, 0x10, 0xe3, 0x95, 0xb0, 0xd1, 0xc0, 0x4e, 0xc3, 0x0f, 0xb5, - 0x41, 0x7b, 0x1a, 0x86, 0x77, 0xc9, 0x7e, 0xe0, 0x7a, 0xdb, 0x49, 0xdb, 0x9d, 0x1b, 0xbc, 0x18, - 0x4b, 0xb8, 0x99, 0xa9, 0x6b, 0xf8, 0xb8, 0x73, 0xde, 0x8e, 0xf4, 0xbc, 0x6b, 0x7f, 0xa0, 0x08, - 0x93, 0x78, 0xa9, 0xfc, 0xde, 0x44, 0xdc, 0x4e, 0x4f, 0xc4, 0x71, 0xe7, 0xbc, 0xed, 0x3d, 0x1b, - 0x3f, 0x6f, 0xc1, 0x24, 0x4b, 0x88, 0x21, 0xc2, 0x62, 0xb9, 0xbe, 0x77, 0x02, 0x7c, 0xed, 0x45, - 0x18, 0x0c, 0x68, 0xa3, 0xc9, 0xbc, 0x8e, 0xac, 0x27, 0x98, 0xc3, 0xd0, 0x13, 0x30, 0xc0, 0xba, - 0x40, 0x27, 0x6f, 0x8c, 0xa7, 0xc4, 0x2a, 0x3b, 0x91, 0x83, 0x59, 0x29, 0x8b, 0xf5, 0x82, 0x49, - 0xbb, 0xe9, 0xf2, 0x4e, 0xc7, 0xca, 0xd4, 0x77, 0x87, 0xeb, 0x76, 0x66, 0xd7, 0xde, 0x59, 0xac, - 0x97, 0x6c, 0x92, 0xdd, 0xdf, 0x8c, 0x7f, 0x54, 0x80, 0xf3, 0x99, 0xf5, 0xfa, 0x8e, 0xf5, 0xd2, - 0xbd, 0xf6, 0xa3, 0x4c, 0x79, 0x50, 0x3c, 0x41, 0xcb, 0xc8, 0x81, 0x7e, 0x59, 0xd9, 0xc1, 0x3e, - 0x42, 0xb0, 0x64, 0x0e, 0xd9, 0xbb, 0x24, 0x04, 0x4b, 0x66, 0xdf, 0x72, 0xde, 0xbc, 0x7f, 0x51, - 0xc8, 0xf9, 0x16, 0xf6, 0xfa, 0xbd, 0x4c, 0xcf, 0x19, 0x06, 0x0c, 0xe5, 0x8b, 0x92, 0x9f, 0x31, - 0xbc, 0x0c, 0x2b, 0x28, 0x5a, 0x84, 0xc9, 0x96, 0xeb, 0xd1, 0xc3, 0x67, 0xdf, 0xe4, 0x30, 0x55, - 0x84, 0xac, 0x35, 0x13, 0x8c, 0x93, 0xf8, 0xc8, 0xd5, 0xc2, 0xb3, 0x14, 0xf2, 0x33, 0xa5, 0xe7, - 0xf6, 0x76, 0xc1, 0x54, 0x34, 0xab, 0x51, 0xcc, 0x08, 0xd5, 0xb2, 0xa6, 0x09, 0x3d, 0x8a, 0xfd, - 0x0b, 0x3d, 0xc6, 0xb2, 0x05, 0x1e, 0x73, 0xaf, 0xc2, 0xf8, 0x43, 0x4b, 0xb9, 0xed, 0xaf, 0x16, - 0xe1, 0xf1, 0x2e, 0xdb, 0x9e, 0x9f, 0xf5, 0xc6, 0x1c, 0x68, 0x67, 0x7d, 0x6a, 0x1e, 0xaa, 0x70, - 0x6a, 0xab, 0xd3, 0x6c, 0xee, 0x33, 0x87, 0x01, 0xd2, 0x90, 0x18, 0x82, 0xa7, 0x94, 0x2f, 0xfd, - 0x53, 0xab, 0x19, 0x38, 0x38, 0xb3, 0x26, 0x7d, 0x39, 0xd0, 0x9b, 0x64, 0x5f, 0x91, 0x4a, 0xbc, - 0x1c, 0xb0, 0x0e, 0xc4, 0x26, 0x2e, 0xba, 0x06, 0xd3, 0xce, 0x9e, 0xe3, 0xf2, 0x18, 0xb7, 0x92, - 0x00, 0x7f, 0x3a, 0x28, 0xe1, 0xe4, 0x62, 0x12, 0x01, 0xa7, 0xeb, 0xa0, 0xd7, 0x01, 0xf9, 0x9b, - 0xcc, 0xac, 0xb8, 0x71, 0x8d, 0x78, 0x42, 0x1f, 0xc8, 0xe6, 0xae, 0x18, 0x1f, 0x09, 0xb7, 0x52, - 0x18, 0x38, 0xa3, 0x56, 0x22, 0xdc, 0xc9, 0x50, 0x7e, 0xb8, 0x93, 0xee, 0xe7, 0x62, 0xcf, 0x6c, - 0x1b, 0xff, 0xc9, 0xa2, 0xd7, 0x17, 0x67, 0xf2, 0xcd, 0xa8, 0x7d, 0xaf, 0x32, 0x7b, 0x3e, 0x2e, - 0xb8, 0xd4, 0x82, 0x74, 0x9c, 0xd6, 0xec, 0xf9, 0x62, 0x20, 0x36, 0x71, 0xf9, 0x82, 0x08, 0x63, - 0xdf, 0x50, 0x83, 0xc5, 0x17, 0xa1, 0x85, 0x14, 0x06, 0xfa, 0x24, 0x0c, 0x37, 0xdc, 0x3d, 0x37, - 0x14, 0x62, 0x9b, 0x23, 0xeb, 0x48, 0xe2, 0x73, 0xb0, 0xcc, 0xc9, 0x60, 0x49, 0xcf, 0xfe, 0x81, - 0x02, 0x8c, 0xcb, 0x16, 0xdf, 0xe8, 0xf8, 0x91, 0x73, 0x02, 0xd7, 0xf2, 0x35, 0xe3, 0x5a, 0xfe, - 0x40, 0xb7, 0xf8, 0x4a, 0xac, 0x4b, 0xb9, 0xd7, 0xf1, 0xad, 0xc4, 0x75, 0xfc, 0x54, 0x6f, 0x52, - 0xdd, 0xaf, 0xe1, 0x7f, 0x6c, 0xc1, 0xb4, 0x81, 0x7f, 0x02, 0xb7, 0xc1, 0xaa, 0x79, 0x1b, 0x3c, - 0xd9, 0xf3, 0x1b, 0x72, 0x6e, 0x81, 0xef, 0x2e, 0x26, 0xfa, 0xce, 0x4e, 0xff, 0xb7, 0x60, 0x60, - 0xc7, 0x09, 0x1a, 0xdd, 0xe2, 0xc9, 0xa7, 0x2a, 0x2d, 0x5c, 0x77, 0x02, 0xa1, 0x10, 0x7d, 0x56, - 0x25, 0x2a, 0x77, 0x82, 0xde, 0xca, 0x50, 0xd6, 0x14, 0x7a, 0x19, 0x86, 0xc2, 0xba, 0xdf, 0x56, - 0xee, 0x02, 0x17, 0x78, 0x12, 0x73, 0x5a, 0x72, 0x78, 0x30, 0x8f, 0xcc, 0xe6, 0x68, 0x31, 0x16, - 0xf8, 0xe8, 0x53, 0x30, 0xce, 0x7e, 0x29, 0xeb, 0xa4, 0x62, 0x7e, 0xee, 0xa9, 0x9a, 0x8e, 0xc8, - 0x4d, 0xf7, 0x8c, 0x22, 0x6c, 0x92, 0x9a, 0xdb, 0x86, 0x92, 0xfa, 0xac, 0x47, 0xaa, 0x84, 0xfc, - 0xf7, 0x45, 0x98, 0xc9, 0x58, 0x73, 0x28, 0x34, 0x66, 0xe2, 0x6a, 0x9f, 0x4b, 0xf5, 0x1d, 0xce, - 0x45, 0xc8, 0x5e, 0x43, 0x0d, 0xb1, 0xb6, 0xfa, 0x6e, 0xf4, 0x76, 0x48, 0x92, 0x8d, 0xd2, 0xa2, - 0xde, 0x8d, 0xd2, 0xc6, 0x4e, 0x6c, 0xa8, 0x69, 0x43, 0xaa, 0xa7, 0x8f, 0x74, 0x4e, 0xff, 0xb4, - 0x08, 0xa7, 0xb2, 0x42, 0xbe, 0xa1, 0xcf, 0x27, 0xb2, 0x19, 0xbe, 0xd8, 0x6f, 0xb0, 0x38, 0x9e, - 0xe2, 0x90, 0x0b, 0x9b, 0x97, 0x16, 0xcc, 0xfc, 0x86, 0x3d, 0x87, 0x59, 0xb4, 0xc9, 0xe2, 0x1e, - 0x04, 0x3c, 0x0b, 0xa5, 0x3c, 0x3e, 0x3e, 0xdc, 0x77, 0x07, 0x44, 0xfa, 0xca, 0x30, 0x61, 0xf9, - 0x20, 0x8b, 0x7b, 0x5b, 0x3e, 0xc8, 0x96, 0xe7, 0x5c, 0x18, 0xd5, 0xbe, 0xe6, 0x91, 0xce, 0xf8, - 0x2e, 0xbd, 0xad, 0xb4, 0x7e, 0x3f, 0xd2, 0x59, 0xff, 0x51, 0x0b, 0x12, 0xc6, 0xf0, 0x4a, 0x2c, - 0x66, 0xe5, 0x8a, 0xc5, 0x2e, 0xc0, 0x40, 0xe0, 0x37, 0x49, 0x32, 0xed, 0x1f, 0xf6, 0x9b, 0x04, - 0x33, 0x08, 0xc5, 0x88, 0x62, 0x61, 0xc7, 0x98, 0xfe, 0x90, 0x13, 0x4f, 0xb4, 0x8b, 0x30, 0xd8, - 0x24, 0x7b, 0xa4, 0x99, 0xcc, 0xce, 0x72, 0x93, 0x16, 0x62, 0x0e, 0xb3, 0x7f, 0x7e, 0x00, 0xce, - 0x75, 0x8d, 0x1c, 0x42, 0x9f, 0x43, 0xdb, 0x4e, 0x44, 0xee, 0x39, 0xfb, 0xc9, 0x34, 0x0a, 0xd7, - 0x78, 0x31, 0x96, 0x70, 0xe6, 0xae, 0xc4, 0xa3, 0x21, 0x27, 0x84, 0x88, 0x22, 0x08, 0xb2, 0x80, - 0x9a, 0x42, 0xa9, 0xe2, 0x71, 0x08, 0xa5, 0x9e, 0x07, 0x08, 0xc3, 0x26, 0x37, 0x19, 0x6a, 0x08, - 0x3f, 0xa8, 0x38, 0x6a, 0x76, 0xed, 0xa6, 0x80, 0x60, 0x0d, 0x0b, 0x95, 0x61, 0xaa, 0x1d, 0xf8, - 0x11, 0x97, 0xc9, 0x96, 0xb9, 0x55, 0xdd, 0xa0, 0x19, 0xb4, 0xa1, 0x9a, 0x80, 0xe3, 0x54, 0x0d, - 0xf4, 0x12, 0x8c, 0x8a, 0x40, 0x0e, 0x55, 0xdf, 0x6f, 0x0a, 0x31, 0x90, 0x32, 0x34, 0xab, 0xc5, - 0x20, 0xac, 0xe3, 0x69, 0xd5, 0x98, 0xa0, 0x77, 0x38, 0xb3, 0x1a, 0x17, 0xf6, 0x6a, 0x78, 0x89, - 0xf0, 0x8f, 0x23, 0x7d, 0x85, 0x7f, 0x8c, 0x05, 0x63, 0xa5, 0xbe, 0x95, 0x68, 0xd0, 0x53, 0x94, - 0xf4, 0x33, 0x03, 0x30, 0x23, 0x16, 0xce, 0xa3, 0x5e, 0x2e, 0xb7, 0xd3, 0xcb, 0xe5, 0x38, 0x44, - 0x67, 0xef, 0xad, 0x99, 0x93, 0x5e, 0x33, 0x3f, 0x68, 0x81, 0xc9, 0x5e, 0xa1, 0xff, 0x27, 0x37, - 0x0f, 0xcd, 0x4b, 0xb9, 0xec, 0x5a, 0x43, 0x5e, 0x20, 0xef, 0x30, 0x23, 0x8d, 0xfd, 0x1f, 0x2d, - 0x78, 0xb2, 0x27, 0x45, 0xb4, 0x02, 0x25, 0xc6, 0x03, 0x6a, 0xaf, 0xb3, 0xa7, 0x94, 0xd5, 0xad, - 0x04, 0xe4, 0xb0, 0xa4, 0x71, 0x4d, 0xb4, 0x92, 0x4a, 0xf8, 0xf3, 0x74, 0x46, 0xc2, 0x9f, 0xd3, - 0xc6, 0xf0, 0x3c, 0x64, 0xc6, 0x9f, 0xef, 0xa7, 0x37, 0x8e, 0xe1, 0xf1, 0x82, 0x3e, 0x6c, 0x88, - 0xfd, 0xec, 0x84, 0xd8, 0x0f, 0x99, 0xd8, 0xda, 0x1d, 0xf2, 0x71, 0x98, 0x62, 0x11, 0x9e, 0x98, - 0x0d, 0xb8, 0xf0, 0xc5, 0x29, 0xc4, 0x76, 0x9e, 0x37, 0x13, 0x30, 0x9c, 0xc2, 0xb6, 0xff, 0xb0, - 0x08, 0x43, 0x7c, 0xfb, 0x9d, 0xc0, 0x9b, 0xf0, 0x19, 0x28, 0xb9, 0xad, 0x56, 0x87, 0xe7, 0x70, - 0x19, 0xe4, 0x0e, 0xb8, 0x74, 0x9e, 0x2a, 0xb2, 0x10, 0xc7, 0x70, 0xb4, 0x2a, 0x24, 0xce, 0x5d, - 0x82, 0x48, 0xf2, 0x8e, 0x2f, 0x94, 0x9d, 0xc8, 0xe1, 0x0c, 0x8e, 0xba, 0x67, 0x63, 0xd9, 0x34, - 0xfa, 0x0c, 0x40, 0x18, 0x05, 0xae, 0xb7, 0x4d, 0xcb, 0x44, 0xcc, 0xd4, 0x0f, 0x76, 0xa1, 0x56, - 0x53, 0xc8, 0x9c, 0x66, 0x7c, 0xe6, 0x28, 0x00, 0xd6, 0x28, 0xa2, 0x05, 0xe3, 0xa6, 0x9f, 0x4b, - 0xcc, 0x1d, 0x70, 0xaa, 0xf1, 0x9c, 0xcd, 0x7d, 0x04, 0x4a, 0x8a, 0x78, 0x2f, 0xf9, 0xd3, 0x98, - 0xce, 0x16, 0x7d, 0x0c, 0x26, 0x13, 0x7d, 0x3b, 0x92, 0xf8, 0xea, 0x17, 0x2c, 0x98, 0xe4, 0x9d, - 0x59, 0xf1, 0xf6, 0xc4, 0x6d, 0xf0, 0x36, 0x9c, 0x6a, 0x66, 0x9c, 0xca, 0x62, 0xfa, 0xfb, 0x3f, - 0xc5, 0x95, 0xb8, 0x2a, 0x0b, 0x8a, 0x33, 0xdb, 0x40, 0x97, 0xe9, 0x8e, 0xa3, 0xa7, 0xae, 0xd3, - 0x14, 0xfe, 0xb8, 0x63, 0x7c, 0xb7, 0xf1, 0x32, 0xac, 0xa0, 0xf6, 0xef, 0x58, 0x30, 0xcd, 0x7b, - 0x7e, 0x83, 0xec, 0xab, 0xb3, 0xe9, 0xeb, 0xd9, 0x77, 0x91, 0x3d, 0xac, 0x90, 0x93, 0x3d, 0x4c, - 0xff, 0xb4, 0x62, 0xd7, 0x4f, 0xfb, 0x69, 0x0b, 0xc4, 0x0a, 0x39, 0x01, 0x21, 0xc4, 0xb7, 0x9a, - 0x42, 0x88, 0xb9, 0xfc, 0x4d, 0x90, 0x23, 0x7d, 0xf8, 0x73, 0x0b, 0xa6, 0x38, 0x42, 0xac, 0x2d, - 0xff, 0xba, 0xce, 0x43, 0x3f, 0x39, 0x86, 0x6f, 0x90, 0xfd, 0x0d, 0xbf, 0xea, 0x44, 0x3b, 0xd9, - 0x1f, 0x65, 0x4c, 0xd6, 0x40, 0xd7, 0xc9, 0x6a, 0xc8, 0x0d, 0x74, 0x84, 0xc4, 0xe5, 0x47, 0x4e, - 0xae, 0x61, 0x7f, 0xcd, 0x02, 0xc4, 0x9b, 0x31, 0x18, 0x37, 0xca, 0x0e, 0xb1, 0x52, 0xed, 0xa2, - 0x8b, 0x8f, 0x26, 0x05, 0xc1, 0x1a, 0xd6, 0xb1, 0x0c, 0x4f, 0xc2, 0xe4, 0xa1, 0xd8, 0xdb, 0xe4, - 0xe1, 0x08, 0x23, 0xfa, 0xaf, 0x87, 0x20, 0xe9, 0xf5, 0x83, 0xee, 0xc0, 0x58, 0xdd, 0x69, 0x3b, - 0x9b, 0x6e, 0xd3, 0x8d, 0x5c, 0x12, 0x76, 0x33, 0xca, 0x5a, 0xd6, 0xf0, 0x84, 0x92, 0x5a, 0x2b, - 0xc1, 0x06, 0x1d, 0xb4, 0x00, 0xd0, 0x0e, 0xdc, 0x3d, 0xb7, 0x49, 0xb6, 0x99, 0xac, 0x84, 0x45, - 0x00, 0xe0, 0x96, 0x46, 0xb2, 0x14, 0x6b, 0x18, 0x19, 0x2e, 0xd6, 0xc5, 0x47, 0xec, 0x62, 0x0d, - 0x27, 0xe6, 0x62, 0x3d, 0x70, 0x24, 0x17, 0xeb, 0x91, 0x23, 0xbb, 0x58, 0x0f, 0xf6, 0xe5, 0x62, - 0x8d, 0xe1, 0x8c, 0xe4, 0x3d, 0xe9, 0xff, 0x55, 0xb7, 0x49, 0xc4, 0x83, 0x83, 0x87, 0x2d, 0x98, - 0x7b, 0x70, 0x30, 0x7f, 0x06, 0x67, 0x62, 0xe0, 0x9c, 0x9a, 0xe8, 0x13, 0x30, 0xeb, 0x34, 0x9b, - 0xfe, 0x3d, 0x35, 0xa9, 0x2b, 0x61, 0xdd, 0x69, 0x72, 0x25, 0xc4, 0x30, 0xa3, 0xfa, 0xc4, 0x83, - 0x83, 0xf9, 0xd9, 0xc5, 0x1c, 0x1c, 0x9c, 0x5b, 0x1b, 0x7d, 0x14, 0x4a, 0xed, 0xc0, 0xaf, 0xaf, - 0x69, 0xae, 0x89, 0xe7, 0xe9, 0x00, 0x56, 0x65, 0xe1, 0xe1, 0xc1, 0xfc, 0xb8, 0xfa, 0xc3, 0x2e, - 0xfc, 0xb8, 0x42, 0x86, 0xcf, 0xf4, 0xe8, 0xb1, 0xfa, 0x4c, 0xef, 0xc2, 0x4c, 0x8d, 0x04, 0x2e, - 0x4b, 0x73, 0xde, 0x88, 0xcf, 0xa7, 0x0d, 0x28, 0x05, 0x89, 0x13, 0xb9, 0xaf, 0xc0, 0x8e, 0x5a, - 0x96, 0x03, 0x79, 0x02, 0xc7, 0x84, 0xec, 0xff, 0x65, 0xc1, 0xb0, 0xf0, 0xf2, 0x39, 0x01, 0xae, - 0x71, 0xd1, 0xd0, 0x24, 0xcc, 0x67, 0x0f, 0x18, 0xeb, 0x4c, 0xae, 0x0e, 0xa1, 0x92, 0xd0, 0x21, - 0x3c, 0xd9, 0x8d, 0x48, 0x77, 0xed, 0xc1, 0x5f, 0x2b, 0x52, 0xee, 0xdd, 0xf0, 0x37, 0x7d, 0xf4, - 0x43, 0xb0, 0x0e, 0xc3, 0xa1, 0xf0, 0x77, 0x2c, 0xe4, 0x1b, 0xe8, 0x27, 0x27, 0x31, 0xb6, 0x63, - 0x13, 0x1e, 0x8e, 0x92, 0x48, 0xa6, 0x23, 0x65, 0xf1, 0x11, 0x3a, 0x52, 0xf6, 0xf2, 0xc8, 0x1d, - 0x38, 0x0e, 0x8f, 0x5c, 0xfb, 0x2b, 0xec, 0xe6, 0xd4, 0xcb, 0x4f, 0x80, 0xa9, 0xba, 0x66, 0xde, - 0xb1, 0x76, 0x97, 0x95, 0x25, 0x3a, 0x95, 0xc3, 0x5c, 0xfd, 0x9c, 0x05, 0xe7, 0x32, 0xbe, 0x4a, - 0xe3, 0xb4, 0x9e, 0x85, 0x11, 0xa7, 0xd3, 0x70, 0xd5, 0x5e, 0xd6, 0xf4, 0x89, 0x8b, 0xa2, 0x1c, - 0x2b, 0x0c, 0xb4, 0x0c, 0xd3, 0xe4, 0x7e, 0xdb, 0xe5, 0xaa, 0x54, 0xdd, 0xaa, 0xb5, 0xc8, 0x5d, - 0xc3, 0x56, 0x92, 0x40, 0x9c, 0xc6, 0x57, 0x51, 0x50, 0x8a, 0xb9, 0x51, 0x50, 0xfe, 0x9e, 0x05, - 0xa3, 0xca, 0xe3, 0xef, 0x91, 0x8f, 0xf6, 0xc7, 0xcd, 0xd1, 0x7e, 0xbc, 0xcb, 0x68, 0xe7, 0x0c, - 0xf3, 0x6f, 0x15, 0x54, 0x7f, 0xab, 0x7e, 0x10, 0xf5, 0xc1, 0xc1, 0x3d, 0xbc, 0x1d, 0xfe, 0x55, - 0x18, 0x75, 0xda, 0x6d, 0x09, 0x90, 0x36, 0x68, 0x2c, 0x4c, 0x6f, 0x5c, 0x8c, 0x75, 0x1c, 0xe5, - 0x16, 0x50, 0xcc, 0x75, 0x0b, 0x68, 0x00, 0x44, 0x4e, 0xb0, 0x4d, 0x22, 0x5a, 0x26, 0x22, 0x96, - 0xe5, 0x9f, 0x37, 0x9d, 0xc8, 0x6d, 0x2e, 0xb8, 0x5e, 0x14, 0x46, 0xc1, 0x42, 0xc5, 0x8b, 0x6e, - 0x05, 0xfc, 0x09, 0xa9, 0x85, 0x04, 0x52, 0xb4, 0xb0, 0x46, 0x57, 0x7a, 0xb7, 0xb3, 0x36, 0x06, - 0x4d, 0x63, 0x86, 0x75, 0x51, 0x8e, 0x15, 0x86, 0xfd, 0x11, 0x76, 0xfb, 0xb0, 0x31, 0x3d, 0x5a, - 0x0c, 0x9d, 0xff, 0x3a, 0xa6, 0x66, 0x83, 0x69, 0x32, 0xcb, 0x7a, 0xa4, 0x9e, 0xee, 0x87, 0x3d, - 0x6d, 0x58, 0x77, 0x52, 0x8b, 0xc3, 0xf9, 0xa0, 0x6f, 0x4b, 0x19, 0xa8, 0x3c, 0xd7, 0xe3, 0xd6, - 0x38, 0x82, 0x49, 0x0a, 0xcb, 0xd9, 0xc1, 0x32, 0x1a, 0x54, 0xaa, 0x62, 0x5f, 0x68, 0x39, 0x3b, - 0x04, 0x00, 0xc7, 0x38, 0x94, 0x99, 0x52, 0x7f, 0xc2, 0x59, 0x14, 0xc7, 0xae, 0x54, 0xd8, 0x21, - 0xd6, 0x30, 0xd0, 0x15, 0x21, 0x50, 0xe0, 0x7a, 0x81, 0xc7, 0x13, 0x02, 0x05, 0x39, 0x5c, 0x9a, - 0x14, 0xe8, 0x2a, 0x8c, 0xaa, 0xb4, 0xbd, 0x55, 0x9e, 0x0d, 0x56, 0x2c, 0xb3, 0x95, 0xb8, 0x18, - 0xeb, 0x38, 0x68, 0x03, 0x26, 0x43, 0x2e, 0x67, 0x53, 0x01, 0x85, 0xb9, 0xbc, 0xf2, 0x83, 0xd2, - 0x0a, 0xa8, 0x66, 0x82, 0x0f, 0x59, 0x11, 0x3f, 0x9d, 0xa4, 0x07, 0x7a, 0x92, 0x04, 0x7a, 0x0d, - 0x26, 0x9a, 0xbe, 0xd3, 0x58, 0x72, 0x9a, 0x8e, 0x57, 0x67, 0xe3, 0x33, 0x62, 0x66, 0x7f, 0xbc, - 0x69, 0x40, 0x71, 0x02, 0x9b, 0x32, 0x6f, 0x7a, 0x89, 0x08, 0x82, 0xed, 0x78, 0xdb, 0x24, 0x14, - 0x49, 0x58, 0x19, 0xf3, 0x76, 0x33, 0x07, 0x07, 0xe7, 0xd6, 0x46, 0x2f, 0xc3, 0x98, 0xfc, 0x7c, - 0x2d, 0x60, 0x43, 0xec, 0x61, 0xa1, 0xc1, 0xb0, 0x81, 0x89, 0xee, 0xc1, 0x69, 0xf9, 0x7f, 0x23, - 0x70, 0xb6, 0xb6, 0xdc, 0xba, 0xf0, 0x62, 0xe6, 0xae, 0x98, 0x8b, 0xd2, 0x5f, 0x70, 0x25, 0x0b, - 0xe9, 0xf0, 0x60, 0xfe, 0x82, 0x18, 0xb5, 0x4c, 0x38, 0x9b, 0xc4, 0x6c, 0xfa, 0x68, 0x0d, 0x66, - 0x76, 0x88, 0xd3, 0x8c, 0x76, 0x96, 0x77, 0x48, 0x7d, 0x57, 0x6e, 0x3a, 0x16, 0x06, 0x42, 0xf3, - 0x4b, 0xb8, 0x9e, 0x46, 0xc1, 0x59, 0xf5, 0xd0, 0x9b, 0x30, 0xdb, 0xee, 0x6c, 0x36, 0xdd, 0x70, - 0x67, 0xdd, 0x8f, 0x98, 0x29, 0x90, 0xca, 0x02, 0x2c, 0xe2, 0x45, 0xa8, 0x40, 0x1b, 0xd5, 0x1c, - 0x3c, 0x9c, 0x4b, 0x01, 0xbd, 0x0d, 0xa7, 0x13, 0x8b, 0x41, 0x78, 0xcc, 0x4f, 0xe4, 0xa7, 0x14, - 0xa8, 0x65, 0x55, 0x10, 0xc1, 0x27, 0xb2, 0x40, 0x38, 0xbb, 0x09, 0xfa, 0xf8, 0xd0, 0x62, 0xb8, - 0x86, 0xb3, 0x53, 0xb1, 0xcd, 0xb2, 0x16, 0xe8, 0x35, 0xc4, 0x06, 0x16, 0x7a, 0x05, 0xc0, 0x6d, - 0xaf, 0x3a, 0x2d, 0xb7, 0x49, 0x1f, 0x99, 0x33, 0xac, 0x0e, 0x7d, 0x70, 0x40, 0xa5, 0x2a, 0x4b, - 0xe9, 0xa9, 0x2e, 0xfe, 0xed, 0x63, 0x0d, 0x1b, 0x55, 0x61, 0x42, 0xfc, 0xdb, 0x17, 0x8b, 0x61, - 0x5a, 0xb9, 0xb4, 0x4f, 0xc8, 0x1a, 0x6a, 0x05, 0x20, 0xb3, 0x84, 0xcd, 0x79, 0xa2, 0x3e, 0xda, - 0x86, 0x73, 0x22, 0xcd, 0x34, 0xd1, 0x57, 0xb7, 0x9c, 0xbd, 0x90, 0x65, 0x00, 0x18, 0xe1, 0xce, - 0x12, 0x8b, 0xdd, 0x10, 0x71, 0x77, 0x3a, 0x94, 0x2b, 0xd0, 0x37, 0x09, 0x77, 0x22, 0x3d, 0xcd, - 0x8d, 0x9a, 0x28, 0x57, 0x70, 0x33, 0x09, 0xc4, 0x69, 0x7c, 0x14, 0xc2, 0x69, 0xd7, 0xcb, 0xda, - 0x13, 0x67, 0x18, 0xa1, 0x8f, 0x71, 0xff, 0xd9, 0xee, 0xfb, 0x21, 0x13, 0xce, 0xf7, 0x43, 0x26, - 0xed, 0x77, 0x66, 0xbb, 0xf7, 0xdb, 0x16, 0xad, 0xad, 0xf1, 0xf7, 0xe8, 0xb3, 0x30, 0xa6, 0x7f, - 0x98, 0xe0, 0x55, 0x2e, 0x65, 0xb3, 0xbf, 0xda, 0xa9, 0xc2, 0x5f, 0x07, 0xea, 0xe4, 0xd0, 0x61, - 0xd8, 0xa0, 0x88, 0xea, 0x19, 0x9e, 0xe6, 0x57, 0xfa, 0xe3, 0x85, 0xfa, 0x37, 0x5d, 0x23, 0x90, - 0xbd, 0x59, 0xd0, 0x4d, 0x18, 0xa9, 0x37, 0x5d, 0xe2, 0x45, 0x95, 0x6a, 0xb7, 0xd8, 0x70, 0xcb, - 0x02, 0x47, 0xec, 0x3e, 0x11, 0xd0, 0x9f, 0x97, 0x61, 0x45, 0xc1, 0xfe, 0xd5, 0x02, 0xcc, 0xf7, - 0xc8, 0x0e, 0x91, 0x50, 0x64, 0x59, 0x7d, 0x29, 0xb2, 0x16, 0x65, 0x82, 0xec, 0xf5, 0x84, 0x8c, - 0x2c, 0x91, 0xfc, 0x3a, 0x96, 0x94, 0x25, 0xf1, 0xfb, 0x76, 0x2c, 0xd0, 0x75, 0x61, 0x03, 0x3d, - 0x5d, 0x63, 0x0c, 0x1d, 0xf8, 0x60, 0xff, 0x0f, 0xe7, 0x5c, 0x7d, 0xa6, 0xfd, 0x95, 0x02, 0x9c, - 0x56, 0x43, 0xf8, 0xcd, 0x3b, 0x70, 0xb7, 0xd3, 0x03, 0x77, 0x0c, 0xda, 0x60, 0xfb, 0x16, 0x0c, - 0xf1, 0x60, 0x77, 0x7d, 0x30, 0xec, 0x17, 0xcd, 0xb8, 0xb0, 0x8a, 0x47, 0x34, 0x62, 0xc3, 0x7e, - 0xaf, 0x05, 0x93, 0x1b, 0xcb, 0xd5, 0x9a, 0x5f, 0xdf, 0x25, 0xd1, 0x22, 0x7f, 0x60, 0x61, 0xcd, - 0x27, 0xf7, 0x61, 0x98, 0xea, 0x2c, 0x76, 0xfd, 0x02, 0x0c, 0xec, 0xf8, 0x61, 0x94, 0x34, 0x15, - 0xb9, 0xee, 0x87, 0x11, 0x66, 0x10, 0xfb, 0x77, 0x2d, 0x18, 0xdc, 0x70, 0x5c, 0x2f, 0x92, 0x6a, - 0x05, 0x2b, 0x47, 0xad, 0xd0, 0xcf, 0x77, 0xa1, 0x97, 0x60, 0x88, 0x6c, 0x6d, 0x91, 0x7a, 0x24, - 0x66, 0x55, 0x06, 0x34, 0x18, 0x5a, 0x61, 0xa5, 0x94, 0x83, 0x64, 0x8d, 0xf1, 0xbf, 0x58, 0x20, - 0xa3, 0xbb, 0x50, 0x8a, 0xdc, 0x16, 0x59, 0x6c, 0x34, 0x84, 0xb2, 0xfd, 0x21, 0x82, 0x32, 0x6c, - 0x48, 0x02, 0x38, 0xa6, 0x65, 0x7f, 0xa9, 0x00, 0x10, 0x47, 0x09, 0xea, 0xf5, 0x89, 0x4b, 0x29, - 0x35, 0xec, 0xa5, 0x0c, 0x35, 0x2c, 0x8a, 0x09, 0x66, 0xe8, 0x60, 0xd5, 0x30, 0x15, 0xfb, 0x1a, - 0xa6, 0x81, 0xa3, 0x0c, 0xd3, 0x32, 0x4c, 0xc7, 0x51, 0x8e, 0xcc, 0x20, 0x6f, 0xec, 0xfa, 0xdc, - 0x48, 0x02, 0x71, 0x1a, 0xdf, 0x26, 0x70, 0x41, 0x05, 0x7b, 0x11, 0x37, 0x1a, 0xb3, 0xe5, 0xd6, - 0xd5, 0xda, 0x3d, 0xc6, 0x29, 0xd6, 0x33, 0x17, 0x72, 0xf5, 0xcc, 0x3f, 0x61, 0xc1, 0xa9, 0x64, - 0x3b, 0xcc, 0x8b, 0xf7, 0x8b, 0x16, 0x9c, 0x66, 0xda, 0x76, 0xd6, 0x6a, 0x5a, 0xb7, 0xff, 0x62, - 0xd7, 0x00, 0x36, 0x39, 0x3d, 0x8e, 0x23, 0x67, 0xac, 0x65, 0x91, 0xc6, 0xd9, 0x2d, 0xda, 0xff, - 0xa1, 0x00, 0xb3, 0x79, 0x91, 0x6f, 0x98, 0xab, 0x87, 0x73, 0xbf, 0xb6, 0x4b, 0xee, 0x09, 0x83, - 0xfa, 0xd8, 0xd5, 0x83, 0x17, 0x63, 0x09, 0x4f, 0x06, 0xfc, 0x2f, 0xf4, 0x19, 0xf0, 0x7f, 0x07, - 0xa6, 0xef, 0xed, 0x10, 0xef, 0xb6, 0x17, 0x3a, 0x91, 0x1b, 0x6e, 0xb9, 0x4c, 0x33, 0xcd, 0xd7, - 0xcd, 0x2b, 0xd2, 0xec, 0xfd, 0x6e, 0x12, 0xe1, 0xf0, 0x60, 0xfe, 0x9c, 0x51, 0x10, 0x77, 0x99, - 0x1f, 0x24, 0x38, 0x4d, 0x34, 0x9d, 0x2f, 0x61, 0xe0, 0x11, 0xe6, 0x4b, 0xb0, 0xbf, 0x68, 0xc1, - 0xd9, 0xdc, 0x24, 0xad, 0xe8, 0x32, 0x8c, 0x38, 0x6d, 0x97, 0x0b, 0xf7, 0xc5, 0x31, 0xca, 0x84, - 0x48, 0xd5, 0x0a, 0x17, 0xed, 0x2b, 0xa8, 0x4a, 0x1e, 0x5f, 0xc8, 0x4d, 0x1e, 0xdf, 0x33, 0x17, - 0xbc, 0xfd, 0x3d, 0x16, 0x08, 0x37, 0xd5, 0x3e, 0xce, 0xee, 0x4f, 0xc1, 0xd8, 0x5e, 0x3a, 0xa7, - 0xd2, 0x85, 0x7c, 0xbf, 0x5d, 0x91, 0x49, 0x49, 0x31, 0x64, 0x46, 0xfe, 0x24, 0x83, 0x96, 0xdd, - 0x00, 0x01, 0x2d, 0x13, 0x26, 0xba, 0xee, 0xdd, 0x9b, 0xe7, 0x01, 0x1a, 0x0c, 0x57, 0xcb, 0xc0, - 0xaf, 0x6e, 0xe6, 0xb2, 0x82, 0x60, 0x0d, 0xcb, 0xfe, 0xb7, 0x05, 0x18, 0x95, 0x39, 0x7c, 0x3a, - 0x5e, 0x3f, 0x02, 0xa6, 0x23, 0x25, 0xf5, 0x44, 0x57, 0xa0, 0xc4, 0x24, 0xa0, 0xd5, 0x58, 0x2e, - 0xa7, 0xe4, 0x0f, 0x6b, 0x12, 0x80, 0x63, 0x1c, 0xba, 0x8b, 0xc2, 0xce, 0x26, 0x43, 0x4f, 0x38, - 0x55, 0xd6, 0x78, 0x31, 0x96, 0x70, 0xf4, 0x09, 0x98, 0xe2, 0xf5, 0x02, 0xbf, 0xed, 0x6c, 0x73, - 0xad, 0xc9, 0xa0, 0x0a, 0xbb, 0x30, 0xb5, 0x96, 0x80, 0x1d, 0x1e, 0xcc, 0x9f, 0x4a, 0x96, 0x31, - 0x75, 0x60, 0x8a, 0x0a, 0x33, 0x8e, 0xe2, 0x8d, 0xd0, 0xdd, 0x9f, 0xb2, 0xa9, 0x8a, 0x41, 0x58, - 0xc7, 0xb3, 0x3f, 0x0b, 0x28, 0x9d, 0xcd, 0x08, 0xbd, 0xce, 0x2d, 0x62, 0xdd, 0x80, 0x34, 0xba, - 0xa9, 0x07, 0xf5, 0xe0, 0x02, 0xd2, 0x1f, 0x8a, 0xd7, 0xc2, 0xaa, 0xbe, 0xfd, 0xff, 0x17, 0x61, - 0x2a, 0xe9, 0x01, 0x8e, 0xae, 0xc3, 0x10, 0x67, 0x3d, 0x04, 0xf9, 0x2e, 0xd6, 0x27, 0x9a, 0xdf, - 0x38, 0x3b, 0x84, 0x05, 0xf7, 0x22, 0xea, 0xa3, 0x37, 0x61, 0xb4, 0xe1, 0xdf, 0xf3, 0xee, 0x39, - 0x41, 0x63, 0xb1, 0x5a, 0x11, 0xcb, 0x39, 0xf3, 0x39, 0x5c, 0x8e, 0xd1, 0x74, 0x5f, 0x74, 0xa6, - 0x69, 0x8d, 0x41, 0x58, 0x27, 0x87, 0x36, 0x58, 0x08, 0xf4, 0x2d, 0x77, 0x7b, 0xcd, 0x69, 0x77, - 0x73, 0x8f, 0x58, 0x96, 0x48, 0x1a, 0xe5, 0x71, 0x11, 0x27, 0x9d, 0x03, 0x70, 0x4c, 0x08, 0x7d, - 0x1e, 0x66, 0xc2, 0x1c, 0x21, 0x7d, 0x5e, 0x72, 0xbb, 0x6e, 0x72, 0xeb, 0xa5, 0xc7, 0x1e, 0x1c, - 0xcc, 0xcf, 0x64, 0x89, 0xf3, 0xb3, 0x9a, 0xb1, 0x7f, 0xe4, 0x14, 0x18, 0x9b, 0xd8, 0xc8, 0x75, - 0x6a, 0x1d, 0x53, 0xae, 0x53, 0x0c, 0x23, 0xa4, 0xd5, 0x8e, 0xf6, 0xcb, 0x6e, 0xd0, 0x2d, 0x03, - 0xf8, 0x8a, 0xc0, 0x49, 0xd3, 0x94, 0x10, 0xac, 0xe8, 0x64, 0x27, 0xa4, 0x2d, 0x7e, 0x1d, 0x13, - 0xd2, 0x0e, 0x9c, 0x60, 0x42, 0xda, 0x75, 0x18, 0xde, 0x76, 0x23, 0x4c, 0xda, 0xbe, 0x60, 0xfa, - 0x33, 0xd7, 0xe1, 0x35, 0x8e, 0x92, 0x4e, 0x7d, 0x28, 0x00, 0x58, 0x12, 0x41, 0xaf, 0xab, 0x1d, - 0x38, 0x94, 0xff, 0x30, 0x4f, 0x9b, 0x49, 0x64, 0xee, 0x41, 0x91, 0x76, 0x76, 0xf8, 0x61, 0xd3, - 0xce, 0xae, 0xca, 0x64, 0xb1, 0x23, 0xf9, 0xbe, 0x4c, 0x2c, 0x17, 0x6c, 0x8f, 0x14, 0xb1, 0x77, - 0xf4, 0x04, 0xbb, 0xa5, 0xfc, 0x93, 0x40, 0xe5, 0xce, 0xed, 0x33, 0xad, 0xee, 0xf7, 0x58, 0x70, - 0xba, 0x9d, 0x95, 0x6b, 0x5a, 0x58, 0x14, 0xbc, 0xd4, 0x77, 0x3a, 0x6b, 0xa3, 0x41, 0x26, 0x89, - 0xcb, 0x44, 0xc3, 0xd9, 0xcd, 0xd1, 0x81, 0x0e, 0x36, 0x1b, 0x42, 0xb3, 0x7d, 0x31, 0x27, 0x3f, - 0x6f, 0x97, 0xac, 0xbc, 0x1b, 0x19, 0xb9, 0x60, 0xdf, 0x9f, 0x97, 0x0b, 0xb6, 0xef, 0x0c, 0xb0, - 0xaf, 0xab, 0xcc, 0xbc, 0xe3, 0xf9, 0x4b, 0x89, 0xe7, 0xdd, 0xed, 0x99, 0x8f, 0xf7, 0x75, 0x95, - 0x8f, 0xb7, 0x4b, 0x7c, 0x5b, 0x9e, 0x6d, 0xb7, 0x67, 0x16, 0x5e, 0x2d, 0x93, 0xee, 0xe4, 0xf1, - 0x64, 0xd2, 0x35, 0xae, 0x1a, 0x9e, 0xcc, 0xf5, 0x99, 0x1e, 0x57, 0x8d, 0x41, 0xb7, 0xfb, 0x65, - 0xc3, 0xb3, 0x06, 0x4f, 0x3f, 0x54, 0xd6, 0xe0, 0x3b, 0x7a, 0x16, 0x5e, 0xd4, 0x23, 0xcd, 0x2c, - 0x45, 0xea, 0x33, 0xf7, 0xee, 0x1d, 0xfd, 0x02, 0x9c, 0xc9, 0xa7, 0xab, 0xee, 0xb9, 0x34, 0xdd, - 0xcc, 0x2b, 0x30, 0x95, 0xd3, 0xf7, 0xd4, 0xc9, 0xe4, 0xf4, 0x3d, 0x7d, 0xec, 0x39, 0x7d, 0xcf, - 0x9c, 0x40, 0x4e, 0xdf, 0xc7, 0x4e, 0x30, 0xa7, 0xef, 0x1d, 0x66, 0x86, 0xc3, 0x83, 0xfd, 0x88, - 0x78, 0xbc, 0xd9, 0xb1, 0x5f, 0xb3, 0x22, 0x02, 0xf1, 0x8f, 0x53, 0x20, 0x1c, 0x93, 0xca, 0xc8, - 0x15, 0x3c, 0xfb, 0x08, 0x72, 0x05, 0xaf, 0xc7, 0xb9, 0x82, 0xcf, 0xe6, 0x4f, 0x75, 0x86, 0xe3, - 0x46, 0x4e, 0x86, 0xe0, 0x3b, 0x7a, 0x66, 0xdf, 0xc7, 0xbb, 0xe8, 0x5a, 0xb2, 0x04, 0x8f, 0x5d, - 0xf2, 0xf9, 0xbe, 0xc6, 0xf3, 0xf9, 0x3e, 0x91, 0x7f, 0x92, 0x27, 0xaf, 0x3b, 0x23, 0x8b, 0x2f, - 0xed, 0x97, 0x8a, 0xfc, 0xc8, 0x22, 0x0f, 0xe7, 0xf4, 0x4b, 0x85, 0x8e, 0x4c, 0xf7, 0x4b, 0x81, - 0x70, 0x4c, 0xca, 0xfe, 0xbe, 0x02, 0x9c, 0xef, 0xbe, 0xdf, 0x62, 0x69, 0x6a, 0x35, 0x56, 0x3d, - 0x27, 0xa4, 0xa9, 0xfc, 0xcd, 0x16, 0x63, 0xf5, 0x1d, 0xc8, 0xee, 0x1a, 0x4c, 0x2b, 0x8f, 0x8f, - 0xa6, 0x5b, 0xdf, 0x5f, 0x8f, 0x5f, 0xbe, 0xca, 0x4b, 0xbe, 0x96, 0x44, 0xc0, 0xe9, 0x3a, 0x68, - 0x11, 0x26, 0x8d, 0xc2, 0x4a, 0x59, 0xbc, 0xcd, 0x94, 0xf8, 0xb6, 0x66, 0x82, 0x71, 0x12, 0xdf, - 0xfe, 0xb2, 0x05, 0x8f, 0xe5, 0x24, 0xc3, 0xeb, 0x3b, 0x4e, 0xdb, 0x16, 0x4c, 0xb6, 0xcd, 0xaa, - 0x3d, 0x42, 0x4b, 0x1a, 0x29, 0xf7, 0x54, 0x5f, 0x13, 0x00, 0x9c, 0x24, 0x6a, 0xff, 0x99, 0x05, - 0xe7, 0xba, 0x9a, 0x30, 0x22, 0x0c, 0x67, 0xb6, 0x5b, 0xa1, 0xb3, 0x1c, 0x90, 0x06, 0xf1, 0x22, - 0xd7, 0x69, 0xd6, 0xda, 0xa4, 0xae, 0xc9, 0xc3, 0x99, 0x2d, 0xe0, 0xb5, 0xb5, 0xda, 0x62, 0x1a, - 0x03, 0xe7, 0xd4, 0x44, 0xab, 0x80, 0xd2, 0x10, 0x31, 0xc3, 0x2c, 0x86, 0x75, 0x9a, 0x1e, 0xce, - 0xa8, 0x81, 0x3e, 0x02, 0xe3, 0xca, 0x34, 0x52, 0x9b, 0x71, 0x76, 0xb0, 0x63, 0x1d, 0x80, 0x4d, - 0xbc, 0xa5, 0xcb, 0xbf, 0xfe, 0xfb, 0xe7, 0xdf, 0xf7, 0x9b, 0xbf, 0x7f, 0xfe, 0x7d, 0xbf, 0xf3, - 0xfb, 0xe7, 0xdf, 0xf7, 0x1d, 0x0f, 0xce, 0x5b, 0xbf, 0xfe, 0xe0, 0xbc, 0xf5, 0x9b, 0x0f, 0xce, - 0x5b, 0xbf, 0xf3, 0xe0, 0xbc, 0xf5, 0x7b, 0x0f, 0xce, 0x5b, 0x5f, 0xfa, 0x83, 0xf3, 0xef, 0xfb, - 0x54, 0x61, 0xef, 0xea, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xd2, 0x6a, 0x19, 0xeb, 0xbc, 0x04, - 0x01, 0x00, + // 14068 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0xbd, 0x69, 0x70, 0x5c, 0xd9, + 0x79, 0x18, 0xaa, 0xdb, 0x8d, 0xad, 0x3f, 0xec, 0x07, 0x24, 0x07, 0xc4, 0x0c, 0x09, 0xce, 0xa5, + 0xc4, 0xe1, 0x68, 0x66, 0x40, 0x71, 0x16, 0x69, 0x3c, 0x23, 0x8d, 0x05, 0xa0, 0x01, 0xb2, 0x87, + 0x04, 0xd8, 0x73, 0x1a, 0x24, 0x25, 0x79, 0xa4, 0xd2, 0x45, 0xf7, 0x01, 0x70, 0x85, 0xee, 0x7b, + 0x7b, 0xee, 0xbd, 0x0d, 0x12, 0xf3, 0xe4, 0x7a, 0x7e, 0xf2, 0x2a, 0x2f, 0xaf, 0x54, 0xaf, 0xfc, + 0xb2, 0xd8, 0x2e, 0x57, 0xca, 0x71, 0xca, 0x56, 0x94, 0xcd, 0xb1, 0x63, 0x3b, 0x96, 0x13, 0x3b, + 0xbb, 0x93, 0x1f, 0x8e, 0xe3, 0x4a, 0x2c, 0x57, 0xb9, 0x82, 0xd8, 0x74, 0xaa, 0x5c, 0xfa, 0x11, + 0xdb, 0x89, 0x93, 0x1f, 0x41, 0x5c, 0x71, 0xea, 0xac, 0xf7, 0x9c, 0xbb, 0x74, 0x37, 0x38, 0x20, + 0x34, 0x52, 0xcd, 0xbf, 0xee, 0xf3, 0x7d, 0xe7, 0x3b, 0xe7, 0x9e, 0xf5, 0x3b, 0xdf, 0x0a, 0xaf, + 0xee, 0xbe, 0x1c, 0x2e, 0xb8, 0xfe, 0x95, 0xdd, 0xce, 0x26, 0x09, 0x3c, 0x12, 0x91, 0xf0, 0xca, + 0x1e, 0xf1, 0x1a, 0x7e, 0x70, 0x45, 0x00, 0x9c, 0xb6, 0x7b, 0xa5, 0xee, 0x07, 0xe4, 0xca, 0xde, + 0xd5, 0x2b, 0xdb, 0xc4, 0x23, 0x81, 0x13, 0x91, 0xc6, 0x42, 0x3b, 0xf0, 0x23, 0x1f, 0x21, 0x8e, + 0xb3, 0xe0, 0xb4, 0xdd, 0x05, 0x8a, 0xb3, 0xb0, 0x77, 0x75, 0xee, 0xb9, 0x6d, 0x37, 0xda, 0xe9, + 0x6c, 0x2e, 0xd4, 0xfd, 0xd6, 0x95, 0x6d, 0x7f, 0xdb, 0xbf, 0xc2, 0x50, 0x37, 0x3b, 0x5b, 0xec, + 0x1f, 0xfb, 0xc3, 0x7e, 0x71, 0x12, 0x73, 0x2f, 0xc6, 0xcd, 0xb4, 0x9c, 0xfa, 0x8e, 0xeb, 0x91, + 0x60, 0xff, 0x4a, 0x7b, 0x77, 0x9b, 0xb5, 0x1b, 0x90, 0xd0, 0xef, 0x04, 0x75, 0x92, 0x6c, 0xb8, + 0x6b, 0xad, 0xf0, 0x4a, 0x8b, 0x44, 0x4e, 0x46, 0x77, 0xe7, 0xae, 0xe4, 0xd5, 0x0a, 0x3a, 0x5e, + 0xe4, 0xb6, 0xd2, 0xcd, 0x7c, 0xb8, 0x57, 0x85, 0xb0, 0xbe, 0x43, 0x5a, 0x4e, 0xaa, 0xde, 0x0b, + 0x79, 0xf5, 0x3a, 0x91, 0xdb, 0xbc, 0xe2, 0x7a, 0x51, 0x18, 0x05, 0xc9, 0x4a, 0xf6, 0xd7, 0x2c, + 0xb8, 0xb0, 0x78, 0xb7, 0xb6, 0xd2, 0x74, 0xc2, 0xc8, 0xad, 0x2f, 0x35, 0xfd, 0xfa, 0x6e, 0x2d, + 0xf2, 0x03, 0x72, 0xc7, 0x6f, 0x76, 0x5a, 0xa4, 0xc6, 0x06, 0x02, 0x3d, 0x0b, 0x23, 0x7b, 0xec, + 0x7f, 0xa5, 0x3c, 0x6b, 0x5d, 0xb0, 0x2e, 0x97, 0x96, 0xa6, 0x7e, 0xe3, 0x60, 0xfe, 0x7d, 0x0f, + 0x0e, 0xe6, 0x47, 0xee, 0x88, 0x72, 0xac, 0x30, 0xd0, 0x25, 0x18, 0xda, 0x0a, 0x37, 0xf6, 0xdb, + 0x64, 0xb6, 0xc0, 0x70, 0x27, 0x04, 0xee, 0xd0, 0x6a, 0x8d, 0x96, 0x62, 0x01, 0x45, 0x57, 0xa0, + 0xd4, 0x76, 0x82, 0xc8, 0x8d, 0x5c, 0xdf, 0x9b, 0x2d, 0x5e, 0xb0, 0x2e, 0x0f, 0x2e, 0x4d, 0x0b, + 0xd4, 0x52, 0x55, 0x02, 0x70, 0x8c, 0x43, 0xbb, 0x11, 0x10, 0xa7, 0x71, 0xcb, 0x6b, 0xee, 0xcf, + 0x0e, 0x5c, 0xb0, 0x2e, 0x8f, 0xc4, 0xdd, 0xc0, 0xa2, 0x1c, 0x2b, 0x0c, 0xfb, 0xc7, 0x0a, 0x30, + 0xb2, 0xb8, 0xb5, 0xe5, 0x7a, 0x6e, 0xb4, 0x8f, 0xee, 0xc0, 0x98, 0xe7, 0x37, 0x88, 0xfc, 0xcf, + 0xbe, 0x62, 0xf4, 0xf9, 0x0b, 0x0b, 0xe9, 0xa5, 0xb4, 0xb0, 0xae, 0xe1, 0x2d, 0x4d, 0x3d, 0x38, + 0x98, 0x1f, 0xd3, 0x4b, 0xb0, 0x41, 0x07, 0x61, 0x18, 0x6d, 0xfb, 0x0d, 0x45, 0xb6, 0xc0, 0xc8, + 0xce, 0x67, 0x91, 0xad, 0xc6, 0x68, 0x4b, 0x93, 0x0f, 0x0e, 0xe6, 0x47, 0xb5, 0x02, 0xac, 0x13, + 0x41, 0x9b, 0x30, 0x49, 0xff, 0x7a, 0x91, 0xab, 0xe8, 0x16, 0x19, 0xdd, 0x8b, 0x79, 0x74, 0x35, + 0xd4, 0xa5, 0x99, 0x07, 0x07, 0xf3, 0x93, 0x89, 0x42, 0x9c, 0x24, 0x68, 0xbf, 0x0d, 0x13, 0x8b, + 0x51, 0xe4, 0xd4, 0x77, 0x48, 0x83, 0xcf, 0x20, 0x7a, 0x11, 0x06, 0x3c, 0xa7, 0x45, 0xc4, 0xfc, + 0x5e, 0x10, 0x03, 0x3b, 0xb0, 0xee, 0xb4, 0xc8, 0xe1, 0xc1, 0xfc, 0xd4, 0x6d, 0xcf, 0x7d, 0xab, + 0x23, 0x56, 0x05, 0x2d, 0xc3, 0x0c, 0x1b, 0x3d, 0x0f, 0xd0, 0x20, 0x7b, 0x6e, 0x9d, 0x54, 0x9d, + 0x68, 0x47, 0xcc, 0x37, 0x12, 0x75, 0xa1, 0xac, 0x20, 0x58, 0xc3, 0xb2, 0xef, 0x43, 0x69, 0x71, + 0xcf, 0x77, 0x1b, 0x55, 0xbf, 0x11, 0xa2, 0x5d, 0x98, 0x6c, 0x07, 0x64, 0x8b, 0x04, 0xaa, 0x68, + 0xd6, 0xba, 0x50, 0xbc, 0x3c, 0xfa, 0xfc, 0xe5, 0xcc, 0x8f, 0x35, 0x51, 0x57, 0xbc, 0x28, 0xd8, + 0x5f, 0x7a, 0x4c, 0xb4, 0x37, 0x99, 0x80, 0xe2, 0x24, 0x65, 0xfb, 0x9f, 0x17, 0xe0, 0xf4, 0xe2, + 0xdb, 0x9d, 0x80, 0x94, 0xdd, 0x70, 0x37, 0xb9, 0xc2, 0x1b, 0x6e, 0xb8, 0xbb, 0x1e, 0x8f, 0x80, + 0x5a, 0x5a, 0x65, 0x51, 0x8e, 0x15, 0x06, 0x7a, 0x0e, 0x86, 0xe9, 0xef, 0xdb, 0xb8, 0x22, 0x3e, + 0x79, 0x46, 0x20, 0x8f, 0x96, 0x9d, 0xc8, 0x29, 0x73, 0x10, 0x96, 0x38, 0x68, 0x0d, 0x46, 0xeb, + 0x6c, 0x43, 0x6e, 0xaf, 0xf9, 0x0d, 0xc2, 0x26, 0xb3, 0xb4, 0xf4, 0x0c, 0x45, 0x5f, 0x8e, 0x8b, + 0x0f, 0x0f, 0xe6, 0x67, 0x79, 0xdf, 0x04, 0x09, 0x0d, 0x86, 0xf5, 0xfa, 0xc8, 0x56, 0xfb, 0x6b, + 0x80, 0x51, 0x82, 0x8c, 0xbd, 0x75, 0x59, 0xdb, 0x2a, 0x83, 0x6c, 0xab, 0x8c, 0x65, 0x6f, 0x13, + 0x74, 0x15, 0x06, 0x76, 0x5d, 0xaf, 0x31, 0x3b, 0xc4, 0x68, 0x9d, 0xa3, 0x73, 0x7e, 0xc3, 0xf5, + 0x1a, 0x87, 0x07, 0xf3, 0xd3, 0x46, 0x77, 0x68, 0x21, 0x66, 0xa8, 0xf6, 0x9f, 0x59, 0x30, 0xcf, + 0x60, 0xab, 0x6e, 0x93, 0x54, 0x49, 0x10, 0xba, 0x61, 0x44, 0xbc, 0xc8, 0x18, 0xd0, 0xe7, 0x01, + 0x42, 0x52, 0x0f, 0x48, 0xa4, 0x0d, 0xa9, 0x5a, 0x18, 0x35, 0x05, 0xc1, 0x1a, 0x16, 0x3d, 0x10, + 0xc2, 0x1d, 0x27, 0x60, 0xeb, 0x4b, 0x0c, 0xac, 0x3a, 0x10, 0x6a, 0x12, 0x80, 0x63, 0x1c, 0xe3, + 0x40, 0x28, 0xf6, 0x3a, 0x10, 0xd0, 0xc7, 0x60, 0x32, 0x6e, 0x2c, 0x6c, 0x3b, 0x75, 0x39, 0x80, + 0x6c, 0xcb, 0xd4, 0x4c, 0x10, 0x4e, 0xe2, 0xda, 0x7f, 0xd3, 0x12, 0x8b, 0x87, 0x7e, 0xf5, 0xbb, + 0xfc, 0x5b, 0xed, 0x5f, 0xb6, 0x60, 0x78, 0xc9, 0xf5, 0x1a, 0xae, 0xb7, 0x8d, 0x3e, 0x0b, 0x23, + 0xf4, 0x6e, 0x6a, 0x38, 0x91, 0x23, 0xce, 0xbd, 0x0f, 0x69, 0x7b, 0x4b, 0x5d, 0x15, 0x0b, 0xed, + 0xdd, 0x6d, 0x5a, 0x10, 0x2e, 0x50, 0x6c, 0xba, 0xdb, 0x6e, 0x6d, 0x7e, 0x8e, 0xd4, 0xa3, 0x35, + 0x12, 0x39, 0xf1, 0xe7, 0xc4, 0x65, 0x58, 0x51, 0x45, 0x37, 0x60, 0x28, 0x72, 0x82, 0x6d, 0x12, + 0x89, 0x03, 0x30, 0xf3, 0xa0, 0xe2, 0x35, 0x31, 0xdd, 0x91, 0xc4, 0xab, 0x93, 0xf8, 0x5a, 0xd8, + 0x60, 0x55, 0xb1, 0x20, 0x61, 0xff, 0xc8, 0x30, 0x9c, 0x5d, 0xae, 0x55, 0x72, 0xd6, 0xd5, 0x25, + 0x18, 0x6a, 0x04, 0xee, 0x1e, 0x09, 0xc4, 0x38, 0x2b, 0x2a, 0x65, 0x56, 0x8a, 0x05, 0x14, 0xbd, + 0x0c, 0x63, 0xfc, 0x42, 0xba, 0xee, 0x78, 0x8d, 0xa6, 0x1c, 0xe2, 0x53, 0x02, 0x7b, 0xec, 0x8e, + 0x06, 0xc3, 0x06, 0xe6, 0x11, 0x17, 0xd5, 0xa5, 0xc4, 0x66, 0xcc, 0xbb, 0xec, 0xbe, 0x68, 0xc1, + 0x14, 0x6f, 0x66, 0x31, 0x8a, 0x02, 0x77, 0xb3, 0x13, 0x91, 0x70, 0x76, 0x90, 0x9d, 0x74, 0xcb, + 0x59, 0xa3, 0x95, 0x3b, 0x02, 0x0b, 0x77, 0x12, 0x54, 0xf8, 0x21, 0x38, 0x2b, 0xda, 0x9d, 0x4a, + 0x82, 0x71, 0xaa, 0x59, 0xf4, 0xdd, 0x16, 0xcc, 0xd5, 0x7d, 0x2f, 0x0a, 0xfc, 0x66, 0x93, 0x04, + 0xd5, 0xce, 0x66, 0xd3, 0x0d, 0x77, 0xf8, 0x3a, 0xc5, 0x64, 0x8b, 0x9d, 0x04, 0x39, 0x73, 0xa8, + 0x90, 0xc4, 0x1c, 0x9e, 0x7f, 0x70, 0x30, 0x3f, 0xb7, 0x9c, 0x4b, 0x0a, 0x77, 0x69, 0x06, 0xed, + 0x02, 0xa2, 0x57, 0x69, 0x2d, 0x72, 0xb6, 0x49, 0xdc, 0xf8, 0x70, 0xff, 0x8d, 0x9f, 0x79, 0x70, + 0x30, 0x8f, 0xd6, 0x53, 0x24, 0x70, 0x06, 0x59, 0xf4, 0x16, 0x9c, 0xa2, 0xa5, 0xa9, 0x6f, 0x1d, + 0xe9, 0xbf, 0xb9, 0xd9, 0x07, 0x07, 0xf3, 0xa7, 0xd6, 0x33, 0x88, 0xe0, 0x4c, 0xd2, 0xe8, 0xbb, + 0x2c, 0x38, 0x1b, 0x7f, 0xfe, 0xca, 0xfd, 0xb6, 0xe3, 0x35, 0xe2, 0x86, 0x4b, 0xfd, 0x37, 0x4c, + 0xcf, 0xe4, 0xb3, 0xcb, 0x79, 0x94, 0x70, 0x7e, 0x23, 0x73, 0xcb, 0x70, 0x3a, 0x73, 0xb5, 0xa0, + 0x29, 0x28, 0xee, 0x12, 0xce, 0x05, 0x95, 0x30, 0xfd, 0x89, 0x4e, 0xc1, 0xe0, 0x9e, 0xd3, 0xec, + 0x88, 0x8d, 0x82, 0xf9, 0x9f, 0x57, 0x0a, 0x2f, 0x5b, 0xf6, 0xbf, 0x28, 0xc2, 0xe4, 0x72, 0xad, + 0xf2, 0x50, 0xbb, 0x50, 0xbf, 0x86, 0x0a, 0x5d, 0xaf, 0xa1, 0xf8, 0x52, 0x2b, 0xe6, 0x5e, 0x6a, + 0xff, 0x77, 0xc6, 0x16, 0x1a, 0x60, 0x5b, 0xe8, 0xdb, 0x72, 0xb6, 0xd0, 0x31, 0x6f, 0x9c, 0xbd, + 0x9c, 0x55, 0x34, 0xc8, 0x26, 0x33, 0x93, 0x63, 0xb9, 0xe9, 0xd7, 0x9d, 0x66, 0xf2, 0xe8, 0x3b, + 0xe2, 0x52, 0x3a, 0x9e, 0x79, 0xac, 0xc3, 0xd8, 0xb2, 0xd3, 0x76, 0x36, 0xdd, 0xa6, 0x1b, 0xb9, + 0x24, 0x44, 0x4f, 0x41, 0xd1, 0x69, 0x34, 0x18, 0xb7, 0x55, 0x5a, 0x3a, 0xfd, 0xe0, 0x60, 0xbe, + 0xb8, 0xd8, 0xa0, 0xd7, 0x3e, 0x28, 0xac, 0x7d, 0x4c, 0x31, 0xd0, 0x07, 0x61, 0xa0, 0x11, 0xf8, + 0xed, 0xd9, 0x02, 0xc3, 0xa4, 0xbb, 0x6e, 0xa0, 0x1c, 0xf8, 0xed, 0x04, 0x2a, 0xc3, 0xb1, 0x7f, + 0xbd, 0x00, 0x4f, 0x2c, 0x93, 0xf6, 0xce, 0x6a, 0x2d, 0xe7, 0xfc, 0xbe, 0x0c, 0x23, 0x2d, 0xdf, + 0x73, 0x23, 0x3f, 0x08, 0x45, 0xd3, 0x6c, 0x45, 0xac, 0x89, 0x32, 0xac, 0xa0, 0xe8, 0x02, 0x0c, + 0xb4, 0x63, 0xa6, 0x72, 0x4c, 0x32, 0xa4, 0x8c, 0x9d, 0x64, 0x10, 0x8a, 0xd1, 0x09, 0x49, 0x20, + 0x56, 0x8c, 0xc2, 0xb8, 0x1d, 0x92, 0x00, 0x33, 0x48, 0x7c, 0x33, 0xd3, 0x3b, 0x5b, 0x9c, 0xd0, + 0x89, 0x9b, 0x99, 0x42, 0xb0, 0x86, 0x85, 0xaa, 0x50, 0x0a, 0x13, 0x33, 0xdb, 0xd7, 0x36, 0x1d, + 0x67, 0x57, 0xb7, 0x9a, 0xc9, 0x98, 0x88, 0x71, 0xa3, 0x0c, 0xf5, 0xbc, 0xba, 0xbf, 0x5a, 0x00, + 0xc4, 0x87, 0xf0, 0x9b, 0x6c, 0xe0, 0x6e, 0xa7, 0x07, 0xae, 0xff, 0x2d, 0x71, 0x5c, 0xa3, 0xf7, + 0xdf, 0x2d, 0x78, 0x62, 0xd9, 0xf5, 0x1a, 0x24, 0xc8, 0x59, 0x80, 0x8f, 0xe6, 0x2d, 0x7b, 0x34, + 0xa6, 0xc1, 0x58, 0x62, 0x03, 0xc7, 0xb0, 0xc4, 0xec, 0x3f, 0xb1, 0x00, 0xf1, 0xcf, 0x7e, 0xd7, + 0x7d, 0xec, 0xed, 0xf4, 0xc7, 0x1e, 0xc3, 0xb2, 0xb0, 0x6f, 0xc2, 0xc4, 0x72, 0xd3, 0x25, 0x5e, + 0x54, 0xa9, 0x2e, 0xfb, 0xde, 0x96, 0xbb, 0x8d, 0x5e, 0x81, 0x89, 0xc8, 0x6d, 0x11, 0xbf, 0x13, + 0xd5, 0x48, 0xdd, 0xf7, 0xd8, 0x4b, 0xd2, 0xba, 0x3c, 0xb8, 0x84, 0x1e, 0x1c, 0xcc, 0x4f, 0x6c, + 0x18, 0x10, 0x9c, 0xc0, 0xb4, 0x7f, 0x8f, 0x8e, 0x9f, 0xdf, 0x6a, 0xfb, 0x1e, 0xf1, 0xa2, 0x65, + 0xdf, 0x6b, 0x70, 0x89, 0xc3, 0x2b, 0x30, 0x10, 0xd1, 0xf1, 0xe0, 0x63, 0x77, 0x49, 0x6e, 0x14, + 0x3a, 0x0a, 0x87, 0x07, 0xf3, 0x67, 0xd2, 0x35, 0xd8, 0x38, 0xb1, 0x3a, 0xe8, 0xdb, 0x60, 0x28, + 0x8c, 0x9c, 0xa8, 0x13, 0x8a, 0xd1, 0x7c, 0x52, 0x8e, 0x66, 0x8d, 0x95, 0x1e, 0x1e, 0xcc, 0x4f, + 0xaa, 0x6a, 0xbc, 0x08, 0x8b, 0x0a, 0xe8, 0x69, 0x18, 0x6e, 0x91, 0x30, 0x74, 0xb6, 0xe5, 0x6d, + 0x38, 0x29, 0xea, 0x0e, 0xaf, 0xf1, 0x62, 0x2c, 0xe1, 0xe8, 0x22, 0x0c, 0x92, 0x20, 0xf0, 0x03, + 0xb1, 0x47, 0xc7, 0x05, 0xe2, 0xe0, 0x0a, 0x2d, 0xc4, 0x1c, 0x66, 0xff, 0x5b, 0x0b, 0x26, 0x55, + 0x5f, 0x79, 0x5b, 0x27, 0xf0, 0x2a, 0xf8, 0x14, 0x40, 0x5d, 0x7e, 0x60, 0xc8, 0x6e, 0x8f, 0xd1, + 0xe7, 0x2f, 0x65, 0x5e, 0xd4, 0xa9, 0x61, 0x8c, 0x29, 0xab, 0xa2, 0x10, 0x6b, 0xd4, 0xec, 0x7f, + 0x64, 0xc1, 0x4c, 0xe2, 0x8b, 0x6e, 0xba, 0x61, 0x84, 0xde, 0x4c, 0x7d, 0xd5, 0x42, 0x7f, 0x5f, + 0x45, 0x6b, 0xb3, 0x6f, 0x52, 0x4b, 0x59, 0x96, 0x68, 0x5f, 0x74, 0x1d, 0x06, 0xdd, 0x88, 0xb4, + 0xe4, 0xc7, 0x5c, 0xec, 0xfa, 0x31, 0xbc, 0x57, 0xf1, 0x8c, 0x54, 0x68, 0x4d, 0xcc, 0x09, 0xd8, + 0xbf, 0x5e, 0x84, 0x12, 0x5f, 0xb6, 0x6b, 0x4e, 0xfb, 0x04, 0xe6, 0xe2, 0x19, 0x28, 0xb9, 0xad, + 0x56, 0x27, 0x72, 0x36, 0xc5, 0x71, 0x3e, 0xc2, 0xb7, 0x56, 0x45, 0x16, 0xe2, 0x18, 0x8e, 0x2a, + 0x30, 0xc0, 0xba, 0xc2, 0xbf, 0xf2, 0xa9, 0xec, 0xaf, 0x14, 0x7d, 0x5f, 0x28, 0x3b, 0x91, 0xc3, + 0x39, 0x29, 0x75, 0x8f, 0xd0, 0x22, 0xcc, 0x48, 0x20, 0x07, 0x60, 0xd3, 0xf5, 0x9c, 0x60, 0x9f, + 0x96, 0xcd, 0x16, 0x19, 0xc1, 0xe7, 0xba, 0x13, 0x5c, 0x52, 0xf8, 0x9c, 0xac, 0xfa, 0xb0, 0x18, + 0x80, 0x35, 0xa2, 0x73, 0x1f, 0x81, 0x92, 0x42, 0x3e, 0x0a, 0x43, 0x34, 0xf7, 0x31, 0x98, 0x4c, + 0xb4, 0xd5, 0xab, 0xfa, 0x98, 0xce, 0x4f, 0xfd, 0x0a, 0x3b, 0x32, 0x44, 0xaf, 0x57, 0xbc, 0x3d, + 0x71, 0xe4, 0xbe, 0x0d, 0xa7, 0x9a, 0x19, 0x27, 0x99, 0x98, 0xd7, 0xfe, 0x4f, 0xbe, 0x27, 0xc4, + 0x67, 0x9f, 0xca, 0x82, 0xe2, 0xcc, 0x36, 0x28, 0x8f, 0xe0, 0xb7, 0xe9, 0x06, 0x71, 0x9a, 0x3a, + 0xbb, 0x7d, 0x4b, 0x94, 0x61, 0x05, 0xa5, 0xe7, 0xdd, 0x29, 0xd5, 0xf9, 0x1b, 0x64, 0xbf, 0x46, + 0x9a, 0xa4, 0x1e, 0xf9, 0xc1, 0x37, 0xb4, 0xfb, 0xe7, 0xf8, 0xe8, 0xf3, 0xe3, 0x72, 0x54, 0x10, + 0x28, 0xde, 0x20, 0xfb, 0x7c, 0x2a, 0xf4, 0xaf, 0x2b, 0x76, 0xfd, 0xba, 0x9f, 0xb3, 0x60, 0x5c, + 0x7d, 0xdd, 0x09, 0x9c, 0x0b, 0x4b, 0xe6, 0xb9, 0x70, 0xae, 0xeb, 0x02, 0xcf, 0x39, 0x11, 0xbe, + 0x5a, 0x80, 0xb3, 0x0a, 0x87, 0xbe, 0x0d, 0xf8, 0x1f, 0xb1, 0xaa, 0xae, 0x40, 0xc9, 0x53, 0x52, + 0x2b, 0xcb, 0x14, 0x17, 0xc5, 0x32, 0xab, 0x18, 0x87, 0xb2, 0x78, 0x5e, 0x2c, 0x5a, 0x1a, 0xd3, + 0xc5, 0xb9, 0x42, 0x74, 0xbb, 0x04, 0xc5, 0x8e, 0xdb, 0x10, 0x17, 0xcc, 0x87, 0xe4, 0x68, 0xdf, + 0xae, 0x94, 0x0f, 0x0f, 0xe6, 0x9f, 0xcc, 0x53, 0x25, 0xd0, 0x9b, 0x2d, 0x5c, 0xb8, 0x5d, 0x29, + 0x63, 0x5a, 0x19, 0x2d, 0xc2, 0xa4, 0xd4, 0x96, 0xdc, 0xa1, 0xec, 0x96, 0xef, 0x89, 0x7b, 0x48, + 0xc9, 0x64, 0xb1, 0x09, 0xc6, 0x49, 0x7c, 0x54, 0x86, 0xa9, 0xdd, 0xce, 0x26, 0x69, 0x92, 0x88, + 0x7f, 0xf0, 0x0d, 0xc2, 0x25, 0x96, 0xa5, 0xf8, 0x65, 0x76, 0x23, 0x01, 0xc7, 0xa9, 0x1a, 0xf6, + 0x5f, 0xb0, 0xfb, 0x40, 0x8c, 0x5e, 0x35, 0xf0, 0xe9, 0xc2, 0xa2, 0xd4, 0xbf, 0x91, 0xcb, 0xb9, + 0x9f, 0x55, 0x71, 0x83, 0xec, 0x6f, 0xf8, 0x94, 0x33, 0xcf, 0x5e, 0x15, 0xc6, 0x9a, 0x1f, 0xe8, + 0xba, 0xe6, 0x7f, 0xa1, 0x00, 0xa7, 0xd5, 0x08, 0x18, 0x4c, 0xe0, 0x37, 0xfb, 0x18, 0x5c, 0x85, + 0xd1, 0x06, 0xd9, 0x72, 0x3a, 0xcd, 0x48, 0x89, 0xcf, 0x07, 0xb9, 0x0a, 0xa5, 0x1c, 0x17, 0x63, + 0x1d, 0xe7, 0x08, 0xc3, 0xf6, 0x3f, 0x46, 0xd9, 0x45, 0x1c, 0x39, 0x74, 0x8d, 0xab, 0x5d, 0x63, + 0xe5, 0xee, 0x9a, 0x8b, 0x30, 0xe8, 0xb6, 0x28, 0x63, 0x56, 0x30, 0xf9, 0xad, 0x0a, 0x2d, 0xc4, + 0x1c, 0x86, 0x3e, 0x00, 0xc3, 0x75, 0xbf, 0xd5, 0x72, 0xbc, 0x06, 0xbb, 0xf2, 0x4a, 0x4b, 0xa3, + 0x94, 0x77, 0x5b, 0xe6, 0x45, 0x58, 0xc2, 0xd0, 0x13, 0x30, 0xe0, 0x04, 0xdb, 0x5c, 0x86, 0x51, + 0x5a, 0x1a, 0xa1, 0x2d, 0x2d, 0x06, 0xdb, 0x21, 0x66, 0xa5, 0xf4, 0x09, 0x76, 0xcf, 0x0f, 0x76, + 0x5d, 0x6f, 0xbb, 0xec, 0x06, 0x62, 0x4b, 0xa8, 0xbb, 0xf0, 0xae, 0x82, 0x60, 0x0d, 0x0b, 0xad, + 0xc2, 0x60, 0xdb, 0x0f, 0xa2, 0x70, 0x76, 0x88, 0x0d, 0xf7, 0x93, 0x39, 0x07, 0x11, 0xff, 0xda, + 0xaa, 0x1f, 0x44, 0xf1, 0x07, 0xd0, 0x7f, 0x21, 0xe6, 0xd5, 0xd1, 0x4d, 0x18, 0x26, 0xde, 0xde, + 0x6a, 0xe0, 0xb7, 0x66, 0x67, 0xf2, 0x29, 0xad, 0x70, 0x14, 0xbe, 0xcc, 0x62, 0x1e, 0x55, 0x14, + 0x63, 0x49, 0x02, 0x7d, 0x1b, 0x14, 0x89, 0xb7, 0x37, 0x3b, 0xcc, 0x28, 0xcd, 0xe5, 0x50, 0xba, + 0xe3, 0x04, 0xf1, 0x99, 0xbf, 0xe2, 0xed, 0x61, 0x5a, 0x07, 0x7d, 0x12, 0x4a, 0xf2, 0xc0, 0x08, + 0x85, 0xb0, 0x2e, 0x73, 0xc1, 0xca, 0x63, 0x06, 0x93, 0xb7, 0x3a, 0x6e, 0x40, 0x5a, 0xc4, 0x8b, + 0xc2, 0xf8, 0x84, 0x94, 0xd0, 0x10, 0xc7, 0xd4, 0xd0, 0x27, 0xa5, 0x84, 0x78, 0xcd, 0xef, 0x78, + 0x51, 0x38, 0x5b, 0x62, 0xdd, 0xcb, 0xd4, 0xdd, 0xdd, 0x89, 0xf1, 0x92, 0x22, 0x64, 0x5e, 0x19, + 0x1b, 0xa4, 0xd0, 0xa7, 0x61, 0x9c, 0xff, 0xe7, 0x1a, 0xb0, 0x70, 0xf6, 0x34, 0xa3, 0x7d, 0x21, + 0x9f, 0x36, 0x47, 0x5c, 0x3a, 0x2d, 0x88, 0x8f, 0xeb, 0xa5, 0x21, 0x36, 0xa9, 0x21, 0x0c, 0xe3, + 0x4d, 0x77, 0x8f, 0x78, 0x24, 0x0c, 0xab, 0x81, 0xbf, 0x49, 0x66, 0x81, 0x0d, 0xcc, 0xd9, 0x6c, + 0x8d, 0x99, 0xbf, 0x49, 0x96, 0xa6, 0x29, 0xcd, 0x9b, 0x7a, 0x1d, 0x6c, 0x92, 0x40, 0xb7, 0x61, + 0x82, 0xbe, 0xd8, 0xdc, 0x98, 0xe8, 0x68, 0x2f, 0xa2, 0xec, 0x5d, 0x85, 0x8d, 0x4a, 0x38, 0x41, + 0x04, 0xdd, 0x82, 0xb1, 0x30, 0x72, 0x82, 0xa8, 0xd3, 0xe6, 0x44, 0xcf, 0xf4, 0x22, 0xca, 0x14, + 0xae, 0x35, 0xad, 0x0a, 0x36, 0x08, 0xa0, 0xd7, 0xa1, 0xd4, 0x74, 0xb7, 0x48, 0x7d, 0xbf, 0xde, + 0x24, 0xb3, 0x63, 0x8c, 0x5a, 0xe6, 0xa1, 0x72, 0x53, 0x22, 0x71, 0x3e, 0x57, 0xfd, 0xc5, 0x71, + 0x75, 0x74, 0x07, 0xce, 0x44, 0x24, 0x68, 0xb9, 0x9e, 0x43, 0x0f, 0x03, 0xf1, 0xb4, 0x62, 0x8a, + 0xcc, 0x71, 0xb6, 0xdb, 0xce, 0x8b, 0xd9, 0x38, 0xb3, 0x91, 0x89, 0x85, 0x73, 0x6a, 0xa3, 0xfb, + 0x30, 0x9b, 0x01, 0xf1, 0x9b, 0x6e, 0x7d, 0x7f, 0xf6, 0x14, 0xa3, 0xfc, 0x51, 0x41, 0x79, 0x76, + 0x23, 0x07, 0xef, 0xb0, 0x0b, 0x0c, 0xe7, 0x52, 0x47, 0xb7, 0x60, 0x92, 0x9d, 0x40, 0xd5, 0x4e, + 0xb3, 0x29, 0x1a, 0x9c, 0x60, 0x0d, 0x7e, 0x40, 0xde, 0xc7, 0x15, 0x13, 0x7c, 0x78, 0x30, 0x0f, + 0xf1, 0x3f, 0x9c, 0xac, 0x8d, 0x36, 0x99, 0xce, 0xac, 0x13, 0xb8, 0xd1, 0x3e, 0x3d, 0x37, 0xc8, + 0xfd, 0x68, 0x76, 0xb2, 0xab, 0xbc, 0x42, 0x47, 0x55, 0x8a, 0x35, 0xbd, 0x10, 0x27, 0x09, 0xd2, + 0x23, 0x35, 0x8c, 0x1a, 0xae, 0x37, 0x3b, 0xc5, 0xdf, 0x25, 0xf2, 0x44, 0xaa, 0xd1, 0x42, 0xcc, + 0x61, 0x4c, 0x5f, 0x46, 0x7f, 0xdc, 0xa2, 0x37, 0xd7, 0x34, 0x43, 0x8c, 0xf5, 0x65, 0x12, 0x80, + 0x63, 0x1c, 0xca, 0x4c, 0x46, 0xd1, 0xfe, 0x2c, 0x62, 0xa8, 0xea, 0x60, 0xd9, 0xd8, 0xf8, 0x24, + 0xa6, 0xe5, 0xf6, 0x26, 0x4c, 0xa8, 0x83, 0x90, 0x8d, 0x09, 0x9a, 0x87, 0x41, 0xc6, 0x3e, 0x09, + 0xe9, 0x5a, 0x89, 0x76, 0x81, 0xb1, 0x56, 0x98, 0x97, 0xb3, 0x2e, 0xb8, 0x6f, 0x93, 0xa5, 0xfd, + 0x88, 0xf0, 0x37, 0x7d, 0x51, 0xeb, 0x82, 0x04, 0xe0, 0x18, 0xc7, 0xfe, 0xdf, 0x9c, 0x0d, 0x8d, + 0x4f, 0xdb, 0x3e, 0xee, 0x97, 0x67, 0x61, 0x64, 0xc7, 0x0f, 0x23, 0x8a, 0xcd, 0xda, 0x18, 0x8c, + 0x19, 0xcf, 0xeb, 0xa2, 0x1c, 0x2b, 0x0c, 0xf4, 0x2a, 0x8c, 0xd7, 0xf5, 0x06, 0xc4, 0xe5, 0xa8, + 0x8e, 0x11, 0xa3, 0x75, 0x6c, 0xe2, 0xa2, 0x97, 0x61, 0x84, 0xd9, 0x80, 0xd4, 0xfd, 0xa6, 0xe0, + 0xda, 0xe4, 0x0d, 0x3f, 0x52, 0x15, 0xe5, 0x87, 0xda, 0x6f, 0xac, 0xb0, 0xd1, 0x25, 0x18, 0xa2, + 0x5d, 0xa8, 0x54, 0xc5, 0xb5, 0xa4, 0x04, 0x45, 0xd7, 0x59, 0x29, 0x16, 0x50, 0xfb, 0xff, 0x2b, + 0x68, 0xa3, 0x4c, 0xdf, 0xc3, 0x04, 0x55, 0x61, 0xf8, 0x9e, 0xe3, 0x46, 0xae, 0xb7, 0x2d, 0xf8, + 0x8f, 0xa7, 0xbb, 0xde, 0x51, 0xac, 0xd2, 0x5d, 0x5e, 0x81, 0xdf, 0xa2, 0xe2, 0x0f, 0x96, 0x64, + 0x28, 0xc5, 0xa0, 0xe3, 0x79, 0x94, 0x62, 0xa1, 0x5f, 0x8a, 0x98, 0x57, 0xe0, 0x14, 0xc5, 0x1f, + 0x2c, 0xc9, 0xa0, 0x37, 0x01, 0xe4, 0x0e, 0x23, 0x0d, 0x61, 0x7b, 0xf1, 0x6c, 0x6f, 0xa2, 0x1b, + 0xaa, 0xce, 0xd2, 0x04, 0xbd, 0xa3, 0xe3, 0xff, 0x58, 0xa3, 0x67, 0x47, 0x8c, 0x4f, 0x4b, 0x77, + 0x06, 0x7d, 0x07, 0x5d, 0xe2, 0x4e, 0x10, 0x91, 0xc6, 0x62, 0x24, 0x06, 0xe7, 0x83, 0xfd, 0x3d, + 0x52, 0x36, 0xdc, 0x16, 0xd1, 0xb7, 0x83, 0x20, 0x82, 0x63, 0x7a, 0xf6, 0x2f, 0x15, 0x61, 0x36, + 0xaf, 0xbb, 0x74, 0xd1, 0x91, 0xfb, 0x6e, 0xb4, 0x4c, 0xd9, 0x2b, 0xcb, 0x5c, 0x74, 0x2b, 0xa2, + 0x1c, 0x2b, 0x0c, 0x3a, 0xfb, 0xa1, 0xbb, 0x2d, 0xdf, 0x98, 0x83, 0xf1, 0xec, 0xd7, 0x58, 0x29, + 0x16, 0x50, 0x8a, 0x17, 0x10, 0x27, 0x14, 0xc6, 0x3d, 0xda, 0x2a, 0xc1, 0xac, 0x14, 0x0b, 0xa8, + 0x2e, 0xed, 0x1a, 0xe8, 0x21, 0xed, 0x32, 0x86, 0x68, 0xf0, 0x78, 0x87, 0x08, 0x7d, 0x06, 0x60, + 0xcb, 0xf5, 0xdc, 0x70, 0x87, 0x51, 0x1f, 0x3a, 0x32, 0x75, 0xc5, 0x9c, 0xad, 0x2a, 0x2a, 0x58, + 0xa3, 0x88, 0x5e, 0x82, 0x51, 0xb5, 0x01, 0x2b, 0x65, 0xa6, 0xe9, 0xd4, 0x2c, 0x47, 0xe2, 0xd3, + 0xa8, 0x8c, 0x75, 0x3c, 0xfb, 0x73, 0xc9, 0xf5, 0x22, 0x76, 0x80, 0x36, 0xbe, 0x56, 0xbf, 0xe3, + 0x5b, 0xe8, 0x3e, 0xbe, 0xf6, 0xd7, 0x8b, 0x30, 0x69, 0x34, 0xd6, 0x09, 0xfb, 0x38, 0xb3, 0xae, + 0xd1, 0x03, 0xdc, 0x89, 0x88, 0xd8, 0x7f, 0x76, 0xef, 0xad, 0xa2, 0x1f, 0xf2, 0x74, 0x07, 0xf0, + 0xfa, 0xe8, 0x33, 0x50, 0x6a, 0x3a, 0x21, 0x93, 0x9c, 0x11, 0xb1, 0xef, 0xfa, 0x21, 0x16, 0x3f, + 0x4c, 0x9c, 0x30, 0xd2, 0x6e, 0x4d, 0x4e, 0x3b, 0x26, 0x49, 0x6f, 0x1a, 0xca, 0x9f, 0x48, 0xeb, + 0x31, 0xd5, 0x09, 0xca, 0xc4, 0xec, 0x63, 0x0e, 0x43, 0x2f, 0xc3, 0x58, 0x40, 0xd8, 0xaa, 0x58, + 0xa6, 0xdc, 0x1c, 0x5b, 0x66, 0x83, 0x31, 0xdb, 0x87, 0x35, 0x18, 0x36, 0x30, 0xe3, 0xb7, 0xc1, + 0x50, 0x97, 0xb7, 0xc1, 0xd3, 0x30, 0xcc, 0x7e, 0xa8, 0x15, 0xa0, 0x66, 0xa3, 0xc2, 0x8b, 0xb1, + 0x84, 0x27, 0x17, 0xcc, 0x48, 0x7f, 0x0b, 0x86, 0xbe, 0x3e, 0xc4, 0xa2, 0x66, 0x5a, 0xe6, 0x11, + 0x7e, 0xca, 0x89, 0x25, 0x8f, 0x25, 0xcc, 0xfe, 0x20, 0x4c, 0x94, 0x1d, 0xd2, 0xf2, 0xbd, 0x15, + 0xaf, 0xd1, 0xf6, 0x5d, 0x2f, 0x42, 0xb3, 0x30, 0xc0, 0x2e, 0x11, 0x7e, 0x04, 0x0c, 0xd0, 0x86, + 0x30, 0x2b, 0xb1, 0xb7, 0xe1, 0x74, 0xd9, 0xbf, 0xe7, 0xdd, 0x73, 0x82, 0xc6, 0x62, 0xb5, 0xa2, + 0xbd, 0xaf, 0xd7, 0xe5, 0xfb, 0x8e, 0x1b, 0x6d, 0x65, 0x1e, 0xbd, 0x5a, 0x4d, 0xce, 0xd6, 0xae, + 0xba, 0x4d, 0x92, 0x23, 0x05, 0xf9, 0xcb, 0x05, 0xa3, 0xa5, 0x18, 0x5f, 0x69, 0xb5, 0xac, 0x5c, + 0xad, 0xd6, 0x1b, 0x30, 0xb2, 0xe5, 0x92, 0x66, 0x03, 0x93, 0x2d, 0xb1, 0x12, 0x9f, 0xca, 0xb7, + 0x43, 0x59, 0xa5, 0x98, 0x52, 0xea, 0xc5, 0x5f, 0x87, 0xab, 0xa2, 0x32, 0x56, 0x64, 0xd0, 0x2e, + 0x4c, 0xc9, 0x07, 0x83, 0x84, 0x8a, 0x75, 0xf9, 0x74, 0xb7, 0x57, 0x88, 0x49, 0xfc, 0xd4, 0x83, + 0x83, 0xf9, 0x29, 0x9c, 0x20, 0x83, 0x53, 0x84, 0xe9, 0x73, 0xb0, 0x45, 0x4f, 0xe0, 0x01, 0x36, + 0xfc, 0xec, 0x39, 0xc8, 0x5e, 0xb6, 0xac, 0xd4, 0xfe, 0x09, 0x0b, 0x1e, 0x4b, 0x8d, 0x8c, 0x78, + 0xe1, 0x1f, 0xf3, 0x2c, 0x24, 0x5f, 0xdc, 0x85, 0xde, 0x2f, 0x6e, 0xfb, 0x6f, 0x59, 0x70, 0x6a, + 0xa5, 0xd5, 0x8e, 0xf6, 0xcb, 0xae, 0xa9, 0x82, 0xfa, 0x08, 0x0c, 0xb5, 0x48, 0xc3, 0xed, 0xb4, + 0xc4, 0xcc, 0xcd, 0xcb, 0x53, 0x6a, 0x8d, 0x95, 0x1e, 0x1e, 0xcc, 0x8f, 0xd7, 0x22, 0x3f, 0x70, + 0xb6, 0x09, 0x2f, 0xc0, 0x02, 0x9d, 0x9d, 0xf5, 0xee, 0xdb, 0xe4, 0xa6, 0xdb, 0x72, 0xa5, 0x5d, + 0x51, 0x57, 0x99, 0xdd, 0x82, 0x1c, 0xd0, 0x85, 0x37, 0x3a, 0x8e, 0x17, 0xb9, 0xd1, 0xbe, 0xd0, + 0x1e, 0x49, 0x22, 0x38, 0xa6, 0x67, 0x7f, 0xcd, 0x82, 0x49, 0xb9, 0xee, 0x17, 0x1b, 0x8d, 0x80, + 0x84, 0x21, 0x9a, 0x83, 0x82, 0xdb, 0x16, 0xbd, 0x04, 0xd1, 0xcb, 0x42, 0xa5, 0x8a, 0x0b, 0x6e, + 0x5b, 0xb2, 0x65, 0xec, 0x20, 0x2c, 0x9a, 0x8a, 0xb4, 0xeb, 0xa2, 0x1c, 0x2b, 0x0c, 0x74, 0x19, + 0x46, 0x3c, 0xbf, 0xc1, 0x6d, 0xbb, 0xf8, 0x95, 0xc6, 0x16, 0xd8, 0xba, 0x28, 0xc3, 0x0a, 0x8a, + 0xaa, 0x50, 0xe2, 0x66, 0x4f, 0xf1, 0xa2, 0xed, 0xcb, 0x78, 0x8a, 0x7d, 0xd9, 0x86, 0xac, 0x89, + 0x63, 0x22, 0xf6, 0xaf, 0x59, 0x30, 0x26, 0xbf, 0xac, 0x4f, 0x9e, 0x93, 0x6e, 0xad, 0x98, 0xdf, + 0x8c, 0xb7, 0x16, 0xe5, 0x19, 0x19, 0xc4, 0x60, 0x15, 0x8b, 0x47, 0x62, 0x15, 0xaf, 0xc2, 0xa8, + 0xd3, 0x6e, 0x57, 0x4d, 0x3e, 0x93, 0x2d, 0xa5, 0xc5, 0xb8, 0x18, 0xeb, 0x38, 0xf6, 0x8f, 0x17, + 0x60, 0x42, 0x7e, 0x41, 0xad, 0xb3, 0x19, 0x92, 0x08, 0x6d, 0x40, 0xc9, 0xe1, 0xb3, 0x44, 0xe4, + 0x22, 0xbf, 0x98, 0x2d, 0x47, 0x30, 0xa6, 0x34, 0xbe, 0xf0, 0x17, 0x65, 0x6d, 0x1c, 0x13, 0x42, + 0x4d, 0x98, 0xf6, 0xfc, 0x88, 0x1d, 0xfe, 0x0a, 0xde, 0x4d, 0xb5, 0x93, 0xa4, 0x7e, 0x56, 0x50, + 0x9f, 0x5e, 0x4f, 0x52, 0xc1, 0x69, 0xc2, 0x68, 0x45, 0xca, 0x66, 0x8a, 0xf9, 0xc2, 0x00, 0x7d, + 0xe2, 0xb2, 0x45, 0x33, 0xf6, 0xaf, 0x5a, 0x50, 0x92, 0x68, 0x27, 0xa1, 0xc5, 0x5b, 0x83, 0xe1, + 0x90, 0x4d, 0x82, 0x1c, 0x1a, 0xbb, 0x5b, 0xc7, 0xf9, 0x7c, 0xc5, 0x77, 0x1a, 0xff, 0x1f, 0x62, + 0x49, 0x83, 0x89, 0xe6, 0x55, 0xf7, 0xdf, 0x25, 0xa2, 0x79, 0xd5, 0x9f, 0x9c, 0x4b, 0xe9, 0x8f, + 0x58, 0x9f, 0x35, 0x59, 0x17, 0x65, 0xbd, 0xda, 0x01, 0xd9, 0x72, 0xef, 0x27, 0x59, 0xaf, 0x2a, + 0x2b, 0xc5, 0x02, 0x8a, 0xde, 0x84, 0xb1, 0xba, 0x94, 0xc9, 0xc6, 0x3b, 0xfc, 0x52, 0x57, 0xfd, + 0x80, 0x52, 0x25, 0x71, 0x59, 0xc8, 0xb2, 0x56, 0x1f, 0x1b, 0xd4, 0x4c, 0x33, 0x82, 0x62, 0x2f, + 0x33, 0x82, 0x98, 0x6e, 0xbe, 0x52, 0xfd, 0x27, 0x2d, 0x18, 0xe2, 0xb2, 0xb8, 0xfe, 0x44, 0xa1, + 0x9a, 0x66, 0x2d, 0x1e, 0xbb, 0x3b, 0xb4, 0x50, 0x68, 0xca, 0xd0, 0x1a, 0x94, 0xd8, 0x0f, 0x26, + 0x4b, 0x2c, 0xe6, 0x5b, 0xdd, 0xf3, 0x56, 0xf5, 0x0e, 0xde, 0x91, 0xd5, 0x70, 0x4c, 0xc1, 0xfe, + 0xd1, 0x22, 0x3d, 0xdd, 0x62, 0x54, 0xe3, 0xd2, 0xb7, 0x1e, 0xdd, 0xa5, 0x5f, 0x78, 0x54, 0x97, + 0xfe, 0x36, 0x4c, 0xd6, 0x35, 0x3d, 0x5c, 0x3c, 0x93, 0x97, 0xbb, 0x2e, 0x12, 0x4d, 0x65, 0xc7, + 0xa5, 0x2c, 0xcb, 0x26, 0x11, 0x9c, 0xa4, 0x8a, 0xbe, 0x03, 0xc6, 0xf8, 0x3c, 0x8b, 0x56, 0xb8, + 0x25, 0xc6, 0x07, 0xf2, 0xd7, 0x8b, 0xde, 0x04, 0x97, 0xca, 0x69, 0xd5, 0xb1, 0x41, 0xcc, 0xfe, + 0x53, 0x0b, 0xd0, 0x4a, 0x7b, 0x87, 0xb4, 0x48, 0xe0, 0x34, 0x63, 0x71, 0xfa, 0x0f, 0x5a, 0x30, + 0x4b, 0x52, 0xc5, 0xcb, 0x7e, 0xab, 0x25, 0x1e, 0x2d, 0x39, 0xef, 0xea, 0x95, 0x9c, 0x3a, 0xca, + 0x2d, 0x61, 0x36, 0x0f, 0x03, 0xe7, 0xb6, 0x87, 0xd6, 0x60, 0x86, 0xdf, 0x92, 0x0a, 0xa0, 0xd9, + 0x5e, 0x3f, 0x2e, 0x08, 0xcf, 0x6c, 0xa4, 0x51, 0x70, 0x56, 0x3d, 0xfb, 0x7b, 0xc6, 0x20, 0xb7, + 0x17, 0xef, 0xe9, 0x11, 0xde, 0xd3, 0x23, 0xbc, 0xa7, 0x47, 0x78, 0x4f, 0x8f, 0xf0, 0x9e, 0x1e, + 0xe1, 0x5b, 0x5e, 0x8f, 0xf0, 0xff, 0x5b, 0x70, 0x5a, 0x5d, 0x03, 0xc6, 0xc3, 0xf7, 0xf3, 0x30, + 0xc3, 0xb7, 0xdb, 0x72, 0xd3, 0x71, 0x5b, 0x1b, 0xa4, 0xd5, 0x6e, 0x3a, 0x91, 0xd4, 0xba, 0x5f, + 0xcd, 0x5c, 0xb9, 0x09, 0x8b, 0x55, 0xa3, 0xe2, 0xd2, 0x63, 0xf4, 0x7a, 0xca, 0x00, 0xe0, 0xac, + 0x66, 0xec, 0x5f, 0x1a, 0x81, 0xc1, 0x95, 0x3d, 0xe2, 0x45, 0x27, 0xf0, 0x44, 0xa8, 0xc3, 0x84, + 0xeb, 0xed, 0xf9, 0xcd, 0x3d, 0xd2, 0xe0, 0xf0, 0xa3, 0xbc, 0x64, 0xcf, 0x08, 0xd2, 0x13, 0x15, + 0x83, 0x04, 0x4e, 0x90, 0x7c, 0x14, 0xd2, 0xe4, 0x6b, 0x30, 0xc4, 0x0f, 0x71, 0x21, 0x4a, 0xce, + 0x3c, 0xb3, 0xd9, 0x20, 0x8a, 0xab, 0x29, 0x96, 0x74, 0xf3, 0x4b, 0x42, 0x54, 0x47, 0x9f, 0x83, + 0x89, 0x2d, 0x37, 0x08, 0xa3, 0x0d, 0xb7, 0x45, 0xc2, 0xc8, 0x69, 0xb5, 0x1f, 0x42, 0x7a, 0xac, + 0xc6, 0x61, 0xd5, 0xa0, 0x84, 0x13, 0x94, 0xd1, 0x36, 0x8c, 0x37, 0x1d, 0xbd, 0xa9, 0xe1, 0x23, + 0x37, 0xa5, 0x6e, 0x87, 0x9b, 0x3a, 0x21, 0x6c, 0xd2, 0xa5, 0xdb, 0xa9, 0xce, 0x04, 0xa0, 0x23, + 0x4c, 0x2c, 0xa0, 0xb6, 0x13, 0x97, 0x7c, 0x72, 0x18, 0x65, 0x74, 0x98, 0x81, 0x6c, 0xc9, 0x64, + 0x74, 0x34, 0x33, 0xd8, 0xcf, 0x42, 0x89, 0xd0, 0x21, 0xa4, 0x84, 0xc5, 0x05, 0x73, 0xa5, 0xbf, + 0xbe, 0xae, 0xb9, 0xf5, 0xc0, 0x37, 0xe5, 0xf6, 0x2b, 0x92, 0x12, 0x8e, 0x89, 0xa2, 0x65, 0x18, + 0x0a, 0x49, 0xe0, 0x92, 0x50, 0x5c, 0x35, 0x5d, 0xa6, 0x91, 0xa1, 0x71, 0xdf, 0x12, 0xfe, 0x1b, + 0x8b, 0xaa, 0x74, 0x79, 0x39, 0x4c, 0xa4, 0xc9, 0x2e, 0x03, 0x6d, 0x79, 0x2d, 0xb2, 0x52, 0x2c, + 0xa0, 0xe8, 0x75, 0x18, 0x0e, 0x48, 0x93, 0x29, 0x86, 0xc6, 0xfb, 0x5f, 0xe4, 0x5c, 0xcf, 0xc4, + 0xeb, 0x61, 0x49, 0x00, 0xdd, 0x00, 0x14, 0x10, 0xca, 0x28, 0xb9, 0xde, 0xb6, 0x32, 0x1b, 0x15, + 0x07, 0xad, 0x62, 0x48, 0x71, 0x8c, 0x21, 0xdd, 0x7c, 0x70, 0x46, 0x35, 0x74, 0x0d, 0xa6, 0x55, + 0x69, 0xc5, 0x0b, 0x23, 0x87, 0x1e, 0x70, 0x93, 0x8c, 0x96, 0x92, 0x53, 0xe0, 0x24, 0x02, 0x4e, + 0xd7, 0xb1, 0xbf, 0x6c, 0x01, 0x1f, 0xe7, 0x13, 0x78, 0x9d, 0xbf, 0x66, 0xbe, 0xce, 0xcf, 0xe6, + 0xce, 0x5c, 0xce, 0xcb, 0xfc, 0xcb, 0x16, 0x8c, 0x6a, 0x33, 0x1b, 0xaf, 0x59, 0xab, 0xcb, 0x9a, + 0xed, 0xc0, 0x14, 0x5d, 0xe9, 0xb7, 0x36, 0x43, 0x12, 0xec, 0x91, 0x06, 0x5b, 0x98, 0x85, 0x87, + 0x5b, 0x98, 0xca, 0x44, 0xed, 0x66, 0x82, 0x20, 0x4e, 0x35, 0x61, 0x7f, 0x56, 0x76, 0x55, 0x59, + 0xf4, 0xd5, 0xd5, 0x9c, 0x27, 0x2c, 0xfa, 0xd4, 0xac, 0xe2, 0x18, 0x87, 0x6e, 0xb5, 0x1d, 0x3f, + 0x8c, 0x92, 0x16, 0x7d, 0xd7, 0xfd, 0x30, 0xc2, 0x0c, 0x62, 0xbf, 0x00, 0xb0, 0x72, 0x9f, 0xd4, + 0xf9, 0x8a, 0xd5, 0x1f, 0x0f, 0x56, 0xfe, 0xe3, 0xc1, 0xfe, 0x6d, 0x0b, 0x26, 0x56, 0x97, 0x8d, + 0x9b, 0x6b, 0x01, 0x80, 0xbf, 0x78, 0xee, 0xde, 0x5d, 0x97, 0xea, 0x70, 0xae, 0xd1, 0x54, 0xa5, + 0x58, 0xc3, 0x40, 0x67, 0xa1, 0xd8, 0xec, 0x78, 0x42, 0x7c, 0x38, 0x4c, 0xaf, 0xc7, 0x9b, 0x1d, + 0x0f, 0xd3, 0x32, 0xcd, 0xa5, 0xa0, 0xd8, 0xb7, 0x4b, 0x41, 0x4f, 0xd7, 0x7e, 0x34, 0x0f, 0x83, + 0xf7, 0xee, 0xb9, 0x0d, 0xee, 0x40, 0x29, 0x54, 0xf5, 0x77, 0xef, 0x56, 0xca, 0x21, 0xe6, 0xe5, + 0xf6, 0x97, 0x8a, 0x30, 0xb7, 0xda, 0x24, 0xf7, 0xdf, 0xa1, 0x13, 0x69, 0xbf, 0x0e, 0x11, 0x47, + 0x13, 0xc4, 0x1c, 0xd5, 0xe9, 0xa5, 0xf7, 0x78, 0x6c, 0xc1, 0x30, 0x37, 0x68, 0x93, 0x2e, 0xa5, + 0xaf, 0x66, 0xb5, 0x9e, 0x3f, 0x20, 0x0b, 0xdc, 0x30, 0x4e, 0x78, 0xc4, 0xa9, 0x0b, 0x53, 0x94, + 0x62, 0x49, 0x7c, 0xee, 0x15, 0x18, 0xd3, 0x31, 0x8f, 0xe4, 0x7e, 0xf6, 0xff, 0x14, 0x61, 0x8a, + 0xf6, 0xe0, 0x91, 0x4e, 0xc4, 0xed, 0xf4, 0x44, 0x1c, 0xb7, 0x0b, 0x52, 0xef, 0xd9, 0x78, 0x33, + 0x39, 0x1b, 0x57, 0xf3, 0x66, 0xe3, 0xa4, 0xe7, 0xe0, 0xbb, 0x2d, 0x98, 0x59, 0x6d, 0xfa, 0xf5, + 0xdd, 0x84, 0x9b, 0xd0, 0x4b, 0x30, 0x4a, 0x8f, 0xe3, 0xd0, 0xf0, 0x60, 0x37, 0x62, 0x1a, 0x08, + 0x10, 0xd6, 0xf1, 0xb4, 0x6a, 0xb7, 0x6f, 0x57, 0xca, 0x59, 0xa1, 0x10, 0x04, 0x08, 0xeb, 0x78, + 0xf6, 0x6f, 0x5a, 0x70, 0xee, 0xda, 0xf2, 0x4a, 0xbc, 0x14, 0x53, 0xd1, 0x18, 0x2e, 0xc1, 0x50, + 0xbb, 0xa1, 0x75, 0x25, 0x16, 0xaf, 0x96, 0x59, 0x2f, 0x04, 0xf4, 0xdd, 0x12, 0x69, 0xe4, 0x67, + 0x2d, 0x98, 0xb9, 0xe6, 0x46, 0xf4, 0x76, 0x4d, 0xc6, 0x05, 0xa0, 0xd7, 0x6b, 0xe8, 0x46, 0x7e, + 0xb0, 0x9f, 0x8c, 0x0b, 0x80, 0x15, 0x04, 0x6b, 0x58, 0xbc, 0xe5, 0x3d, 0x97, 0x99, 0x52, 0x17, + 0x4c, 0x45, 0x13, 0x16, 0xe5, 0x58, 0x61, 0xd0, 0x0f, 0x6b, 0xb8, 0x01, 0x93, 0xd1, 0xed, 0x8b, + 0x13, 0x56, 0x7d, 0x58, 0x59, 0x02, 0x70, 0x8c, 0x63, 0xff, 0xb1, 0x05, 0xf3, 0xd7, 0x9a, 0x9d, + 0x30, 0x22, 0xc1, 0x56, 0x98, 0x73, 0x3a, 0xbe, 0x00, 0x25, 0x22, 0x25, 0xe2, 0xa2, 0xd7, 0x8a, + 0x63, 0x54, 0xa2, 0x72, 0x1e, 0x9e, 0x40, 0xe1, 0xf5, 0xe1, 0x74, 0x78, 0x34, 0xaf, 0xb1, 0x55, + 0x40, 0x44, 0x6f, 0x4b, 0x8f, 0xd7, 0xc0, 0x1c, 0xbf, 0x57, 0x52, 0x50, 0x9c, 0x51, 0xc3, 0xfe, + 0x09, 0x0b, 0x4e, 0xab, 0x0f, 0x7e, 0xd7, 0x7d, 0xa6, 0xfd, 0xf3, 0x05, 0x18, 0xbf, 0xbe, 0xb1, + 0x51, 0xbd, 0x46, 0x22, 0x71, 0x6d, 0xf7, 0xd6, 0x73, 0x63, 0x4d, 0x5d, 0xd7, 0xed, 0x31, 0xd7, + 0x89, 0xdc, 0xe6, 0x02, 0x0f, 0xfb, 0xb3, 0x50, 0xf1, 0xa2, 0x5b, 0x41, 0x2d, 0x0a, 0x5c, 0x6f, + 0x3b, 0x53, 0xc1, 0x27, 0x99, 0x8b, 0x62, 0x1e, 0x73, 0x81, 0x5e, 0x80, 0x21, 0x16, 0x77, 0x48, + 0x4e, 0xc2, 0xe3, 0xea, 0x2d, 0xc4, 0x4a, 0x0f, 0x0f, 0xe6, 0x4b, 0xb7, 0x71, 0x85, 0xff, 0xc1, + 0x02, 0x15, 0xdd, 0x86, 0xd1, 0x9d, 0x28, 0x6a, 0x5f, 0x27, 0x4e, 0x83, 0x04, 0xf2, 0x38, 0x3c, + 0x9f, 0x75, 0x1c, 0xd2, 0x41, 0xe0, 0x68, 0xf1, 0x09, 0x12, 0x97, 0x85, 0x58, 0xa7, 0x63, 0xd7, + 0x00, 0x62, 0xd8, 0x31, 0x69, 0x2a, 0xec, 0x3f, 0xb4, 0x60, 0x98, 0x87, 0x80, 0x08, 0xd0, 0x47, + 0x61, 0x80, 0xdc, 0x27, 0x75, 0xc1, 0xf1, 0x66, 0x76, 0x38, 0xe6, 0xb4, 0xb8, 0xc4, 0x95, 0xfe, + 0xc7, 0xac, 0x16, 0xba, 0x0e, 0xc3, 0xb4, 0xb7, 0xd7, 0x54, 0x3c, 0x8c, 0x27, 0xf3, 0xbe, 0x58, + 0x4d, 0x3b, 0x67, 0xce, 0x44, 0x11, 0x96, 0xd5, 0x99, 0x7a, 0xb8, 0xde, 0xae, 0xd1, 0x13, 0x3b, + 0xea, 0xc6, 0x58, 0x6c, 0x2c, 0x57, 0x39, 0x92, 0xa0, 0xc6, 0xd5, 0xc3, 0xb2, 0x10, 0xc7, 0x44, + 0xec, 0x0d, 0x28, 0xd1, 0x49, 0x5d, 0x6c, 0xba, 0x4e, 0x77, 0x8d, 0xf7, 0x33, 0x50, 0x92, 0xfa, + 0xec, 0x50, 0xb8, 0x7e, 0x33, 0xaa, 0x52, 0xdd, 0x1d, 0xe2, 0x18, 0x6e, 0x6f, 0xc1, 0x29, 0x66, + 0x9d, 0xe8, 0x44, 0x3b, 0xc6, 0x1e, 0xeb, 0xbd, 0x98, 0x9f, 0x15, 0x0f, 0x48, 0x3e, 0x33, 0xb3, + 0x9a, 0x77, 0xe5, 0x98, 0xa4, 0x18, 0x3f, 0x26, 0xed, 0xaf, 0x0f, 0xc0, 0xe3, 0x95, 0x5a, 0x7e, + 0x74, 0x90, 0x97, 0x61, 0x8c, 0xf3, 0xa5, 0x74, 0x69, 0x3b, 0x4d, 0xd1, 0xae, 0x12, 0xb5, 0x6e, + 0x68, 0x30, 0x6c, 0x60, 0xa2, 0x73, 0x50, 0x74, 0xdf, 0xf2, 0x92, 0xbe, 0x47, 0x95, 0x37, 0xd6, + 0x31, 0x2d, 0xa7, 0x60, 0xca, 0xe2, 0xf2, 0xbb, 0x43, 0x81, 0x15, 0x9b, 0xfb, 0x1a, 0x4c, 0xb8, + 0x61, 0x3d, 0x74, 0x2b, 0x1e, 0x3d, 0x67, 0xb4, 0x93, 0x4a, 0x09, 0x37, 0x68, 0xa7, 0x15, 0x14, + 0x27, 0xb0, 0xb5, 0x8b, 0x6c, 0xb0, 0x6f, 0x36, 0xb9, 0xa7, 0x2f, 0x34, 0x7d, 0x01, 0xb4, 0xd9, + 0xd7, 0x85, 0x4c, 0x66, 0x2e, 0x5e, 0x00, 0xfc, 0x83, 0x43, 0x2c, 0x61, 0xf4, 0xe5, 0x58, 0xdf, + 0x71, 0xda, 0x8b, 0x9d, 0x68, 0xa7, 0xec, 0x86, 0x75, 0x7f, 0x8f, 0x04, 0xfb, 0xec, 0xd1, 0x3f, + 0x12, 0xbf, 0x1c, 0x15, 0x60, 0xf9, 0xfa, 0x62, 0x95, 0x62, 0xe2, 0x74, 0x1d, 0xb4, 0x08, 0x93, + 0xb2, 0xb0, 0x46, 0x42, 0x76, 0x85, 0x8d, 0x32, 0x32, 0xca, 0x1b, 0x48, 0x14, 0x2b, 0x22, 0x49, + 0x7c, 0x93, 0x93, 0x86, 0xe3, 0xe0, 0xa4, 0x3f, 0x02, 0xe3, 0xae, 0xe7, 0x46, 0xae, 0x13, 0xf9, + 0x5c, 0xe1, 0xc3, 0xdf, 0xf7, 0x4c, 0x92, 0x5d, 0xd1, 0x01, 0xd8, 0xc4, 0xb3, 0xff, 0xf3, 0x00, + 0x4c, 0xb3, 0x69, 0x7b, 0x6f, 0x85, 0x7d, 0x2b, 0xad, 0xb0, 0xdb, 0xe9, 0x15, 0x76, 0x1c, 0x4f, + 0x84, 0x87, 0x5e, 0x66, 0x9f, 0x83, 0x92, 0x72, 0x80, 0x92, 0x1e, 0x90, 0x56, 0x8e, 0x07, 0x64, + 0x6f, 0xee, 0x43, 0xda, 0x90, 0x15, 0x33, 0x6d, 0xc8, 0xfe, 0xaa, 0x05, 0xb1, 0x06, 0x03, 0x5d, + 0x87, 0x52, 0xdb, 0x67, 0xa6, 0x91, 0x81, 0xb4, 0x37, 0x7e, 0x3c, 0xf3, 0xa2, 0xe2, 0x97, 0x22, + 0xff, 0xf8, 0xaa, 0xac, 0x81, 0xe3, 0xca, 0x68, 0x09, 0x86, 0xdb, 0x01, 0xa9, 0x45, 0x2c, 0x48, + 0x48, 0x4f, 0x3a, 0x7c, 0x8d, 0x70, 0x7c, 0x2c, 0x2b, 0xda, 0xbf, 0x60, 0x01, 0x70, 0x33, 0x2d, + 0xc7, 0xdb, 0x26, 0x27, 0x20, 0xb5, 0x2e, 0xc3, 0x40, 0xd8, 0x26, 0xf5, 0x6e, 0x46, 0xab, 0x71, + 0x7f, 0x6a, 0x6d, 0x52, 0x8f, 0x07, 0x9c, 0xfe, 0xc3, 0xac, 0xb6, 0xfd, 0xbd, 0x00, 0x13, 0x31, + 0x5a, 0x25, 0x22, 0x2d, 0xf4, 0x9c, 0x11, 0x34, 0xe0, 0x6c, 0x22, 0x68, 0x40, 0x89, 0x61, 0x6b, + 0x02, 0xd2, 0xcf, 0x41, 0xb1, 0xe5, 0xdc, 0x17, 0x12, 0xb0, 0x67, 0xba, 0x77, 0x83, 0xd2, 0x5f, + 0x58, 0x73, 0xee, 0xf3, 0x47, 0xe2, 0x33, 0x72, 0x81, 0xac, 0x39, 0xf7, 0x0f, 0xb9, 0x69, 0x2a, + 0x3b, 0xa4, 0x6e, 0xba, 0x61, 0xf4, 0x85, 0xff, 0x14, 0xff, 0x67, 0xcb, 0x8e, 0x36, 0xc2, 0xda, + 0x72, 0x3d, 0x61, 0x81, 0xd4, 0x57, 0x5b, 0xae, 0x97, 0x6c, 0xcb, 0xf5, 0xfa, 0x68, 0xcb, 0xf5, + 0xd0, 0xdb, 0x30, 0x2c, 0x0c, 0x04, 0x45, 0x90, 0x9e, 0x2b, 0x7d, 0xb4, 0x27, 0xec, 0x0b, 0x79, + 0x9b, 0x57, 0xe4, 0x23, 0x58, 0x94, 0xf6, 0x6c, 0x57, 0x36, 0x88, 0xfe, 0x92, 0x05, 0x13, 0xe2, + 0x37, 0x26, 0x6f, 0x75, 0x48, 0x18, 0x09, 0xde, 0xf3, 0xc3, 0xfd, 0xf7, 0x41, 0x54, 0xe4, 0x5d, + 0xf9, 0xb0, 0x3c, 0x66, 0x4d, 0x60, 0xcf, 0x1e, 0x25, 0x7a, 0x81, 0xfe, 0x8e, 0x05, 0xa7, 0x5a, + 0xce, 0x7d, 0xde, 0x22, 0x2f, 0xc3, 0x4e, 0xe4, 0xfa, 0x42, 0xd1, 0xfe, 0xd1, 0xfe, 0xa6, 0x3f, + 0x55, 0x9d, 0x77, 0x52, 0x6a, 0x03, 0x4f, 0x65, 0xa1, 0xf4, 0xec, 0x6a, 0x66, 0xbf, 0xe6, 0xb6, + 0x60, 0x44, 0xae, 0xb7, 0x0c, 0x51, 0x43, 0x59, 0x67, 0xac, 0x8f, 0x6c, 0x9f, 0xa9, 0x3b, 0xe3, + 0xd3, 0x76, 0xc4, 0x5a, 0x7b, 0xa4, 0xed, 0x7c, 0x0e, 0xc6, 0xf4, 0x35, 0xf6, 0x48, 0xdb, 0x7a, + 0x0b, 0x66, 0x32, 0xd6, 0xd2, 0x23, 0x6d, 0xf2, 0x1e, 0x9c, 0xcd, 0x5d, 0x1f, 0x8f, 0xb2, 0x61, + 0xfb, 0xe7, 0x2d, 0xfd, 0x1c, 0x3c, 0x01, 0xd5, 0xc1, 0xb2, 0xa9, 0x3a, 0x38, 0xdf, 0x7d, 0xe7, + 0xe4, 0xe8, 0x0f, 0xde, 0xd4, 0x3b, 0x4d, 0x4f, 0x75, 0xf4, 0x3a, 0x0c, 0x35, 0x69, 0x89, 0x34, + 0x33, 0xb5, 0x7b, 0xef, 0xc8, 0x98, 0x97, 0x62, 0xe5, 0x21, 0x16, 0x14, 0xec, 0x5f, 0xb6, 0x60, + 0xe0, 0x04, 0x46, 0x02, 0x9b, 0x23, 0xf1, 0x5c, 0x2e, 0x69, 0x11, 0x3f, 0x78, 0x01, 0x3b, 0xf7, + 0x56, 0xee, 0x47, 0xc4, 0x0b, 0xd9, 0x53, 0x31, 0x73, 0x60, 0x7e, 0xda, 0x82, 0x99, 0x9b, 0xbe, + 0xd3, 0x58, 0x72, 0x9a, 0x8e, 0x57, 0x27, 0x41, 0xc5, 0xdb, 0x3e, 0x92, 0x8d, 0x74, 0xa1, 0xa7, + 0x8d, 0xf4, 0xb2, 0x34, 0x31, 0x1a, 0xc8, 0x9f, 0x3f, 0xca, 0x48, 0x26, 0xc3, 0xa8, 0x18, 0xc6, + 0xb0, 0x3b, 0x80, 0xf4, 0x5e, 0x0a, 0x8f, 0x15, 0x0c, 0xc3, 0x2e, 0xef, 0xaf, 0x98, 0xc4, 0xa7, + 0xb2, 0x19, 0xbc, 0xd4, 0xe7, 0x69, 0xbe, 0x18, 0xbc, 0x00, 0x4b, 0x42, 0xf6, 0xcb, 0x90, 0xe9, + 0xf6, 0xde, 0x5b, 0xf8, 0x60, 0x7f, 0x12, 0xa6, 0x59, 0xcd, 0x23, 0x3e, 0x8c, 0xed, 0x84, 0x6c, + 0x33, 0x23, 0x20, 0x9e, 0xfd, 0x45, 0x0b, 0x26, 0xd7, 0x13, 0x71, 0xc2, 0x2e, 0x31, 0x6d, 0x68, + 0x86, 0x48, 0xbd, 0xc6, 0x4a, 0xb1, 0x80, 0x1e, 0xbb, 0x24, 0xeb, 0x2f, 0x2c, 0x88, 0x23, 0x51, + 0x9c, 0x00, 0xfb, 0xb6, 0x6c, 0xb0, 0x6f, 0x99, 0x12, 0x16, 0xd5, 0x9d, 0x3c, 0xee, 0x0d, 0xdd, + 0x50, 0x31, 0x9a, 0xba, 0x08, 0x57, 0x62, 0x32, 0x7c, 0x29, 0x4e, 0x98, 0x81, 0x9c, 0x64, 0xd4, + 0x26, 0xfb, 0x77, 0x0a, 0x80, 0x14, 0x6e, 0xdf, 0x31, 0xa4, 0xd2, 0x35, 0x8e, 0x27, 0x86, 0xd4, + 0x1e, 0x20, 0xa6, 0xcf, 0x0f, 0x1c, 0x2f, 0xe4, 0x64, 0x5d, 0x21, 0xbb, 0x3b, 0x9a, 0xb1, 0xc0, + 0x9c, 0x68, 0x12, 0xdd, 0x4c, 0x51, 0xc3, 0x19, 0x2d, 0x68, 0x76, 0x1a, 0x83, 0xfd, 0xda, 0x69, + 0x0c, 0xf5, 0xf0, 0x4a, 0xfb, 0x39, 0x0b, 0xc6, 0xd5, 0x30, 0xbd, 0x4b, 0x6c, 0xc6, 0x55, 0x7f, + 0x72, 0x0e, 0xd0, 0xaa, 0xd6, 0x65, 0x76, 0xb1, 0x7c, 0x3b, 0xf3, 0x2e, 0x74, 0x9a, 0xee, 0xdb, + 0x44, 0x45, 0xf0, 0x9b, 0x17, 0xde, 0x82, 0xa2, 0xf4, 0xf0, 0x60, 0x7e, 0x5c, 0xfd, 0xe3, 0x11, + 0x83, 0xe3, 0x2a, 0xf4, 0x48, 0x9e, 0x4c, 0x2c, 0x45, 0xf4, 0x12, 0x0c, 0xb6, 0x77, 0x9c, 0x90, + 0x24, 0x7c, 0x6b, 0x06, 0xab, 0xb4, 0xf0, 0xf0, 0x60, 0x7e, 0x42, 0x55, 0x60, 0x25, 0x98, 0x63, + 0xf7, 0x1f, 0x99, 0x2b, 0xbd, 0x38, 0x7b, 0x46, 0xe6, 0xfa, 0x53, 0x0b, 0x06, 0xd6, 0xfd, 0xc6, + 0x49, 0x1c, 0x01, 0xaf, 0x19, 0x47, 0xc0, 0x13, 0x79, 0xc1, 0xdc, 0x73, 0x77, 0xff, 0x6a, 0x62, + 0xf7, 0x9f, 0xcf, 0xa5, 0xd0, 0x7d, 0xe3, 0xb7, 0x60, 0x94, 0x85, 0x88, 0x17, 0x7e, 0x44, 0x2f, + 0x18, 0x1b, 0x7e, 0x3e, 0xb1, 0xe1, 0x27, 0x35, 0x54, 0x6d, 0xa7, 0x3f, 0x0d, 0xc3, 0xc2, 0x31, + 0x25, 0xe9, 0xa4, 0x29, 0x70, 0xb1, 0x84, 0xdb, 0x3f, 0x59, 0x04, 0x23, 0x24, 0x3d, 0xfa, 0x55, + 0x0b, 0x16, 0x02, 0x6e, 0xb0, 0xda, 0x28, 0x77, 0x02, 0xd7, 0xdb, 0xae, 0xd5, 0x77, 0x48, 0xa3, + 0xd3, 0x74, 0xbd, 0xed, 0xca, 0xb6, 0xe7, 0xab, 0xe2, 0x95, 0xfb, 0xa4, 0xde, 0x61, 0x4a, 0xb0, + 0x1e, 0xf1, 0xef, 0x95, 0xe1, 0xf7, 0xf3, 0x0f, 0x0e, 0xe6, 0x17, 0xf0, 0x91, 0x68, 0xe3, 0x23, + 0xf6, 0x05, 0xfd, 0xa6, 0x05, 0x57, 0x78, 0xa4, 0xf6, 0xfe, 0xfb, 0xdf, 0xe5, 0xb5, 0x5c, 0x95, + 0xa4, 0x62, 0x22, 0x1b, 0x24, 0x68, 0x2d, 0x7d, 0x44, 0x0c, 0xe8, 0x95, 0xea, 0xd1, 0xda, 0xc2, + 0x47, 0xed, 0x9c, 0xfd, 0x4f, 0x8a, 0x30, 0x2e, 0x22, 0x38, 0x89, 0x3b, 0xe0, 0x25, 0x63, 0x49, + 0x3c, 0x99, 0x58, 0x12, 0xd3, 0x06, 0xf2, 0xf1, 0x1c, 0xff, 0x21, 0x4c, 0xd3, 0xc3, 0xf9, 0x3a, + 0x71, 0x82, 0x68, 0x93, 0x38, 0xdc, 0xfc, 0xaa, 0x78, 0xe4, 0xd3, 0x5f, 0x89, 0xe7, 0x6e, 0x26, + 0x89, 0xe1, 0x34, 0xfd, 0x6f, 0xa5, 0x3b, 0xc7, 0x83, 0xa9, 0x54, 0x10, 0xae, 0x4f, 0x41, 0x49, + 0x79, 0x55, 0x88, 0x43, 0xa7, 0x7b, 0x2c, 0xbb, 0x24, 0x05, 0x2e, 0x42, 0x8b, 0x3d, 0x7a, 0x62, + 0x72, 0xf6, 0xdf, 0x2b, 0x18, 0x0d, 0xf2, 0x49, 0x5c, 0x87, 0x11, 0x27, 0x0c, 0xdd, 0x6d, 0x8f, + 0x34, 0xc4, 0x8e, 0x7d, 0x7f, 0xde, 0x8e, 0x35, 0x9a, 0x61, 0x9e, 0x2d, 0x8b, 0xa2, 0x26, 0x56, + 0x34, 0xd0, 0x75, 0x6e, 0xe4, 0xb6, 0x27, 0xdf, 0x7b, 0xfd, 0x51, 0x03, 0x69, 0x06, 0xb7, 0x47, + 0xb0, 0xa8, 0x8f, 0x3e, 0xcd, 0xad, 0x10, 0x6f, 0x78, 0xfe, 0x3d, 0xef, 0x9a, 0xef, 0xcb, 0x28, + 0x09, 0xfd, 0x11, 0x9c, 0x96, 0xb6, 0x87, 0xaa, 0x3a, 0x36, 0xa9, 0xf5, 0x17, 0xd5, 0xf2, 0xf3, + 0x30, 0x43, 0x49, 0x9b, 0x4e, 0xcc, 0x21, 0x22, 0x30, 0x29, 0xc2, 0x83, 0xc9, 0x32, 0x31, 0x76, + 0x99, 0x4f, 0x39, 0xb3, 0x76, 0x2c, 0x47, 0xbe, 0x61, 0x92, 0xc0, 0x49, 0x9a, 0xf6, 0xcf, 0x58, + 0xc0, 0x1c, 0x3a, 0x4f, 0x80, 0x1f, 0xf9, 0x98, 0xc9, 0x8f, 0xcc, 0xe6, 0x0d, 0x72, 0x0e, 0x2b, + 0xf2, 0x22, 0x5f, 0x59, 0xd5, 0xc0, 0xbf, 0xbf, 0x2f, 0x4c, 0x47, 0x7a, 0xbf, 0x3f, 0xec, 0xff, + 0x65, 0xf1, 0x43, 0x4c, 0xf9, 0x3c, 0xa0, 0xef, 0x84, 0x91, 0xba, 0xd3, 0x76, 0xea, 0x3c, 0x7f, + 0x4a, 0xae, 0x44, 0xcf, 0xa8, 0xb4, 0xb0, 0x2c, 0x6a, 0x70, 0x09, 0x95, 0x0c, 0x33, 0x37, 0x22, + 0x8b, 0x7b, 0x4a, 0xa5, 0x54, 0x93, 0x73, 0xbb, 0x30, 0x6e, 0x10, 0x7b, 0xa4, 0xe2, 0x8c, 0xef, + 0xe4, 0x57, 0xac, 0x0a, 0x8b, 0xd8, 0x82, 0x69, 0x4f, 0xfb, 0x4f, 0x2f, 0x14, 0xf9, 0xb8, 0x7c, + 0x7f, 0xaf, 0x4b, 0x94, 0xdd, 0x3e, 0x9a, 0xaf, 0x68, 0x82, 0x0c, 0x4e, 0x53, 0xb6, 0x7f, 0xca, + 0x82, 0xc7, 0x74, 0x44, 0xcd, 0x1d, 0xa5, 0x97, 0x8e, 0xa0, 0x0c, 0x23, 0x7e, 0x9b, 0x04, 0x4e, + 0xe4, 0x07, 0xe2, 0xd6, 0xb8, 0x2c, 0x07, 0xfd, 0x96, 0x28, 0x3f, 0x14, 0xd1, 0xc7, 0x25, 0x75, + 0x59, 0x8e, 0x55, 0x4d, 0xfa, 0xfa, 0x64, 0x83, 0x11, 0x0a, 0xc7, 0x23, 0x76, 0x06, 0x30, 0x75, + 0x79, 0x88, 0x05, 0xc4, 0xfe, 0xba, 0xc5, 0x17, 0x96, 0xde, 0x75, 0xf4, 0x16, 0x4c, 0xb5, 0x9c, + 0xa8, 0xbe, 0xb3, 0x72, 0xbf, 0x1d, 0x70, 0x8d, 0x8b, 0x1c, 0xa7, 0x67, 0x7a, 0x8d, 0x93, 0xf6, + 0x91, 0xb1, 0x61, 0xe5, 0x5a, 0x82, 0x18, 0x4e, 0x91, 0x47, 0x9b, 0x30, 0xca, 0xca, 0x98, 0x4f, + 0x5d, 0xd8, 0x8d, 0x35, 0xc8, 0x6b, 0x4d, 0x59, 0x1c, 0xac, 0xc5, 0x74, 0xb0, 0x4e, 0xd4, 0xfe, + 0x4a, 0x91, 0xef, 0x76, 0xc6, 0xca, 0x3f, 0x0d, 0xc3, 0x6d, 0xbf, 0xb1, 0x5c, 0x29, 0x63, 0x31, + 0x0b, 0xea, 0x1a, 0xa9, 0xf2, 0x62, 0x2c, 0xe1, 0xe8, 0x32, 0x8c, 0x88, 0x9f, 0x52, 0x43, 0xc6, + 0xce, 0x66, 0x81, 0x17, 0x62, 0x05, 0x45, 0xcf, 0x03, 0xb4, 0x03, 0x7f, 0xcf, 0x6d, 0xb0, 0x58, + 0x0f, 0x45, 0xd3, 0x58, 0xa8, 0xaa, 0x20, 0x58, 0xc3, 0x42, 0xaf, 0xc2, 0x78, 0xc7, 0x0b, 0x39, + 0x3b, 0xa2, 0x45, 0x76, 0x55, 0x66, 0x2c, 0xb7, 0x75, 0x20, 0x36, 0x71, 0xd1, 0x22, 0x0c, 0x45, + 0x0e, 0x33, 0x7e, 0x19, 0xcc, 0x37, 0xbe, 0xdd, 0xa0, 0x18, 0x7a, 0xaa, 0x0e, 0x5a, 0x01, 0x8b, + 0x8a, 0xe8, 0x53, 0xd2, 0xbd, 0x95, 0x1f, 0xec, 0xc2, 0xea, 0xbd, 0xbf, 0x4b, 0x40, 0x73, 0x6e, + 0x15, 0xd6, 0xf4, 0x06, 0x2d, 0xf4, 0x0a, 0x00, 0xb9, 0x1f, 0x91, 0xc0, 0x73, 0x9a, 0xca, 0xb6, + 0x4c, 0xf1, 0x05, 0x65, 0x7f, 0xdd, 0x8f, 0x6e, 0x87, 0x64, 0x45, 0x61, 0x60, 0x0d, 0xdb, 0xfe, + 0xcd, 0x12, 0x40, 0xcc, 0xb7, 0xa3, 0xb7, 0x53, 0x07, 0xd7, 0xb3, 0xdd, 0x39, 0xfd, 0xe3, 0x3b, + 0xb5, 0xd0, 0xf7, 0x59, 0x30, 0xea, 0x34, 0x9b, 0x7e, 0xdd, 0xe1, 0xb1, 0x77, 0x0b, 0xdd, 0x0f, + 0x4e, 0xd1, 0xfe, 0x62, 0x5c, 0x83, 0x77, 0xe1, 0x05, 0xb9, 0x42, 0x35, 0x48, 0xcf, 0x5e, 0xe8, + 0x0d, 0xa3, 0x0f, 0xc9, 0xa7, 0x62, 0xd1, 0x18, 0x4a, 0xf5, 0x54, 0x2c, 0xb1, 0x3b, 0x42, 0x7f, + 0x25, 0xde, 0x36, 0x5e, 0x89, 0x03, 0xf9, 0xfe, 0x7b, 0x06, 0xfb, 0xda, 0xeb, 0x81, 0x88, 0xaa, + 0xba, 0x2f, 0xff, 0x60, 0xbe, 0xb3, 0x9c, 0xf6, 0x4e, 0xea, 0xe1, 0xc7, 0xff, 0x39, 0x98, 0x6c, + 0x98, 0x4c, 0x80, 0x58, 0x89, 0x4f, 0xe5, 0xd1, 0x4d, 0xf0, 0x0c, 0xf1, 0xb5, 0x9f, 0x00, 0xe0, + 0x24, 0x61, 0x54, 0xe5, 0xa1, 0x1d, 0x2a, 0xde, 0x96, 0x2f, 0x3c, 0x2f, 0xec, 0xdc, 0xb9, 0xdc, + 0x0f, 0x23, 0xd2, 0xa2, 0x98, 0xf1, 0xed, 0xbe, 0x2e, 0xea, 0x62, 0x45, 0x05, 0xbd, 0x0e, 0x43, + 0xcc, 0x5b, 0x2a, 0x9c, 0x1d, 0xc9, 0x97, 0x38, 0x9b, 0xb1, 0xca, 0xe2, 0x0d, 0xc9, 0xfe, 0x86, + 0x58, 0x50, 0x40, 0xd7, 0xa5, 0x2f, 0x62, 0x58, 0xf1, 0x6e, 0x87, 0x84, 0xf9, 0x22, 0x96, 0x96, + 0xde, 0x1f, 0xbb, 0x19, 0xf2, 0xf2, 0xcc, 0x84, 0x5e, 0x46, 0x4d, 0xca, 0x45, 0x89, 0xff, 0x32, + 0x4f, 0xd8, 0x2c, 0xe4, 0x77, 0xcf, 0xcc, 0x25, 0x16, 0x0f, 0xe7, 0x1d, 0x93, 0x04, 0x4e, 0xd2, + 0xa4, 0x1c, 0x29, 0xdf, 0xf5, 0xc2, 0x77, 0xa3, 0xd7, 0xd9, 0xc1, 0x1f, 0xe2, 0xec, 0x36, 0xe2, + 0x25, 0x58, 0xd4, 0x3f, 0x51, 0xf6, 0x60, 0xce, 0x83, 0xa9, 0xe4, 0x16, 0x7d, 0xa4, 0xec, 0xc8, + 0x1f, 0x0e, 0xc0, 0x84, 0xb9, 0xa4, 0xd0, 0x15, 0x28, 0x09, 0x22, 0x2a, 0xb6, 0xbf, 0xda, 0x25, + 0x6b, 0x12, 0x80, 0x63, 0x1c, 0x96, 0xd2, 0x81, 0x55, 0xd7, 0x8c, 0x75, 0xe3, 0x94, 0x0e, 0x0a, + 0x82, 0x35, 0x2c, 0xfa, 0xb0, 0xda, 0xf4, 0xfd, 0x48, 0x5d, 0x48, 0x6a, 0xdd, 0x2d, 0xb1, 0x52, + 0x2c, 0xa0, 0xf4, 0x22, 0xda, 0x25, 0x81, 0x47, 0x9a, 0x66, 0x14, 0x60, 0x75, 0x11, 0xdd, 0xd0, + 0x81, 0xd8, 0xc4, 0xa5, 0xd7, 0xa9, 0x1f, 0xb2, 0x85, 0x2c, 0x9e, 0x6f, 0xb1, 0xf1, 0x73, 0x8d, + 0xbb, 0x43, 0x4b, 0x38, 0xfa, 0x24, 0x3c, 0xa6, 0x22, 0x1d, 0x61, 0xae, 0xcd, 0x90, 0x2d, 0x0e, + 0x19, 0xd2, 0x96, 0xc7, 0x96, 0xb3, 0xd1, 0x70, 0x5e, 0x7d, 0xf4, 0x1a, 0x4c, 0x08, 0x16, 0x5f, + 0x52, 0x1c, 0x36, 0x0d, 0x6c, 0x6e, 0x18, 0x50, 0x9c, 0xc0, 0x96, 0x71, 0x8c, 0x19, 0x97, 0x2d, + 0x29, 0x8c, 0xa4, 0xe3, 0x18, 0xeb, 0x70, 0x9c, 0xaa, 0x81, 0x16, 0x61, 0x92, 0xf3, 0x60, 0xae, + 0xb7, 0xcd, 0xe7, 0x44, 0xb8, 0x56, 0xa9, 0x2d, 0x75, 0xcb, 0x04, 0xe3, 0x24, 0x3e, 0x7a, 0x19, + 0xc6, 0x9c, 0xa0, 0xbe, 0xe3, 0x46, 0xa4, 0x1e, 0x75, 0x02, 0xee, 0x73, 0xa5, 0x59, 0x28, 0x2d, + 0x6a, 0x30, 0x6c, 0x60, 0xda, 0x6f, 0xc3, 0x4c, 0x46, 0x9c, 0x04, 0xba, 0x70, 0x9c, 0xb6, 0x2b, + 0xbf, 0x29, 0x61, 0xc6, 0xbc, 0x58, 0xad, 0xc8, 0xaf, 0xd1, 0xb0, 0xe8, 0xea, 0x64, 0xf1, 0x14, + 0xb4, 0xb4, 0x80, 0x6a, 0x75, 0xae, 0x4a, 0x00, 0x8e, 0x71, 0xec, 0xff, 0x56, 0x80, 0xc9, 0x0c, + 0xdd, 0x0a, 0x4b, 0x4d, 0x97, 0x78, 0xa4, 0xc4, 0x99, 0xe8, 0xcc, 0xb0, 0xd8, 0x85, 0x23, 0x84, + 0xc5, 0x2e, 0xf6, 0x0a, 0x8b, 0x3d, 0xf0, 0x4e, 0xc2, 0x62, 0x9b, 0x23, 0x36, 0xd8, 0xd7, 0x88, + 0x65, 0x84, 0xd2, 0x1e, 0x3a, 0x62, 0x28, 0x6d, 0x63, 0xd0, 0x87, 0xfb, 0x18, 0xf4, 0x1f, 0x2d, + 0xc0, 0x54, 0xd2, 0x92, 0xf2, 0x04, 0xe4, 0xb6, 0xaf, 0x1b, 0x72, 0xdb, 0xcb, 0xfd, 0xb8, 0xc2, + 0xe6, 0xca, 0x70, 0x71, 0x42, 0x86, 0xfb, 0xc1, 0xbe, 0xa8, 0x75, 0x97, 0xe7, 0xfe, 0xf5, 0x02, + 0x9c, 0xce, 0xf4, 0xc5, 0x3d, 0x81, 0xb1, 0xb9, 0x65, 0x8c, 0xcd, 0x73, 0x7d, 0xbb, 0x09, 0xe7, + 0x0e, 0xd0, 0xdd, 0xc4, 0x00, 0x5d, 0xe9, 0x9f, 0x64, 0xf7, 0x51, 0xfa, 0x5a, 0x11, 0xce, 0x67, + 0xd6, 0x8b, 0xc5, 0x9e, 0xab, 0x86, 0xd8, 0xf3, 0xf9, 0x84, 0xd8, 0xd3, 0xee, 0x5e, 0xfb, 0x78, + 0xe4, 0xa0, 0xc2, 0x5d, 0x96, 0x39, 0xfd, 0x3f, 0xa4, 0x0c, 0xd4, 0x70, 0x97, 0x55, 0x84, 0xb0, + 0x49, 0xf7, 0x5b, 0x49, 0xf6, 0xf9, 0xaf, 0x2d, 0x38, 0x9b, 0x39, 0x37, 0x27, 0x20, 0xeb, 0x5a, + 0x37, 0x65, 0x5d, 0x4f, 0xf7, 0xbd, 0x5a, 0x73, 0x84, 0x5f, 0x5f, 0x19, 0xcc, 0xf9, 0x16, 0xf6, + 0x92, 0xbf, 0x05, 0xa3, 0x4e, 0xbd, 0x4e, 0xc2, 0x70, 0xcd, 0x6f, 0xa8, 0xc8, 0xbf, 0xcf, 0xb1, + 0x77, 0x56, 0x5c, 0x7c, 0x78, 0x30, 0x3f, 0x97, 0x24, 0x11, 0x83, 0xb1, 0x4e, 0x01, 0x7d, 0x1a, + 0x46, 0x42, 0x71, 0x6f, 0x8a, 0xb9, 0x7f, 0xa1, 0xcf, 0xc1, 0x71, 0x36, 0x49, 0xd3, 0x0c, 0x4d, + 0xa4, 0x24, 0x15, 0x8a, 0xa4, 0x19, 0xc6, 0xa4, 0x70, 0xac, 0x61, 0x4c, 0x9e, 0x07, 0xd8, 0x53, + 0x8f, 0x81, 0xa4, 0xfc, 0x41, 0x7b, 0x26, 0x68, 0x58, 0xe8, 0xe3, 0x30, 0x15, 0xf2, 0xd8, 0x7d, + 0xcb, 0x4d, 0x27, 0x64, 0xce, 0x32, 0x62, 0x15, 0xb2, 0xf0, 0x47, 0xb5, 0x04, 0x0c, 0xa7, 0xb0, + 0xd1, 0xaa, 0x6c, 0x95, 0x05, 0x1a, 0xe4, 0x0b, 0xf3, 0x52, 0xdc, 0xa2, 0x48, 0x8c, 0x7b, 0x2a, + 0x39, 0xfc, 0x6c, 0xe0, 0xb5, 0x9a, 0xe8, 0xd3, 0x00, 0x74, 0xf9, 0x08, 0x39, 0xc4, 0x70, 0xfe, + 0xe1, 0x49, 0x4f, 0x95, 0x46, 0xa6, 0x6d, 0x2f, 0xf3, 0x70, 0x2d, 0x2b, 0x22, 0x58, 0x23, 0x88, + 0xb6, 0x60, 0x3c, 0xfe, 0x17, 0xe7, 0x8d, 0x3c, 0x62, 0x0b, 0x4c, 0xee, 0x5d, 0xd6, 0xe9, 0x60, + 0x93, 0xac, 0xfd, 0xa3, 0x03, 0xf0, 0x78, 0x97, 0xb3, 0x18, 0x2d, 0x9a, 0xfa, 0xde, 0x67, 0x92, + 0x8f, 0xf8, 0xb9, 0xcc, 0xca, 0xc6, 0xab, 0x3e, 0xb1, 0xe4, 0x0b, 0xef, 0x78, 0xc9, 0xff, 0x90, + 0xa5, 0x89, 0x57, 0xb8, 0x65, 0xe9, 0xc7, 0x8e, 0x78, 0xc7, 0x1c, 0xa3, 0xbc, 0x65, 0x2b, 0x43, + 0x68, 0xf1, 0x7c, 0xdf, 0xdd, 0xe9, 0x5b, 0x8a, 0x71, 0xb2, 0xd2, 0xe8, 0xdf, 0xb6, 0xe0, 0x5c, + 0xd7, 0xe0, 0x20, 0xdf, 0x84, 0x8c, 0x89, 0xfd, 0x05, 0x0b, 0x9e, 0xcc, 0xac, 0x61, 0x98, 0x33, + 0x5d, 0x81, 0x52, 0x9d, 0x16, 0x6a, 0xde, 0xa0, 0xb1, 0x9b, 0xbc, 0x04, 0xe0, 0x18, 0xc7, 0xb0, + 0x5a, 0x2a, 0xf4, 0xb4, 0x5a, 0xfa, 0x35, 0x0b, 0x52, 0x87, 0xcb, 0x09, 0xdc, 0x72, 0x15, 0xf3, + 0x96, 0x7b, 0x7f, 0x3f, 0xa3, 0x99, 0x73, 0xc1, 0xfd, 0xc9, 0x24, 0x9c, 0xc9, 0xf1, 0x86, 0xda, + 0x83, 0xe9, 0xed, 0x3a, 0x31, 0xfd, 0x6c, 0xbb, 0xc5, 0x9f, 0xe9, 0xea, 0x94, 0xcb, 0x52, 0x84, + 0x4e, 0xa7, 0x50, 0x70, 0xba, 0x09, 0xf4, 0x05, 0x0b, 0x4e, 0x39, 0xf7, 0xc2, 0x15, 0xca, 0xad, + 0xb8, 0xf5, 0xa5, 0xa6, 0x5f, 0xdf, 0xa5, 0x57, 0x81, 0xdc, 0x08, 0x2f, 0x66, 0x4a, 0x90, 0xee, + 0xd6, 0x52, 0xf8, 0x46, 0xf3, 0x2c, 0x67, 0x6a, 0x16, 0x16, 0xce, 0x6c, 0x0b, 0x61, 0x11, 0x48, + 0x9f, 0xbe, 0x85, 0xba, 0x78, 0x82, 0x67, 0xb9, 0xad, 0xf1, 0xeb, 0x57, 0x42, 0xb0, 0xa2, 0x83, + 0x3e, 0x0b, 0xa5, 0x6d, 0xe9, 0x4b, 0x9a, 0x71, 0xbd, 0xc7, 0x03, 0xd9, 0xdd, 0xc3, 0x96, 0xab, + 0x81, 0x15, 0x12, 0x8e, 0x89, 0xa2, 0xd7, 0xa0, 0xe8, 0x6d, 0x85, 0xdd, 0xd2, 0x8e, 0x26, 0xec, + 0xfd, 0x78, 0xbc, 0x85, 0xf5, 0xd5, 0x1a, 0xa6, 0x15, 0xd1, 0x75, 0x28, 0x06, 0x9b, 0x0d, 0x21, + 0xfe, 0xcc, 0xdc, 0xa4, 0x78, 0xa9, 0x9c, 0xd3, 0x2b, 0x46, 0x09, 0x2f, 0x95, 0x31, 0x25, 0x81, + 0xaa, 0x30, 0xc8, 0x5c, 0x88, 0xc4, 0x65, 0x9a, 0xf9, 0x6c, 0xe8, 0xe2, 0x8a, 0xc7, 0x83, 0x32, + 0x30, 0x04, 0xcc, 0x09, 0xa1, 0x0d, 0x18, 0xaa, 0xb3, 0x14, 0x95, 0xe2, 0xf6, 0xfc, 0x50, 0xa6, + 0xa0, 0xb3, 0x4b, 0xee, 0x4e, 0x21, 0xf7, 0x63, 0x18, 0x58, 0xd0, 0x62, 0x54, 0x49, 0x7b, 0x67, + 0x2b, 0x14, 0x29, 0x95, 0xb3, 0xa9, 0x76, 0x49, 0x49, 0x2b, 0xa8, 0x32, 0x0c, 0x2c, 0x68, 0xa1, + 0x57, 0xa0, 0xb0, 0x55, 0x17, 0xee, 0x41, 0x99, 0x12, 0x4f, 0x33, 0x64, 0xc6, 0xd2, 0xd0, 0x83, + 0x83, 0xf9, 0xc2, 0xea, 0x32, 0x2e, 0x6c, 0xd5, 0xd1, 0x3a, 0x0c, 0x6f, 0x71, 0x27, 0x7b, 0x21, + 0xd4, 0x7c, 0x2a, 0xdb, 0xff, 0x3f, 0xe5, 0x87, 0xcf, 0x3d, 0x63, 0x04, 0x00, 0x4b, 0x22, 0x2c, + 0x2e, 0xbd, 0x0a, 0x16, 0x20, 0x62, 0x95, 0x2d, 0x1c, 0x2d, 0xc0, 0x03, 0x67, 0x6e, 0xe2, 0x90, + 0x03, 0x58, 0xa3, 0x48, 0x57, 0xb5, 0x23, 0xf3, 0xda, 0x8b, 0xa0, 0x36, 0x99, 0xab, 0xba, 0x47, + 0xca, 0x7f, 0xbe, 0xaa, 0x15, 0x12, 0x8e, 0x89, 0xa2, 0x5d, 0x18, 0xdf, 0x0b, 0xdb, 0x3b, 0x44, + 0x6e, 0x69, 0x16, 0xe3, 0x26, 0xe7, 0x5e, 0xbe, 0x23, 0x10, 0xdd, 0x20, 0xea, 0x38, 0xcd, 0xd4, + 0x29, 0xc4, 0x78, 0xa8, 0x3b, 0x3a, 0x31, 0x6c, 0xd2, 0xa6, 0xc3, 0xff, 0x56, 0xc7, 0xdf, 0xdc, + 0x8f, 0x88, 0x08, 0x31, 0x96, 0x39, 0xfc, 0x6f, 0x70, 0x94, 0xf4, 0xf0, 0x0b, 0x00, 0x96, 0x44, + 0xd0, 0x1d, 0x31, 0x3c, 0xec, 0xf4, 0x9c, 0xca, 0x8f, 0x03, 0xba, 0x28, 0x91, 0x72, 0x06, 0x85, + 0x9d, 0x96, 0x31, 0x29, 0x76, 0x4a, 0xb6, 0x77, 0xfc, 0xc8, 0xf7, 0x12, 0x27, 0xf4, 0x74, 0xfe, + 0x29, 0x59, 0xcd, 0xc0, 0x4f, 0x9f, 0x92, 0x59, 0x58, 0x38, 0xb3, 0x2d, 0xd4, 0x80, 0x89, 0xb6, + 0x1f, 0x44, 0xf7, 0xfc, 0x40, 0xae, 0x2f, 0xd4, 0x45, 0x28, 0x63, 0x60, 0x8a, 0x16, 0x59, 0xf4, + 0x3e, 0x13, 0x82, 0x13, 0x34, 0xd1, 0x27, 0x60, 0x38, 0xac, 0x3b, 0x4d, 0x52, 0xb9, 0x35, 0x3b, + 0x93, 0x7f, 0xfd, 0xd4, 0x38, 0x4a, 0xce, 0xea, 0xe2, 0x41, 0xec, 0x39, 0x0a, 0x96, 0xe4, 0xd0, + 0x2a, 0x0c, 0xb2, 0xbc, 0x63, 0x2c, 0x1e, 0x5e, 0x4e, 0x38, 0xd3, 0x94, 0xf5, 0x35, 0x3f, 0x9b, + 0x58, 0x31, 0xe6, 0xd5, 0xe9, 0x1e, 0x10, 0x6f, 0x13, 0x3f, 0x9c, 0x3d, 0x9d, 0xbf, 0x07, 0xc4, + 0x93, 0xe6, 0x56, 0xad, 0xdb, 0x1e, 0x50, 0x48, 0x38, 0x26, 0x4a, 0x4f, 0x66, 0x7a, 0x9a, 0x9e, + 0xe9, 0x62, 0x36, 0x94, 0x7b, 0x96, 0xb2, 0x93, 0x99, 0x9e, 0xa4, 0x94, 0x84, 0xfd, 0xfb, 0xc3, + 0x69, 0x9e, 0x85, 0xbd, 0x66, 0xbf, 0xc7, 0x4a, 0x29, 0x3a, 0x3f, 0xdc, 0xaf, 0x70, 0xed, 0x18, + 0x59, 0xf0, 0x2f, 0x58, 0x70, 0xa6, 0x9d, 0xf9, 0x21, 0x82, 0x01, 0xe8, 0x4f, 0x46, 0xc7, 0x3f, + 0x5d, 0xc5, 0x4e, 0xcc, 0x86, 0xe3, 0x9c, 0x96, 0x92, 0xcf, 0x9c, 0xe2, 0x3b, 0x7e, 0xe6, 0xac, + 0xc1, 0x08, 0x63, 0x32, 0x7b, 0xa4, 0x6c, 0x4e, 0xbe, 0xf9, 0x18, 0x2b, 0xb1, 0x2c, 0x2a, 0x62, + 0x45, 0x02, 0xfd, 0xb0, 0x05, 0xe7, 0x92, 0x5d, 0xc7, 0x84, 0x81, 0x45, 0xc0, 0x45, 0xfe, 0x90, + 0x5e, 0x15, 0xdf, 0x9f, 0xe2, 0xff, 0x0d, 0xe4, 0xc3, 0x5e, 0x08, 0xb8, 0x7b, 0x63, 0xa8, 0x9c, + 0xf1, 0x92, 0x1f, 0x32, 0xb5, 0x17, 0x7d, 0xbc, 0xe6, 0x5f, 0x84, 0xb1, 0x96, 0xdf, 0xf1, 0x22, + 0x61, 0x65, 0x24, 0x2c, 0x1e, 0x98, 0xa6, 0x7f, 0x4d, 0x2b, 0xc7, 0x06, 0x56, 0x42, 0x06, 0x30, + 0xf2, 0xd0, 0x32, 0x80, 0x37, 0x61, 0xcc, 0xd3, 0xcc, 0x62, 0x05, 0x3f, 0x70, 0x29, 0x3f, 0x58, + 0xaa, 0x6e, 0x44, 0xcb, 0x7b, 0xa9, 0x97, 0x60, 0x83, 0xda, 0xc9, 0x3e, 0xf8, 0xbe, 0x6c, 0x65, + 0x30, 0xf5, 0x5c, 0x04, 0xf0, 0x51, 0x53, 0x04, 0x70, 0x29, 0x29, 0x02, 0x48, 0x49, 0xae, 0x8d, + 0xd7, 0x7f, 0xff, 0xb9, 0x60, 0xfa, 0x0d, 0xb8, 0x68, 0x37, 0xe1, 0x42, 0xaf, 0x6b, 0x89, 0x99, + 0x9b, 0x35, 0x94, 0x9e, 0x32, 0x36, 0x37, 0x6b, 0x54, 0xca, 0x98, 0x41, 0xfa, 0x0d, 0xe5, 0x63, + 0xff, 0x17, 0x0b, 0x8a, 0x55, 0xbf, 0x71, 0x02, 0x0f, 0xde, 0x8f, 0x19, 0x0f, 0xde, 0xc7, 0xb3, + 0x2f, 0xc4, 0x46, 0xae, 0xdc, 0x7d, 0x25, 0x21, 0x77, 0x3f, 0x97, 0x47, 0xa0, 0xbb, 0x94, 0xfd, + 0xa7, 0x8b, 0x30, 0x5a, 0xf5, 0x1b, 0xca, 0xd6, 0xfb, 0x9f, 0x3d, 0x8c, 0xad, 0x77, 0x6e, 0x46, + 0x03, 0x8d, 0x32, 0xb3, 0x52, 0x93, 0x6e, 0xae, 0xdf, 0x64, 0x26, 0xdf, 0x77, 0x89, 0xbb, 0xbd, + 0x13, 0x91, 0x46, 0xf2, 0x73, 0x4e, 0xce, 0xe4, 0xfb, 0xf7, 0x0b, 0x30, 0x99, 0x68, 0x1d, 0x35, + 0x61, 0xbc, 0xa9, 0x4b, 0x75, 0xc5, 0x3a, 0x7d, 0x28, 0x81, 0xb0, 0x30, 0x99, 0xd5, 0x8a, 0xb0, + 0x49, 0x1c, 0x2d, 0x00, 0x28, 0x35, 0xa7, 0x14, 0xeb, 0x31, 0xae, 0x5f, 0xe9, 0x41, 0x43, 0xac, + 0x61, 0xa0, 0x97, 0x60, 0x34, 0xf2, 0xdb, 0x7e, 0xd3, 0xdf, 0xde, 0xbf, 0x41, 0x64, 0xf0, 0x28, + 0x65, 0x08, 0xb7, 0x11, 0x83, 0xb0, 0x8e, 0x87, 0xee, 0xc3, 0xb4, 0x22, 0x52, 0x3b, 0x06, 0x49, + 0x37, 0x93, 0x2a, 0xac, 0x27, 0x29, 0xe2, 0x74, 0x23, 0xf6, 0xcf, 0x16, 0xf9, 0x10, 0x7b, 0x91, + 0xfb, 0xde, 0x6e, 0x78, 0x77, 0xef, 0x86, 0xaf, 0x59, 0x30, 0x45, 0x5b, 0x67, 0x56, 0x3e, 0xf2, + 0x9a, 0x57, 0xe1, 0x99, 0xad, 0x2e, 0xe1, 0x99, 0x2f, 0xd1, 0x53, 0xb3, 0xe1, 0x77, 0x22, 0x21, + 0xbb, 0xd3, 0x8e, 0x45, 0x5a, 0x8a, 0x05, 0x54, 0xe0, 0x91, 0x20, 0x10, 0x9e, 0x89, 0x3a, 0x1e, + 0x09, 0x02, 0x2c, 0xa0, 0x32, 0x7a, 0xf3, 0x40, 0x76, 0xf4, 0x66, 0x1e, 0x84, 0x53, 0xd8, 0x83, + 0x08, 0x86, 0x4b, 0x0b, 0xc2, 0x29, 0x0d, 0x45, 0x62, 0x1c, 0xfb, 0xe7, 0x8b, 0x30, 0x56, 0xf5, + 0x1b, 0xb1, 0x8a, 0xf3, 0x45, 0x43, 0xc5, 0x79, 0x21, 0xa1, 0xe2, 0x9c, 0xd2, 0x71, 0xdf, 0x53, + 0x68, 0x7e, 0xa3, 0x14, 0x9a, 0xff, 0xd8, 0x62, 0xb3, 0x56, 0x5e, 0xaf, 0x71, 0xa3, 0x31, 0x74, + 0x15, 0x46, 0xd9, 0x01, 0xc3, 0x5c, 0x61, 0xa5, 0xde, 0x8f, 0x65, 0x25, 0x5a, 0x8f, 0x8b, 0xb1, + 0x8e, 0x83, 0x2e, 0xc3, 0x48, 0x48, 0x9c, 0xa0, 0xbe, 0xa3, 0x4e, 0x57, 0xa1, 0xa4, 0xe3, 0x65, + 0x58, 0x41, 0xd1, 0x1b, 0x71, 0xfc, 0xc7, 0x62, 0xbe, 0x6b, 0x9d, 0xde, 0x1f, 0xbe, 0x45, 0xf2, + 0x83, 0x3e, 0xda, 0x77, 0x01, 0xa5, 0xf1, 0xfb, 0x08, 0x7c, 0x36, 0x6f, 0x06, 0x3e, 0x2b, 0xa5, + 0x82, 0x9e, 0xfd, 0xb9, 0x05, 0x13, 0x55, 0xbf, 0x41, 0xb7, 0xee, 0xb7, 0xd2, 0x3e, 0xd5, 0x83, + 0xdf, 0x0e, 0x75, 0x09, 0x7e, 0x7b, 0x11, 0x06, 0xab, 0x7e, 0xa3, 0x52, 0xed, 0xe6, 0xd7, 0x6e, + 0xff, 0x0d, 0x0b, 0x86, 0xab, 0x7e, 0xe3, 0x04, 0xd4, 0x02, 0x1f, 0x35, 0xd5, 0x02, 0x8f, 0xe5, + 0xac, 0x9b, 0x1c, 0x4d, 0xc0, 0x5f, 0x1b, 0x80, 0x71, 0xda, 0x4f, 0x7f, 0x5b, 0x4e, 0xa5, 0x31, + 0x6c, 0x56, 0x1f, 0xc3, 0x46, 0xb9, 0x70, 0xbf, 0xd9, 0xf4, 0xef, 0x25, 0xa7, 0x75, 0x95, 0x95, + 0x62, 0x01, 0x45, 0xcf, 0xc2, 0x48, 0x3b, 0x20, 0x7b, 0xae, 0x2f, 0xd8, 0x5b, 0x4d, 0xc9, 0x52, + 0x15, 0xe5, 0x58, 0x61, 0xd0, 0x67, 0x61, 0xe8, 0x7a, 0xf4, 0x2a, 0xaf, 0xfb, 0x5e, 0x83, 0x4b, + 0xce, 0x8b, 0x22, 0x43, 0x83, 0x56, 0x8e, 0x0d, 0x2c, 0x74, 0x17, 0x4a, 0xec, 0x3f, 0x3b, 0x76, + 0x8e, 0x9e, 0xeb, 0x53, 0xe4, 0x7e, 0x13, 0x04, 0x70, 0x4c, 0x0b, 0x3d, 0x0f, 0x10, 0xc9, 0x28, + 0xe7, 0xa1, 0x08, 0x72, 0xa5, 0x9e, 0x02, 0x2a, 0xfe, 0x79, 0x88, 0x35, 0x2c, 0xf4, 0x0c, 0x94, + 0x22, 0xc7, 0x6d, 0xde, 0x74, 0x3d, 0x12, 0x32, 0x89, 0x78, 0x51, 0xa6, 0x60, 0x13, 0x85, 0x38, + 0x86, 0x53, 0x56, 0x8c, 0x45, 0x80, 0xe0, 0x99, 0x82, 0x47, 0x18, 0x36, 0x63, 0xc5, 0x6e, 0xaa, + 0x52, 0xac, 0x61, 0xa0, 0x1d, 0x78, 0xc2, 0xf5, 0x58, 0x36, 0x03, 0x52, 0xdb, 0x75, 0xdb, 0x1b, + 0x37, 0x6b, 0x77, 0x48, 0xe0, 0x6e, 0xed, 0x2f, 0x39, 0xf5, 0x5d, 0xe2, 0xc9, 0x2c, 0x8e, 0xef, + 0x17, 0x5d, 0x7c, 0xa2, 0xd2, 0x05, 0x17, 0x77, 0xa5, 0x64, 0xbf, 0x0c, 0xa7, 0xab, 0x7e, 0xa3, + 0xea, 0x07, 0xd1, 0xaa, 0x1f, 0xdc, 0x73, 0x82, 0x86, 0x5c, 0x29, 0xf3, 0x32, 0x1a, 0x03, 0x3d, + 0x0a, 0x07, 0xf9, 0x41, 0x61, 0x44, 0x5a, 0x78, 0x81, 0x31, 0x5f, 0x47, 0xf4, 0x21, 0xaa, 0x33, + 0x36, 0x40, 0xa5, 0xf6, 0xb8, 0xe6, 0x44, 0x04, 0xdd, 0x62, 0x29, 0x8b, 0xe3, 0x1b, 0x51, 0x54, + 0x7f, 0x5a, 0x4b, 0x59, 0x1c, 0x03, 0x33, 0xaf, 0x50, 0xb3, 0xbe, 0xfd, 0x5f, 0x07, 0xd9, 0xe1, + 0x98, 0x48, 0x0f, 0x81, 0x3e, 0x03, 0x13, 0x21, 0xb9, 0xe9, 0x7a, 0x9d, 0xfb, 0x52, 0x1a, 0xd1, + 0xc5, 0x0b, 0xac, 0xb6, 0xa2, 0x63, 0x72, 0x99, 0xa6, 0x59, 0x86, 0x13, 0xd4, 0x50, 0x0b, 0x26, + 0xee, 0xb9, 0x5e, 0xc3, 0xbf, 0x17, 0x4a, 0xfa, 0x23, 0xf9, 0xa2, 0xcd, 0xbb, 0x1c, 0x33, 0xd1, + 0x47, 0xa3, 0xb9, 0xbb, 0x06, 0x31, 0x9c, 0x20, 0x4e, 0x17, 0x60, 0xd0, 0xf1, 0x16, 0xc3, 0xdb, + 0x21, 0x09, 0x44, 0xf2, 0x69, 0xb6, 0x00, 0xb1, 0x2c, 0xc4, 0x31, 0x9c, 0x2e, 0x40, 0xf6, 0xe7, + 0x5a, 0xe0, 0x77, 0x78, 0x2e, 0x02, 0xb1, 0x00, 0xb1, 0x2a, 0xc5, 0x1a, 0x06, 0xdd, 0xa0, 0xec, + 0xdf, 0xba, 0xef, 0x61, 0xdf, 0x8f, 0xe4, 0x96, 0x66, 0xe9, 0x4e, 0xb5, 0x72, 0x6c, 0x60, 0xa1, + 0x55, 0x40, 0x61, 0xa7, 0xdd, 0x6e, 0x32, 0xf3, 0x12, 0xa7, 0xc9, 0x48, 0x71, 0x95, 0x7b, 0x91, + 0x87, 0x68, 0xad, 0xa5, 0xa0, 0x38, 0xa3, 0x06, 0x3d, 0xab, 0xb7, 0x44, 0x57, 0x07, 0x59, 0x57, + 0xb9, 0x1a, 0xa4, 0xc6, 0xfb, 0x29, 0x61, 0x68, 0x05, 0x86, 0xc3, 0xfd, 0xb0, 0x1e, 0x89, 0x58, + 0x73, 0x39, 0x19, 0x80, 0x6a, 0x0c, 0x45, 0x4b, 0x40, 0xc7, 0xab, 0x60, 0x59, 0x17, 0xd5, 0x61, + 0x46, 0x50, 0x5c, 0xde, 0x71, 0x3c, 0x95, 0x4f, 0x85, 0x5b, 0xd9, 0x5e, 0x7d, 0x70, 0x30, 0x3f, + 0x23, 0x5a, 0xd6, 0xc1, 0x87, 0x07, 0xf3, 0x67, 0xaa, 0x7e, 0x23, 0x03, 0x82, 0xb3, 0xa8, 0xf1, + 0xc5, 0x57, 0xaf, 0xfb, 0xad, 0x76, 0x35, 0xf0, 0xb7, 0xdc, 0x26, 0xe9, 0xa6, 0x4a, 0xaa, 0x19, + 0x98, 0x62, 0xf1, 0x19, 0x65, 0x38, 0x41, 0xcd, 0xfe, 0x4e, 0xc6, 0xcf, 0xb0, 0x7c, 0xcb, 0x51, + 0x27, 0x20, 0xa8, 0x05, 0xe3, 0x6d, 0xb6, 0x4d, 0x44, 0x86, 0x00, 0xb1, 0xd6, 0x5f, 0xec, 0x53, + 0x24, 0x72, 0x8f, 0x5e, 0x03, 0xa6, 0x99, 0x4a, 0x55, 0x27, 0x87, 0x4d, 0xea, 0xf6, 0x4f, 0x3c, + 0xc6, 0x6e, 0xc4, 0x1a, 0x97, 0x73, 0x0c, 0x0b, 0xa3, 0x7e, 0xf1, 0xb4, 0x9a, 0xcb, 0x17, 0xb8, + 0xc5, 0xd3, 0x22, 0x1c, 0x03, 0xb0, 0xac, 0x8b, 0x3e, 0x0d, 0x13, 0xf4, 0xa5, 0xa2, 0x6e, 0xa5, + 0x70, 0xf6, 0x54, 0x7e, 0xf0, 0x05, 0x85, 0xa5, 0x67, 0x0f, 0xd1, 0x2b, 0xe3, 0x04, 0x31, 0xf4, + 0x06, 0x33, 0x0b, 0x91, 0xa4, 0x0b, 0xfd, 0x90, 0xd6, 0x2d, 0x40, 0x24, 0x59, 0x8d, 0x08, 0xea, + 0xc0, 0x4c, 0x3a, 0xd7, 0x58, 0x38, 0x6b, 0xe7, 0xb3, 0x7c, 0xe9, 0x74, 0x61, 0x71, 0x9a, 0x87, + 0x34, 0x2c, 0xc4, 0x59, 0xf4, 0xd1, 0x4d, 0x18, 0x17, 0x49, 0x87, 0xc5, 0xca, 0x2d, 0x1a, 0x72, + 0xc0, 0x71, 0xac, 0x03, 0x0f, 0x93, 0x05, 0xd8, 0xac, 0x8c, 0xb6, 0xe1, 0x9c, 0x96, 0x04, 0xe8, + 0x5a, 0xe0, 0x30, 0x65, 0xbe, 0xcb, 0x8e, 0x53, 0xed, 0xae, 0x7e, 0xf2, 0xc1, 0xc1, 0xfc, 0xb9, + 0x8d, 0x6e, 0x88, 0xb8, 0x3b, 0x1d, 0x74, 0x0b, 0x4e, 0x73, 0xd7, 0xe1, 0x32, 0x71, 0x1a, 0x4d, + 0xd7, 0x53, 0xcc, 0x00, 0xdf, 0xf2, 0x67, 0x1f, 0x1c, 0xcc, 0x9f, 0x5e, 0xcc, 0x42, 0xc0, 0xd9, + 0xf5, 0xd0, 0x47, 0xa1, 0xd4, 0xf0, 0x42, 0x31, 0x06, 0x43, 0x46, 0x9e, 0xa5, 0x52, 0x79, 0xbd, + 0xa6, 0xbe, 0x3f, 0xfe, 0x83, 0xe3, 0x0a, 0x68, 0x9b, 0xcb, 0x8a, 0x95, 0x04, 0x63, 0x38, 0x15, + 0x3a, 0x29, 0x29, 0xe4, 0x33, 0x9c, 0x07, 0xb9, 0x92, 0x44, 0xd9, 0xd4, 0x1b, 0x7e, 0x85, 0x06, + 0x61, 0xf4, 0x3a, 0x20, 0xfa, 0x82, 0x70, 0xeb, 0x64, 0xb1, 0xce, 0xd2, 0x4f, 0x30, 0xd1, 0xfa, + 0x88, 0xe9, 0xce, 0x56, 0x4b, 0x61, 0xe0, 0x8c, 0x5a, 0xe8, 0x3a, 0x3d, 0x55, 0xf4, 0x52, 0x71, + 0x6a, 0xa9, 0xac, 0x78, 0x65, 0xd2, 0x0e, 0x48, 0xdd, 0x89, 0x48, 0xc3, 0xa4, 0x88, 0x13, 0xf5, + 0x50, 0x03, 0x9e, 0x70, 0x3a, 0x91, 0xcf, 0xc4, 0xf0, 0x26, 0xea, 0x86, 0xbf, 0x4b, 0x3c, 0xa6, + 0x01, 0x1b, 0x59, 0xba, 0x40, 0xb9, 0x8d, 0xc5, 0x2e, 0x78, 0xb8, 0x2b, 0x15, 0xca, 0x25, 0xaa, + 0x34, 0xb8, 0x60, 0x06, 0x84, 0xca, 0x48, 0x85, 0xfb, 0x12, 0x8c, 0xee, 0xf8, 0x61, 0xb4, 0x4e, + 0xa2, 0x7b, 0x7e, 0xb0, 0x2b, 0xe2, 0x7a, 0xc6, 0xb1, 0xa0, 0x63, 0x10, 0xd6, 0xf1, 0xe8, 0x33, + 0x90, 0xd9, 0x67, 0x54, 0xca, 0x4c, 0x35, 0x3e, 0x12, 0x9f, 0x31, 0xd7, 0x79, 0x31, 0x96, 0x70, + 0x89, 0x5a, 0xa9, 0x2e, 0x33, 0x35, 0x77, 0x02, 0xb5, 0x52, 0x5d, 0xc6, 0x12, 0x4e, 0x97, 0x6b, + 0xb8, 0xe3, 0x04, 0xa4, 0x1a, 0xf8, 0x75, 0x12, 0x6a, 0x11, 0xc8, 0x1f, 0xe7, 0x51, 0x4b, 0xe9, + 0x72, 0xad, 0x65, 0x21, 0xe0, 0xec, 0x7a, 0x88, 0xa4, 0x13, 0x60, 0x4d, 0xe4, 0xeb, 0x27, 0xd2, + 0xfc, 0x4c, 0x9f, 0x39, 0xb0, 0x3c, 0x98, 0x52, 0xa9, 0xb7, 0x78, 0x9c, 0xd2, 0x70, 0x76, 0x92, + 0xad, 0xed, 0xfe, 0x83, 0x9c, 0x2a, 0x8d, 0x4f, 0x25, 0x41, 0x09, 0xa7, 0x68, 0x1b, 0x31, 0xbf, + 0xa6, 0x7a, 0xc6, 0xfc, 0xba, 0x02, 0xa5, 0xb0, 0xb3, 0xd9, 0xf0, 0x5b, 0x8e, 0xeb, 0x31, 0x35, + 0xb7, 0xf6, 0x1e, 0xa9, 0x49, 0x00, 0x8e, 0x71, 0xd0, 0x2a, 0x8c, 0x38, 0x52, 0x9d, 0x83, 0xf2, + 0xa3, 0xbc, 0x28, 0x25, 0x0e, 0x0f, 0x7c, 0x20, 0x15, 0x38, 0xaa, 0x2e, 0x7a, 0x15, 0xc6, 0x85, + 0xeb, 0xab, 0xc8, 0xfa, 0x38, 0x63, 0xfa, 0x27, 0xd5, 0x74, 0x20, 0x36, 0x71, 0xd1, 0x6d, 0x18, + 0x8d, 0xfc, 0x26, 0x73, 0xb2, 0xa1, 0x6c, 0xde, 0x99, 0xfc, 0x78, 0x65, 0x1b, 0x0a, 0x4d, 0x97, + 0xa4, 0xaa, 0xaa, 0x58, 0xa7, 0x83, 0x36, 0xf8, 0x7a, 0x67, 0x91, 0xb8, 0x49, 0x38, 0xfb, 0x58, + 0xfe, 0x9d, 0xa4, 0x02, 0x76, 0x9b, 0xdb, 0x41, 0xd4, 0xc4, 0x3a, 0x19, 0x74, 0x0d, 0xa6, 0xdb, + 0x81, 0xeb, 0xb3, 0x35, 0xa1, 0x34, 0x79, 0xb3, 0x66, 0x1a, 0xa0, 0x6a, 0x12, 0x01, 0xa7, 0xeb, + 0x30, 0xcf, 0x65, 0x51, 0x38, 0x7b, 0x96, 0x27, 0x86, 0xe6, 0xcf, 0x3b, 0x5e, 0x86, 0x15, 0x14, + 0xad, 0xb1, 0x93, 0x98, 0x4b, 0x26, 0x66, 0xe7, 0xf2, 0x03, 0xcb, 0xe8, 0x12, 0x0c, 0xce, 0xbc, + 0xaa, 0xbf, 0x38, 0xa6, 0x80, 0x1a, 0x5a, 0x06, 0x41, 0xfa, 0x62, 0x08, 0x67, 0x9f, 0xe8, 0x62, + 0x24, 0x97, 0x78, 0x5e, 0xc4, 0x0c, 0x81, 0x51, 0x1c, 0xe2, 0x04, 0x4d, 0xf4, 0x71, 0x98, 0x12, + 0xe1, 0xf0, 0xe2, 0x61, 0x3a, 0x17, 0x9b, 0x2e, 0xe3, 0x04, 0x0c, 0xa7, 0xb0, 0x79, 0x86, 0x02, + 0x67, 0xb3, 0x49, 0xc4, 0xd1, 0x77, 0xd3, 0xf5, 0x76, 0xc3, 0xd9, 0xf3, 0xec, 0x7c, 0x10, 0x19, + 0x0a, 0x92, 0x50, 0x9c, 0x51, 0x03, 0x6d, 0xc0, 0x54, 0x3b, 0x20, 0xa4, 0xc5, 0x18, 0x7d, 0x71, + 0x9f, 0xcd, 0x73, 0xc7, 0x7d, 0xda, 0x93, 0x6a, 0x02, 0x76, 0x98, 0x51, 0x86, 0x53, 0x14, 0xd0, + 0x3d, 0x18, 0xf1, 0xf7, 0x48, 0xb0, 0x43, 0x9c, 0xc6, 0xec, 0x85, 0x2e, 0xa6, 0xf4, 0xe2, 0x72, + 0xbb, 0x25, 0x70, 0x13, 0xda, 0x7f, 0x59, 0xdc, 0x5b, 0xfb, 0x2f, 0x1b, 0x43, 0x3f, 0x62, 0xc1, + 0x59, 0xa9, 0x30, 0xa8, 0xb5, 0xe9, 0xa8, 0x2f, 0xfb, 0x5e, 0x18, 0x05, 0xdc, 0xd5, 0xfc, 0xc9, + 0x7c, 0xf7, 0xeb, 0x8d, 0x9c, 0x4a, 0x4a, 0x38, 0x7a, 0x36, 0x0f, 0x23, 0xc4, 0xf9, 0x2d, 0xa2, + 0x65, 0x98, 0x0e, 0x49, 0x24, 0x0f, 0xa3, 0xc5, 0x70, 0xf5, 0x8d, 0xf2, 0xfa, 0xec, 0x45, 0xee, + 0x27, 0x4f, 0x37, 0x43, 0x2d, 0x09, 0xc4, 0x69, 0xfc, 0xb9, 0x6f, 0x87, 0xe9, 0xd4, 0xf5, 0x7f, + 0x94, 0xcc, 0x2b, 0x73, 0xbb, 0x30, 0x6e, 0x0c, 0xf1, 0x23, 0xd5, 0x1e, 0xff, 0xcb, 0x61, 0x28, + 0x29, 0xcd, 0x22, 0xba, 0x62, 0x2a, 0x8c, 0xcf, 0x26, 0x15, 0xc6, 0x23, 0xf4, 0x5d, 0xaf, 0xeb, + 0x88, 0x37, 0x32, 0xa2, 0x83, 0xe5, 0x6d, 0xe8, 0xfe, 0xdd, 0xbe, 0x35, 0x71, 0x6d, 0xb1, 0x6f, + 0xcd, 0xf3, 0x40, 0x57, 0x09, 0xf0, 0x35, 0x98, 0xf6, 0x7c, 0xc6, 0x73, 0x92, 0x86, 0x64, 0x28, + 0x18, 0xdf, 0x50, 0xd2, 0xc3, 0x6d, 0x24, 0x10, 0x70, 0xba, 0x0e, 0x6d, 0x90, 0x5f, 0xfc, 0x49, + 0x91, 0x33, 0xe7, 0x0b, 0xb0, 0x80, 0xa2, 0x8b, 0x30, 0xd8, 0xf6, 0x1b, 0x95, 0xaa, 0xe0, 0x37, + 0xb5, 0x98, 0x94, 0x8d, 0x4a, 0x15, 0x73, 0x18, 0x5a, 0x84, 0x21, 0xf6, 0x23, 0x9c, 0x1d, 0xcb, + 0x8f, 0xab, 0xc0, 0x6a, 0x68, 0x79, 0x6d, 0x58, 0x05, 0x2c, 0x2a, 0x32, 0xd1, 0x17, 0x65, 0xd2, + 0x99, 0xe8, 0x6b, 0xf8, 0x21, 0x45, 0x5f, 0x92, 0x00, 0x8e, 0x69, 0xa1, 0xfb, 0x70, 0xda, 0x78, + 0x18, 0xf1, 0x25, 0x42, 0x42, 0xe1, 0xdb, 0x7d, 0xb1, 0xeb, 0x8b, 0x48, 0x68, 0xaa, 0xcf, 0x89, + 0x4e, 0x9f, 0xae, 0x64, 0x51, 0xc2, 0xd9, 0x0d, 0xa0, 0x26, 0x4c, 0xd7, 0x53, 0xad, 0x8e, 0xf4, + 0xdf, 0xaa, 0x9a, 0xd0, 0x74, 0x8b, 0x69, 0xc2, 0xe8, 0x55, 0x18, 0x79, 0xcb, 0x0f, 0xd9, 0x59, + 0x2d, 0x78, 0x64, 0xe9, 0x18, 0x3c, 0xf2, 0xc6, 0xad, 0x1a, 0x2b, 0x3f, 0x3c, 0x98, 0x1f, 0xad, + 0xfa, 0x0d, 0xf9, 0x17, 0xab, 0x0a, 0xe8, 0xfb, 0x2d, 0x98, 0x4b, 0xbf, 0xbc, 0x54, 0xa7, 0xc7, + 0xfb, 0xef, 0xb4, 0x2d, 0x1a, 0x9d, 0x5b, 0xc9, 0x25, 0x87, 0xbb, 0x34, 0x65, 0xff, 0x8a, 0xc5, + 0xa4, 0x6e, 0x42, 0x03, 0x44, 0xc2, 0x4e, 0xf3, 0x24, 0xd2, 0x79, 0xae, 0x18, 0xca, 0xa9, 0x87, + 0xb6, 0x5c, 0xf8, 0xa7, 0x16, 0xb3, 0x5c, 0x38, 0x41, 0x17, 0x85, 0x37, 0x60, 0x24, 0x92, 0x69, + 0x56, 0xbb, 0x64, 0x20, 0xd5, 0x3a, 0xc5, 0xac, 0x37, 0x14, 0xc7, 0xaa, 0x32, 0xaa, 0x2a, 0x32, + 0xf6, 0x3f, 0xe0, 0x33, 0x20, 0x21, 0x27, 0xa0, 0x03, 0x28, 0x9b, 0x3a, 0x80, 0xf9, 0x1e, 0x5f, + 0x90, 0xa3, 0x0b, 0xf8, 0xfb, 0x66, 0xbf, 0x99, 0xa4, 0xe6, 0xdd, 0x6e, 0x32, 0x63, 0x7f, 0xd1, + 0x02, 0x88, 0x43, 0xfe, 0x32, 0xf9, 0xb2, 0x1f, 0xc8, 0x5c, 0x8e, 0x59, 0x59, 0x8b, 0x5e, 0xa6, + 0x3c, 0xaa, 0x1f, 0xf9, 0x75, 0xbf, 0x29, 0x34, 0x5c, 0x4f, 0xc4, 0x6a, 0x08, 0x5e, 0x7e, 0xa8, + 0xfd, 0xc6, 0x0a, 0x1b, 0xcd, 0xcb, 0x00, 0x63, 0xc5, 0x58, 0x31, 0x66, 0x04, 0x17, 0xfb, 0x31, + 0x0b, 0x4e, 0x65, 0xd9, 0xbb, 0xd2, 0x17, 0x0f, 0x97, 0x59, 0x29, 0x73, 0x26, 0x35, 0x9b, 0x77, + 0x44, 0x39, 0x56, 0x18, 0x7d, 0x67, 0x28, 0x3b, 0x5a, 0xac, 0xdd, 0x5b, 0x30, 0x5e, 0x0d, 0x88, + 0x76, 0xb9, 0xbe, 0xc6, 0x9d, 0xd6, 0x79, 0x7f, 0x9e, 0x3d, 0xb2, 0xc3, 0xba, 0xfd, 0x95, 0x02, + 0x9c, 0xe2, 0x56, 0x01, 0x8b, 0x7b, 0xbe, 0xdb, 0xa8, 0xfa, 0x0d, 0x91, 0x5d, 0xee, 0x53, 0x30, + 0xd6, 0xd6, 0x04, 0x8d, 0xdd, 0xe2, 0x46, 0xea, 0x02, 0xc9, 0x58, 0x34, 0xa2, 0x97, 0x62, 0x83, + 0x16, 0x6a, 0xc0, 0x18, 0xd9, 0x73, 0xeb, 0x4a, 0xb5, 0x5c, 0x38, 0xf2, 0x45, 0xa7, 0x5a, 0x59, + 0xd1, 0xe8, 0x60, 0x83, 0xea, 0x23, 0xc8, 0x1b, 0x6c, 0xff, 0xb8, 0x05, 0x8f, 0xe5, 0x44, 0x99, + 0xa4, 0xcd, 0xdd, 0x63, 0xf6, 0x17, 0x62, 0xd9, 0xaa, 0xe6, 0xb8, 0x55, 0x06, 0x16, 0x50, 0xf4, + 0x09, 0x00, 0x6e, 0x55, 0x41, 0x9f, 0xdc, 0xbd, 0xc2, 0xf1, 0x19, 0x91, 0xc4, 0xb4, 0xa0, 0x50, + 0xb2, 0x3e, 0xd6, 0x68, 0xd9, 0x5f, 0x1a, 0x80, 0x41, 0x9e, 0xe3, 0x7c, 0x15, 0x86, 0x77, 0x78, + 0xce, 0x8d, 0x7e, 0xd2, 0x7b, 0xc4, 0xc2, 0x10, 0x5e, 0x80, 0x65, 0x65, 0xb4, 0x06, 0x33, 0x3c, + 0x67, 0x49, 0xb3, 0x4c, 0x9a, 0xce, 0xbe, 0x94, 0xdc, 0xf1, 0x7c, 0x9f, 0x4a, 0x82, 0x59, 0x49, + 0xa3, 0xe0, 0xac, 0x7a, 0xe8, 0x35, 0x98, 0xa0, 0x2f, 0x29, 0xbf, 0x13, 0x49, 0x4a, 0x3c, 0x5b, + 0x89, 0x7a, 0xba, 0x6d, 0x18, 0x50, 0x9c, 0xc0, 0xa6, 0x8f, 0xf9, 0x76, 0x4a, 0x46, 0x39, 0x18, + 0x3f, 0xe6, 0x4d, 0xb9, 0xa4, 0x89, 0xcb, 0x0c, 0x5d, 0x3b, 0xcc, 0xac, 0x77, 0x63, 0x27, 0x20, + 0xe1, 0x8e, 0xdf, 0x6c, 0x30, 0xa6, 0x6f, 0x50, 0x33, 0x74, 0x4d, 0xc0, 0x71, 0xaa, 0x06, 0xa5, + 0xb2, 0xe5, 0xb8, 0xcd, 0x4e, 0x40, 0x62, 0x2a, 0x43, 0x26, 0x95, 0xd5, 0x04, 0x1c, 0xa7, 0x6a, + 0xf4, 0x16, 0xbe, 0x0e, 0x1f, 0x8f, 0xf0, 0x95, 0x2e, 0xd8, 0xd3, 0xd5, 0xc0, 0xa7, 0x27, 0xb6, + 0x8c, 0xd1, 0xa3, 0xcc, 0xa4, 0x87, 0xa5, 0x3b, 0x71, 0x97, 0x68, 0x76, 0xc2, 0x90, 0x94, 0x53, + 0x30, 0x2c, 0x15, 0x6a, 0xc2, 0x91, 0x58, 0x52, 0x41, 0x57, 0x61, 0x54, 0xa4, 0xbc, 0x60, 0xd6, + 0xbc, 0x7c, 0x8d, 0x30, 0xcb, 0x8a, 0x72, 0x5c, 0x8c, 0x75, 0x1c, 0xfb, 0x07, 0x0a, 0x30, 0x93, + 0xe1, 0x8e, 0xc1, 0xcf, 0xc4, 0x6d, 0x37, 0x8c, 0x54, 0xf2, 0x44, 0xed, 0x4c, 0xe4, 0xe5, 0x58, + 0x61, 0xd0, 0x8d, 0xc7, 0x4f, 0xdd, 0xe4, 0x49, 0x2b, 0xcc, 0x9d, 0x05, 0xf4, 0x88, 0x69, 0x08, + 0x2f, 0xc0, 0x40, 0x27, 0x24, 0x32, 0x0e, 0xa5, 0xba, 0x83, 0x98, 0xc2, 0x8d, 0x41, 0xe8, 0x9b, + 0x60, 0x5b, 0xe9, 0xae, 0xb4, 0x37, 0x01, 0xd7, 0x5e, 0x71, 0x18, 0xed, 0x5c, 0x44, 0x3c, 0xc7, + 0x8b, 0xc4, 0xcb, 0x21, 0x0e, 0xa8, 0xc6, 0x4a, 0xb1, 0x80, 0xda, 0x5f, 0x2a, 0xc2, 0xd9, 0x5c, + 0x07, 0x2d, 0xda, 0xf5, 0x96, 0xef, 0xb9, 0x91, 0xaf, 0x4c, 0x56, 0x78, 0x10, 0x35, 0xd2, 0xde, + 0x59, 0x13, 0xe5, 0x58, 0x61, 0xa0, 0x4b, 0x30, 0xc8, 0xc4, 0x75, 0xa9, 0x34, 0x92, 0x4b, 0x65, + 0x1e, 0x55, 0x87, 0x83, 0xfb, 0x4e, 0xd1, 0x7b, 0x91, 0x5e, 0xc7, 0x7e, 0x33, 0x79, 0x3a, 0xd2, + 0xee, 0xfa, 0x7e, 0x13, 0x33, 0x20, 0xfa, 0x80, 0x18, 0xaf, 0x84, 0x8d, 0x06, 0x76, 0x1a, 0x7e, + 0xa8, 0x0d, 0xda, 0xd3, 0x30, 0xbc, 0x4b, 0xf6, 0x03, 0xd7, 0xdb, 0x4e, 0xda, 0xee, 0xdc, 0xe0, + 0xc5, 0x58, 0xc2, 0xcd, 0x8c, 0x60, 0xc3, 0xc7, 0x9d, 0x5b, 0x77, 0xa4, 0xe7, 0x5d, 0xfb, 0x43, + 0x45, 0x98, 0xc4, 0x4b, 0xe5, 0xf7, 0x26, 0xe2, 0x76, 0x7a, 0x22, 0x8e, 0x3b, 0xb7, 0x6e, 0xef, + 0xd9, 0xf8, 0x45, 0x0b, 0x26, 0x59, 0xe2, 0x0d, 0x11, 0x7e, 0xcb, 0xf5, 0xbd, 0x13, 0xe0, 0x6b, + 0x2f, 0xc2, 0x60, 0x40, 0x1b, 0x4d, 0xe6, 0x8f, 0x64, 0x3d, 0xc1, 0x1c, 0x86, 0x9e, 0x80, 0x01, + 0xd6, 0x05, 0x3a, 0x79, 0x63, 0x3c, 0xf5, 0x56, 0xd9, 0x89, 0x1c, 0xcc, 0x4a, 0x59, 0x4c, 0x19, + 0x4c, 0xda, 0x4d, 0x97, 0x77, 0x3a, 0x56, 0xa6, 0xbe, 0x3b, 0x5c, 0xb7, 0x33, 0xbb, 0xf6, 0xce, + 0x62, 0xca, 0x64, 0x93, 0xec, 0xfe, 0x66, 0xfc, 0xe3, 0x02, 0x9c, 0xcf, 0xac, 0xd7, 0x77, 0x4c, + 0x99, 0xee, 0xb5, 0x1f, 0x65, 0x6a, 0x85, 0xe2, 0x09, 0x5a, 0x46, 0x0e, 0xf4, 0xcb, 0xca, 0x0e, + 0xf6, 0x11, 0xea, 0x25, 0x73, 0xc8, 0xde, 0x25, 0xa1, 0x5e, 0x32, 0xfb, 0x96, 0xf3, 0xe6, 0xfd, + 0x8b, 0x42, 0xce, 0xb7, 0xb0, 0xd7, 0xef, 0x65, 0x7a, 0xce, 0x30, 0x60, 0x28, 0x5f, 0x94, 0xfc, + 0x8c, 0xe1, 0x65, 0x58, 0x41, 0xd1, 0x22, 0x4c, 0xb6, 0x5c, 0x8f, 0x1e, 0x3e, 0xfb, 0x26, 0x87, + 0xa9, 0x22, 0x71, 0xad, 0x99, 0x60, 0x9c, 0xc4, 0x47, 0xae, 0x16, 0x06, 0xa6, 0x90, 0x9f, 0x91, + 0x3d, 0xb7, 0xb7, 0x0b, 0xa6, 0xa2, 0x59, 0x8d, 0x62, 0x46, 0x48, 0x98, 0x35, 0x4d, 0xe8, 0x51, + 0xec, 0x5f, 0xe8, 0x31, 0x96, 0x2d, 0xf0, 0x98, 0x7b, 0x15, 0xc6, 0x1f, 0x5a, 0xca, 0x6d, 0x7f, + 0xad, 0x08, 0x8f, 0x77, 0xd9, 0xf6, 0xfc, 0xac, 0x37, 0xe6, 0x40, 0x3b, 0xeb, 0x53, 0xf3, 0x50, + 0x85, 0x53, 0x5b, 0x9d, 0x66, 0x73, 0x9f, 0x39, 0x0c, 0x90, 0x86, 0xc4, 0x10, 0x3c, 0xa5, 0x7c, + 0xe9, 0x9f, 0x5a, 0xcd, 0xc0, 0xc1, 0x99, 0x35, 0xe9, 0xcb, 0x81, 0xde, 0x24, 0xfb, 0x8a, 0x54, + 0xe2, 0xe5, 0x80, 0x75, 0x20, 0x36, 0x71, 0xd1, 0x35, 0x98, 0x76, 0xf6, 0x1c, 0x97, 0xc7, 0xd2, + 0x95, 0x04, 0xf8, 0xd3, 0x41, 0x09, 0x27, 0x17, 0x93, 0x08, 0x38, 0x5d, 0x07, 0xbd, 0x0e, 0xc8, + 0xdf, 0x64, 0x66, 0xc5, 0x8d, 0x6b, 0xc4, 0x13, 0xfa, 0x40, 0x36, 0x77, 0xc5, 0xf8, 0x48, 0xb8, + 0x95, 0xc2, 0xc0, 0x19, 0xb5, 0x12, 0xe1, 0x4e, 0x86, 0xf2, 0xc3, 0x9d, 0x74, 0x3f, 0x17, 0x7b, + 0x66, 0xf5, 0xf8, 0x8f, 0x16, 0xbd, 0xbe, 0x38, 0x93, 0x6f, 0x46, 0x07, 0x7c, 0x95, 0xd9, 0xf3, + 0x71, 0xc1, 0xa5, 0x16, 0xa4, 0xe3, 0xb4, 0x66, 0xcf, 0x17, 0x03, 0xb1, 0x89, 0xcb, 0x17, 0x44, + 0x18, 0xfb, 0x86, 0x1a, 0x2c, 0xbe, 0x08, 0x61, 0xa4, 0x30, 0xd0, 0x27, 0x61, 0xb8, 0xe1, 0xee, + 0xb9, 0xa1, 0x10, 0xdb, 0x1c, 0x59, 0x47, 0x12, 0x9f, 0x83, 0x65, 0x4e, 0x06, 0x4b, 0x7a, 0xf6, + 0x0f, 0x15, 0x60, 0x5c, 0xb6, 0xf8, 0x46, 0xc7, 0x8f, 0x9c, 0x13, 0xb8, 0x96, 0xaf, 0x19, 0xd7, + 0xf2, 0x07, 0xba, 0xc5, 0x71, 0x62, 0x5d, 0xca, 0xbd, 0x8e, 0x6f, 0x25, 0xae, 0xe3, 0xa7, 0x7a, + 0x93, 0xea, 0x7e, 0x0d, 0xff, 0x43, 0x0b, 0xa6, 0x0d, 0xfc, 0x13, 0xb8, 0x0d, 0x56, 0xcd, 0xdb, + 0xe0, 0xc9, 0x9e, 0xdf, 0x90, 0x73, 0x0b, 0x7c, 0x6f, 0x31, 0xd1, 0x77, 0x76, 0xfa, 0xbf, 0x05, + 0x03, 0x3b, 0x4e, 0xd0, 0xe8, 0x16, 0xb7, 0x3e, 0x55, 0x69, 0xe1, 0xba, 0x13, 0x08, 0x85, 0xe8, + 0xb3, 0x2a, 0x21, 0xba, 0x13, 0xf4, 0x56, 0x86, 0xb2, 0xa6, 0xd0, 0xcb, 0x30, 0x14, 0xd6, 0xfd, + 0xb6, 0x72, 0x17, 0xb8, 0xc0, 0x93, 0xa5, 0xd3, 0x92, 0xc3, 0x83, 0x79, 0x64, 0x36, 0x47, 0x8b, + 0xb1, 0xc0, 0x47, 0x9f, 0x82, 0x71, 0xf6, 0x4b, 0x59, 0x27, 0x15, 0xf3, 0x73, 0x5c, 0xd5, 0x74, + 0x44, 0x6e, 0xba, 0x67, 0x14, 0x61, 0x93, 0xd4, 0xdc, 0x36, 0x94, 0xd4, 0x67, 0x3d, 0x52, 0x25, + 0xe4, 0xbf, 0x2b, 0xc2, 0x4c, 0xc6, 0x9a, 0x43, 0xa1, 0x31, 0x13, 0x57, 0xfb, 0x5c, 0xaa, 0xef, + 0x70, 0x2e, 0x42, 0xf6, 0x1a, 0x6a, 0x88, 0xb5, 0xd5, 0x77, 0xa3, 0xb7, 0x43, 0x92, 0x6c, 0x94, + 0x16, 0xf5, 0x6e, 0x94, 0x36, 0x76, 0x62, 0x43, 0x4d, 0x1b, 0x52, 0x3d, 0x7d, 0xa4, 0x73, 0xfa, + 0x67, 0x45, 0x38, 0x95, 0x15, 0x5a, 0x0e, 0x7d, 0x3e, 0x91, 0x35, 0xf1, 0xc5, 0x7e, 0x83, 0xd2, + 0xf1, 0x54, 0x8a, 0x5c, 0xd8, 0xbc, 0xb4, 0x60, 0xe6, 0x51, 0xec, 0x39, 0xcc, 0xa2, 0x4d, 0x16, + 0xf7, 0x20, 0xe0, 0xd9, 0x2e, 0xe5, 0xf1, 0xf1, 0xe1, 0xbe, 0x3b, 0x20, 0xd2, 0x64, 0x86, 0x09, + 0xcb, 0x07, 0x59, 0xdc, 0xdb, 0xf2, 0x41, 0xb6, 0x3c, 0xe7, 0xc2, 0xa8, 0xf6, 0x35, 0x8f, 0x74, + 0xc6, 0x77, 0xe9, 0x6d, 0xa5, 0xf5, 0xfb, 0x91, 0xce, 0xfa, 0x8f, 0x5b, 0x90, 0x30, 0x86, 0x57, + 0x62, 0x31, 0x2b, 0x57, 0x2c, 0x76, 0x01, 0x06, 0x02, 0xbf, 0x49, 0x92, 0xe9, 0x05, 0xb1, 0xdf, + 0x24, 0x98, 0x41, 0x28, 0x46, 0x14, 0x0b, 0x3b, 0xc6, 0xf4, 0x87, 0x9c, 0x78, 0xa2, 0x5d, 0x84, + 0xc1, 0x26, 0xd9, 0x23, 0xcd, 0x64, 0x16, 0x98, 0x9b, 0xb4, 0x10, 0x73, 0x98, 0xfd, 0x8b, 0x03, + 0x70, 0xae, 0x6b, 0xe4, 0x10, 0xfa, 0x1c, 0xda, 0x76, 0x22, 0x72, 0xcf, 0xd9, 0x4f, 0xa6, 0x6b, + 0xb8, 0xc6, 0x8b, 0xb1, 0x84, 0x33, 0x77, 0x25, 0x1e, 0x75, 0x39, 0x21, 0x44, 0x14, 0xc1, 0x96, + 0x05, 0xd4, 0x14, 0x4a, 0x15, 0x8f, 0x43, 0x28, 0xf5, 0x3c, 0x40, 0x18, 0x36, 0xb9, 0xc9, 0x50, + 0x43, 0xf8, 0x41, 0xc5, 0xd1, 0xb9, 0x6b, 0x37, 0x05, 0x04, 0x6b, 0x58, 0xa8, 0x0c, 0x53, 0xed, + 0xc0, 0x8f, 0xb8, 0x4c, 0xb6, 0xcc, 0xad, 0xea, 0x06, 0xcd, 0xa0, 0x0d, 0xd5, 0x04, 0x1c, 0xa7, + 0x6a, 0xa0, 0x97, 0x60, 0x54, 0x04, 0x72, 0xa8, 0xfa, 0x7e, 0x53, 0x88, 0x81, 0x94, 0xa1, 0x59, + 0x2d, 0x06, 0x61, 0x1d, 0x4f, 0xab, 0xc6, 0x04, 0xbd, 0xc3, 0x99, 0xd5, 0xb8, 0xb0, 0x57, 0xc3, + 0x4b, 0x84, 0x99, 0x1c, 0xe9, 0x2b, 0xcc, 0x64, 0x2c, 0x18, 0x2b, 0xf5, 0xad, 0x44, 0x83, 0x9e, + 0xa2, 0xa4, 0x9f, 0x1b, 0x80, 0x19, 0xb1, 0x70, 0x1e, 0xf5, 0x72, 0xb9, 0x9d, 0x5e, 0x2e, 0xc7, + 0x21, 0x3a, 0x7b, 0x6f, 0xcd, 0x9c, 0xf4, 0x9a, 0xf9, 0x61, 0x0b, 0x4c, 0xf6, 0x0a, 0xfd, 0x5f, + 0xb9, 0xf9, 0x6e, 0x5e, 0xca, 0x65, 0xd7, 0x1a, 0xf2, 0x02, 0x79, 0x87, 0x99, 0x6f, 0xec, 0xff, + 0x60, 0xc1, 0x93, 0x3d, 0x29, 0xa2, 0x15, 0x28, 0x31, 0x1e, 0x50, 0x7b, 0x9d, 0x3d, 0xa5, 0xac, + 0x6e, 0x25, 0x20, 0x87, 0x25, 0x8d, 0x6b, 0xa2, 0x95, 0x54, 0x62, 0xa1, 0xa7, 0x33, 0x12, 0x0b, + 0x9d, 0x36, 0x86, 0xe7, 0x21, 0x33, 0x0b, 0xfd, 0x20, 0xbd, 0x71, 0x0c, 0x8f, 0x17, 0xf4, 0x61, + 0x43, 0xec, 0x67, 0x27, 0xc4, 0x7e, 0xc8, 0xc4, 0xd6, 0xee, 0x90, 0x8f, 0xc3, 0x14, 0x8b, 0xf0, + 0xc4, 0x6c, 0xc0, 0x85, 0x2f, 0x4e, 0x21, 0xb6, 0xf3, 0xbc, 0x99, 0x80, 0xe1, 0x14, 0xb6, 0xfd, + 0x47, 0x45, 0x18, 0xe2, 0xdb, 0xef, 0x04, 0xde, 0x84, 0xcf, 0x40, 0xc9, 0x6d, 0xb5, 0x3a, 0x3c, + 0x57, 0xcc, 0x20, 0x77, 0xc0, 0xa5, 0xf3, 0x54, 0x91, 0x85, 0x38, 0x86, 0xa3, 0x55, 0x21, 0x71, + 0xee, 0x12, 0x44, 0x92, 0x77, 0x7c, 0xa1, 0xec, 0x44, 0x0e, 0x67, 0x70, 0xd4, 0x3d, 0x1b, 0xcb, + 0xa6, 0xd1, 0x67, 0x00, 0xc2, 0x28, 0x70, 0xbd, 0x6d, 0x5a, 0x26, 0x62, 0xa6, 0x7e, 0xb0, 0x0b, + 0xb5, 0x9a, 0x42, 0xe6, 0x34, 0xe3, 0x33, 0x47, 0x01, 0xb0, 0x46, 0x11, 0x2d, 0x18, 0x37, 0xfd, + 0x5c, 0x62, 0xee, 0x80, 0x53, 0x8d, 0xe7, 0x6c, 0xee, 0x23, 0x50, 0x52, 0xc4, 0x7b, 0xc9, 0x9f, + 0xc6, 0x74, 0xb6, 0xe8, 0x63, 0x30, 0x99, 0xe8, 0xdb, 0x91, 0xc4, 0x57, 0xbf, 0x64, 0xc1, 0x24, + 0xef, 0xcc, 0x8a, 0xb7, 0x27, 0x6e, 0x83, 0xb7, 0xe1, 0x54, 0x33, 0xe3, 0x54, 0x16, 0xd3, 0xdf, + 0xff, 0x29, 0xae, 0xc4, 0x55, 0x59, 0x50, 0x9c, 0xd9, 0x06, 0xba, 0x4c, 0x77, 0x1c, 0x3d, 0x75, + 0x9d, 0xa6, 0xf0, 0xc7, 0x1d, 0xe3, 0xbb, 0x8d, 0x97, 0x61, 0x05, 0xb5, 0x7f, 0xd7, 0x82, 0x69, + 0xde, 0xf3, 0x1b, 0x64, 0x5f, 0x9d, 0x4d, 0xdf, 0xc8, 0xbe, 0x8b, 0x2c, 0x65, 0x85, 0x9c, 0x2c, + 0x65, 0xfa, 0xa7, 0x15, 0xbb, 0x7e, 0xda, 0x57, 0x2c, 0x10, 0x2b, 0xe4, 0x04, 0x84, 0x10, 0xdf, + 0x6e, 0x0a, 0x21, 0xe6, 0xf2, 0x37, 0x41, 0x8e, 0xf4, 0xe1, 0xcf, 0x2d, 0x98, 0xe2, 0x08, 0xb1, + 0xb6, 0xfc, 0x1b, 0x3a, 0x0f, 0xfd, 0xe4, 0x32, 0xbe, 0x41, 0xf6, 0x37, 0xfc, 0xaa, 0x13, 0xed, + 0x64, 0x7f, 0x94, 0x31, 0x59, 0x03, 0x5d, 0x27, 0xab, 0x21, 0x37, 0xd0, 0x11, 0x12, 0xa4, 0x1f, + 0x39, 0x89, 0x87, 0xfd, 0x75, 0x0b, 0x10, 0x6f, 0xc6, 0x60, 0xdc, 0x28, 0x3b, 0xc4, 0x4a, 0xb5, + 0x8b, 0x2e, 0x3e, 0x9a, 0x14, 0x04, 0x6b, 0x58, 0xc7, 0x32, 0x3c, 0x09, 0x93, 0x87, 0x62, 0x6f, + 0x93, 0x87, 0x23, 0x8c, 0xe8, 0xbf, 0x1a, 0x82, 0xa4, 0xd7, 0x0f, 0xba, 0x03, 0x63, 0x75, 0xa7, + 0xed, 0x6c, 0xba, 0x4d, 0x37, 0x72, 0x49, 0xd8, 0xcd, 0x28, 0x6b, 0x59, 0xc3, 0x13, 0x4a, 0x6a, + 0xad, 0x04, 0x1b, 0x74, 0xd0, 0x02, 0x40, 0x3b, 0x70, 0xf7, 0xdc, 0x26, 0xd9, 0x66, 0xb2, 0x12, + 0x16, 0x01, 0x80, 0x5b, 0x1a, 0xc9, 0x52, 0xac, 0x61, 0x64, 0xb8, 0x58, 0x17, 0x1f, 0xb1, 0x8b, + 0x35, 0x9c, 0x98, 0x8b, 0xf5, 0xc0, 0x91, 0x5c, 0xac, 0x47, 0x8e, 0xec, 0x62, 0x3d, 0xd8, 0x97, + 0x8b, 0x35, 0x86, 0x33, 0x92, 0xf7, 0xa4, 0xff, 0x57, 0xdd, 0x26, 0x11, 0x0f, 0x0e, 0x1e, 0xb6, + 0x60, 0xee, 0xc1, 0xc1, 0xfc, 0x19, 0x9c, 0x89, 0x81, 0x73, 0x6a, 0xa2, 0x4f, 0xc0, 0xac, 0xd3, + 0x6c, 0xfa, 0xf7, 0xd4, 0xa4, 0xae, 0x84, 0x75, 0xa7, 0xc9, 0x95, 0x10, 0xc3, 0x8c, 0xea, 0x13, + 0x0f, 0x0e, 0xe6, 0x67, 0x17, 0x73, 0x70, 0x70, 0x6e, 0x6d, 0xf4, 0x51, 0x28, 0xb5, 0x03, 0xbf, + 0xbe, 0xa6, 0xb9, 0x26, 0x9e, 0xa7, 0x03, 0x58, 0x95, 0x85, 0x87, 0x07, 0xf3, 0xe3, 0xea, 0x0f, + 0xbb, 0xf0, 0xe3, 0x0a, 0x19, 0x3e, 0xd3, 0xa3, 0xc7, 0xea, 0x33, 0xbd, 0x0b, 0x33, 0x35, 0x12, + 0xb8, 0x2c, 0x9d, 0x7a, 0x23, 0x3e, 0x9f, 0x36, 0xa0, 0x14, 0x24, 0x4e, 0xe4, 0xbe, 0x02, 0x3b, + 0x6a, 0xd9, 0x14, 0xe4, 0x09, 0x1c, 0x13, 0xb2, 0xff, 0xa7, 0x05, 0xc3, 0xc2, 0xcb, 0xe7, 0x04, + 0xb8, 0xc6, 0x45, 0x43, 0x93, 0x30, 0x9f, 0x3d, 0x60, 0xac, 0x33, 0xb9, 0x3a, 0x84, 0x4a, 0x42, + 0x87, 0xf0, 0x64, 0x37, 0x22, 0xdd, 0xb5, 0x07, 0x7f, 0xa5, 0x48, 0xb9, 0x77, 0xc3, 0xdf, 0xf4, + 0xd1, 0x0f, 0xc1, 0x3a, 0x0c, 0x87, 0xc2, 0xdf, 0xb1, 0x90, 0x6f, 0xa0, 0x9f, 0x9c, 0xc4, 0xd8, + 0x8e, 0x4d, 0x78, 0x38, 0x4a, 0x22, 0x99, 0x8e, 0x94, 0xc5, 0x47, 0xe8, 0x48, 0xd9, 0xcb, 0x23, + 0x77, 0xe0, 0x38, 0x3c, 0x72, 0xed, 0xaf, 0xb2, 0x9b, 0x53, 0x2f, 0x3f, 0x01, 0xa6, 0xea, 0x9a, + 0x79, 0xc7, 0xda, 0x5d, 0x56, 0x96, 0xe8, 0x54, 0x0e, 0x73, 0xf5, 0x0b, 0x16, 0x9c, 0xcb, 0xf8, + 0x2a, 0x8d, 0xd3, 0x7a, 0x16, 0x46, 0x9c, 0x4e, 0xc3, 0x55, 0x7b, 0x59, 0xd3, 0x27, 0x2e, 0x8a, + 0x72, 0xac, 0x30, 0xd0, 0x32, 0x4c, 0x93, 0xfb, 0x6d, 0x97, 0xab, 0x52, 0x75, 0xab, 0xd6, 0x22, + 0x77, 0x0d, 0x5b, 0x49, 0x02, 0x71, 0x1a, 0x5f, 0x45, 0x41, 0x29, 0xe6, 0x46, 0x41, 0xf9, 0xdb, + 0x16, 0x8c, 0x2a, 0x8f, 0xbf, 0x47, 0x3e, 0xda, 0x1f, 0x37, 0x47, 0xfb, 0xf1, 0x2e, 0xa3, 0x9d, + 0x33, 0xcc, 0xbf, 0x5d, 0x50, 0xfd, 0xad, 0xfa, 0x41, 0xd4, 0x07, 0x07, 0xf7, 0xf0, 0x76, 0xf8, + 0x57, 0x61, 0xd4, 0x69, 0xb7, 0x25, 0x40, 0xda, 0xa0, 0xb1, 0x30, 0xbd, 0x71, 0x31, 0xd6, 0x71, + 0x94, 0x5b, 0x40, 0x31, 0xd7, 0x2d, 0xa0, 0x01, 0x10, 0x39, 0xc1, 0x36, 0x89, 0x68, 0x99, 0x88, + 0x58, 0x96, 0x7f, 0xde, 0x74, 0x22, 0xb7, 0xb9, 0xe0, 0x7a, 0x51, 0x18, 0x05, 0x0b, 0x15, 0x2f, + 0xba, 0x15, 0xf0, 0x27, 0xa4, 0x16, 0x12, 0x48, 0xd1, 0xc2, 0x1a, 0x5d, 0xe9, 0xdd, 0xce, 0xda, + 0x18, 0x34, 0x8d, 0x19, 0xd6, 0x45, 0x39, 0x56, 0x18, 0xf6, 0x47, 0xd8, 0xed, 0xc3, 0xc6, 0xf4, + 0x68, 0x31, 0x74, 0xfe, 0xee, 0x98, 0x9a, 0x0d, 0xa6, 0xc9, 0x2c, 0xeb, 0x91, 0x7a, 0xba, 0x1f, + 0xf6, 0xb4, 0x61, 0xdd, 0x49, 0x2d, 0x0e, 0xe7, 0x83, 0xbe, 0x23, 0x65, 0xa0, 0xf2, 0x5c, 0x8f, + 0x5b, 0xe3, 0x08, 0x26, 0x29, 0x2c, 0x67, 0x07, 0xcb, 0x68, 0x50, 0xa9, 0x8a, 0x7d, 0xa1, 0xe5, + 0xec, 0x10, 0x00, 0x1c, 0xe3, 0x50, 0x66, 0x4a, 0xfd, 0x09, 0x67, 0x51, 0x1c, 0xbb, 0x52, 0x61, + 0x87, 0x58, 0xc3, 0x40, 0x57, 0x84, 0x40, 0x81, 0xeb, 0x05, 0x1e, 0x4f, 0x08, 0x14, 0xe4, 0x70, + 0x69, 0x52, 0xa0, 0xab, 0x30, 0xaa, 0xd2, 0x03, 0x57, 0x79, 0xd6, 0x59, 0xb1, 0xcc, 0x56, 0xe2, + 0x62, 0xac, 0xe3, 0xa0, 0x0d, 0x98, 0x0c, 0xb9, 0x9c, 0x4d, 0x05, 0x14, 0xe6, 0xf2, 0xca, 0x0f, + 0x4a, 0x2b, 0xa0, 0x9a, 0x09, 0x3e, 0x64, 0x45, 0xfc, 0x74, 0x92, 0x1e, 0xe8, 0x49, 0x12, 0xe8, + 0x35, 0x98, 0x68, 0xfa, 0x4e, 0x63, 0xc9, 0x69, 0x3a, 0x5e, 0x9d, 0x8d, 0xcf, 0x88, 0x99, 0x65, + 0xf2, 0xa6, 0x01, 0xc5, 0x09, 0x6c, 0xca, 0xbc, 0xe9, 0x25, 0x22, 0x08, 0xb6, 0xe3, 0x6d, 0x93, + 0x50, 0x24, 0x7b, 0x65, 0xcc, 0xdb, 0xcd, 0x1c, 0x1c, 0x9c, 0x5b, 0x1b, 0xbd, 0x0c, 0x63, 0xf2, + 0xf3, 0xb5, 0x80, 0x0d, 0xb1, 0x87, 0x85, 0x06, 0xc3, 0x06, 0x26, 0xba, 0x07, 0xa7, 0xe5, 0xff, + 0x8d, 0xc0, 0xd9, 0xda, 0x72, 0xeb, 0xc2, 0x8b, 0x99, 0xbb, 0x62, 0x2e, 0x4a, 0x7f, 0xc1, 0x95, + 0x2c, 0xa4, 0xc3, 0x83, 0xf9, 0x0b, 0x62, 0xd4, 0x32, 0xe1, 0x6c, 0x12, 0xb3, 0xe9, 0xa3, 0x35, + 0x98, 0xd9, 0x21, 0x4e, 0x33, 0xda, 0x59, 0xde, 0x21, 0xf5, 0x5d, 0xb9, 0xe9, 0x58, 0x18, 0x08, + 0xcd, 0x2f, 0xe1, 0x7a, 0x1a, 0x05, 0x67, 0xd5, 0x43, 0x6f, 0xc2, 0x6c, 0xbb, 0xb3, 0xd9, 0x74, + 0xc3, 0x9d, 0x75, 0x3f, 0x62, 0xa6, 0x40, 0x2a, 0xdb, 0xb0, 0x88, 0x17, 0xa1, 0x02, 0x6d, 0x54, + 0x73, 0xf0, 0x70, 0x2e, 0x05, 0xf4, 0x36, 0x9c, 0x4e, 0x2c, 0x06, 0xe1, 0x31, 0x3f, 0x91, 0x9f, + 0x52, 0xa0, 0x96, 0x55, 0x41, 0x04, 0x9f, 0xc8, 0x02, 0xe1, 0xec, 0x26, 0xd0, 0x2b, 0x00, 0x6e, + 0x7b, 0xd5, 0x69, 0xb9, 0x4d, 0xfa, 0x5c, 0x9c, 0x61, 0xeb, 0x84, 0x3e, 0x1d, 0xa0, 0x52, 0x95, + 0xa5, 0xf4, 0x7c, 0x16, 0xff, 0xf6, 0xb1, 0x86, 0x8d, 0xaa, 0x30, 0x21, 0xfe, 0xed, 0x8b, 0x69, + 0x9d, 0x56, 0xce, 0xe9, 0x13, 0xb2, 0x86, 0x9a, 0x4b, 0x64, 0x96, 0xb0, 0xd9, 0x4b, 0xd4, 0x47, + 0xdb, 0x70, 0x4e, 0x24, 0xa6, 0x26, 0xfa, 0x3a, 0x95, 0xf3, 0x10, 0xb2, 0x58, 0xfe, 0x23, 0xdc, + 0xed, 0x61, 0xb1, 0x1b, 0x22, 0xee, 0x4e, 0x87, 0xde, 0xef, 0xfa, 0x72, 0xe7, 0xee, 0xa0, 0xa7, + 0xb9, 0x79, 0x12, 0xbd, 0xdf, 0x6f, 0x26, 0x81, 0x38, 0x8d, 0x8f, 0x42, 0x38, 0xed, 0x7a, 0x59, + 0xab, 0xfb, 0x0c, 0x23, 0xf4, 0x31, 0xee, 0x09, 0xdb, 0x7d, 0x65, 0x67, 0xc2, 0xf9, 0xca, 0xce, + 0xa4, 0xfd, 0xce, 0xac, 0xf0, 0x7e, 0xc7, 0xa2, 0xb5, 0x35, 0x4e, 0x1d, 0x7d, 0x16, 0xc6, 0xf4, + 0x0f, 0x13, 0x5c, 0xc7, 0xa5, 0x6c, 0x46, 0x56, 0x3b, 0x1f, 0x38, 0x9f, 0xaf, 0xce, 0x00, 0x1d, + 0x86, 0x0d, 0x8a, 0xa8, 0x9e, 0xe1, 0x33, 0x7e, 0xa5, 0x3f, 0xae, 0xa6, 0x7f, 0x23, 0x34, 0x02, + 0xd9, 0xcb, 0x1e, 0xdd, 0x84, 0x91, 0x7a, 0xd3, 0x25, 0x5e, 0x54, 0xa9, 0x76, 0x8b, 0xf2, 0xb6, + 0x2c, 0x70, 0xc4, 0x3e, 0x12, 0xa1, 0xf9, 0x79, 0x19, 0x56, 0x14, 0xec, 0x5f, 0x2f, 0xc0, 0x7c, + 0x8f, 0x3c, 0x0f, 0x09, 0x95, 0x94, 0xd5, 0x97, 0x4a, 0x6a, 0x51, 0xa6, 0xd4, 0x5e, 0x4f, 0x48, + 0xbb, 0x12, 0xe9, 0xb2, 0x63, 0x99, 0x57, 0x12, 0xbf, 0x6f, 0x17, 0x01, 0x5d, 0xab, 0x35, 0xd0, + 0xd3, 0xc9, 0xc5, 0xd0, 0x66, 0x0f, 0xf6, 0xff, 0x04, 0xce, 0xd5, 0x4c, 0xda, 0x5f, 0x2d, 0xc0, + 0x69, 0x35, 0x84, 0xdf, 0xba, 0x03, 0x77, 0x3b, 0x3d, 0x70, 0xc7, 0xa0, 0xd7, 0xb5, 0x6f, 0xc1, + 0x10, 0x0f, 0x5b, 0xd7, 0x07, 0xeb, 0x7d, 0xd1, 0x8c, 0xf0, 0xaa, 0xb8, 0x3d, 0x23, 0xca, 0xeb, + 0xf7, 0x5b, 0x30, 0xb9, 0xb1, 0x5c, 0xad, 0xf9, 0xf5, 0x5d, 0x12, 0x2d, 0xf2, 0xa7, 0x12, 0xd6, + 0xbc, 0x6b, 0x1f, 0x86, 0x3d, 0xce, 0x62, 0xbc, 0x2f, 0xc0, 0xc0, 0x8e, 0x1f, 0x46, 0x49, 0xa3, + 0x8f, 0xeb, 0x7e, 0x18, 0x61, 0x06, 0xb1, 0x7f, 0xcf, 0x82, 0xc1, 0x0d, 0xc7, 0xf5, 0x22, 0xa9, + 0x20, 0xb0, 0x72, 0x14, 0x04, 0xfd, 0x7c, 0x17, 0x7a, 0x09, 0x86, 0xc8, 0xd6, 0x16, 0xa9, 0x47, + 0x62, 0x56, 0x65, 0x68, 0x82, 0xa1, 0x15, 0x56, 0x4a, 0x79, 0x41, 0xd6, 0x18, 0xff, 0x8b, 0x05, + 0x32, 0xba, 0x0b, 0xa5, 0xc8, 0x6d, 0x91, 0xc5, 0x46, 0x43, 0xa8, 0xcd, 0x1f, 0x22, 0xbc, 0xc2, + 0x86, 0x24, 0x80, 0x63, 0x5a, 0xf6, 0x97, 0x0a, 0x00, 0x71, 0xbc, 0x9f, 0x5e, 0x9f, 0xb8, 0x94, + 0x52, 0xa8, 0x5e, 0xca, 0x50, 0xa8, 0xa2, 0x98, 0x60, 0x86, 0x36, 0x55, 0x0d, 0x53, 0xb1, 0xaf, + 0x61, 0x1a, 0x38, 0xca, 0x30, 0x2d, 0xc3, 0x74, 0x1c, 0xaf, 0xc8, 0x0c, 0xd7, 0xc6, 0xae, 0xcf, + 0x8d, 0x24, 0x10, 0xa7, 0xf1, 0x6d, 0x02, 0x17, 0x54, 0xd8, 0x16, 0x71, 0xa3, 0x31, 0xab, 0x6c, + 0x5d, 0x41, 0xdd, 0x63, 0x9c, 0x62, 0x8d, 0x71, 0x21, 0x57, 0x63, 0xfc, 0x53, 0x16, 0x9c, 0x4a, + 0xb6, 0xc3, 0xfc, 0x71, 0xbf, 0x68, 0xc1, 0x69, 0xa6, 0x37, 0x67, 0xad, 0xa6, 0xb5, 0xf4, 0x2f, + 0x76, 0x0d, 0x45, 0x93, 0xd3, 0xe3, 0x38, 0x06, 0xc6, 0x5a, 0x16, 0x69, 0x9c, 0xdd, 0xa2, 0xfd, + 0xef, 0x0b, 0x30, 0x9b, 0x17, 0xc3, 0x86, 0x39, 0x6d, 0x38, 0xf7, 0x6b, 0xbb, 0xe4, 0x9e, 0x30, + 0x8d, 0x8f, 0x9d, 0x36, 0x78, 0x31, 0x96, 0xf0, 0x64, 0xe8, 0xfe, 0x42, 0x9f, 0xa1, 0xfb, 0x77, + 0x60, 0xfa, 0xde, 0x0e, 0xf1, 0x6e, 0x7b, 0xa1, 0x13, 0xb9, 0xe1, 0x96, 0xcb, 0x74, 0xcc, 0x7c, + 0xdd, 0xbc, 0x22, 0x0d, 0xd8, 0xef, 0x26, 0x11, 0x0e, 0x0f, 0xe6, 0xcf, 0x19, 0x05, 0x71, 0x97, + 0xf9, 0x41, 0x82, 0xd3, 0x44, 0xd3, 0x99, 0x0f, 0x06, 0x1e, 0x61, 0xe6, 0x03, 0xfb, 0x8b, 0x16, + 0x9c, 0xcd, 0x4d, 0xba, 0x8a, 0x2e, 0xc3, 0x88, 0xd3, 0x76, 0xb9, 0x98, 0x5e, 0x1c, 0xa3, 0x4c, + 0x1c, 0x54, 0xad, 0x70, 0x21, 0xbd, 0x82, 0xaa, 0x74, 0xf3, 0x85, 0xdc, 0x74, 0xf3, 0x3d, 0xb3, + 0xc7, 0xdb, 0xdf, 0x67, 0x81, 0x70, 0x38, 0xed, 0xe3, 0xec, 0xfe, 0x14, 0x8c, 0xed, 0xa5, 0xb3, + 0x23, 0x5d, 0xc8, 0xf7, 0xc0, 0x15, 0x39, 0x91, 0x14, 0x43, 0x66, 0x64, 0x42, 0x32, 0x68, 0xd9, + 0x0d, 0x10, 0xd0, 0x32, 0x61, 0x42, 0xe8, 0xde, 0xbd, 0x79, 0x1e, 0xa0, 0xc1, 0x70, 0xb5, 0x9c, + 0xfd, 0xea, 0x66, 0x2e, 0x2b, 0x08, 0xd6, 0xb0, 0xec, 0x7f, 0x53, 0x80, 0x51, 0x99, 0x8d, 0xa7, + 0xe3, 0xf5, 0x23, 0x2a, 0x3a, 0x52, 0x7a, 0x4e, 0x74, 0x05, 0x4a, 0x4c, 0x96, 0x59, 0x8d, 0x25, + 0x6c, 0x4a, 0x92, 0xb0, 0x26, 0x01, 0x38, 0xc6, 0xa1, 0xbb, 0x28, 0xec, 0x6c, 0x32, 0xf4, 0x84, + 0x7b, 0x64, 0x8d, 0x17, 0x63, 0x09, 0x47, 0x9f, 0x80, 0x29, 0x5e, 0x2f, 0xf0, 0xdb, 0xce, 0x36, + 0xd7, 0x7f, 0x0c, 0xaa, 0x00, 0x0a, 0x53, 0x6b, 0x09, 0xd8, 0xe1, 0xc1, 0xfc, 0xa9, 0x64, 0x19, + 0x53, 0xec, 0xa5, 0xa8, 0x30, 0x33, 0x27, 0xde, 0x08, 0xdd, 0xfd, 0x29, 0xeb, 0xa8, 0x18, 0x84, + 0x75, 0x3c, 0xfb, 0xb3, 0x80, 0xd2, 0x79, 0x89, 0xd0, 0xeb, 0xdc, 0xb6, 0xd5, 0x0d, 0x48, 0xa3, + 0x9b, 0xa2, 0x4f, 0x0f, 0x13, 0x20, 0x3d, 0x9b, 0x78, 0x2d, 0xac, 0xea, 0xdb, 0xff, 0x6f, 0x11, + 0xa6, 0x92, 0xbe, 0xdc, 0xe8, 0x3a, 0x0c, 0x71, 0xd6, 0x43, 0x90, 0xef, 0x62, 0x47, 0xa2, 0x79, + 0x80, 0xb3, 0x43, 0x58, 0x70, 0x2f, 0xa2, 0x3e, 0x7a, 0x13, 0x46, 0x1b, 0xfe, 0x3d, 0xef, 0x9e, + 0x13, 0x34, 0x16, 0xab, 0x15, 0xb1, 0x9c, 0x33, 0x1f, 0xb6, 0xe5, 0x18, 0x4d, 0xf7, 0x2a, 0x67, + 0x3a, 0xd3, 0x18, 0x84, 0x75, 0x72, 0x68, 0x83, 0x05, 0x33, 0xdf, 0x72, 0xb7, 0xd7, 0x9c, 0x76, + 0x37, 0x47, 0x87, 0x65, 0x89, 0xa4, 0x51, 0x1e, 0x17, 0x11, 0xcf, 0x39, 0x00, 0xc7, 0x84, 0xd0, + 0xe7, 0x61, 0x26, 0xcc, 0x11, 0xb7, 0xe7, 0xa5, 0xa9, 0xeb, 0x26, 0x81, 0x5e, 0x7a, 0xec, 0xc1, + 0xc1, 0xfc, 0x4c, 0x96, 0x60, 0x3e, 0xab, 0x19, 0xfb, 0xc7, 0x4e, 0x81, 0xb1, 0x89, 0x8d, 0xac, + 0xa5, 0xd6, 0x31, 0x65, 0x2d, 0xc5, 0x30, 0x42, 0x5a, 0xed, 0x68, 0xbf, 0xec, 0x06, 0xdd, 0x72, + 0x86, 0xaf, 0x08, 0x9c, 0x34, 0x4d, 0x09, 0xc1, 0x8a, 0x4e, 0x76, 0x6a, 0xd9, 0xe2, 0x37, 0x30, + 0xb5, 0xec, 0xc0, 0x09, 0xa6, 0x96, 0x5d, 0x87, 0xe1, 0x6d, 0x37, 0xc2, 0xa4, 0xed, 0x0b, 0xa6, + 0x3f, 0x73, 0x1d, 0x5e, 0xe3, 0x28, 0xe9, 0x24, 0x86, 0x02, 0x80, 0x25, 0x11, 0xf4, 0xba, 0xda, + 0x81, 0x43, 0xf9, 0x0f, 0xf3, 0xb4, 0xc1, 0x43, 0xe6, 0x1e, 0x14, 0x09, 0x64, 0x87, 0x1f, 0x36, + 0x81, 0xec, 0xaa, 0x4c, 0xfb, 0x3a, 0x92, 0xef, 0x95, 0xc4, 0xb2, 0xba, 0xf6, 0x48, 0xf6, 0x7a, + 0x47, 0x4f, 0x95, 0x5b, 0xca, 0x3f, 0x09, 0x54, 0x16, 0xdc, 0x3e, 0x13, 0xe4, 0x7e, 0x9f, 0x05, + 0xa7, 0xdb, 0x59, 0x59, 0xa3, 0x85, 0x6d, 0xc0, 0x4b, 0x7d, 0x27, 0xa6, 0x36, 0x1a, 0x64, 0x32, + 0xb5, 0x4c, 0x34, 0x9c, 0xdd, 0x1c, 0x1d, 0xe8, 0x60, 0xb3, 0x21, 0x74, 0xd4, 0x17, 0x73, 0x32, + 0xed, 0x76, 0xc9, 0xaf, 0xbb, 0x91, 0x91, 0xd5, 0xf5, 0xfd, 0x79, 0x59, 0x5d, 0xfb, 0xce, 0xe5, + 0xfa, 0xba, 0xca, 0xb1, 0x3b, 0x9e, 0xbf, 0x94, 0x78, 0x06, 0xdd, 0x9e, 0x99, 0x75, 0x5f, 0x57, + 0x99, 0x75, 0xbb, 0x44, 0xaa, 0xe5, 0x79, 0x73, 0x7b, 0xe6, 0xd3, 0xd5, 0x72, 0xe2, 0x4e, 0x1e, + 0x4f, 0x4e, 0x5c, 0xe3, 0xaa, 0xe1, 0x69, 0x59, 0x9f, 0xe9, 0x71, 0xd5, 0x18, 0x74, 0xbb, 0x5f, + 0x36, 0x3c, 0xff, 0xef, 0xf4, 0x43, 0xe5, 0xff, 0xbd, 0xa3, 0xe7, 0xd3, 0x45, 0x3d, 0x12, 0xc6, + 0x52, 0xa4, 0x3e, 0xb3, 0xe8, 0xde, 0xd1, 0x2f, 0xc0, 0x99, 0x7c, 0xba, 0xea, 0x9e, 0x4b, 0xd3, + 0xcd, 0xbc, 0x02, 0x53, 0xd9, 0x79, 0x4f, 0x9d, 0x4c, 0x76, 0xde, 0xd3, 0xc7, 0x9e, 0x9d, 0xf7, + 0xcc, 0x09, 0x64, 0xe7, 0x7d, 0xec, 0x04, 0xb3, 0xf3, 0xde, 0x61, 0x06, 0x35, 0x3c, 0x6c, 0x8f, + 0x88, 0xac, 0x9b, 0x1d, 0xc5, 0x35, 0x2b, 0xb6, 0x0f, 0xff, 0x38, 0x05, 0xc2, 0x31, 0xa9, 0x8c, + 0xac, 0xbf, 0xb3, 0x8f, 0x20, 0xeb, 0xef, 0x7a, 0x9c, 0xf5, 0xf7, 0x6c, 0xfe, 0x54, 0x67, 0xb8, + 0x60, 0xe4, 0xe4, 0xfa, 0xbd, 0xa3, 0xe7, 0xe8, 0x7d, 0xbc, 0x8b, 0xd6, 0x24, 0x4b, 0xf0, 0xd8, + 0x25, 0x33, 0xef, 0x6b, 0x3c, 0x33, 0xef, 0x13, 0xf9, 0x27, 0x79, 0xf2, 0xba, 0x33, 0xf2, 0xf1, + 0xd2, 0x7e, 0xa9, 0x18, 0x8e, 0x2c, 0x86, 0x70, 0x4e, 0xbf, 0x54, 0x10, 0xc8, 0x74, 0xbf, 0x14, + 0x08, 0xc7, 0xa4, 0xec, 0x1f, 0x28, 0xc0, 0xf9, 0xee, 0xfb, 0x2d, 0x96, 0xa6, 0x56, 0x63, 0x25, + 0x72, 0x42, 0x9a, 0xca, 0xdf, 0x6c, 0x31, 0x56, 0xdf, 0x21, 0xe9, 0xae, 0xc1, 0xb4, 0xf2, 0xdd, + 0x68, 0xba, 0xf5, 0xfd, 0xf5, 0xf8, 0xe5, 0xab, 0xfc, 0xdd, 0x6b, 0x49, 0x04, 0x9c, 0xae, 0x83, + 0x16, 0x61, 0xd2, 0x28, 0xac, 0x94, 0xc5, 0xdb, 0x4c, 0x89, 0x6f, 0x6b, 0x26, 0x18, 0x27, 0xf1, + 0xed, 0x2f, 0x5b, 0xf0, 0x58, 0x4e, 0x5a, 0xbb, 0xbe, 0x23, 0xae, 0x6d, 0xc1, 0x64, 0xdb, 0xac, + 0xda, 0x23, 0x48, 0xa4, 0x91, 0x3c, 0x4f, 0xf5, 0x35, 0x01, 0xc0, 0x49, 0xa2, 0xf6, 0xcf, 0x14, + 0xe0, 0x5c, 0x57, 0x63, 0x44, 0x84, 0xe1, 0xcc, 0x76, 0x2b, 0x74, 0x96, 0x03, 0xd2, 0x20, 0x5e, + 0xe4, 0x3a, 0xcd, 0x5a, 0x9b, 0xd4, 0x35, 0x79, 0x38, 0xb3, 0xea, 0xbb, 0xb6, 0x56, 0x5b, 0x4c, + 0x63, 0xe0, 0x9c, 0x9a, 0x68, 0x15, 0x50, 0x1a, 0x22, 0x66, 0x98, 0x45, 0xa3, 0x4e, 0xd3, 0xc3, + 0x19, 0x35, 0xd0, 0x47, 0x60, 0x5c, 0x19, 0x39, 0x6a, 0x33, 0xce, 0x0e, 0x76, 0xac, 0x03, 0xb0, + 0x89, 0x87, 0xae, 0xf2, 0x70, 0xe6, 0x22, 0xf0, 0xbd, 0x10, 0x9e, 0x4f, 0xca, 0x58, 0xe5, 0xa2, + 0x18, 0xeb, 0x38, 0x4b, 0x97, 0x7f, 0xe3, 0x0f, 0xce, 0xbf, 0xef, 0xb7, 0xfe, 0xe0, 0xfc, 0xfb, + 0x7e, 0xf7, 0x0f, 0xce, 0xbf, 0xef, 0xbb, 0x1e, 0x9c, 0xb7, 0x7e, 0xe3, 0xc1, 0x79, 0xeb, 0xb7, + 0x1e, 0x9c, 0xb7, 0x7e, 0xf7, 0xc1, 0x79, 0xeb, 0xf7, 0x1f, 0x9c, 0xb7, 0xbe, 0xf4, 0x87, 0xe7, + 0xdf, 0xf7, 0xa9, 0xc2, 0xde, 0xd5, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0x11, 0xbd, 0x2b, 0x2b, + 0x30, 0x04, 0x01, 0x00, } func (m *AWSElasticBlockStoreVolumeSource) Marshal() (dAtA []byte, err error) { @@ -9923,53 +9894,6 @@ return len(dAtA) - i, nil } -func (m *EphemeralContainers) Marshal() (dAtA []byte, err error) { - size := m.Size() - dAtA = make([]byte, size) - n, err := m.MarshalToSizedBuffer(dAtA[:size]) - if err != nil { - return nil, err - } - return dAtA[:n], nil -} - -func (m *EphemeralContainers) MarshalTo(dAtA []byte) (int, error) { - size := m.Size() - return m.MarshalToSizedBuffer(dAtA[:size]) -} - -func (m *EphemeralContainers) MarshalToSizedBuffer(dAtA []byte) (int, error) { - i := len(dAtA) - _ = i - var l int - _ = l - if len(m.EphemeralContainers) > 0 { - for iNdEx := len(m.EphemeralContainers) - 1; iNdEx >= 0; iNdEx-- { - { - size, err := m.EphemeralContainers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0x12 - } - } - { - size, err := m.ObjectMeta.MarshalToSizedBuffer(dAtA[:i]) - if err != nil { - return 0, err - } - i -= size - i = encodeVarintGenerated(dAtA, i, uint64(size)) - } - i-- - dAtA[i] = 0xa - return len(dAtA) - i, nil -} - func (m *EphemeralVolumeSource) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -13230,6 +13154,18 @@ _ = i var l int _ = l + if m.DataSourceRef != nil { + { + size, err := m.DataSourceRef.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenerated(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } if m.DataSource != nil { { size, err := m.DataSource.MarshalToSizedBuffer(dAtA[:i]) @@ -18111,17 +18047,6 @@ i-- dAtA[i] = 0x8a } - if len(m.TopologyKeys) > 0 { - for iNdEx := len(m.TopologyKeys) - 1; iNdEx >= 0; iNdEx-- { - i -= len(m.TopologyKeys[iNdEx]) - copy(dAtA[i:], m.TopologyKeys[iNdEx]) - i = encodeVarintGenerated(dAtA, i, uint64(len(m.TopologyKeys[iNdEx]))) - i-- - dAtA[i] = 0x1 - i-- - dAtA[i] = 0x82 - } - } if m.SessionAffinityConfig != nil { { size, err := m.SessionAffinityConfig.MarshalToSizedBuffer(dAtA[:i]) @@ -19496,6 +19421,16 @@ _ = i var l int _ = l + if m.HostProcess != nil { + i-- + if *m.HostProcess { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x20 + } if m.RunAsUserName != nil { i -= len(*m.RunAsUserName) copy(dAtA[i:], *m.RunAsUserName) @@ -20608,23 +20543,6 @@ return n } -func (m *EphemeralContainers) Size() (n int) { - if m == nil { - return 0 - } - var l int - _ = l - l = m.ObjectMeta.Size() - n += 1 + l + sovGenerated(uint64(l)) - if len(m.EphemeralContainers) > 0 { - for _, e := range m.EphemeralContainers { - l = e.Size() - n += 1 + l + sovGenerated(uint64(l)) - } - } - return n -} - func (m *EphemeralVolumeSource) Size() (n int) { if m == nil { return 0 @@ -21838,6 +21756,10 @@ l = m.DataSource.Size() n += 1 + l + sovGenerated(uint64(l)) } + if m.DataSourceRef != nil { + l = m.DataSourceRef.Size() + n += 1 + l + sovGenerated(uint64(l)) + } return n } @@ -23602,12 +23524,6 @@ l = m.SessionAffinityConfig.Size() n += 1 + l + sovGenerated(uint64(l)) } - if len(m.TopologyKeys) > 0 { - for _, s := range m.TopologyKeys { - l = len(s) - n += 2 + l + sovGenerated(uint64(l)) - } - } if m.IPFamilyPolicy != nil { l = len(*m.IPFamilyPolicy) n += 2 + l + sovGenerated(uint64(l)) @@ -24098,6 +24014,9 @@ l = len(*m.RunAsUserName) n += 1 + l + sovGenerated(uint64(l)) } + if m.HostProcess != nil { + n += 2 + } return n } @@ -24906,22 +24825,6 @@ }, "") return s } -func (this *EphemeralContainers) String() string { - if this == nil { - return "nil" - } - repeatedStringForEphemeralContainers := "[]EphemeralContainer{" - for _, f := range this.EphemeralContainers { - repeatedStringForEphemeralContainers += strings.Replace(strings.Replace(f.String(), "EphemeralContainer", "EphemeralContainer", 1), `&`, ``, 1) + "," - } - repeatedStringForEphemeralContainers += "}" - s := strings.Join([]string{`&EphemeralContainers{`, - `ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`, - `EphemeralContainers:` + repeatedStringForEphemeralContainers + `,`, - `}`, - }, "") - return s -} func (this *EphemeralVolumeSource) String() string { if this == nil { return "nil" @@ -25885,6 +25788,7 @@ `StorageClassName:` + valueToStringGenerated(this.StorageClassName) + `,`, `VolumeMode:` + valueToStringGenerated(this.VolumeMode) + `,`, `DataSource:` + strings.Replace(this.DataSource.String(), "TypedLocalObjectReference", "TypedLocalObjectReference", 1) + `,`, + `DataSourceRef:` + strings.Replace(this.DataSourceRef.String(), "TypedLocalObjectReference", "TypedLocalObjectReference", 1) + `,`, `}`, }, "") return s @@ -27224,7 +27128,6 @@ `HealthCheckNodePort:` + fmt.Sprintf("%v", this.HealthCheckNodePort) + `,`, `PublishNotReadyAddresses:` + fmt.Sprintf("%v", this.PublishNotReadyAddresses) + `,`, `SessionAffinityConfig:` + strings.Replace(this.SessionAffinityConfig.String(), "SessionAffinityConfig", "SessionAffinityConfig", 1) + `,`, - `TopologyKeys:` + fmt.Sprintf("%v", this.TopologyKeys) + `,`, `IPFamilyPolicy:` + valueToStringGenerated(this.IPFamilyPolicy) + `,`, `ClusterIPs:` + fmt.Sprintf("%v", this.ClusterIPs) + `,`, `IPFamilies:` + fmt.Sprintf("%v", this.IPFamilies) + `,`, @@ -27519,6 +27422,7 @@ `GMSACredentialSpecName:` + valueToStringGenerated(this.GMSACredentialSpecName) + `,`, `GMSACredentialSpec:` + valueToStringGenerated(this.GMSACredentialSpec) + `,`, `RunAsUserName:` + valueToStringGenerated(this.RunAsUserName) + `,`, + `HostProcess:` + valueToStringGenerated(this.HostProcess) + `,`, `}`, }, "") return s @@ -36642,123 +36546,6 @@ } return nil } -func (m *EphemeralContainers) Unmarshal(dAtA []byte) error { - l := len(dAtA) - iNdEx := 0 - for iNdEx < l { - preIndex := iNdEx - var wire uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - wire |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - fieldNum := int32(wire >> 3) - wireType := int(wire & 0x7) - if wireType == 4 { - return fmt.Errorf("proto: EphemeralContainers: wiretype end group for non-group") - } - if fieldNum <= 0 { - return fmt.Errorf("proto: EphemeralContainers: illegal tag %d (wire type %d)", fieldNum, wire) - } - switch fieldNum { - case 1: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ObjectMeta", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - if err := m.ObjectMeta.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field EphemeralContainers", wireType) - } - var msglen int - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - msglen |= int(b&0x7F) << shift - if b < 0x80 { - break - } - } - if msglen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + msglen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.EphemeralContainers = append(m.EphemeralContainers, EphemeralContainer{}) - if err := m.EphemeralContainers[len(m.EphemeralContainers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { - return err - } - iNdEx = postIndex - default: - iNdEx = preIndex - skippy, err := skipGenerated(dAtA[iNdEx:]) - if err != nil { - return err - } - if (skippy < 0) || (iNdEx+skippy) < 0 { - return ErrInvalidLengthGenerated - } - if (iNdEx + skippy) > l { - return io.ErrUnexpectedEOF - } - iNdEx += skippy - } - } - - if iNdEx > l { - return io.ErrUnexpectedEOF - } - return nil -} func (m *EphemeralVolumeSource) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -47641,6 +47428,42 @@ return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DataSourceRef", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.DataSourceRef == nil { + m.DataSourceRef = &TypedLocalObjectReference{} + } + if err := m.DataSourceRef.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -63111,38 +62934,6 @@ return err } iNdEx = postIndex - case 16: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field TopologyKeys", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowGenerated - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthGenerated - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthGenerated - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.TopologyKeys = append(m.TopologyKeys, string(dAtA[iNdEx:postIndex])) - iNdEx = postIndex case 17: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field IPFamilyPolicy", wireType) @@ -67320,6 +67111,27 @@ s := string(dAtA[iNdEx:postIndex]) m.RunAsUserName = &s iNdEx = postIndex + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field HostProcess", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + b := bool(v != 0) + m.HostProcess = &b default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/generated.proto libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/generated.proto --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/generated.proto 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/generated.proto 2021-12-26 00:45:51.000000000 +0000 @@ -498,6 +498,7 @@ } // Selects a key from a ConfigMap. +// +structType=atomic message ConfigMapKeySelector { // The ConfigMap to select from. optional LocalObjectReference localObjectReference = 1; @@ -521,6 +522,7 @@ } // ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node. +// This API is deprecated since 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration message ConfigMapNodeConfigSource { // Namespace is the metadata.namespace of the referenced ConfigMap. // This field is required in all cases. @@ -621,10 +623,10 @@ // Entrypoint array. Not executed within a shell. // The docker image's ENTRYPOINT is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional repeated string command = 3; @@ -632,10 +634,10 @@ // Arguments to the entrypoint. // The docker image's CMD is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional repeated string args = 4; @@ -754,8 +756,8 @@ // +optional optional string imagePullPolicy = 14; - // Security options the pod should run with. - // More info: https://kubernetes.io/docs/concepts/policy/security-context/ + // SecurityContext defines the security options the container should be run with. + // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. // More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ // +optional optional SecurityContext securityContext = 15; @@ -786,6 +788,7 @@ message ContainerImage { // Names by which this image is known. // e.g. ["k8s.gcr.io/hyperkube:v1.0.7", "dockerhub.io/google_containers/hyperkube:v1.0.7"] + // +optional repeated string names = 1; // The size of the image in bytes. @@ -1010,6 +1013,7 @@ } // EndpointAddress is a tuple that describes single IP address. +// +structType=atomic message EndpointAddress { // The IP of this endpoint. // May not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), @@ -1033,6 +1037,7 @@ } // EndpointPort is a tuple that describes a single port. +// +structType=atomic message EndpointPort { // The name of this port. This must match the 'name' field in the // corresponding ServicePort. @@ -1056,8 +1061,6 @@ // RFC-6335 and http://www.iana.org/assignments/service-names). // Non-standard protocols should use prefixed names such as // mycompany.com/my-custom-protocol. - // This is a beta field that is guarded by the ServiceAppProtocol feature - // gate and enabled by default. // +optional optional string appProtocol = 4; } @@ -1150,11 +1153,12 @@ optional string name = 1; // Variable references $(VAR_NAME) are expanded - // using the previous defined environment variables in the container and + // using the previously defined environment variables in the container and // any service environment variables. If a variable cannot be resolved, - // the reference in the input string will be unchanged. The $(VAR_NAME) - // syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped - // references will never be expanded, regardless of whether the variable + // the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + // "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + // Escaped references will never be expanded, regardless of whether the variable // exists or not. // Defaults to "". // +optional @@ -1226,10 +1230,10 @@ // Entrypoint array. Not executed within a shell. // The docker image's ENTRYPOINT is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional repeated string command = 3; @@ -1237,10 +1241,10 @@ // Arguments to the entrypoint. // The docker image's CMD is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional repeated string args = 4; @@ -1333,7 +1337,8 @@ // +optional optional string imagePullPolicy = 14; - // SecurityContext is not allowed for ephemeral containers. + // Optional: SecurityContext defines the security options the ephemeral container should be run with. + // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. // +optional optional SecurityContext securityContext = 15; @@ -1359,19 +1364,6 @@ optional bool tty = 18; } -// A list of ephemeral containers used with the Pod ephemeralcontainers subresource. -message EphemeralContainers { - // +optional - optional k8s.io.apimachinery.pkg.apis.meta.v1.ObjectMeta metadata = 1; - - // A list of ephemeral containers associated with this pod. New ephemeral containers - // may be appended to this list, but existing ephemeral containers may not be removed - // or modified. - // +patchMergeKey=name - // +patchStrategy=merge - repeated EphemeralContainer ephemeralContainers = 2; -} - // Represents an ephemeral volume that is handled by a normal storage driver. message EphemeralVolumeSource { // Will be used to create a stand-alone PVC to provision the volume. @@ -2049,6 +2041,7 @@ // LocalObjectReference contains enough information to let you locate the // referenced object inside the same namespace. +// +structType=atomic message LocalObjectReference { // Name of the referent. // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names @@ -2240,6 +2233,7 @@ } // NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil. +// This API is deprecated since 1.22 message NodeConfigSource { // ConfigMap is a reference to a Node's ConfigMap optional ConfigMapNodeConfigSource configMap = 2; @@ -2330,6 +2324,7 @@ // A node selector represents the union of the results of one or more label queries // over a set of nodes; that is, it represents the OR of the selectors represented // by the node selector terms. +// +structType=atomic message NodeSelector { // Required. A list of node selector terms. The terms are ORed. repeated NodeSelectorTerm nodeSelectorTerms = 1; @@ -2357,6 +2352,7 @@ // A null or empty node selector term matches no objects. The requirements of // them are ANDed. // The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. +// +structType=atomic message NodeSelectorTerm { // A list of node selector requirements by node's labels. // +optional @@ -2393,8 +2389,9 @@ // +optional repeated Taint taints = 5; - // If specified, the source to get node configuration from - // The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field + // Deprecated. If specified, the source of the node's configuration. + // The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field. + // This field is deprecated as of 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration // +optional optional NodeConfigSource configSource = 6; @@ -2504,6 +2501,7 @@ } // ObjectFieldSelector selects an APIVersioned field of an object. +// +structType=atomic message ObjectFieldSelector { // Version of the schema the FieldPath is written in terms of, defaults to "v1". // +optional @@ -2529,6 +2527,7 @@ // Instead of using this type, create a locally provided and used type that is well-focused on your reference. // For example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 . // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +structType=atomic message ObjectReference { // Kind of the referent. // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds @@ -2684,13 +2683,32 @@ // This field can be used to specify either: // * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) // * An existing PVC (PersistentVolumeClaim) - // * An existing custom resource that implements data population (Alpha) - // In order to use custom resource types that implement data population, - // the AnyVolumeDataSource feature gate must be enabled. // If the provisioner or an external controller can support the specified data source, // it will create a new volume based on the contents of the specified data source. + // If the AnyVolumeDataSource feature gate is enabled, this field will always have + // the same contents as the DataSourceRef field. // +optional optional TypedLocalObjectReference dataSource = 7; + + // Specifies the object from which to populate the volume with data, if a non-empty + // volume is desired. This may be any local object from a non-empty API group (non + // core object) or a PersistentVolumeClaim object. + // When this field is specified, volume binding will only succeed if the type of + // the specified object matches some installed volume populator or dynamic + // provisioner. + // This field will replace the functionality of the DataSource field and as such + // if both fields are non-empty, they must have the same value. For backwards + // compatibility, both fields (DataSource and DataSourceRef) will be set to the same + // value automatically if one of them is empty and the other is non-empty. + // There are two important differences between DataSource and DataSourceRef: + // * While DataSource only allows two specific types of objects, DataSourceRef + // allows any non-core object, as well as PersistentVolumeClaim objects. + // * While DataSource ignores disallowed values (dropping them), DataSourceRef + // preserves all values, and generates an error if a disallowed value is + // specified. + // (Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + // +optional + optional TypedLocalObjectReference dataSourceRef = 8; } // PersistentVolumeClaimStatus is the current status of a persistent volume claim. @@ -3024,7 +3042,7 @@ // and the ones listed in the namespaces field. // null selector and null or empty namespaces list means "this pod's namespace". // An empty selector ({}) matches all namespaces. - // This field is alpha-level and is only honored when PodAffinityNamespaceSelector feature is enabled. + // This field is beta-level and is only honored when PodAffinityNamespaceSelector feature is enabled. // +optional optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector namespaceSelector = 4; } @@ -3450,6 +3468,7 @@ // Selector which must match a node's labels for the pod to be scheduled on that node. // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ // +optional + // +mapType=atomic map nodeSelector = 7; // ServiceAccountName is the name of the ServiceAccount to use to run this pod. @@ -3571,7 +3590,7 @@ // If specified, all readiness gates will be evaluated for pod readiness. // A pod is ready when all its containers are ready AND // all conditions specified in the readiness gates have status equal to "True" - // More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md + // More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates // +optional repeated PodReadinessGate readinessGates = 28; @@ -3579,7 +3598,7 @@ // to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. // If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an // empty definition that uses the default runtime handler. - // More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + // More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class // This is a beta feature as of Kubernetes v1.14. // +optional optional string runtimeClassName = 29; @@ -3603,8 +3622,8 @@ // The RuntimeClass admission controller will reject Pod create requests which have the overhead already // set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value // defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. - // More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md - // This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature. + // More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md + // This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. // +optional map overhead = 32; @@ -3893,7 +3912,8 @@ // value overrides the value provided by the pod spec. // Value must be non-negative integer. The value zero indicates stop immediately via // the kill signal (no opportunity to shut down). - // This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate. + // This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + // Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. // +optional optional int64 terminationGracePeriodSeconds = 7; } @@ -4138,6 +4158,7 @@ // controller, if empty defaulted to labels on Pod template. // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors // +optional + // +mapType=atomic map selector = 2; // Template is the object that describes the pod that will be created if @@ -4178,6 +4199,7 @@ } // ResourceFieldSelector represents container resources (cpu, memory) and their output format +// +structType=atomic message ResourceFieldSelector { // Container name: required for volumes, optional for env vars // +optional @@ -4380,6 +4402,7 @@ // A scope selector represents the AND of the selectors represented // by the scoped-resource selector requirements. +// +structType=atomic message ScopeSelector { // A list of scope selector requirements by scope of the resources. // +optional @@ -4475,6 +4498,7 @@ } // SecretKeySelector selects a key of a Secret. +// +structType=atomic message SecretKeySelector { // The name of the secret in the pod's namespace to select from. optional LocalObjectReference localObjectReference = 1; @@ -4525,6 +4549,7 @@ // SecretReference represents a Secret Reference. It has enough information to retrieve secret // in any namespace +// +structType=atomic message SecretReference { // Name is unique within a namespace to reference a secret resource. // +optional @@ -4780,8 +4805,6 @@ // RFC-6335 and http://www.iana.org/assignments/service-names). // Non-standard protocols should use prefixed names such as // mycompany.com/my-custom-protocol. - // This is a beta field that is guarded by the ServiceAppProtocol feature - // gate and enabled by default. // +optional optional string appProtocol = 6; @@ -4841,6 +4864,7 @@ // Ignored if type is ExternalName. // More info: https://kubernetes.io/docs/concepts/services-networking/service/ // +optional + // +mapType=atomic map selector = 2; // clusterIP is the IP address of the service and is usually assigned @@ -4937,7 +4961,7 @@ // If specified and supported by the platform, this will restrict traffic through the cloud-provider // load-balancer will be restricted to the specified client IPs. This field will be ignored if the // cloud-provider does not support the feature." - // More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/ + // More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/ // +optional repeated string loadBalancerSourceRanges = 9; @@ -4984,23 +5008,6 @@ // +optional optional SessionAffinityConfig sessionAffinityConfig = 14; - // topologyKeys is a preference-order list of topology keys which - // implementations of services should use to preferentially sort endpoints - // when accessing this Service, it can not be used at the same time as - // externalTrafficPolicy=Local. - // Topology keys must be valid label keys and at most 16 keys may be specified. - // Endpoints are chosen based on the first topology key with available backends. - // If this field is specified and all entries have no backends that match - // the topology of the client, the service has no backends for that client - // and connections should fail. - // The special value "*" may be used to mean "any topology". This catch-all - // value, if used, only makes sense as the last value in the list. - // If this is not specified or empty, no topology constraints will be applied. - // This field is alpha-level and is only honored by servers that enable the ServiceTopology feature. - // This field is deprecated and will be removed in a future version. - // +optional - repeated string topologyKeys = 16; - // IPFamilies is a list of IP families (e.g. IPv4, IPv6) assigned to this // service, and is gated by the "IPv6DualStack" feature gate. This field // is usually assigned automatically based on cluster configuration and the @@ -5035,11 +5042,14 @@ optional string ipFamilyPolicy = 17; // allocateLoadBalancerNodePorts defines if NodePorts will be automatically - // allocated for services with type LoadBalancer. Default is "true". It may be - // set to "false" if the cluster load-balancer does not rely on NodePorts. - // allocateLoadBalancerNodePorts may only be set for services with type LoadBalancer - // and will be cleared if the type is changed to any other type. - // This field is alpha-level and is only honored by servers that enable the ServiceLBNodePortControl feature. + // allocated for services with type LoadBalancer. Default is "true". It + // may be set to "false" if the cluster load-balancer does not rely on + // NodePorts. If the caller requests specific NodePorts (by specifying a + // value), those requests will be respected, regardless of this field. + // This field may only be set for services with type LoadBalancer and will + // be cleared if the type is changed to any other type. + // This field is beta-level and is only honored by servers that enable the ServiceLBNodePortControl feature. + // +featureGate=ServiceLBNodePortControl // +optional optional bool allocateLoadBalancerNodePorts = 20; @@ -5246,6 +5256,7 @@ // The requirements of them are ANDed. // It provides a subset of functionality as NodeSelectorTerm. // This is an alpha feature and may change in the future. +// +structType=atomic message TopologySelectorTerm { // A list of topology selector requirements by labels. // +optional @@ -5312,6 +5323,7 @@ // TypedLocalObjectReference contains enough information to let you locate the // typed referenced object inside the same namespace. +// +structType=atomic message TypedLocalObjectReference { // APIGroup is the group for the resource being referenced. // If APIGroup is not specified, the specified Kind must be in the core API group. @@ -5625,5 +5637,15 @@ // PodSecurityContext, the value specified in SecurityContext takes precedence. // +optional optional string runAsUserName = 3; + + // HostProcess determines if a container should be run as a 'Host Process' container. + // This field is alpha-level and will only be honored by components that enable the + // WindowsHostProcessContainers feature flag. Setting this field without the feature + // flag will result in errors when validating the Pod. All of a Pod's containers must + // have the same effective HostProcess value (it is not allowed to have a mix of HostProcess + // containers and non-HostProcess containers). In addition, if HostProcess is true + // then HostNetwork must also be set to true. + // +optional + optional bool hostProcess = 4; } diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/register.go libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/register.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/register.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/register.go 2021-12-26 00:45:51.000000000 +0000 @@ -88,7 +88,6 @@ &RangeAllocation{}, &ConfigMap{}, &ConfigMapList{}, - &EphemeralContainers{}, ) // Add common types diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/types.go libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/types.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -30,8 +30,6 @@ NamespaceAll string = "" // NamespaceNodeLease is the namespace where we place node lease objects (used for node heartbeats) NamespaceNodeLease string = "kube-node-lease" - // TopologyKeyAny is the service topology key that matches any node - TopologyKeyAny string = "*" ) // Volume represents a named volume in a pod that may be accessed by any container in the pod. @@ -494,13 +492,31 @@ // This field can be used to specify either: // * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) // * An existing PVC (PersistentVolumeClaim) - // * An existing custom resource that implements data population (Alpha) - // In order to use custom resource types that implement data population, - // the AnyVolumeDataSource feature gate must be enabled. // If the provisioner or an external controller can support the specified data source, // it will create a new volume based on the contents of the specified data source. + // If the AnyVolumeDataSource feature gate is enabled, this field will always have + // the same contents as the DataSourceRef field. // +optional DataSource *TypedLocalObjectReference `json:"dataSource,omitempty" protobuf:"bytes,7,opt,name=dataSource"` + // Specifies the object from which to populate the volume with data, if a non-empty + // volume is desired. This may be any local object from a non-empty API group (non + // core object) or a PersistentVolumeClaim object. + // When this field is specified, volume binding will only succeed if the type of + // the specified object matches some installed volume populator or dynamic + // provisioner. + // This field will replace the functionality of the DataSource field and as such + // if both fields are non-empty, they must have the same value. For backwards + // compatibility, both fields (DataSource and DataSourceRef) will be set to the same + // value automatically if one of them is empty and the other is non-empty. + // There are two important differences between DataSource and DataSourceRef: + // * While DataSource only allows two specific types of objects, DataSourceRef + // allows any non-core object, as well as PersistentVolumeClaim objects. + // * While DataSource ignores disallowed values (dropping them), DataSourceRef + // preserves all values, and generates an error if a disallowed value is + // specified. + // (Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled. + // +optional + DataSourceRef *TypedLocalObjectReference `json:"dataSourceRef,omitempty" protobuf:"bytes,8,opt,name=dataSourceRef"` } // PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type @@ -562,6 +578,9 @@ ReadOnlyMany PersistentVolumeAccessMode = "ReadOnlyMany" // can be mounted in read/write mode to many hosts ReadWriteMany PersistentVolumeAccessMode = "ReadWriteMany" + // can be mounted in read/write mode to exactly 1 pod + // cannot be used in combination with other access modes + ReadWriteOncePod PersistentVolumeAccessMode = "ReadWriteOncePod" ) type PersistentVolumePhase string @@ -861,6 +880,7 @@ // SecretReference represents a Secret Reference. It has enough information to retrieve secret // in any namespace +// +structType=atomic type SecretReference struct { // Name is unique within a namespace to reference a secret resource. // +optional @@ -1915,11 +1935,12 @@ // Optional: no more than one of the following may be specified. // Variable references $(VAR_NAME) are expanded - // using the previous defined environment variables in the container and + // using the previously defined environment variables in the container and // any service environment variables. If a variable cannot be resolved, - // the reference in the input string will be unchanged. The $(VAR_NAME) - // syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped - // references will never be expanded, regardless of whether the variable + // the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + // "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + // Escaped references will never be expanded, regardless of whether the variable // exists or not. // Defaults to "". // +optional @@ -1948,6 +1969,7 @@ } // ObjectFieldSelector selects an APIVersioned field of an object. +// +structType=atomic type ObjectFieldSelector struct { // Version of the schema the FieldPath is written in terms of, defaults to "v1". // +optional @@ -1957,6 +1979,7 @@ } // ResourceFieldSelector represents container resources (cpu, memory) and their output format +// +structType=atomic type ResourceFieldSelector struct { // Container name: required for volumes, optional for env vars // +optional @@ -1969,6 +1992,7 @@ } // Selects a key from a ConfigMap. +// +structType=atomic type ConfigMapKeySelector struct { // The ConfigMap to select from. LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"` @@ -1980,6 +2004,7 @@ } // SecretKeySelector selects a key of a Secret. +// +structType=atomic type SecretKeySelector struct { // The name of the secret in the pod's namespace to select from. LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"` @@ -2125,7 +2150,8 @@ // value overrides the value provided by the pod spec. // Value must be non-negative integer. The value zero indicates stop immediately via // the kill signal (no opportunity to shut down). - // This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate. + // This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. + // Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset. // +optional TerminationGracePeriodSeconds *int64 `json:"terminationGracePeriodSeconds,omitempty" protobuf:"varint,7,opt,name=terminationGracePeriodSeconds"` } @@ -2212,20 +2238,20 @@ // Entrypoint array. Not executed within a shell. // The docker image's ENTRYPOINT is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional Command []string `json:"command,omitempty" protobuf:"bytes,3,rep,name=command"` // Arguments to the entrypoint. // The docker image's CMD is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional Args []string `json:"args,omitempty" protobuf:"bytes,4,rep,name=args"` @@ -2329,8 +2355,8 @@ // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images // +optional ImagePullPolicy PullPolicy `json:"imagePullPolicy,omitempty" protobuf:"bytes,14,opt,name=imagePullPolicy,casttype=PullPolicy"` - // Security options the pod should run with. - // More info: https://kubernetes.io/docs/concepts/policy/security-context/ + // SecurityContext defines the security options the container should be run with. + // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. // More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ // +optional SecurityContext *SecurityContext `json:"securityContext,omitempty" protobuf:"bytes,15,opt,name=securityContext"` @@ -2522,6 +2548,7 @@ PodFailed PodPhase = "Failed" // PodUnknown means that for some reason the state of the pod could not be obtained, typically due // to an error in communicating with the host of the pod. + // Deprecated: It isn't being set since 2015 (74da3b14b0c0f658b3bb8d2def5094686d0e9095) PodUnknown PodPhase = "Unknown" ) @@ -2616,6 +2643,7 @@ // A node selector represents the union of the results of one or more label queries // over a set of nodes; that is, it represents the OR of the selectors represented // by the node selector terms. +// +structType=atomic type NodeSelector struct { //Required. A list of node selector terms. The terms are ORed. NodeSelectorTerms []NodeSelectorTerm `json:"nodeSelectorTerms" protobuf:"bytes,1,rep,name=nodeSelectorTerms"` @@ -2624,6 +2652,7 @@ // A null or empty node selector term matches no objects. The requirements of // them are ANDed. // The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. +// +structType=atomic type NodeSelectorTerm struct { // A list of node selector requirements by node's labels. // +optional @@ -2668,7 +2697,10 @@ // The requirements of them are ANDed. // It provides a subset of functionality as NodeSelectorTerm. // This is an alpha feature and may change in the future. +// +structType=atomic type TopologySelectorTerm struct { + // Usage: Fields of type []TopologySelectorTerm must be listType=atomic. + // A list of topology selector requirements by labels. // +optional MatchLabelExpressions []TopologySelectorLabelRequirement `json:"matchLabelExpressions,omitempty" protobuf:"bytes,1,rep,name=matchLabelExpressions"` @@ -2803,7 +2835,7 @@ // and the ones listed in the namespaces field. // null selector and null or empty namespaces list means "this pod's namespace". // An empty selector ({}) matches all namespaces. - // This field is alpha-level and is only honored when PodAffinityNamespaceSelector feature is enabled. + // This field is beta-level and is only honored when PodAffinityNamespaceSelector feature is enabled. // +optional NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty" protobuf:"bytes,4,opt,name=namespaceSelector"` } @@ -3005,6 +3037,7 @@ // Selector which must match a node's labels for the pod to be scheduled on that node. // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ // +optional + // +mapType=atomic NodeSelector map[string]string `json:"nodeSelector,omitempty" protobuf:"bytes,7,rep,name=nodeSelector"` // ServiceAccountName is the name of the ServiceAccount to use to run this pod. @@ -3108,14 +3141,14 @@ // If specified, all readiness gates will be evaluated for pod readiness. // A pod is ready when all its containers are ready AND // all conditions specified in the readiness gates have status equal to "True" - // More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md + // More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates // +optional ReadinessGates []PodReadinessGate `json:"readinessGates,omitempty" protobuf:"bytes,28,opt,name=readinessGates"` // RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used // to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. // If unset or empty, the "legacy" RuntimeClass will be used, which is an implicit class with an // empty definition that uses the default runtime handler. - // More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + // More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class // This is a beta feature as of Kubernetes v1.14. // +optional RuntimeClassName *string `json:"runtimeClassName,omitempty" protobuf:"bytes,29,opt,name=runtimeClassName"` @@ -3136,8 +3169,8 @@ // The RuntimeClass admission controller will reject Pod create requests which have the overhead already // set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value // defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. - // More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md - // This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature. + // More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md + // This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature. // +optional Overhead ResourceList `json:"overhead,omitempty" protobuf:"bytes,32,opt,name=overhead"` // TopologySpreadConstraints describes how a group of pods ought to spread across topology @@ -3423,20 +3456,20 @@ // Entrypoint array. Not executed within a shell. // The docker image's ENTRYPOINT is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional Command []string `json:"command,omitempty" protobuf:"bytes,3,rep,name=command"` // Arguments to the entrypoint. // The docker image's CMD is used if this is not provided. // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable - // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax - // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, - // regardless of whether the variable exists or not. - // Cannot be updated. + // cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + // to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + // produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + // of whether the variable exists or not. Cannot be updated. // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell // +optional Args []string `json:"args,omitempty" protobuf:"bytes,4,rep,name=args"` @@ -3514,7 +3547,8 @@ // More info: https://kubernetes.io/docs/concepts/containers/images#updating-images // +optional ImagePullPolicy PullPolicy `json:"imagePullPolicy,omitempty" protobuf:"bytes,14,opt,name=imagePullPolicy,casttype=PullPolicy"` - // SecurityContext is not allowed for ephemeral containers. + // Optional: SecurityContext defines the security options the ephemeral container should be run with. + // If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. // +optional SecurityContext *SecurityContext `json:"securityContext,omitempty" protobuf:"bytes,15,opt,name=securityContext"` @@ -3678,8 +3712,7 @@ } // +genclient -// +genclient:method=GetEphemeralContainers,verb=get,subresource=ephemeralcontainers,result=EphemeralContainers -// +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers,input=EphemeralContainers,result=EphemeralContainers +// +genclient:method=UpdateEphemeralContainers,verb=update,subresource=ephemeralcontainers // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // Pod is a collection of containers that can run on a host. This resource is created @@ -3785,6 +3818,7 @@ // controller, if empty defaulted to labels on Pod template. // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors // +optional + // +mapType=atomic Selector map[string]string `json:"selector,omitempty" protobuf:"bytes,2,rep,name=selector"` // TemplateRef is a reference to an object that describes the pod that will be created if @@ -4026,11 +4060,6 @@ Ports []PortStatus `json:"ports,omitempty" protobuf:"bytes,4,rep,name=ports"` } -const ( - // MaxServiceTopologyKeys is the largest number of topology keys allowed on a service - MaxServiceTopologyKeys = 16 -) - // IPFamily represents the IP Family (IPv4 or IPv6). This type is used // to express the family of an IP expressed by a type (e.g. service.spec.ipFamilies). type IPFamily string @@ -4083,6 +4112,7 @@ // Ignored if type is ExternalName. // More info: https://kubernetes.io/docs/concepts/services-networking/service/ // +optional + // +mapType=atomic Selector map[string]string `json:"selector,omitempty" protobuf:"bytes,2,rep,name=selector"` // clusterIP is the IP address of the service and is usually assigned @@ -4179,7 +4209,7 @@ // If specified and supported by the platform, this will restrict traffic through the cloud-provider // load-balancer will be restricted to the specified client IPs. This field will be ignored if the // cloud-provider does not support the feature." - // More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/ + // More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/ // +optional LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges,omitempty" protobuf:"bytes,9,opt,name=loadBalancerSourceRanges"` @@ -4226,22 +4256,8 @@ // +optional SessionAffinityConfig *SessionAffinityConfig `json:"sessionAffinityConfig,omitempty" protobuf:"bytes,14,opt,name=sessionAffinityConfig"` - // topologyKeys is a preference-order list of topology keys which - // implementations of services should use to preferentially sort endpoints - // when accessing this Service, it can not be used at the same time as - // externalTrafficPolicy=Local. - // Topology keys must be valid label keys and at most 16 keys may be specified. - // Endpoints are chosen based on the first topology key with available backends. - // If this field is specified and all entries have no backends that match - // the topology of the client, the service has no backends for that client - // and connections should fail. - // The special value "*" may be used to mean "any topology". This catch-all - // value, if used, only makes sense as the last value in the list. - // If this is not specified or empty, no topology constraints will be applied. - // This field is alpha-level and is only honored by servers that enable the ServiceTopology feature. - // This field is deprecated and will be removed in a future version. - // +optional - TopologyKeys []string `json:"topologyKeys,omitempty" protobuf:"bytes,16,opt,name=topologyKeys"` + // TopologyKeys is tombstoned to show why 16 is reserved protobuf tag. + //TopologyKeys []string `json:"topologyKeys,omitempty" protobuf:"bytes,16,opt,name=topologyKeys"` // IPFamily is tombstoned to show why 15 is a reserved protobuf tag. // IPFamily *IPFamily `json:"ipFamily,omitempty" protobuf:"bytes,15,opt,name=ipFamily,Configcasttype=IPFamily"` @@ -4280,11 +4296,14 @@ IPFamilyPolicy *IPFamilyPolicyType `json:"ipFamilyPolicy,omitempty" protobuf:"bytes,17,opt,name=ipFamilyPolicy,casttype=IPFamilyPolicyType"` // allocateLoadBalancerNodePorts defines if NodePorts will be automatically - // allocated for services with type LoadBalancer. Default is "true". It may be - // set to "false" if the cluster load-balancer does not rely on NodePorts. - // allocateLoadBalancerNodePorts may only be set for services with type LoadBalancer - // and will be cleared if the type is changed to any other type. - // This field is alpha-level and is only honored by servers that enable the ServiceLBNodePortControl feature. + // allocated for services with type LoadBalancer. Default is "true". It + // may be set to "false" if the cluster load-balancer does not rely on + // NodePorts. If the caller requests specific NodePorts (by specifying a + // value), those requests will be respected, regardless of this field. + // This field may only be set for services with type LoadBalancer and will + // be cleared if the type is changed to any other type. + // This field is beta-level and is only honored by servers that enable the ServiceLBNodePortControl feature. + // +featureGate=ServiceLBNodePortControl // +optional AllocateLoadBalancerNodePorts *bool `json:"allocateLoadBalancerNodePorts,omitempty" protobuf:"bytes,20,opt,name=allocateLoadBalancerNodePorts"` @@ -4335,8 +4354,6 @@ // RFC-6335 and http://www.iana.org/assignments/service-names). // Non-standard protocols should use prefixed names such as // mycompany.com/my-custom-protocol. - // This is a beta field that is guarded by the ServiceAppProtocol feature - // gate and enabled by default. // +optional AppProtocol *string `json:"appProtocol,omitempty" protobuf:"bytes,6,opt,name=appProtocol"` @@ -4523,6 +4540,7 @@ } // EndpointAddress is a tuple that describes single IP address. +// +structType=atomic type EndpointAddress struct { // The IP of this endpoint. // May not be loopback (127.0.0.0/8), link-local (169.254.0.0/16), @@ -4543,6 +4561,7 @@ } // EndpointPort is a tuple that describes a single port. +// +structType=atomic type EndpointPort struct { // The name of this port. This must match the 'name' field in the // corresponding ServicePort. @@ -4566,8 +4585,6 @@ // RFC-6335 and http://www.iana.org/assignments/service-names). // Non-standard protocols should use prefixed names such as // mycompany.com/my-custom-protocol. - // This is a beta field that is guarded by the ServiceAppProtocol feature - // gate and enabled by default. // +optional AppProtocol *string `json:"appProtocol,omitempty" protobuf:"bytes,4,opt,name=appProtocol"` } @@ -4609,8 +4626,10 @@ // If specified, the node's taints. // +optional Taints []Taint `json:"taints,omitempty" protobuf:"bytes,5,opt,name=taints"` - // If specified, the source to get node configuration from - // The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field + + // Deprecated. If specified, the source of the node's configuration. + // The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field. + // This field is deprecated as of 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration // +optional ConfigSource *NodeConfigSource `json:"configSource,omitempty" protobuf:"bytes,6,opt,name=configSource"` @@ -4621,6 +4640,7 @@ } // NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil. +// This API is deprecated since 1.22 type NodeConfigSource struct { // For historical context, regarding the below kind, apiVersion, and configMapRef deprecation tags: // 1. kind/apiVersion were used by the kubelet to persist this struct to disk (they had no protobuf tags) @@ -4638,6 +4658,7 @@ } // ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node. +// This API is deprecated since 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration type ConfigMapNodeConfigSource struct { // Namespace is the metadata.namespace of the referenced ConfigMap. // This field is required in all cases. @@ -4856,6 +4877,7 @@ type ContainerImage struct { // Names by which this image is known. // e.g. ["k8s.gcr.io/hyperkube:v1.0.7", "dockerhub.io/google_containers/hyperkube:v1.0.7"] + // +optional Names []string `json:"names" protobuf:"bytes,1,rep,name=names"` // The size of the image in bytes. // +optional @@ -4916,11 +4938,44 @@ // These are valid address type of node. const ( - NodeHostName NodeAddressType = "Hostname" - NodeExternalIP NodeAddressType = "ExternalIP" - NodeInternalIP NodeAddressType = "InternalIP" - NodeExternalDNS NodeAddressType = "ExternalDNS" + // NodeHostName identifies a name of the node. Although every node can be assumed + // to have a NodeAddress of this type, its exact syntax and semantics are not + // defined, and are not consistent between different clusters. + NodeHostName NodeAddressType = "Hostname" + + // NodeInternalIP identifies an IP address which is assigned to one of the node's + // network interfaces. Every node should have at least one address of this type. + // + // An internal IP is normally expected to be reachable from every other node, but + // may not be visible to hosts outside the cluster. By default it is assumed that + // kube-apiserver can reach node internal IPs, though it is possible to configure + // clusters where this is not the case. + // + // NodeInternalIP is the default type of node IP, and does not necessarily imply + // that the IP is ONLY reachable internally. If a node has multiple internal IPs, + // no specific semantics are assigned to the additional IPs. + NodeInternalIP NodeAddressType = "InternalIP" + + // NodeExternalIP identifies an IP address which is, in some way, intended to be + // more usable from outside the cluster then an internal IP, though no specific + // semantics are defined. It may be a globally routable IP, though it is not + // required to be. + // + // External IPs may be assigned directly to an interface on the node, like a + // NodeInternalIP, or alternatively, packets sent to the external IP may be NAT'ed + // to an internal node IP rather than being delivered directly (making the IP less + // efficient for node-to-node traffic than a NodeInternalIP). + NodeExternalIP NodeAddressType = "ExternalIP" + + // NodeInternalDNS identifies a DNS name which resolves to an IP address which has + // the characteristics of a NodeInternalIP. The IP it resolves to may or may not + // be a listed NodeInternalIP address. NodeInternalDNS NodeAddressType = "InternalDNS" + + // NodeExternalDNS identifies a DNS name which resolves to an IP address which has + // the characteristics of a NodeExternalIP. The IP it resolves to may or may not + // be a listed NodeExternalIP address. + NodeExternalDNS NodeAddressType = "ExternalDNS" ) // NodeAddress contains information for the node's address. @@ -5135,22 +5190,6 @@ Target ObjectReference `json:"target" protobuf:"bytes,2,opt,name=target"` } -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// A list of ephemeral containers used with the Pod ephemeralcontainers subresource. -type EphemeralContainers struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - - // A list of ephemeral containers associated with this pod. New ephemeral containers - // may be appended to this list, but existing ephemeral containers may not be removed - // or modified. - // +patchMergeKey=name - // +patchStrategy=merge - EphemeralContainers []EphemeralContainer `json:"ephemeralContainers" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,2,rep,name=ephemeralContainers"` -} - // Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out. // +k8s:openapi-gen=false type Preconditions struct { @@ -5362,6 +5401,7 @@ // Instead of using this type, create a locally provided and used type that is well-focused on your reference. // For example, ServiceReferences for admission registration: https://github.com/kubernetes/api/blob/release-1.17/admissionregistration/v1/types.go#L533 . // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +structType=atomic type ObjectReference struct { // Kind of the referent. // More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds @@ -5401,6 +5441,7 @@ // LocalObjectReference contains enough information to let you locate the // referenced object inside the same namespace. +// +structType=atomic type LocalObjectReference struct { // Name of the referent. // More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names @@ -5411,6 +5452,7 @@ // TypedLocalObjectReference contains enough information to let you locate the // typed referenced object inside the same namespace. +// +structType=atomic type TypedLocalObjectReference struct { // APIGroup is the group for the resource being referenced. // If APIGroup is not specified, the specified Kind must be in the core API group. @@ -5686,7 +5728,7 @@ // Match all pod objects that have priority class mentioned ResourceQuotaScopePriorityClass ResourceQuotaScope = "PriorityClass" // Match all pod objects that have cross-namespace pod (anti)affinity mentioned. - // This is an alpha feature enabled by the PodAffinityNamespaceSelector feature flag. + // This is a beta feature enabled by the PodAffinityNamespaceSelector feature flag. ResourceQuotaScopeCrossNamespacePodAffinity ResourceQuotaScope = "CrossNamespacePodAffinity" ) @@ -5709,6 +5751,7 @@ // A scope selector represents the AND of the selectors represented // by the scoped-resource selector requirements. +// +structType=atomic type ScopeSelector struct { // A list of scope selector requirements by scope of the resources. // +optional @@ -5906,7 +5949,7 @@ // TODO: Consider supporting different formats, specifying CA/destinationCA. SecretTypeTLS SecretType = "kubernetes.io/tls" - // TLSCertKey is the key for tls certificates in a TLS secert. + // TLSCertKey is the key for tls certificates in a TLS secret. TLSCertKey = "tls.crt" // TLSPrivateKeyKey is the key for the private key field in a TLS secret. TLSPrivateKeyKey = "tls.key" @@ -6212,6 +6255,16 @@ // PodSecurityContext, the value specified in SecurityContext takes precedence. // +optional RunAsUserName *string `json:"runAsUserName,omitempty" protobuf:"bytes,3,opt,name=runAsUserName"` + + // HostProcess determines if a container should be run as a 'Host Process' container. + // This field is alpha-level and will only be honored by components that enable the + // WindowsHostProcessContainers feature flag. Setting this field without the feature + // flag will result in errors when validating the Pod. All of a Pod's containers must + // have the same effective HostProcess value (it is not allowed to have a mix of HostProcess + // containers and non-HostProcess containers). In addition, if HostProcess is true + // then HostNetwork must also be set to true. + // +optional + HostProcess *bool `json:"hostProcess,omitempty" protobuf:"bytes,4,opt,name=hostProcess"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go 2021-12-26 00:45:51.000000000 +0000 @@ -291,7 +291,7 @@ } var map_ConfigMapNodeConfigSource = map[string]string{ - "": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node.", + "": "ConfigMapNodeConfigSource contains the information to reference a ConfigMap as a config source for the Node. This API is deprecated since 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration", "namespace": "Namespace is the metadata.namespace of the referenced ConfigMap. This field is required in all cases.", "name": "Name is the metadata.name of the referenced ConfigMap. This field is required in all cases.", "uid": "UID is the metadata.UID of the referenced ConfigMap. This field is forbidden in Node.Spec, and required in Node.Status.", @@ -328,8 +328,8 @@ "": "A single application container that you want to run within a pod.", "name": "Name of the container specified as a DNS_LABEL. Each container in a pod must have a unique name (DNS_LABEL). Cannot be updated.", "image": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images This field is optional to allow higher level config management to default or override container images in workload controllers like Deployments and StatefulSets.", - "command": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - "args": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "command": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "args": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", "workingDir": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", "ports": "List of ports to expose from the container. Exposing a port here gives the system additional information about the network connections a container uses, but is primarily informational. Not specifying a port here DOES NOT prevent that port from being exposed. Any port which is listening on the default \"0.0.0.0\" address inside a container will be accessible from the network. Cannot be updated.", "envFrom": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", @@ -344,7 +344,7 @@ "terminationMessagePath": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", "terminationMessagePolicy": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", "imagePullPolicy": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", - "securityContext": "Security options the pod should run with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", + "securityContext": "SecurityContext defines the security options the container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/", "stdin": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", "stdinOnce": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", "tty": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", @@ -506,7 +506,7 @@ "name": "The name of this port. This must match the 'name' field in the corresponding ServicePort. Must be a DNS_LABEL. Optional only if one port is defined.", "port": "The port number of the endpoint.", "protocol": "The IP protocol for this port. Must be UDP, TCP, or SCTP. Default is TCP.", - "appProtocol": "The application protocol for this port. This field follows standard Kubernetes label syntax. Un-prefixed names are reserved for IANA standard service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). Non-standard protocols should use prefixed names such as mycompany.com/my-custom-protocol. This is a beta field that is guarded by the ServiceAppProtocol feature gate and enabled by default.", + "appProtocol": "The application protocol for this port. This field follows standard Kubernetes label syntax. Un-prefixed names are reserved for IANA standard service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). Non-standard protocols should use prefixed names such as mycompany.com/my-custom-protocol.", } func (EndpointPort) SwaggerDoc() map[string]string { @@ -558,7 +558,7 @@ var map_EnvVar = map[string]string{ "": "EnvVar represents an environment variable present in a Container.", "name": "Name of the environment variable. Must be a C_IDENTIFIER.", - "value": "Variable references $(VAR_NAME) are expanded using the previous defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", + "value": "Variable references $(VAR_NAME) are expanded using the previously defined environment variables in the container and any service environment variables. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Defaults to \"\".", "valueFrom": "Source for the environment variable's value. Cannot be used if value is not empty.", } @@ -591,8 +591,8 @@ "": "EphemeralContainerCommon is a copy of all fields in Container to be inlined in EphemeralContainer. This separate type allows easy conversion from EphemeralContainer to Container and allows separate documentation for the fields of EphemeralContainer. When a new field is added to Container it must be added here as well.", "name": "Name of the ephemeral container specified as a DNS_LABEL. This name must be unique among all containers, init containers and ephemeral containers.", "image": "Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images", - "command": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", - "args": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "command": "Entrypoint array. Not executed within a shell. The docker image's ENTRYPOINT is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", + "args": "Arguments to the entrypoint. The docker image's CMD is used if this is not provided. Variable references $(VAR_NAME) are expanded using the container's environment. If a variable cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. \"$$(VAR_NAME)\" will produce the string literal \"$(VAR_NAME)\". Escaped references will never be expanded, regardless of whether the variable exists or not. Cannot be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell", "workingDir": "Container's working directory. If not specified, the container runtime's default will be used, which might be configured in the container image. Cannot be updated.", "ports": "Ports are not allowed for ephemeral containers.", "envFrom": "List of sources to populate environment variables in the container. The keys defined within a source must be a C_IDENTIFIER. All invalid keys will be reported as an event when the container is starting. When a key exists in multiple sources, the value associated with the last source will take precedence. Values defined by an Env with a duplicate key will take precedence. Cannot be updated.", @@ -607,7 +607,7 @@ "terminationMessagePath": "Optional: Path at which the file to which the container's termination message will be written is mounted into the container's filesystem. Message written is intended to be brief final status, such as an assertion failure message. Will be truncated by the node if greater than 4096 bytes. The total message length across all containers will be limited to 12kb. Defaults to /dev/termination-log. Cannot be updated.", "terminationMessagePolicy": "Indicate how the termination message should be populated. File will use the contents of terminationMessagePath to populate the container status message on both success and failure. FallbackToLogsOnError will use the last chunk of container log output if the termination message file is empty and the container exited with an error. The log output is limited to 2048 bytes or 80 lines, whichever is smaller. Defaults to File. Cannot be updated.", "imagePullPolicy": "Image pull policy. One of Always, Never, IfNotPresent. Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images", - "securityContext": "SecurityContext is not allowed for ephemeral containers.", + "securityContext": "Optional: SecurityContext defines the security options the ephemeral container should be run with. If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext.", "stdin": "Whether this container should allocate a buffer for stdin in the container runtime. If this is not set, reads from stdin in the container will always result in EOF. Default is false.", "stdinOnce": "Whether the container runtime should close the stdin channel after it has been opened by a single attach. When stdin is true the stdin stream will remain open across multiple attach sessions. If stdinOnce is set to true, stdin is opened on container start, is empty until the first client attaches to stdin, and then remains open and accepts data until the client disconnects, at which time stdin is closed and remains closed until the container is restarted. If this flag is false, a container processes that reads from stdin will never receive an EOF. Default is false", "tty": "Whether this container should allocate a TTY for itself, also requires 'stdin' to be true. Default is false.", @@ -617,15 +617,6 @@ return map_EphemeralContainerCommon } -var map_EphemeralContainers = map[string]string{ - "": "A list of ephemeral containers used with the Pod ephemeralcontainers subresource.", - "ephemeralContainers": "A list of ephemeral containers associated with this pod. New ephemeral containers may be appended to this list, but existing ephemeral containers may not be removed or modified.", -} - -func (EphemeralContainers) SwaggerDoc() map[string]string { - return map_EphemeralContainers -} - var map_EphemeralVolumeSource = map[string]string{ "": "Represents an ephemeral volume that is handled by a normal storage driver.", "volumeClaimTemplate": "Will be used to create a stand-alone PVC to provision the volume. The pod in which this EphemeralVolumeSource is embedded will be the owner of the PVC, i.e. the PVC will be deleted together with the pod. The name of the PVC will be `-` where `` is the name from the `PodSpec.Volumes` array entry. Pod validation will reject the pod if the concatenated name is not valid for a PVC (for example, too long).\n\nAn existing PVC with that name that is not owned by the pod will *not* be used for the pod to avoid using an unrelated volume by mistake. Starting the pod is then blocked until the unrelated PVC is removed. If such a pre-created PVC is meant to be used by the pod, the PVC has to updated with an owner reference to the pod once the pod exists. Normally this should not be necessary, but it may be useful when manually reconstructing a broken cluster.\n\nThis field is read-only and no changes will be made by Kubernetes to the PVC after it has been created.\n\nRequired, must not be nil.", @@ -1094,7 +1085,7 @@ } var map_NodeConfigSource = map[string]string{ - "": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil.", + "": "NodeConfigSource specifies a source of node configuration. Exactly one subfield (excluding metadata) must be non-nil. This API is deprecated since 1.22", "configMap": "ConfigMap is a reference to a Node's ConfigMap", } @@ -1188,7 +1179,7 @@ "providerID": "ID of the node assigned by the cloud provider in the format: ://", "unschedulable": "Unschedulable controls node schedulability of new pods. By default, node is schedulable. More info: https://kubernetes.io/docs/concepts/nodes/node/#manual-node-administration", "taints": "If specified, the node's taints.", - "configSource": "If specified, the source to get node configuration from The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field", + "configSource": "Deprecated. If specified, the source of the node's configuration. The DynamicKubeletConfig feature gate must be enabled for the Kubelet to use this field. This field is deprecated as of 1.22: https://git.k8s.io/enhancements/keps/sig-node/281-dynamic-kubelet-configuration", "externalID": "Deprecated. Not all kubelets will set this field. Remove field after 1.13. see: https://issues.k8s.io/61966", } @@ -1310,7 +1301,8 @@ "volumeName": "VolumeName is the binding reference to the PersistentVolume backing this claim.", "storageClassName": "Name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", "volumeMode": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.", - "dataSource": "This field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) * An existing custom resource that implements data population (Alpha) In order to use custom resource types that implement data population, the AnyVolumeDataSource feature gate must be enabled. If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source.", + "dataSource": "This field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * An existing PVC (PersistentVolumeClaim) If the provisioner or an external controller can support the specified data source, it will create a new volume based on the contents of the specified data source. If the AnyVolumeDataSource feature gate is enabled, this field will always have the same contents as the DataSourceRef field.", + "dataSourceRef": "Specifies the object from which to populate the volume with data, if a non-empty volume is desired. This may be any local object from a non-empty API group (non core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled.", } func (PersistentVolumeClaimSpec) SwaggerDoc() map[string]string { @@ -1452,7 +1444,7 @@ "labelSelector": "A label query over a set of resources, in this case pods.", "namespaces": "namespaces specifies a static list of namespace names that the term applies to. The term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means \"this pod's namespace\"", "topologyKey": "This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching the labelSelector in the specified namespaces, where co-located is defined as running on a node whose value of the label with key topologyKey matches that of any node on which any of the selected pods is running. Empty topologyKey is not allowed.", - "namespaceSelector": "A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means \"this pod's namespace\". An empty selector ({}) matches all namespaces. This field is alpha-level and is only honored when PodAffinityNamespaceSelector feature is enabled.", + "namespaceSelector": "A label query over the set of namespaces that the term applies to. The term is applied to the union of the namespaces selected by this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means \"this pod's namespace\". An empty selector ({}) matches all namespaces. This field is beta-level and is only honored when PodAffinityNamespaceSelector feature is enabled.", } func (PodAffinityTerm) SwaggerDoc() map[string]string { @@ -1650,11 +1642,11 @@ "priorityClassName": "If specified, indicates the pod's priority. \"system-node-critical\" and \"system-cluster-critical\" are two special keywords which indicate the highest priorities with the former being the highest priority. Any other name must be defined by creating a PriorityClass object with that name. If not specified, the pod priority will be default or zero if there is no default.", "priority": "The priority value. Various system components use this field to find the priority of the pod. When Priority Admission Controller is enabled, it prevents users from setting this field. The admission controller populates this field from PriorityClassName. The higher the value, the higher the priority.", "dnsConfig": "Specifies the DNS parameters of a pod. Parameters specified here will be merged to the generated DNS configuration based on DNSPolicy.", - "readinessGates": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md", - "runtimeClassName": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md This is a beta feature as of Kubernetes v1.14.", + "readinessGates": "If specified, all readiness gates will be evaluated for pod readiness. A pod is ready when all its containers are ready AND all conditions specified in the readiness gates have status equal to \"True\" More info: https://git.k8s.io/enhancements/keps/sig-network/580-pod-readiness-gates", + "runtimeClassName": "RuntimeClassName refers to a RuntimeClass object in the node.k8s.io group, which should be used to run this pod. If no RuntimeClass resource matches the named class, the pod will not be run. If unset or empty, the \"legacy\" RuntimeClass will be used, which is an implicit class with an empty definition that uses the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/585-runtime-class This is a beta feature as of Kubernetes v1.14.", "enableServiceLinks": "EnableServiceLinks indicates whether information about services should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Defaults to true.", "preemptionPolicy": "PreemptionPolicy is the Policy for preempting pods with lower priority. One of Never, PreemptLowerPriority. Defaults to PreemptLowerPriority if unset. This field is beta-level, gated by the NonPreemptingPriority feature-gate.", - "overhead": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md This field is alpha-level as of Kubernetes v1.16, and is only honored by servers that enable the PodOverhead feature.", + "overhead": "Overhead represents the resource overhead associated with running a pod for a given RuntimeClass. This field will be autopopulated at admission time by the RuntimeClass admission controller. If the RuntimeClass admission controller is enabled, overhead must not be set in Pod create requests. The RuntimeClass admission controller will reject Pod create requests which have the overhead already set. If RuntimeClass is configured and selected in the PodSpec, Overhead will be set to the value defined in the corresponding RuntimeClass, otherwise it will remain unset and treated as zero. More info: https://git.k8s.io/enhancements/keps/sig-node/688-pod-overhead/README.md This field is beta-level as of Kubernetes v1.18, and is only honored by servers that enable the PodOverhead feature.", "topologySpreadConstraints": "TopologySpreadConstraints describes how a group of pods ought to spread across topology domains. Scheduler will schedule pods in a way which abides by the constraints. All topologySpreadConstraints are ANDed.", "setHostnameAsFQDN": "If true the pod's hostname will be configured as the pod's FQDN, rather than the leaf name (the default). In Linux containers, this means setting the FQDN in the hostname field of the kernel (the nodename field of struct utsname). In Windows containers, this means setting the registry value of hostname for the registry key HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters to FQDN. If a pod does not have FQDN, this has no effect. Default to false.", } @@ -1783,7 +1775,7 @@ "periodSeconds": "How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.", "successThreshold": "Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1.", "failureThreshold": "Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. Minimum value is 1.", - "terminationGracePeriodSeconds": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate.", + "terminationGracePeriodSeconds": "Optional duration in seconds the pod needs to terminate gracefully upon probe failure. The grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal. Set this value longer than the expected cleanup time for your process. If this value is nil, the pod's terminationGracePeriodSeconds will be used. Otherwise, this value overrides the value provided by the pod spec. Value must be non-negative integer. The value zero indicates stop immediately via the kill signal (no opportunity to shut down). This is a beta field and requires enabling ProbeTerminationGracePeriod feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds is used if unset.", } func (Probe) SwaggerDoc() map[string]string { @@ -2218,7 +2210,7 @@ "": "ServicePort contains information on service's port.", "name": "The name of this port within the service. This must be a DNS_LABEL. All ports within a ServiceSpec must have unique names. When considering the endpoints for a Service, this must match the 'name' field in the EndpointPort. Optional if only one ServicePort is defined on this service.", "protocol": "The IP protocol for this port. Supports \"TCP\", \"UDP\", and \"SCTP\". Default is TCP.", - "appProtocol": "The application protocol for this port. This field follows standard Kubernetes label syntax. Un-prefixed names are reserved for IANA standard service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). Non-standard protocols should use prefixed names such as mycompany.com/my-custom-protocol. This is a beta field that is guarded by the ServiceAppProtocol feature gate and enabled by default.", + "appProtocol": "The application protocol for this port. This field follows standard Kubernetes label syntax. Un-prefixed names are reserved for IANA standard service names (as per RFC-6335 and http://www.iana.org/assignments/service-names). Non-standard protocols should use prefixed names such as mycompany.com/my-custom-protocol.", "port": "The port that will be exposed by this service.", "targetPort": "Number or name of the port to access on the pods targeted by the service. Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME. If this is a string, it will be looked up as a named port in the target Pod's container ports. If this is not specified, the value of the 'port' field is used (an identity map). This field is ignored for services with clusterIP=None, and should be omitted or set equal to the 'port' field. More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service", "nodePort": "The port on each node on which this service is exposed when type is NodePort or LoadBalancer. Usually assigned by the system. If a value is specified, in-range, and not in use it will be used, otherwise the operation will fail. If not specified, a port will be allocated if this Service requires one. If this field is specified when creating a Service which does not need it, creation will fail. This field will be wiped when updating a Service to no longer need it (e.g. changing type from NodePort to ClusterIP). More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport", @@ -2247,16 +2239,15 @@ "externalIPs": "externalIPs is a list of IP addresses for which nodes in the cluster will also accept traffic for this service. These IPs are not managed by Kubernetes. The user is responsible for ensuring that traffic arrives at a node with this IP. A common example is external load-balancers that are not part of the Kubernetes system.", "sessionAffinity": "Supports \"ClientIP\" and \"None\". Used to maintain session affinity. Enable client IP based session affinity. Must be ClientIP or None. Defaults to None. More info: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies", "loadBalancerIP": "Only applies to Service Type: LoadBalancer LoadBalancer will get created with the IP specified in this field. This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature.", - "loadBalancerSourceRanges": "If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/", + "loadBalancerSourceRanges": "If specified and supported by the platform, this will restrict traffic through the cloud-provider load-balancer will be restricted to the specified client IPs. This field will be ignored if the cloud-provider does not support the feature.\" More info: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/", "externalName": "externalName is the external reference that discovery mechanisms will return as an alias for this service (e.g. a DNS CNAME record). No proxying will be involved. Must be a lowercase RFC-1123 hostname (https://tools.ietf.org/html/rfc1123) and requires `type` to be \"ExternalName\".", "externalTrafficPolicy": "externalTrafficPolicy denotes if this Service desires to route external traffic to node-local or cluster-wide endpoints. \"Local\" preserves the client source IP and avoids a second hop for LoadBalancer and Nodeport type services, but risks potentially imbalanced traffic spreading. \"Cluster\" obscures the client source IP and may cause a second hop to another node, but should have good overall load-spreading.", "healthCheckNodePort": "healthCheckNodePort specifies the healthcheck nodePort for the service. This only applies when type is set to LoadBalancer and externalTrafficPolicy is set to Local. If a value is specified, is in-range, and is not in use, it will be used. If not specified, a value will be automatically allocated. External systems (e.g. load-balancers) can use this port to determine if a given node holds endpoints for this service or not. If this field is specified when creating a Service which does not need it, creation will fail. This field will be wiped when updating a Service to no longer need it (e.g. changing type).", "publishNotReadyAddresses": "publishNotReadyAddresses indicates that any agent which deals with endpoints for this Service should disregard any indications of ready/not-ready. The primary use case for setting this field is for a StatefulSet's Headless Service to propagate SRV DNS records for its Pods for the purpose of peer discovery. The Kubernetes controllers that generate Endpoints and EndpointSlice resources for Services interpret this to mean that all endpoints are considered \"ready\" even if the Pods themselves are not. Agents which consume only Kubernetes generated endpoints through the Endpoints or EndpointSlice resources can safely assume this behavior.", "sessionAffinityConfig": "sessionAffinityConfig contains the configurations of session affinity.", - "topologyKeys": "topologyKeys is a preference-order list of topology keys which implementations of services should use to preferentially sort endpoints when accessing this Service, it can not be used at the same time as externalTrafficPolicy=Local. Topology keys must be valid label keys and at most 16 keys may be specified. Endpoints are chosen based on the first topology key with available backends. If this field is specified and all entries have no backends that match the topology of the client, the service has no backends for that client and connections should fail. The special value \"*\" may be used to mean \"any topology\". This catch-all value, if used, only makes sense as the last value in the list. If this is not specified or empty, no topology constraints will be applied. This field is alpha-level and is only honored by servers that enable the ServiceTopology feature. This field is deprecated and will be removed in a future version.", "ipFamilies": "IPFamilies is a list of IP families (e.g. IPv4, IPv6) assigned to this service, and is gated by the \"IPv6DualStack\" feature gate. This field is usually assigned automatically based on cluster configuration and the ipFamilyPolicy field. If this field is specified manually, the requested family is available in the cluster, and ipFamilyPolicy allows it, it will be used; otherwise creation of the service will fail. This field is conditionally mutable: it allows for adding or removing a secondary IP family, but it does not allow changing the primary IP family of the Service. Valid values are \"IPv4\" and \"IPv6\". This field only applies to Services of types ClusterIP, NodePort, and LoadBalancer, and does apply to \"headless\" services. This field will be wiped when updating a Service to type ExternalName.\n\nThis field may hold a maximum of two entries (dual-stack families, in either order). These families must correspond to the values of the clusterIPs field, if specified. Both clusterIPs and ipFamilies are governed by the ipFamilyPolicy field.", "ipFamilyPolicy": "IPFamilyPolicy represents the dual-stack-ness requested or required by this Service, and is gated by the \"IPv6DualStack\" feature gate. If there is no value provided, then this field will be set to SingleStack. Services can be \"SingleStack\" (a single IP family), \"PreferDualStack\" (two IP families on dual-stack configured clusters or a single IP family on single-stack clusters), or \"RequireDualStack\" (two IP families on dual-stack configured clusters, otherwise fail). The ipFamilies and clusterIPs fields depend on the value of this field. This field will be wiped when updating a service to type ExternalName.", - "allocateLoadBalancerNodePorts": "allocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for services with type LoadBalancer. Default is \"true\". It may be set to \"false\" if the cluster load-balancer does not rely on NodePorts. allocateLoadBalancerNodePorts may only be set for services with type LoadBalancer and will be cleared if the type is changed to any other type. This field is alpha-level and is only honored by servers that enable the ServiceLBNodePortControl feature.", + "allocateLoadBalancerNodePorts": "allocateLoadBalancerNodePorts defines if NodePorts will be automatically allocated for services with type LoadBalancer. Default is \"true\". It may be set to \"false\" if the cluster load-balancer does not rely on NodePorts. If the caller requests specific NodePorts (by specifying a value), those requests will be respected, regardless of this field. This field may only be set for services with type LoadBalancer and will be cleared if the type is changed to any other type. This field is beta-level and is only honored by servers that enable the ServiceLBNodePortControl feature.", "loadBalancerClass": "loadBalancerClass is the class of the load balancer implementation this Service belongs to. If specified, the value of this field must be a label-style identifier, with an optional prefix, e.g. \"internal-vip\" or \"example.com/internal-vip\". Unprefixed names are reserved for end-users. This field can only be set when the Service type is 'LoadBalancer'. If not set, the default load balancer implementation is used, today this is typically done through the cloud provider integration, but should apply for any default implementation. If set, it is assumed that a load balancer implementation is watching for Services with a matching class. Any default load balancer implementation (e.g. cloud providers) should ignore Services that set this field. This field can only be set when creating or updating a Service to type 'LoadBalancer'. Once set, it can not be changed. This field will be wiped when a service is updated to a non 'LoadBalancer' type.", "internalTrafficPolicy": "InternalTrafficPolicy specifies if the cluster internal traffic should be routed to all endpoints or node-local endpoints only. \"Cluster\" routes internal traffic to a Service to all endpoints. \"Local\" routes traffic to node-local endpoints only, traffic is dropped if no node-local endpoints are ready. The default value is \"Cluster\".", } @@ -2515,6 +2506,7 @@ "gmsaCredentialSpecName": "GMSACredentialSpecName is the name of the GMSA credential spec to use.", "gmsaCredentialSpec": "GMSACredentialSpec is where the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the GMSA credential spec named by the GMSACredentialSpecName field.", "runAsUserName": "The UserName in Windows to run the entrypoint of the container process. Defaults to the user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.", + "hostProcess": "HostProcess determines if a container should be run as a 'Host Process' container. This field is alpha-level and will only be honored by components that enable the WindowsHostProcessContainers feature flag. Setting this field without the feature flag will result in errors when validating the Pod. All of a Pod's containers must have the same effective HostProcess value (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). In addition, if HostProcess is true then HostNetwork must also be set to true.", } func (WindowsSecurityContextOptions) SwaggerDoc() map[string]string { diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/zz_generated.deepcopy.go libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/zz_generated.deepcopy.go --- libpod-3.2.1+ds1/vendor/k8s.io/api/core/v1/zz_generated.deepcopy.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/api/core/v1/zz_generated.deepcopy.go 2021-12-26 00:45:51.000000000 +0000 @@ -1401,39 +1401,6 @@ } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EphemeralContainers) DeepCopyInto(out *EphemeralContainers) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - if in.EphemeralContainers != nil { - in, out := &in.EphemeralContainers, &out.EphemeralContainers - *out = make([]EphemeralContainer, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EphemeralContainers. -func (in *EphemeralContainers) DeepCopy() *EphemeralContainers { - if in == nil { - return nil - } - out := new(EphemeralContainers) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EphemeralContainers) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *EphemeralVolumeSource) DeepCopyInto(out *EphemeralVolumeSource) { *out = *in if in.VolumeClaimTemplate != nil { @@ -2967,6 +2934,11 @@ *out = new(TypedLocalObjectReference) (*in).DeepCopyInto(*out) } + if in.DataSourceRef != nil { + in, out := &in.DataSourceRef, &out.DataSourceRef + *out = new(TypedLocalObjectReference) + (*in).DeepCopyInto(*out) + } return } @@ -5330,11 +5302,6 @@ *out = new(SessionAffinityConfig) (*in).DeepCopyInto(*out) } - if in.TopologyKeys != nil { - in, out := &in.TopologyKeys, &out.TopologyKeys - *out = make([]string, len(*in)) - copy(*out, *in) - } if in.IPFamilies != nil { in, out := &in.IPFamilies, &out.IPFamilies *out = make([]IPFamily, len(*in)) @@ -5943,6 +5910,11 @@ *out = new(string) **out = **in } + if in.HostProcess != nil { + in, out := &in.HostProcess, &out.HostProcess + *out = new(bool) + **out = **in + } return } diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go 2021-12-26 00:45:51.000000000 +0000 @@ -696,6 +696,15 @@ return nil } +// NewDecimalQuantity returns a new Quantity representing the given +// value in the given format. +func NewDecimalQuantity(b inf.Dec, format Format) *Quantity { + return &Quantity{ + d: infDecAmount{&b}, + Format: format, + } +} + // NewQuantity returns a new Quantity representing the given // value in the given format. func NewQuantity(value int64, format Format) *Quantity { diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go 2021-12-26 00:45:51.000000000 +0000 @@ -166,7 +166,7 @@ if err != nil { return err } - if skippy < 0 { + if (skippy < 0) || (iNdEx+skippy) < 0 { return ErrInvalidLengthGenerated } if (iNdEx + skippy) > l { diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.pb.go 2021-12-26 00:45:51.000000000 +0000 @@ -1326,183 +1326,184 @@ } var fileDescriptor_cf52fa777ced5367 = []byte{ - // 2813 bytes of a gzipped FileDescriptorProto + // 2829 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x3a, 0xcb, 0x6f, 0x24, 0x47, 0xf9, 0xee, 0x19, 0x8f, 0x3d, 0xf3, 0x8d, 0xc7, 0x8f, 0x5a, 0xef, 0xef, 0x37, 0x6b, 0x84, 0xc7, 0xe9, 0xa0, 0x68, 0x03, 0xc9, 0x38, 0x5e, 0x42, 0xb4, 0xd9, 0x90, 0x80, 0xc7, 0xb3, 0xde, 0x98, 0xac, 0x63, 0xab, 0xbc, 0xbb, 0x40, 0x88, 0x50, 0xda, 0xdd, 0xe5, 0x71, 0xe3, 0x9e, 0xee, 0x49, - 0x55, 0x8f, 0x37, 0x03, 0x07, 0x72, 0x00, 0x01, 0x12, 0x44, 0xe1, 0x86, 0x38, 0xa0, 0x44, 0xf0, - 0x17, 0x70, 0x81, 0x3f, 0x00, 0x89, 0x1c, 0x23, 0x71, 0x89, 0x04, 0x1a, 0x25, 0xe6, 0xc0, 0x11, - 0x71, 0xf5, 0x05, 0x54, 0x8f, 0xee, 0xae, 0x9e, 0xc7, 0xba, 0x27, 0xbb, 0x44, 0xdc, 0xa6, 0xbf, - 0x77, 0x55, 0x7d, 0xf5, 0xbd, 0x6a, 0x60, 0xf7, 0xe4, 0x3a, 0xab, 0xbb, 0xc1, 0xfa, 0x49, 0xf7, - 0x90, 0x50, 0x9f, 0x84, 0x84, 0xad, 0x9f, 0x12, 0xdf, 0x09, 0xe8, 0xba, 0x42, 0x58, 0x1d, 0xb7, - 0x6d, 0xd9, 0xc7, 0xae, 0x4f, 0x68, 0x6f, 0xbd, 0x73, 0xd2, 0xe2, 0x00, 0xb6, 0xde, 0x26, 0xa1, - 0xb5, 0x7e, 0xba, 0xb1, 0xde, 0x22, 0x3e, 0xa1, 0x56, 0x48, 0x9c, 0x7a, 0x87, 0x06, 0x61, 0x80, - 0xbe, 0x20, 0xb9, 0xea, 0x3a, 0x57, 0xbd, 0x73, 0xd2, 0xe2, 0x00, 0x56, 0xe7, 0x5c, 0xf5, 0xd3, - 0x8d, 0x95, 0xa7, 0x5b, 0x6e, 0x78, 0xdc, 0x3d, 0xac, 0xdb, 0x41, 0x7b, 0xbd, 0x15, 0xb4, 0x82, - 0x75, 0xc1, 0x7c, 0xd8, 0x3d, 0x12, 0x5f, 0xe2, 0x43, 0xfc, 0x92, 0x42, 0x57, 0xc6, 0x9a, 0x42, - 0xbb, 0x7e, 0xe8, 0xb6, 0xc9, 0xa0, 0x15, 0x2b, 0xcf, 0x5d, 0xc4, 0xc0, 0xec, 0x63, 0xd2, 0xb6, - 0x06, 0xf9, 0xcc, 0x3f, 0xe7, 0xa1, 0xb8, 0xb9, 0xbf, 0x73, 0x8b, 0x06, 0xdd, 0x0e, 0x5a, 0x83, - 0x69, 0xdf, 0x6a, 0x93, 0xaa, 0xb1, 0x66, 0x5c, 0x2d, 0x35, 0xe6, 0x3e, 0xe8, 0xd7, 0xa6, 0xce, - 0xfa, 0xb5, 0xe9, 0x57, 0xad, 0x36, 0xc1, 0x02, 0x83, 0x3c, 0x28, 0x9e, 0x12, 0xca, 0xdc, 0xc0, - 0x67, 0xd5, 0xdc, 0x5a, 0xfe, 0x6a, 0xf9, 0xda, 0x4b, 0xf5, 0x2c, 0xeb, 0xaf, 0x0b, 0x05, 0xf7, - 0x24, 0xeb, 0x76, 0x40, 0x9b, 0x2e, 0xb3, 0x83, 0x53, 0x42, 0x7b, 0x8d, 0x45, 0xa5, 0xa5, 0xa8, - 0x90, 0x0c, 0xc7, 0x1a, 0xd0, 0x8f, 0x0c, 0x58, 0xec, 0x50, 0x72, 0x44, 0x28, 0x25, 0x8e, 0xc2, - 0x57, 0xf3, 0x6b, 0xc6, 0x23, 0x50, 0x5b, 0x55, 0x6a, 0x17, 0xf7, 0x07, 0xe4, 0xe3, 0x21, 0x8d, - 0xe8, 0xb7, 0x06, 0xac, 0x30, 0x42, 0x4f, 0x09, 0xdd, 0x74, 0x1c, 0x4a, 0x18, 0x6b, 0xf4, 0xb6, - 0x3c, 0x97, 0xf8, 0xe1, 0xd6, 0x4e, 0x13, 0xb3, 0xea, 0xb4, 0xd8, 0x87, 0xaf, 0x65, 0x33, 0xe8, - 0x60, 0x9c, 0x9c, 0x86, 0xa9, 0x2c, 0x5a, 0x19, 0x4b, 0xc2, 0xf0, 0x03, 0xcc, 0x30, 0x8f, 0x60, - 0x2e, 0x3a, 0xc8, 0xdb, 0x2e, 0x0b, 0xd1, 0x3d, 0x98, 0x69, 0xf1, 0x0f, 0x56, 0x35, 0x84, 0x81, - 0xf5, 0x6c, 0x06, 0x46, 0x32, 0x1a, 0xf3, 0xca, 0x9e, 0x19, 0xf1, 0xc9, 0xb0, 0x92, 0x66, 0xfe, - 0x6c, 0x1a, 0xca, 0x9b, 0xfb, 0x3b, 0x98, 0xb0, 0xa0, 0x4b, 0x6d, 0x92, 0xc1, 0x69, 0xae, 0xc3, - 0x1c, 0x73, 0xfd, 0x56, 0xd7, 0xb3, 0x28, 0x87, 0x56, 0x67, 0x04, 0xe5, 0xb2, 0xa2, 0x9c, 0x3b, - 0xd0, 0x70, 0x38, 0x45, 0x89, 0xae, 0x01, 0x70, 0x09, 0xac, 0x63, 0xd9, 0xc4, 0xa9, 0xe6, 0xd6, - 0x8c, 0xab, 0xc5, 0x06, 0x52, 0x7c, 0xf0, 0x6a, 0x8c, 0xc1, 0x1a, 0x15, 0x7a, 0x1c, 0x0a, 0xc2, - 0xd2, 0x6a, 0x51, 0xa8, 0xa9, 0x28, 0xf2, 0x82, 0x58, 0x06, 0x96, 0x38, 0xf4, 0x24, 0xcc, 0x2a, - 0x2f, 0xab, 0x96, 0x04, 0xd9, 0x82, 0x22, 0x9b, 0x8d, 0xdc, 0x20, 0xc2, 0xf3, 0xf5, 0x9d, 0xb8, - 0xbe, 0x23, 0xfc, 0x4e, 0x5b, 0xdf, 0x2b, 0xae, 0xef, 0x60, 0x81, 0x41, 0xb7, 0xa1, 0x70, 0x4a, - 0xe8, 0x21, 0xf7, 0x04, 0xee, 0x9a, 0x5f, 0xca, 0xb6, 0xd1, 0xf7, 0x38, 0x4b, 0xa3, 0xc4, 0x4d, - 0x13, 0x3f, 0xb1, 0x14, 0x82, 0xea, 0x00, 0xec, 0x38, 0xa0, 0xa1, 0x58, 0x5e, 0xb5, 0xb0, 0x96, - 0xbf, 0x5a, 0x6a, 0xcc, 0xf3, 0xf5, 0x1e, 0xc4, 0x50, 0xac, 0x51, 0x70, 0x7a, 0xdb, 0x0a, 0x49, - 0x2b, 0xa0, 0x2e, 0x61, 0xd5, 0xd9, 0x84, 0x7e, 0x2b, 0x86, 0x62, 0x8d, 0x02, 0x7d, 0x03, 0x10, - 0x0b, 0x03, 0x6a, 0xb5, 0x88, 0x5a, 0xea, 0xcb, 0x16, 0x3b, 0xae, 0x82, 0x58, 0xdd, 0x8a, 0x5a, - 0x1d, 0x3a, 0x18, 0xa2, 0xc0, 0x23, 0xb8, 0xcc, 0xdf, 0x1b, 0xb0, 0xa0, 0xf9, 0x82, 0xf0, 0xbb, - 0xeb, 0x30, 0xd7, 0xd2, 0x6e, 0x9d, 0xf2, 0x8b, 0xf8, 0xb4, 0xf5, 0x1b, 0x89, 0x53, 0x94, 0x88, - 0x40, 0x89, 0x2a, 0x49, 0x51, 0x74, 0xd9, 0xc8, 0xec, 0xb4, 0x91, 0x0d, 0x89, 0x26, 0x0d, 0xc8, - 0x70, 0x22, 0xd9, 0xfc, 0x87, 0x21, 0x1c, 0x38, 0x8a, 0x37, 0xe8, 0xaa, 0x16, 0xd3, 0x0c, 0xb1, - 0x7d, 0x73, 0x63, 0xe2, 0xd1, 0x05, 0x81, 0x20, 0xf7, 0x3f, 0x11, 0x08, 0x6e, 0x14, 0x7f, 0xf5, - 0x5e, 0x6d, 0xea, 0xed, 0xbf, 0xad, 0x4d, 0x99, 0xbf, 0x34, 0x60, 0x6e, 0xb3, 0xd3, 0xf1, 0x7a, - 0x7b, 0x9d, 0x50, 0x2c, 0xc0, 0x84, 0x19, 0x87, 0xf6, 0x70, 0xd7, 0x57, 0x0b, 0x05, 0x7e, 0xbf, - 0x9b, 0x02, 0x82, 0x15, 0x86, 0xdf, 0x9f, 0xa3, 0x80, 0xda, 0x44, 0x5d, 0xb7, 0xf8, 0xfe, 0x6c, - 0x73, 0x20, 0x96, 0x38, 0x7e, 0xc8, 0x47, 0x2e, 0xf1, 0x9c, 0x5d, 0xcb, 0xb7, 0x5a, 0x84, 0xaa, - 0xcb, 0x11, 0x6f, 0xfd, 0xb6, 0x86, 0xc3, 0x29, 0x4a, 0xf3, 0xdf, 0x39, 0x28, 0x6d, 0x05, 0xbe, - 0xe3, 0x86, 0xea, 0x72, 0x85, 0xbd, 0xce, 0x50, 0xf0, 0xb8, 0xd3, 0xeb, 0x10, 0x2c, 0x30, 0xe8, - 0x79, 0x98, 0x61, 0xa1, 0x15, 0x76, 0x99, 0xb0, 0xa7, 0xd4, 0x78, 0x2c, 0x0a, 0x4b, 0x07, 0x02, - 0x7a, 0xde, 0xaf, 0x2d, 0xc4, 0xe2, 0x24, 0x08, 0x2b, 0x06, 0xee, 0xe9, 0xc1, 0xa1, 0xd8, 0x28, - 0xe7, 0x96, 0x4c, 0x7b, 0x51, 0xfe, 0xc8, 0x27, 0x9e, 0xbe, 0x37, 0x44, 0x81, 0x47, 0x70, 0xa1, - 0x53, 0x40, 0x9e, 0xc5, 0xc2, 0x3b, 0xd4, 0xf2, 0x99, 0xd0, 0x75, 0xc7, 0x6d, 0x13, 0x75, 0xe1, - 0xbf, 0x98, 0xed, 0xc4, 0x39, 0x47, 0xa2, 0xf7, 0xf6, 0x90, 0x34, 0x3c, 0x42, 0x03, 0x7a, 0x02, - 0x66, 0x28, 0xb1, 0x58, 0xe0, 0x57, 0x0b, 0x62, 0xf9, 0x71, 0x54, 0xc6, 0x02, 0x8a, 0x15, 0x96, - 0x07, 0xb4, 0x36, 0x61, 0xcc, 0x6a, 0x45, 0xe1, 0x35, 0x0e, 0x68, 0xbb, 0x12, 0x8c, 0x23, 0xbc, - 0xd9, 0x86, 0xca, 0x16, 0x25, 0x56, 0x48, 0x26, 0xf1, 0x8a, 0x4f, 0x7f, 0xe0, 0x3f, 0xc9, 0x43, - 0xa5, 0x49, 0x3c, 0x92, 0xe8, 0xdb, 0x06, 0xd4, 0xa2, 0x96, 0x4d, 0xf6, 0x09, 0x75, 0x03, 0xe7, - 0x80, 0xd8, 0x81, 0xef, 0x30, 0xe1, 0x02, 0xf9, 0xc6, 0xff, 0xf1, 0xbd, 0xb9, 0x35, 0x84, 0xc5, - 0x23, 0x38, 0x90, 0x07, 0x95, 0x0e, 0x15, 0xbf, 0xc5, 0x7e, 0x49, 0x0f, 0x29, 0x5f, 0xfb, 0x72, - 0xb6, 0xe3, 0xd8, 0xd7, 0x59, 0x1b, 0x4b, 0x67, 0xfd, 0x5a, 0x25, 0x05, 0xc2, 0x69, 0xe1, 0xe8, - 0xeb, 0xb0, 0x18, 0xd0, 0xce, 0xb1, 0xe5, 0x37, 0x49, 0x87, 0xf8, 0x0e, 0xf1, 0x43, 0x26, 0x76, - 0xa1, 0xd8, 0x58, 0xe6, 0x75, 0xc4, 0xde, 0x00, 0x0e, 0x0f, 0x51, 0xa3, 0xd7, 0x60, 0xa9, 0x43, - 0x83, 0x8e, 0xd5, 0x12, 0x2e, 0xb5, 0x1f, 0x78, 0xae, 0xdd, 0x13, 0x2e, 0x54, 0x6a, 0x3c, 0x75, - 0xd6, 0xaf, 0x2d, 0xed, 0x0f, 0x22, 0xcf, 0xfb, 0xb5, 0x4b, 0x62, 0xeb, 0x38, 0x24, 0x41, 0xe2, - 0x61, 0x31, 0xda, 0x19, 0x16, 0xc6, 0x9d, 0xa1, 0xb9, 0x03, 0xc5, 0x66, 0x57, 0xf9, 0xf3, 0x8b, - 0x50, 0x74, 0xd4, 0x6f, 0xb5, 0xf3, 0xd1, 0xc5, 0x8a, 0x69, 0xce, 0xfb, 0xb5, 0x0a, 0x2f, 0x1d, - 0xeb, 0x11, 0x00, 0xc7, 0x2c, 0xe6, 0x13, 0x50, 0x14, 0x47, 0xce, 0xee, 0x6d, 0xa0, 0x45, 0xc8, - 0x63, 0xeb, 0xbe, 0x90, 0x32, 0x87, 0xf9, 0x4f, 0x2d, 0x02, 0xed, 0x01, 0xdc, 0x22, 0x61, 0x74, - 0xf0, 0x9b, 0xb0, 0x10, 0x85, 0xe1, 0x74, 0x76, 0xf8, 0x7f, 0xa5, 0x7b, 0x01, 0xa7, 0xd1, 0x78, - 0x90, 0xde, 0x7c, 0x1d, 0x4a, 0x22, 0x83, 0xf0, 0xf4, 0x9b, 0xa4, 0x7a, 0xe3, 0x01, 0xa9, 0x3e, - 0xca, 0xdf, 0xb9, 0x71, 0xf9, 0x5b, 0x33, 0xd7, 0x83, 0x8a, 0xe4, 0x8d, 0x8a, 0x9b, 0x4c, 0x1a, - 0x9e, 0x82, 0x62, 0x64, 0xa6, 0xd2, 0x12, 0x17, 0xb5, 0x91, 0x20, 0x1c, 0x53, 0x68, 0xda, 0x8e, - 0x21, 0x95, 0x0d, 0xb3, 0x29, 0xd3, 0x2a, 0x97, 0xdc, 0x83, 0x2b, 0x17, 0x4d, 0xd3, 0x0f, 0xa1, - 0x3a, 0xae, 0x12, 0x7e, 0x88, 0x7c, 0x9d, 0xdd, 0x14, 0xf3, 0x1d, 0x03, 0x16, 0x75, 0x49, 0xd9, - 0x8f, 0x2f, 0xbb, 0x92, 0x8b, 0x2b, 0x35, 0x6d, 0x47, 0x7e, 0x63, 0xc0, 0x72, 0x6a, 0x69, 0x13, - 0x9d, 0xf8, 0x04, 0x46, 0xe9, 0xce, 0x91, 0x9f, 0xc0, 0x39, 0xfe, 0x92, 0x83, 0xca, 0x6d, 0xeb, - 0x90, 0x78, 0x07, 0xc4, 0x23, 0x76, 0x18, 0x50, 0xf4, 0x03, 0x28, 0xb7, 0xad, 0xd0, 0x3e, 0x16, - 0xd0, 0xa8, 0xaa, 0x6f, 0x66, 0x0b, 0x76, 0x29, 0x49, 0xf5, 0xdd, 0x44, 0xcc, 0x4d, 0x3f, 0xa4, - 0xbd, 0xc6, 0x25, 0x65, 0x52, 0x59, 0xc3, 0x60, 0x5d, 0x9b, 0x68, 0xc5, 0xc4, 0xf7, 0xcd, 0xb7, - 0x3a, 0xbc, 0xe4, 0x98, 0xbc, 0x03, 0x4c, 0x99, 0x80, 0xc9, 0x9b, 0x5d, 0x97, 0x92, 0x36, 0xf1, - 0xc3, 0xa4, 0x15, 0xdb, 0x1d, 0x90, 0x8f, 0x87, 0x34, 0xae, 0xbc, 0x04, 0x8b, 0x83, 0xc6, 0xf3, - 0xf8, 0x73, 0x42, 0x7a, 0xf2, 0xbc, 0x30, 0xff, 0x89, 0x96, 0xa1, 0x70, 0x6a, 0x79, 0x5d, 0x75, - 0x1b, 0xb1, 0xfc, 0xb8, 0x91, 0xbb, 0x6e, 0x98, 0xbf, 0x33, 0xa0, 0x3a, 0xce, 0x10, 0xf4, 0x79, - 0x4d, 0x50, 0xa3, 0xac, 0xac, 0xca, 0xbf, 0x42, 0x7a, 0x52, 0xea, 0x4d, 0x28, 0x06, 0x1d, 0x5e, - 0x0f, 0x04, 0x54, 0x9d, 0xfa, 0x93, 0xd1, 0x49, 0xee, 0x29, 0xf8, 0x79, 0xbf, 0x76, 0x39, 0x25, - 0x3e, 0x42, 0xe0, 0x98, 0x95, 0x47, 0x6a, 0x61, 0x0f, 0xcf, 0x1e, 0x71, 0xa4, 0xbe, 0x27, 0x20, - 0x58, 0x61, 0xcc, 0x3f, 0x1a, 0x30, 0x2d, 0x8a, 0xe9, 0xd7, 0xa1, 0xc8, 0xf7, 0xcf, 0xb1, 0x42, - 0x4b, 0xd8, 0x95, 0xb9, 0x8d, 0xe3, 0xdc, 0xbb, 0x24, 0xb4, 0x12, 0x6f, 0x8b, 0x20, 0x38, 0x96, - 0x88, 0x30, 0x14, 0xdc, 0x90, 0xb4, 0xa3, 0x83, 0x7c, 0x7a, 0xac, 0x68, 0x35, 0x44, 0xa8, 0x63, - 0xeb, 0xfe, 0xcd, 0xb7, 0x42, 0xe2, 0xf3, 0xc3, 0x48, 0xae, 0xc6, 0x0e, 0x97, 0x81, 0xa5, 0x28, - 0xf3, 0x5f, 0x06, 0xc4, 0xaa, 0xb8, 0xf3, 0x33, 0xe2, 0x1d, 0xdd, 0x76, 0xfd, 0x13, 0xb5, 0xad, - 0xb1, 0x39, 0x07, 0x0a, 0x8e, 0x63, 0x8a, 0x51, 0xe9, 0x21, 0x37, 0x59, 0x7a, 0xe0, 0x0a, 0xed, - 0xc0, 0x0f, 0x5d, 0xbf, 0x3b, 0x74, 0xdb, 0xb6, 0x14, 0x1c, 0xc7, 0x14, 0xbc, 0x10, 0xa1, 0xa4, - 0x6d, 0xb9, 0xbe, 0xeb, 0xb7, 0xf8, 0x22, 0xb6, 0x82, 0xae, 0x1f, 0x8a, 0x8c, 0xac, 0x0a, 0x11, - 0x3c, 0x84, 0xc5, 0x23, 0x38, 0xcc, 0x3f, 0x4c, 0x43, 0x99, 0xaf, 0x39, 0xca, 0x73, 0x2f, 0x40, - 0xc5, 0xd3, 0xbd, 0x40, 0xad, 0xfd, 0xb2, 0x32, 0x25, 0x7d, 0xaf, 0x71, 0x9a, 0x96, 0x33, 0x8b, - 0xfa, 0x29, 0x66, 0xce, 0xa5, 0x99, 0xb7, 0x75, 0x24, 0x4e, 0xd3, 0xf2, 0xe8, 0x75, 0x9f, 0xdf, - 0x0f, 0x55, 0x99, 0xc4, 0x47, 0xf4, 0x4d, 0x0e, 0xc4, 0x12, 0x87, 0x76, 0xe1, 0x92, 0xe5, 0x79, - 0xc1, 0x7d, 0x01, 0x6c, 0x04, 0xc1, 0x49, 0xdb, 0xa2, 0x27, 0x4c, 0x34, 0xc2, 0xc5, 0xc6, 0xe7, - 0x14, 0xcb, 0xa5, 0xcd, 0x61, 0x12, 0x3c, 0x8a, 0x6f, 0xd4, 0xb1, 0x4d, 0x4f, 0x78, 0x6c, 0xc7, - 0xb0, 0x3c, 0x00, 0x12, 0xb7, 0x5c, 0x75, 0xa5, 0xcf, 0x2a, 0x39, 0xcb, 0x78, 0x04, 0xcd, 0xf9, - 0x18, 0x38, 0x1e, 0x29, 0x11, 0xdd, 0x80, 0x79, 0xee, 0xc9, 0x41, 0x37, 0x8c, 0xea, 0xce, 0x82, - 0x38, 0x6e, 0x74, 0xd6, 0xaf, 0xcd, 0xdf, 0x49, 0x61, 0xf0, 0x00, 0x25, 0xdf, 0x5c, 0xcf, 0x6d, - 0xbb, 0x61, 0x75, 0x56, 0xb0, 0xc4, 0x9b, 0x7b, 0x9b, 0x03, 0xb1, 0xc4, 0xa5, 0x3c, 0xb0, 0x78, - 0x91, 0x07, 0x9a, 0xbf, 0xce, 0x03, 0x92, 0x85, 0xb2, 0x23, 0xeb, 0x29, 0x19, 0xd2, 0x78, 0x35, - 0xaf, 0x0a, 0x6d, 0x63, 0xa0, 0x9a, 0x57, 0x35, 0x76, 0x84, 0x47, 0xbb, 0x50, 0x92, 0xa1, 0x25, - 0xb9, 0x2e, 0xeb, 0x8a, 0xb8, 0xb4, 0x17, 0x21, 0xce, 0xfb, 0xb5, 0x95, 0x94, 0x9a, 0x18, 0x23, - 0x3a, 0xad, 0x44, 0x02, 0xba, 0x06, 0x60, 0x75, 0x5c, 0x7d, 0xd6, 0x56, 0x4a, 0x26, 0x2e, 0x49, - 0xd7, 0x8c, 0x35, 0x2a, 0xf4, 0x32, 0x4c, 0x87, 0x9f, 0xae, 0x1b, 0x2a, 0x8a, 0x66, 0x8f, 0xf7, - 0x3e, 0x42, 0x02, 0xd7, 0x2e, 0xfc, 0x99, 0x71, 0xb3, 0x54, 0x23, 0x13, 0x6b, 0xdf, 0x8e, 0x31, - 0x58, 0xa3, 0x42, 0xdf, 0x82, 0xe2, 0x91, 0x2a, 0x45, 0xc5, 0xc1, 0x64, 0x0e, 0x91, 0x51, 0x01, - 0x2b, 0xdb, 0xfd, 0xe8, 0x0b, 0xc7, 0xd2, 0xcc, 0x37, 0xa1, 0xb4, 0xeb, 0xda, 0x34, 0x10, 0x8d, - 0xd8, 0x93, 0x30, 0xcb, 0x52, 0x9d, 0x4a, 0x7c, 0x24, 0x91, 0xbb, 0x44, 0x78, 0xee, 0x27, 0xbe, - 0xe5, 0x07, 0xb2, 0x1f, 0x29, 0x24, 0x7e, 0xf2, 0x2a, 0x07, 0x62, 0x89, 0xbb, 0xb1, 0xcc, 0x33, - 0xfd, 0x4f, 0xdf, 0xaf, 0x4d, 0xbd, 0xfb, 0x7e, 0x6d, 0xea, 0xbd, 0xf7, 0x55, 0xd6, 0x3f, 0x07, - 0x80, 0xbd, 0xc3, 0xef, 0x11, 0x5b, 0xc6, 0xcf, 0x4c, 0xb3, 0xb5, 0x68, 0xa4, 0x2b, 0x66, 0x6b, - 0xb9, 0x81, 0xea, 0x4d, 0xc3, 0xe1, 0x14, 0x25, 0x5a, 0x87, 0x52, 0x3c, 0x35, 0x53, 0x07, 0xbd, - 0x14, 0x39, 0x4e, 0x3c, 0x5a, 0xc3, 0x09, 0x4d, 0x2a, 0x98, 0x4f, 0x5f, 0x18, 0xcc, 0x1b, 0x90, - 0xef, 0xba, 0x8e, 0xea, 0x5a, 0x9f, 0x89, 0x92, 0xe9, 0xdd, 0x9d, 0xe6, 0x79, 0xbf, 0xf6, 0xd8, - 0xb8, 0x61, 0x35, 0xef, 0xf8, 0x59, 0xfd, 0xee, 0x4e, 0x13, 0x73, 0xe6, 0x51, 0x91, 0x65, 0x66, - 0xc2, 0xc8, 0x72, 0x0d, 0xa0, 0x95, 0xf4, 0xfe, 0xf2, 0xe2, 0xc6, 0x1e, 0xa5, 0xf5, 0xfc, 0x1a, - 0x15, 0x62, 0xb0, 0x64, 0xf3, 0x06, 0x59, 0xf5, 0xe0, 0x2c, 0xb4, 0xda, 0x72, 0x9a, 0x38, 0x99, - 0x73, 0x5f, 0x51, 0x6a, 0x96, 0xb6, 0x06, 0x85, 0xe1, 0x61, 0xf9, 0x28, 0x80, 0x25, 0x47, 0xb5, - 0x7a, 0x89, 0xd2, 0xd2, 0xc4, 0x4a, 0x2f, 0x73, 0x85, 0xcd, 0x41, 0x41, 0x78, 0x58, 0x36, 0xfa, - 0x2e, 0xac, 0x44, 0xc0, 0xe1, 0x7e, 0x5b, 0x44, 0xde, 0x7c, 0x63, 0xf5, 0xac, 0x5f, 0x5b, 0x69, - 0x8e, 0xa5, 0xc2, 0x0f, 0x90, 0x80, 0x1c, 0x98, 0xf1, 0x64, 0xa5, 0x5a, 0x16, 0xd5, 0xc5, 0x57, - 0xb3, 0xad, 0x22, 0xf1, 0xfe, 0xba, 0x5e, 0xa1, 0xc6, 0x73, 0x0f, 0x55, 0x9c, 0x2a, 0xd9, 0xe8, - 0x2d, 0x28, 0x5b, 0xbe, 0x1f, 0x84, 0x96, 0x9c, 0x00, 0xcc, 0x09, 0x55, 0x9b, 0x13, 0xab, 0xda, - 0x4c, 0x64, 0x0c, 0x54, 0xc4, 0x1a, 0x06, 0xeb, 0xaa, 0xd0, 0x7d, 0x58, 0x08, 0xee, 0xfb, 0x84, - 0x62, 0x72, 0x44, 0x28, 0xf1, 0x6d, 0xc2, 0xaa, 0x15, 0xa1, 0xfd, 0xd9, 0x8c, 0xda, 0x53, 0xcc, - 0x89, 0x4b, 0xa7, 0xe1, 0x0c, 0x0f, 0x6a, 0x41, 0x75, 0x1e, 0x24, 0x7d, 0xcb, 0x73, 0xbf, 0x4f, - 0x28, 0xab, 0xce, 0x27, 0x03, 0xdf, 0xed, 0x18, 0x8a, 0x35, 0x0a, 0xf4, 0x15, 0x28, 0xdb, 0x5e, - 0x97, 0x85, 0x44, 0x4e, 0xdf, 0x17, 0xc4, 0x0d, 0x8a, 0xd7, 0xb7, 0x95, 0xa0, 0xb0, 0x4e, 0x87, - 0xba, 0x50, 0x69, 0xeb, 0x29, 0xa3, 0xba, 0x24, 0x56, 0x77, 0x3d, 0xdb, 0xea, 0x86, 0x93, 0x5a, - 0x52, 0xc1, 0xa4, 0x70, 0x38, 0xad, 0x65, 0xe5, 0x79, 0x28, 0x7f, 0xca, 0xe2, 0x9e, 0x37, 0x07, - 0x83, 0xe7, 0x38, 0x51, 0x73, 0xf0, 0xa7, 0x1c, 0xcc, 0xa7, 0x77, 0x7f, 0x20, 0x1d, 0x16, 0x32, - 0xa5, 0xc3, 0xa8, 0x0d, 0x35, 0xc6, 0x3e, 0x18, 0x44, 0x61, 0x3d, 0x3f, 0x36, 0xac, 0xab, 0xe8, - 0x39, 0xfd, 0x30, 0xd1, 0xb3, 0x0e, 0xc0, 0xeb, 0x0c, 0x1a, 0x78, 0x1e, 0xa1, 0x22, 0x70, 0x16, - 0xd5, 0xc3, 0x40, 0x0c, 0xc5, 0x1a, 0x05, 0xaf, 0x86, 0x0f, 0xbd, 0xc0, 0x3e, 0x11, 0x5b, 0x10, - 0x5d, 0x7a, 0x11, 0x32, 0x8b, 0xb2, 0x1a, 0x6e, 0x0c, 0x61, 0xf1, 0x08, 0x0e, 0xb3, 0x07, 0x97, - 0xf7, 0x2d, 0x1a, 0xba, 0x96, 0x97, 0x5c, 0x30, 0xd1, 0x6e, 0xbc, 0x31, 0xd4, 0xcc, 0x3c, 0x33, - 0xe9, 0x45, 0x4d, 0x36, 0x3f, 0x81, 0x25, 0x0d, 0x8d, 0xf9, 0x57, 0x03, 0xae, 0x8c, 0xd4, 0xfd, - 0x19, 0x34, 0x53, 0x6f, 0xa4, 0x9b, 0xa9, 0x17, 0x32, 0x4e, 0x21, 0x47, 0x59, 0x3b, 0xa6, 0xb5, - 0x9a, 0x85, 0xc2, 0x3e, 0x2f, 0x62, 0xcd, 0x5f, 0x18, 0x30, 0x27, 0x7e, 0x4d, 0x32, 0xc1, 0xad, - 0xa5, 0xe7, 0xfa, 0xa5, 0x47, 0x38, 0xd3, 0x7f, 0xc7, 0x80, 0xf4, 0xec, 0x14, 0xbd, 0x24, 0xfd, - 0xd7, 0x88, 0x87, 0x9b, 0x13, 0xfa, 0xee, 0x8b, 0xe3, 0x5a, 0xc1, 0x4b, 0x99, 0xa6, 0x84, 0x4f, - 0x41, 0x09, 0x07, 0x41, 0xb8, 0x6f, 0x85, 0xc7, 0x8c, 0x2f, 0xbc, 0xc3, 0x7f, 0xa8, 0xbd, 0x11, - 0x0b, 0x17, 0x18, 0x2c, 0xe1, 0xe6, 0xcf, 0x0d, 0xb8, 0x32, 0xf6, 0xad, 0x85, 0x87, 0x00, 0x3b, - 0xfe, 0x52, 0x2b, 0x8a, 0xbd, 0x30, 0xa1, 0xc3, 0x1a, 0x15, 0xef, 0xe1, 0x52, 0x0f, 0x34, 0x83, - 0x3d, 0x5c, 0x4a, 0x1b, 0x4e, 0xd3, 0x9a, 0xff, 0xcc, 0x81, 0x7a, 0xdc, 0xf8, 0x2f, 0x7b, 0xec, - 0x13, 0x03, 0x4f, 0x2b, 0xf3, 0xe9, 0xa7, 0x95, 0xf8, 0x1d, 0x45, 0x7b, 0x5b, 0xc8, 0x3f, 0xf8, - 0x6d, 0x01, 0x3d, 0x17, 0x3f, 0x57, 0xc8, 0xd0, 0xb5, 0x9a, 0x7e, 0xae, 0x38, 0xef, 0xd7, 0xe6, - 0x94, 0xf0, 0xf4, 0xf3, 0xc5, 0x6b, 0x30, 0xeb, 0x90, 0xd0, 0x72, 0x3d, 0xd9, 0x8f, 0x65, 0x1e, - 0xe2, 0x4b, 0x61, 0x4d, 0xc9, 0xda, 0x28, 0x73, 0x9b, 0xd4, 0x07, 0x8e, 0x04, 0xf2, 0x68, 0x6b, - 0x07, 0x8e, 0x6c, 0x27, 0x0a, 0x49, 0xb4, 0xdd, 0x0a, 0x1c, 0x82, 0x05, 0xc6, 0x7c, 0xd7, 0x80, - 0xb2, 0x94, 0xb4, 0x65, 0x75, 0x19, 0x41, 0x1b, 0xf1, 0x2a, 0xe4, 0x71, 0x5f, 0xd1, 0xdf, 0xa5, - 0xce, 0xfb, 0xb5, 0x92, 0x20, 0x13, 0x9d, 0xc8, 0x88, 0xf7, 0x97, 0xdc, 0x05, 0x7b, 0xf4, 0x38, - 0x14, 0xc4, 0xed, 0x51, 0x9b, 0x99, 0x3c, 0xb0, 0x71, 0x20, 0x96, 0x38, 0xf3, 0xe3, 0x1c, 0x54, - 0x52, 0x8b, 0xcb, 0xd0, 0x0b, 0xc4, 0xa3, 0xcb, 0x5c, 0x86, 0x71, 0xf8, 0xf8, 0xe7, 0x6c, 0x95, - 0x7b, 0x66, 0x1e, 0x26, 0xf7, 0x7c, 0x1b, 0x66, 0x6c, 0xbe, 0x47, 0xd1, 0xbf, 0x23, 0x36, 0x26, - 0x39, 0x4e, 0xb1, 0xbb, 0x89, 0x37, 0x8a, 0x4f, 0x86, 0x95, 0x40, 0x74, 0x0b, 0x96, 0x28, 0x09, - 0x69, 0x6f, 0xf3, 0x28, 0x24, 0x54, 0x6f, 0xe2, 0x0b, 0x49, 0xc5, 0x8d, 0x07, 0x09, 0xf0, 0x30, - 0x8f, 0x79, 0x08, 0x73, 0x77, 0xac, 0x43, 0x2f, 0x7e, 0x96, 0xc2, 0x50, 0x71, 0x7d, 0xdb, 0xeb, - 0x3a, 0x44, 0x46, 0xe3, 0x28, 0x7a, 0x45, 0x97, 0x76, 0x47, 0x47, 0x9e, 0xf7, 0x6b, 0x97, 0x52, - 0x00, 0xf9, 0x0e, 0x83, 0xd3, 0x22, 0x4c, 0x0f, 0xa6, 0x3f, 0xc3, 0xee, 0xf1, 0x3b, 0x50, 0x4a, - 0xea, 0xfb, 0x47, 0xac, 0xd2, 0x7c, 0x03, 0x8a, 0xdc, 0xe3, 0xa3, 0xbe, 0xf4, 0x82, 0x12, 0x27, - 0x5d, 0x38, 0xe5, 0xb2, 0x14, 0x4e, 0x66, 0x1b, 0x2a, 0x77, 0x3b, 0xce, 0x43, 0x3e, 0x4c, 0xe6, - 0x32, 0x67, 0xad, 0x6b, 0x20, 0xff, 0x78, 0xc1, 0x13, 0x84, 0xcc, 0xdc, 0x5a, 0x82, 0xd0, 0x13, - 0xaf, 0x36, 0x95, 0xff, 0xb1, 0x01, 0x20, 0xc6, 0x5f, 0x37, 0x4f, 0x89, 0x1f, 0x66, 0x78, 0xbe, - 0xbe, 0x0b, 0x33, 0x81, 0xf4, 0x26, 0xf9, 0x38, 0x39, 0xe1, 0x8c, 0x35, 0xbe, 0x04, 0xd2, 0x9f, - 0xb0, 0x12, 0xd6, 0xb8, 0xfa, 0xc1, 0x27, 0xab, 0x53, 0x1f, 0x7e, 0xb2, 0x3a, 0xf5, 0xd1, 0x27, - 0xab, 0x53, 0x6f, 0x9f, 0xad, 0x1a, 0x1f, 0x9c, 0xad, 0x1a, 0x1f, 0x9e, 0xad, 0x1a, 0x1f, 0x9d, - 0xad, 0x1a, 0x1f, 0x9f, 0xad, 0x1a, 0xef, 0xfe, 0x7d, 0x75, 0xea, 0xb5, 0xdc, 0xe9, 0xc6, 0x7f, - 0x02, 0x00, 0x00, 0xff, 0xff, 0xf8, 0xee, 0x35, 0x7b, 0xee, 0x26, 0x00, 0x00, + 0x55, 0x8f, 0x37, 0x03, 0x07, 0x72, 0x00, 0x01, 0x12, 0x44, 0xe1, 0xc6, 0x09, 0x25, 0x82, 0xbf, + 0x80, 0x0b, 0xfc, 0x01, 0x48, 0xe4, 0x18, 0xc4, 0x25, 0x12, 0x68, 0x94, 0x98, 0x03, 0x47, 0xc4, + 0xd5, 0x17, 0x50, 0x3d, 0xba, 0xbb, 0x7a, 0x1e, 0xeb, 0x9e, 0xec, 0x12, 0x71, 0x9b, 0xfe, 0xde, + 0x55, 0xf5, 0xd5, 0xf7, 0xaa, 0x81, 0xdd, 0x93, 0xeb, 0xac, 0xee, 0x06, 0xeb, 0x27, 0xdd, 0x43, + 0x42, 0x7d, 0x12, 0x12, 0xb6, 0x7e, 0x4a, 0x7c, 0x27, 0xa0, 0xeb, 0x0a, 0x61, 0x75, 0xdc, 0xb6, + 0x65, 0x1f, 0xbb, 0x3e, 0xa1, 0xbd, 0xf5, 0xce, 0x49, 0x8b, 0x03, 0xd8, 0x7a, 0x9b, 0x84, 0xd6, + 0xfa, 0xe9, 0xc6, 0x7a, 0x8b, 0xf8, 0x84, 0x5a, 0x21, 0x71, 0xea, 0x1d, 0x1a, 0x84, 0x01, 0xfa, + 0x82, 0xe4, 0xaa, 0xeb, 0x5c, 0xf5, 0xce, 0x49, 0x8b, 0x03, 0x58, 0x9d, 0x73, 0xd5, 0x4f, 0x37, + 0x56, 0x9e, 0x6e, 0xb9, 0xe1, 0x71, 0xf7, 0xb0, 0x6e, 0x07, 0xed, 0xf5, 0x56, 0xd0, 0x0a, 0xd6, + 0x05, 0xf3, 0x61, 0xf7, 0x48, 0x7c, 0x89, 0x0f, 0xf1, 0x4b, 0x0a, 0x5d, 0x19, 0x6b, 0x0a, 0xed, + 0xfa, 0xa1, 0xdb, 0x26, 0x83, 0x56, 0xac, 0x3c, 0x77, 0x11, 0x03, 0xb3, 0x8f, 0x49, 0xdb, 0x1a, + 0xe4, 0x33, 0xff, 0x94, 0x87, 0xe2, 0xe6, 0xfe, 0xce, 0x2d, 0x1a, 0x74, 0x3b, 0x68, 0x0d, 0xa6, + 0x7d, 0xab, 0x4d, 0xaa, 0xc6, 0x9a, 0x71, 0xb5, 0xd4, 0x98, 0xfb, 0xa0, 0x5f, 0x9b, 0x3a, 0xeb, + 0xd7, 0xa6, 0x5f, 0xb5, 0xda, 0x04, 0x0b, 0x0c, 0xf2, 0xa0, 0x78, 0x4a, 0x28, 0x73, 0x03, 0x9f, + 0x55, 0x73, 0x6b, 0xf9, 0xab, 0xe5, 0x6b, 0x2f, 0xd5, 0xb3, 0xac, 0xbf, 0x2e, 0x14, 0xdc, 0x93, + 0xac, 0xdb, 0x01, 0x6d, 0xba, 0xcc, 0x0e, 0x4e, 0x09, 0xed, 0x35, 0x16, 0x95, 0x96, 0xa2, 0x42, + 0x32, 0x1c, 0x6b, 0x40, 0x3f, 0x32, 0x60, 0xb1, 0x43, 0xc9, 0x11, 0xa1, 0x94, 0x38, 0x0a, 0x5f, + 0xcd, 0xaf, 0x19, 0x8f, 0x40, 0x6d, 0x55, 0xa9, 0x5d, 0xdc, 0x1f, 0x90, 0x8f, 0x87, 0x34, 0xa2, + 0xdf, 0x18, 0xb0, 0xc2, 0x08, 0x3d, 0x25, 0x74, 0xd3, 0x71, 0x28, 0x61, 0xac, 0xd1, 0xdb, 0xf2, + 0x5c, 0xe2, 0x87, 0x5b, 0x3b, 0x4d, 0xcc, 0xaa, 0xd3, 0x62, 0x1f, 0xbe, 0x96, 0xcd, 0xa0, 0x83, + 0x71, 0x72, 0x1a, 0xa6, 0xb2, 0x68, 0x65, 0x2c, 0x09, 0xc3, 0x0f, 0x30, 0xc3, 0x3c, 0x82, 0xb9, + 0xe8, 0x20, 0x6f, 0xbb, 0x2c, 0x44, 0xf7, 0x60, 0xa6, 0xc5, 0x3f, 0x58, 0xd5, 0x10, 0x06, 0xd6, + 0xb3, 0x19, 0x18, 0xc9, 0x68, 0xcc, 0x2b, 0x7b, 0x66, 0xc4, 0x27, 0xc3, 0x4a, 0x9a, 0xf9, 0xb3, + 0x69, 0x28, 0x6f, 0xee, 0xef, 0x60, 0xc2, 0x82, 0x2e, 0xb5, 0x49, 0x06, 0xa7, 0xb9, 0x0e, 0x73, + 0xcc, 0xf5, 0x5b, 0x5d, 0xcf, 0xa2, 0x1c, 0x5a, 0x9d, 0x11, 0x94, 0xcb, 0x8a, 0x72, 0xee, 0x40, + 0xc3, 0xe1, 0x14, 0x25, 0xba, 0x06, 0xc0, 0x25, 0xb0, 0x8e, 0x65, 0x13, 0xa7, 0x9a, 0x5b, 0x33, + 0xae, 0x16, 0x1b, 0x48, 0xf1, 0xc1, 0xab, 0x31, 0x06, 0x6b, 0x54, 0xe8, 0x71, 0x28, 0x08, 0x4b, + 0xab, 0x45, 0xa1, 0xa6, 0xa2, 0xc8, 0x0b, 0x62, 0x19, 0x58, 0xe2, 0xd0, 0x93, 0x30, 0xab, 0xbc, + 0xac, 0x5a, 0x12, 0x64, 0x0b, 0x8a, 0x6c, 0x36, 0x72, 0x83, 0x08, 0xcf, 0xd7, 0x77, 0xe2, 0xfa, + 0x8e, 0xf0, 0x3b, 0x6d, 0x7d, 0xaf, 0xb8, 0xbe, 0x83, 0x05, 0x06, 0xdd, 0x86, 0xc2, 0x29, 0xa1, + 0x87, 0xdc, 0x13, 0xb8, 0x6b, 0x7e, 0x29, 0xdb, 0x46, 0xdf, 0xe3, 0x2c, 0x8d, 0x12, 0x37, 0x4d, + 0xfc, 0xc4, 0x52, 0x08, 0xaa, 0x03, 0xb0, 0xe3, 0x80, 0x86, 0x62, 0x79, 0xd5, 0xc2, 0x5a, 0xfe, + 0x6a, 0xa9, 0x31, 0xcf, 0xd7, 0x7b, 0x10, 0x43, 0xb1, 0x46, 0xc1, 0xe9, 0x6d, 0x2b, 0x24, 0xad, + 0x80, 0xba, 0x84, 0x55, 0x67, 0x13, 0xfa, 0xad, 0x18, 0x8a, 0x35, 0x0a, 0xf4, 0x0d, 0x40, 0x2c, + 0x0c, 0xa8, 0xd5, 0x22, 0x6a, 0xa9, 0x2f, 0x5b, 0xec, 0xb8, 0x0a, 0x62, 0x75, 0x2b, 0x6a, 0x75, + 0xe8, 0x60, 0x88, 0x02, 0x8f, 0xe0, 0x32, 0x7f, 0x67, 0xc0, 0x82, 0xe6, 0x0b, 0xc2, 0xef, 0xae, + 0xc3, 0x5c, 0x4b, 0xbb, 0x75, 0xca, 0x2f, 0xe2, 0xd3, 0xd6, 0x6f, 0x24, 0x4e, 0x51, 0x22, 0x02, + 0x25, 0xaa, 0x24, 0x45, 0xd1, 0x65, 0x23, 0xb3, 0xd3, 0x46, 0x36, 0x24, 0x9a, 0x34, 0x20, 0xc3, + 0x89, 0x64, 0xf3, 0x1f, 0x86, 0x70, 0xe0, 0x28, 0xde, 0xa0, 0xab, 0x5a, 0x4c, 0x33, 0xc4, 0xf6, + 0xcd, 0x8d, 0x89, 0x47, 0x17, 0x04, 0x82, 0xdc, 0xff, 0x44, 0x20, 0xb8, 0x51, 0xfc, 0xd5, 0x7b, + 0xb5, 0xa9, 0xb7, 0xff, 0xb6, 0x36, 0x65, 0xfe, 0xd2, 0x80, 0xb9, 0xcd, 0x4e, 0xc7, 0xeb, 0xed, + 0x75, 0x42, 0xb1, 0x00, 0x13, 0x66, 0x1c, 0xda, 0xc3, 0x5d, 0x5f, 0x2d, 0x14, 0xf8, 0xfd, 0x6e, + 0x0a, 0x08, 0x56, 0x18, 0x7e, 0x7f, 0x8e, 0x02, 0x6a, 0x13, 0x75, 0xdd, 0xe2, 0xfb, 0xb3, 0xcd, + 0x81, 0x58, 0xe2, 0xf8, 0x21, 0x1f, 0xb9, 0xc4, 0x73, 0x76, 0x2d, 0xdf, 0x6a, 0x11, 0xaa, 0x2e, + 0x47, 0xbc, 0xf5, 0xdb, 0x1a, 0x0e, 0xa7, 0x28, 0xcd, 0x7f, 0xe7, 0xa0, 0xb4, 0x15, 0xf8, 0x8e, + 0x1b, 0xaa, 0xcb, 0x15, 0xf6, 0x3a, 0x43, 0xc1, 0xe3, 0x4e, 0xaf, 0x43, 0xb0, 0xc0, 0xa0, 0xe7, + 0x61, 0x86, 0x85, 0x56, 0xd8, 0x65, 0xc2, 0x9e, 0x52, 0xe3, 0xb1, 0x28, 0x2c, 0x1d, 0x08, 0xe8, + 0x79, 0xbf, 0xb6, 0x10, 0x8b, 0x93, 0x20, 0xac, 0x18, 0xb8, 0xa7, 0x07, 0x87, 0x62, 0xa3, 0x9c, + 0x5b, 0x32, 0xed, 0x45, 0xf9, 0x23, 0x9f, 0x78, 0xfa, 0xde, 0x10, 0x05, 0x1e, 0xc1, 0x85, 0x4e, + 0x01, 0x79, 0x16, 0x0b, 0xef, 0x50, 0xcb, 0x67, 0x42, 0xd7, 0x1d, 0xb7, 0x4d, 0xd4, 0x85, 0xff, + 0x62, 0xb6, 0x13, 0xe7, 0x1c, 0x89, 0xde, 0xdb, 0x43, 0xd2, 0xf0, 0x08, 0x0d, 0xe8, 0x09, 0x98, + 0xa1, 0xc4, 0x62, 0x81, 0x5f, 0x2d, 0x88, 0xe5, 0xc7, 0x51, 0x19, 0x0b, 0x28, 0x56, 0x58, 0x1e, + 0xd0, 0xda, 0x84, 0x31, 0xab, 0x15, 0x85, 0xd7, 0x38, 0xa0, 0xed, 0x4a, 0x30, 0x8e, 0xf0, 0x66, + 0x1b, 0x2a, 0x5b, 0x94, 0x58, 0x21, 0x99, 0xc4, 0x2b, 0x3e, 0xfd, 0x81, 0xff, 0x24, 0x0f, 0x95, + 0x26, 0xf1, 0x48, 0xa2, 0x6f, 0x1b, 0x50, 0x8b, 0x5a, 0x36, 0xd9, 0x27, 0xd4, 0x0d, 0x9c, 0x03, + 0x62, 0x07, 0xbe, 0xc3, 0x84, 0x0b, 0xe4, 0x1b, 0xff, 0xc7, 0xf7, 0xe6, 0xd6, 0x10, 0x16, 0x8f, + 0xe0, 0x40, 0x1e, 0x54, 0x3a, 0x54, 0xfc, 0x16, 0xfb, 0x25, 0x3d, 0xa4, 0x7c, 0xed, 0xcb, 0xd9, + 0x8e, 0x63, 0x5f, 0x67, 0x6d, 0x2c, 0x9d, 0xf5, 0x6b, 0x95, 0x14, 0x08, 0xa7, 0x85, 0xa3, 0xaf, + 0xc3, 0x62, 0x40, 0x3b, 0xc7, 0x96, 0xdf, 0x24, 0x1d, 0xe2, 0x3b, 0xc4, 0x0f, 0x99, 0xd8, 0x85, + 0x62, 0x63, 0x99, 0xd7, 0x11, 0x7b, 0x03, 0x38, 0x3c, 0x44, 0x8d, 0x5e, 0x83, 0xa5, 0x0e, 0x0d, + 0x3a, 0x56, 0x4b, 0xb8, 0xd4, 0x7e, 0xe0, 0xb9, 0x76, 0x4f, 0xb8, 0x50, 0xa9, 0xf1, 0xd4, 0x59, + 0xbf, 0xb6, 0xb4, 0x3f, 0x88, 0x3c, 0xef, 0xd7, 0x2e, 0x89, 0xad, 0xe3, 0x90, 0x04, 0x89, 0x87, + 0xc5, 0x68, 0x67, 0x58, 0x18, 0x77, 0x86, 0xe6, 0x0e, 0x14, 0x9b, 0x5d, 0xe5, 0xcf, 0x2f, 0x42, + 0xd1, 0x51, 0xbf, 0xd5, 0xce, 0x47, 0x17, 0x2b, 0xa6, 0x39, 0xef, 0xd7, 0x2a, 0xbc, 0x74, 0xac, + 0x47, 0x00, 0x1c, 0xb3, 0x98, 0x4f, 0x40, 0x51, 0x1c, 0x39, 0xbb, 0xb7, 0x81, 0x16, 0x21, 0x8f, + 0xad, 0xfb, 0x42, 0xca, 0x1c, 0xe6, 0x3f, 0xb5, 0x08, 0xb4, 0x07, 0x70, 0x8b, 0x84, 0xd1, 0xc1, + 0x6f, 0xc2, 0x42, 0x14, 0x86, 0xd3, 0xd9, 0xe1, 0xff, 0x95, 0xee, 0x05, 0x9c, 0x46, 0xe3, 0x41, + 0x7a, 0xf3, 0x75, 0x28, 0x89, 0x0c, 0xc2, 0xd3, 0x6f, 0x92, 0xea, 0x8d, 0x07, 0xa4, 0xfa, 0x28, + 0x7f, 0xe7, 0xc6, 0xe5, 0x6f, 0xcd, 0x5c, 0x0f, 0x2a, 0x92, 0x37, 0x2a, 0x6e, 0x32, 0x69, 0x78, + 0x0a, 0x8a, 0x91, 0x99, 0x4a, 0x4b, 0x5c, 0xd4, 0x46, 0x82, 0x70, 0x4c, 0xa1, 0x69, 0x3b, 0x86, + 0x54, 0x36, 0xcc, 0xa6, 0x4c, 0xab, 0x5c, 0x72, 0x0f, 0xae, 0x5c, 0x34, 0x4d, 0x3f, 0x84, 0xea, + 0xb8, 0x4a, 0xf8, 0x21, 0xf2, 0x75, 0x76, 0x53, 0xcc, 0x77, 0x0c, 0x58, 0xd4, 0x25, 0x65, 0x3f, + 0xbe, 0xec, 0x4a, 0x2e, 0xae, 0xd4, 0xb4, 0x1d, 0xf9, 0xb5, 0x01, 0xcb, 0xa9, 0xa5, 0x4d, 0x74, + 0xe2, 0x13, 0x18, 0xa5, 0x3b, 0x47, 0x7e, 0x02, 0xe7, 0xf8, 0x4b, 0x0e, 0x2a, 0xb7, 0xad, 0x43, + 0xe2, 0x1d, 0x10, 0x8f, 0xd8, 0x61, 0x40, 0xd1, 0x0f, 0xa0, 0xdc, 0xb6, 0x42, 0xfb, 0x58, 0x40, + 0xa3, 0xaa, 0xbe, 0x99, 0x2d, 0xd8, 0xa5, 0x24, 0xd5, 0x77, 0x13, 0x31, 0x37, 0xfd, 0x90, 0xf6, + 0x1a, 0x97, 0x94, 0x49, 0x65, 0x0d, 0x83, 0x75, 0x6d, 0xa2, 0x15, 0x13, 0xdf, 0x37, 0xdf, 0xea, + 0xf0, 0x92, 0x63, 0xf2, 0x0e, 0x30, 0x65, 0x02, 0x26, 0x6f, 0x76, 0x5d, 0x4a, 0xda, 0xc4, 0x0f, + 0x93, 0x56, 0x6c, 0x77, 0x40, 0x3e, 0x1e, 0xd2, 0xb8, 0xf2, 0x12, 0x2c, 0x0e, 0x1a, 0xcf, 0xe3, + 0xcf, 0x09, 0xe9, 0xc9, 0xf3, 0xc2, 0xfc, 0x27, 0x5a, 0x86, 0xc2, 0xa9, 0xe5, 0x75, 0xd5, 0x6d, + 0xc4, 0xf2, 0xe3, 0x46, 0xee, 0xba, 0x61, 0xfe, 0xd6, 0x80, 0xea, 0x38, 0x43, 0xd0, 0xe7, 0x35, + 0x41, 0x8d, 0xb2, 0xb2, 0x2a, 0xff, 0x0a, 0xe9, 0x49, 0xa9, 0x37, 0xa1, 0x18, 0x74, 0x78, 0x3d, + 0x10, 0x50, 0x75, 0xea, 0x4f, 0x46, 0x27, 0xb9, 0xa7, 0xe0, 0xe7, 0xfd, 0xda, 0xe5, 0x94, 0xf8, + 0x08, 0x81, 0x63, 0x56, 0x1e, 0xa9, 0x85, 0x3d, 0x3c, 0x7b, 0xc4, 0x91, 0xfa, 0x9e, 0x80, 0x60, + 0x85, 0x31, 0xff, 0x60, 0xc0, 0xb4, 0x28, 0xa6, 0x5f, 0x87, 0x22, 0xdf, 0x3f, 0xc7, 0x0a, 0x2d, + 0x61, 0x57, 0xe6, 0x36, 0x8e, 0x73, 0xef, 0x92, 0xd0, 0x4a, 0xbc, 0x2d, 0x82, 0xe0, 0x58, 0x22, + 0xc2, 0x50, 0x70, 0x43, 0xd2, 0x8e, 0x0e, 0xf2, 0xe9, 0xb1, 0xa2, 0xd5, 0x10, 0xa1, 0x8e, 0xad, + 0xfb, 0x37, 0xdf, 0x0a, 0x89, 0xcf, 0x0f, 0x23, 0xb9, 0x1a, 0x3b, 0x5c, 0x06, 0x96, 0xa2, 0xcc, + 0x7f, 0x19, 0x10, 0xab, 0xe2, 0xce, 0xcf, 0x88, 0x77, 0x74, 0xdb, 0xf5, 0x4f, 0xd4, 0xb6, 0xc6, + 0xe6, 0x1c, 0x28, 0x38, 0x8e, 0x29, 0x46, 0xa5, 0x87, 0xdc, 0x64, 0xe9, 0x81, 0x2b, 0xb4, 0x03, + 0x3f, 0x74, 0xfd, 0xee, 0xd0, 0x6d, 0xdb, 0x52, 0x70, 0x1c, 0x53, 0xf0, 0x42, 0x84, 0x92, 0xb6, + 0xe5, 0xfa, 0xae, 0xdf, 0xe2, 0x8b, 0xd8, 0x0a, 0xba, 0x7e, 0x28, 0x32, 0xb2, 0x2a, 0x44, 0xf0, + 0x10, 0x16, 0x8f, 0xe0, 0x30, 0x7f, 0x3f, 0x0d, 0x65, 0xbe, 0xe6, 0x28, 0xcf, 0xbd, 0x00, 0x15, + 0x4f, 0xf7, 0x02, 0xb5, 0xf6, 0xcb, 0xca, 0x94, 0xf4, 0xbd, 0xc6, 0x69, 0x5a, 0xce, 0x2c, 0xea, + 0xa7, 0x98, 0x39, 0x97, 0x66, 0xde, 0xd6, 0x91, 0x38, 0x4d, 0xcb, 0xa3, 0xd7, 0x7d, 0x7e, 0x3f, + 0x54, 0x65, 0x12, 0x1f, 0xd1, 0x37, 0x39, 0x10, 0x4b, 0x1c, 0xda, 0x85, 0x4b, 0x96, 0xe7, 0x05, + 0xf7, 0x05, 0xb0, 0x11, 0x04, 0x27, 0x6d, 0x8b, 0x9e, 0x30, 0xd1, 0x08, 0x17, 0x1b, 0x9f, 0x53, + 0x2c, 0x97, 0x36, 0x87, 0x49, 0xf0, 0x28, 0xbe, 0x51, 0xc7, 0x36, 0x3d, 0xe1, 0xb1, 0x1d, 0xc3, + 0xf2, 0x00, 0x48, 0xdc, 0x72, 0xd5, 0x95, 0x3e, 0xab, 0xe4, 0x2c, 0xe3, 0x11, 0x34, 0xe7, 0x63, + 0xe0, 0x78, 0xa4, 0x44, 0x74, 0x03, 0xe6, 0xb9, 0x27, 0x07, 0xdd, 0x30, 0xaa, 0x3b, 0x0b, 0xe2, + 0xb8, 0xd1, 0x59, 0xbf, 0x36, 0x7f, 0x27, 0x85, 0xc1, 0x03, 0x94, 0x7c, 0x73, 0x3d, 0xb7, 0xed, + 0x86, 0xd5, 0x59, 0xc1, 0x12, 0x6f, 0xee, 0x6d, 0x0e, 0xc4, 0x12, 0x97, 0xf2, 0xc0, 0xe2, 0x45, + 0x1e, 0x68, 0xfe, 0x39, 0x0f, 0x48, 0x16, 0xca, 0x8e, 0xac, 0xa7, 0x64, 0x48, 0xe3, 0xd5, 0xbc, + 0x2a, 0xb4, 0x8d, 0x81, 0x6a, 0x5e, 0xd5, 0xd8, 0x11, 0x1e, 0xed, 0x42, 0x49, 0x86, 0x96, 0xe4, + 0xba, 0xac, 0x2b, 0xe2, 0xd2, 0x5e, 0x84, 0x38, 0xef, 0xd7, 0x56, 0x52, 0x6a, 0x62, 0x8c, 0xe8, + 0xb4, 0x12, 0x09, 0xe8, 0x1a, 0x80, 0xd5, 0x71, 0xf5, 0x59, 0x5b, 0x29, 0x99, 0xb8, 0x24, 0x5d, + 0x33, 0xd6, 0xa8, 0xd0, 0xcb, 0x30, 0x1d, 0x7e, 0xba, 0x6e, 0xa8, 0x28, 0x9a, 0x3d, 0xde, 0xfb, + 0x08, 0x09, 0x5c, 0xbb, 0xf0, 0x67, 0xc6, 0xcd, 0x52, 0x8d, 0x4c, 0xac, 0x7d, 0x3b, 0xc6, 0x60, + 0x8d, 0x0a, 0x7d, 0x0b, 0x8a, 0x47, 0xaa, 0x14, 0x15, 0x07, 0x93, 0x39, 0x44, 0x46, 0x05, 0xac, + 0x6c, 0xf7, 0xa3, 0x2f, 0x1c, 0x4b, 0x43, 0x5f, 0x81, 0x32, 0xeb, 0x1e, 0xc6, 0xd9, 0x5b, 0x9e, + 0x66, 0x9c, 0x2a, 0x0f, 0x12, 0x14, 0xd6, 0xe9, 0xcc, 0x37, 0xa1, 0xb4, 0xeb, 0xda, 0x34, 0x10, + 0xfd, 0xdb, 0x93, 0x30, 0xcb, 0x52, 0x0d, 0x4e, 0x7c, 0x92, 0x91, 0x97, 0x45, 0x78, 0xee, 0x5e, + 0xbe, 0xe5, 0x07, 0xb2, 0x8d, 0x29, 0x24, 0xee, 0xf5, 0x2a, 0x07, 0x62, 0x89, 0xbb, 0xb1, 0xcc, + 0x0b, 0x84, 0x9f, 0xbe, 0x5f, 0x9b, 0x7a, 0xf7, 0xfd, 0xda, 0xd4, 0x7b, 0xef, 0xab, 0x62, 0xe1, + 0x1c, 0x00, 0xf6, 0x0e, 0xbf, 0x47, 0x6c, 0x19, 0x76, 0x33, 0x8d, 0xe4, 0xa2, 0x49, 0xb0, 0x18, + 0xc9, 0xe5, 0x06, 0x8a, 0x3e, 0x0d, 0x87, 0x53, 0x94, 0x68, 0x1d, 0x4a, 0xf1, 0xb0, 0x4d, 0xf9, + 0xc7, 0x52, 0xe4, 0x6f, 0xf1, 0x44, 0x0e, 0x27, 0x34, 0xa9, 0x1c, 0x30, 0x7d, 0x61, 0x0e, 0x68, + 0x40, 0xbe, 0xeb, 0x3a, 0xaa, 0xd9, 0x7d, 0x26, 0xca, 0xc1, 0x77, 0x77, 0x9a, 0xe7, 0xfd, 0xda, + 0x63, 0xe3, 0x66, 0xdc, 0x61, 0xaf, 0x43, 0x58, 0xfd, 0xee, 0x4e, 0x13, 0x73, 0xe6, 0x51, 0x01, + 0x69, 0x66, 0xc2, 0x80, 0x74, 0x0d, 0xa0, 0x95, 0x8c, 0x0c, 0xe4, 0x7d, 0x8f, 0x1d, 0x51, 0x1b, + 0x15, 0x68, 0x54, 0x88, 0xc1, 0x92, 0xcd, 0xfb, 0x6a, 0xd5, 0xba, 0xb3, 0xd0, 0x6a, 0xcb, 0x21, + 0xe4, 0x64, 0x77, 0xe2, 0x8a, 0x52, 0xb3, 0xb4, 0x35, 0x28, 0x0c, 0x0f, 0xcb, 0x47, 0x01, 0x2c, + 0x39, 0xaa, 0x43, 0x4c, 0x94, 0x96, 0x26, 0x56, 0x7a, 0x99, 0x2b, 0x6c, 0x0e, 0x0a, 0xc2, 0xc3, + 0xb2, 0xd1, 0x77, 0x61, 0x25, 0x02, 0x0e, 0xb7, 0xe9, 0x22, 0x60, 0xe7, 0x1b, 0xab, 0x67, 0xfd, + 0xda, 0x4a, 0x73, 0x2c, 0x15, 0x7e, 0x80, 0x04, 0xe4, 0xc0, 0x8c, 0x27, 0x0b, 0xdc, 0xb2, 0x28, + 0x4a, 0xbe, 0x9a, 0x6d, 0x15, 0x89, 0xf7, 0xd7, 0xf5, 0xc2, 0x36, 0x1e, 0x97, 0xa8, 0x9a, 0x56, + 0xc9, 0x46, 0x6f, 0x41, 0xd9, 0xf2, 0xfd, 0x20, 0xb4, 0xe4, 0xe0, 0x60, 0x4e, 0xa8, 0xda, 0x9c, + 0x58, 0xd5, 0x66, 0x22, 0x63, 0xa0, 0x90, 0xd6, 0x30, 0x58, 0x57, 0x85, 0xee, 0xc3, 0x42, 0x70, + 0xdf, 0x27, 0x14, 0x93, 0x23, 0x42, 0x89, 0x6f, 0x13, 0x56, 0xad, 0x08, 0xed, 0xcf, 0x66, 0xd4, + 0x9e, 0x62, 0x4e, 0x5c, 0x3a, 0x0d, 0x67, 0x78, 0x50, 0x0b, 0xaa, 0xf3, 0xd8, 0xea, 0x5b, 0x9e, + 0xfb, 0x7d, 0x42, 0x59, 0x75, 0x3e, 0x99, 0x13, 0x6f, 0xc7, 0x50, 0xac, 0x51, 0xf0, 0xe8, 0x67, + 0x7b, 0x5d, 0x16, 0x12, 0x39, 0xb4, 0x5f, 0x48, 0x47, 0xbf, 0xad, 0x04, 0x85, 0x75, 0x3a, 0xd4, + 0x85, 0x4a, 0x5b, 0xcf, 0x34, 0xd5, 0x25, 0xb1, 0xba, 0xeb, 0xd9, 0x56, 0x37, 0x9c, 0x0b, 0x93, + 0xc2, 0x27, 0x85, 0xc3, 0x69, 0x2d, 0x2b, 0xcf, 0x43, 0xf9, 0x53, 0xf6, 0x04, 0xbc, 0xa7, 0x18, + 0x3c, 0xc7, 0x89, 0x7a, 0x8a, 0x3f, 0xe6, 0x60, 0x3e, 0xbd, 0xfb, 0x03, 0x59, 0xb4, 0x90, 0x29, + 0x8b, 0x46, 0xdd, 0xab, 0x31, 0xf6, 0x9d, 0x21, 0x0a, 0xeb, 0xf9, 0xb1, 0x61, 0x5d, 0x45, 0xcf, + 0xe9, 0x87, 0x89, 0x9e, 0x75, 0x00, 0x5e, 0x9e, 0xd0, 0xc0, 0xf3, 0x08, 0x15, 0x81, 0xb3, 0xa8, + 0xde, 0x13, 0x62, 0x28, 0xd6, 0x28, 0x78, 0x11, 0x7d, 0xe8, 0x05, 0xf6, 0x89, 0xd8, 0x82, 0xe8, + 0xd2, 0x8b, 0x90, 0x59, 0x94, 0x45, 0x74, 0x63, 0x08, 0x8b, 0x47, 0x70, 0x98, 0x3d, 0xb8, 0xbc, + 0x6f, 0xd1, 0xd0, 0xb5, 0xbc, 0xe4, 0x82, 0x89, 0x2e, 0xe5, 0x8d, 0xa1, 0x1e, 0xe8, 0x99, 0x49, + 0x2f, 0x6a, 0xb2, 0xf9, 0x09, 0x2c, 0xe9, 0x83, 0xcc, 0xbf, 0x1a, 0x70, 0x65, 0xa4, 0xee, 0xcf, + 0xa0, 0x07, 0x7b, 0x23, 0xdd, 0x83, 0xbd, 0x90, 0x71, 0x78, 0x39, 0xca, 0xda, 0x31, 0x1d, 0xd9, + 0x2c, 0x14, 0xf6, 0x79, 0xed, 0x6b, 0xfe, 0xc2, 0x80, 0x39, 0xf1, 0x6b, 0x92, 0xc1, 0x6f, 0x2d, + 0xfd, 0x1c, 0x50, 0x7a, 0x84, 0x4f, 0x01, 0xef, 0x18, 0x90, 0x1e, 0xb9, 0xa2, 0x97, 0xa4, 0xff, + 0x1a, 0xf1, 0x4c, 0x74, 0x42, 0xdf, 0x7d, 0x71, 0x5c, 0x07, 0x79, 0x29, 0xd3, 0x70, 0xf1, 0x29, + 0x28, 0xe1, 0x20, 0x08, 0xf7, 0xad, 0xf0, 0x98, 0xf1, 0x85, 0x77, 0xf8, 0x0f, 0xb5, 0x37, 0x62, + 0xe1, 0x02, 0x83, 0x25, 0xdc, 0xfc, 0xb9, 0x01, 0x57, 0xc6, 0x3e, 0xd1, 0xf0, 0x10, 0x60, 0xc7, + 0x5f, 0x6a, 0x45, 0xb1, 0x17, 0x26, 0x74, 0x58, 0xa3, 0xe2, 0xad, 0x5f, 0xea, 0x5d, 0x67, 0xb0, + 0xf5, 0x4b, 0x69, 0xc3, 0x69, 0x5a, 0xf3, 0x9f, 0x39, 0x50, 0x6f, 0x22, 0xff, 0x65, 0x8f, 0x7d, + 0x62, 0xe0, 0x45, 0x66, 0x3e, 0xfd, 0x22, 0x13, 0x3f, 0xbf, 0x68, 0x4f, 0x12, 0xf9, 0x07, 0x3f, + 0x49, 0xa0, 0xe7, 0xe2, 0x57, 0x0e, 0x19, 0xba, 0x56, 0xd3, 0xaf, 0x1c, 0xe7, 0xfd, 0xda, 0x9c, + 0x12, 0x9e, 0x7e, 0xf5, 0x78, 0x0d, 0x66, 0x1d, 0x12, 0x5a, 0xae, 0x27, 0xdb, 0xb8, 0xcc, 0xb3, + 0x7f, 0x29, 0xac, 0x29, 0x59, 0x1b, 0x65, 0x6e, 0x93, 0xfa, 0xc0, 0x91, 0x40, 0x1e, 0x6d, 0xed, + 0xc0, 0x91, 0x5d, 0x48, 0x21, 0x89, 0xb6, 0x5b, 0x81, 0x43, 0xb0, 0xc0, 0x98, 0xef, 0x1a, 0x50, + 0x96, 0x92, 0xb6, 0xac, 0x2e, 0x23, 0x68, 0x23, 0x5e, 0x85, 0x3c, 0xee, 0x2b, 0xfa, 0x73, 0xd6, + 0x79, 0xbf, 0x56, 0x12, 0x64, 0xa2, 0x81, 0x19, 0xf1, 0x6c, 0x93, 0xbb, 0x60, 0x8f, 0x1e, 0x87, + 0x82, 0xb8, 0x3d, 0x6a, 0x33, 0x93, 0x77, 0x39, 0x0e, 0xc4, 0x12, 0x67, 0x7e, 0x9c, 0x83, 0x4a, + 0x6a, 0x71, 0x19, 0x7a, 0x81, 0x78, 0xe2, 0x99, 0xcb, 0x30, 0x45, 0x1f, 0xff, 0x0a, 0xae, 0x72, + 0xcf, 0xcc, 0xc3, 0xe4, 0x9e, 0x6f, 0xc3, 0x8c, 0xcd, 0xf7, 0x28, 0xfa, 0x53, 0xc5, 0xc6, 0x24, + 0xc7, 0x29, 0x76, 0x37, 0xf1, 0x46, 0xf1, 0xc9, 0xb0, 0x12, 0x88, 0x6e, 0xc1, 0x12, 0x25, 0x21, + 0xed, 0x6d, 0x1e, 0x85, 0x84, 0xea, 0xbd, 0x7f, 0x21, 0xa9, 0xb8, 0xf1, 0x20, 0x01, 0x1e, 0xe6, + 0x31, 0x0f, 0x61, 0xee, 0x8e, 0x75, 0xe8, 0xc5, 0xaf, 0x59, 0x18, 0x2a, 0xae, 0x6f, 0x7b, 0x5d, + 0x87, 0xc8, 0x68, 0x1c, 0x45, 0xaf, 0xe8, 0xd2, 0xee, 0xe8, 0xc8, 0xf3, 0x7e, 0xed, 0x52, 0x0a, + 0x20, 0x9f, 0x6f, 0x70, 0x5a, 0x84, 0xe9, 0xc1, 0xf4, 0x67, 0xd8, 0x3d, 0x7e, 0x07, 0x4a, 0x49, + 0x7d, 0xff, 0x88, 0x55, 0x9a, 0x6f, 0x40, 0x91, 0x7b, 0x7c, 0xd4, 0x97, 0x5e, 0x50, 0xe2, 0xa4, + 0x0b, 0xa7, 0x5c, 0x96, 0xc2, 0xc9, 0x6c, 0x43, 0xe5, 0x6e, 0xc7, 0x79, 0xc8, 0xf7, 0xcc, 0x5c, + 0xe6, 0xac, 0x75, 0x0d, 0xe4, 0xff, 0x35, 0x78, 0x82, 0x90, 0x99, 0x5b, 0x4b, 0x10, 0x7a, 0xe2, + 0xd5, 0x86, 0xf9, 0x3f, 0x36, 0x00, 0xc4, 0xd4, 0xec, 0xe6, 0x29, 0xf1, 0xc3, 0x0c, 0xaf, 0xde, + 0x77, 0x61, 0x26, 0x90, 0xde, 0x24, 0xdf, 0x34, 0x27, 0x1c, 0xcd, 0xc6, 0x97, 0x40, 0xfa, 0x13, + 0x56, 0xc2, 0x1a, 0x57, 0x3f, 0xf8, 0x64, 0x75, 0xea, 0xc3, 0x4f, 0x56, 0xa7, 0x3e, 0xfa, 0x64, + 0x75, 0xea, 0xed, 0xb3, 0x55, 0xe3, 0x83, 0xb3, 0x55, 0xe3, 0xc3, 0xb3, 0x55, 0xe3, 0xa3, 0xb3, + 0x55, 0xe3, 0xe3, 0xb3, 0x55, 0xe3, 0xdd, 0xbf, 0xaf, 0x4e, 0xbd, 0x96, 0x3b, 0xdd, 0xf8, 0x4f, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x0d, 0x18, 0xc5, 0x8c, 0x25, 0x27, 0x00, 0x00, } func (m *APIGroup) Marshal() (dAtA []byte, err error) { @@ -2568,6 +2569,11 @@ _ = i var l int _ = l + i -= len(m.Subresource) + copy(dAtA[i:], m.Subresource) + i = encodeVarintGenerated(dAtA, i, uint64(len(m.Subresource))) + i-- + dAtA[i] = 0x42 if m.FieldsV1 != nil { { size, err := m.FieldsV1.MarshalToSizedBuffer(dAtA[:i]) @@ -3914,6 +3920,8 @@ l = m.FieldsV1.Size() n += 1 + l + sovGenerated(uint64(l)) } + l = len(m.Subresource) + n += 1 + l + sovGenerated(uint64(l)) return n } @@ -4508,6 +4516,7 @@ `Time:` + strings.Replace(fmt.Sprintf("%v", this.Time), "Time", "Time", 1) + `,`, `FieldsType:` + fmt.Sprintf("%v", this.FieldsType) + `,`, `FieldsV1:` + strings.Replace(fmt.Sprintf("%v", this.FieldsV1), "FieldsV1", "FieldsV1", 1) + `,`, + `Subresource:` + fmt.Sprintf("%v", this.Subresource) + `,`, `}`, }, "") return s @@ -8442,6 +8451,38 @@ return err } iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Subresource", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenerated + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenerated + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Subresource = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto 2021-12-26 00:45:51.000000000 +0000 @@ -499,8 +499,6 @@ // assume bookmarks are returned at any specific interval, nor may they // assume the server will send any BOOKMARK event during a session. // If this is not a watch, this field is ignored. - // If the feature gate WatchBookmarks is not enabled in apiserver, - // this field is ignored. // +optional optional bool allowWatchBookmarks = 9; @@ -589,6 +587,15 @@ // FieldsV1 holds the first JSON version format as described in the "FieldsV1" type. // +optional optional FieldsV1 fieldsV1 = 7; + + // Subresource is the name of the subresource used to update that object, or + // empty string if the object was updated through the main resource. The + // value of this field is used to distinguish between managers, even if they + // share the same name. For example, a status update will be distinct from a + // regular update using the same manager name. + // Note that the APIVersion field is not related to the Subresource field and + // it always corresponds to the version of the main resource. + optional string subresource = 8; } // MicroTime is version of Time with microsecond level precision. @@ -788,6 +795,7 @@ // OwnerReference contains enough information to let you identify an owning // object. An owning object must be in the same namespace as the dependent, or // be cluster-scoped, so there is no namespace field. +// +structType=atomic message OwnerReference { // API version of the referent. optional string apiVersion = 5; diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go 2021-12-26 00:45:51.000000000 +0000 @@ -38,13 +38,13 @@ if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 { return labels.Everything(), nil } - selector := labels.NewSelector() + requirements := make([]labels.Requirement, 0, len(ps.MatchLabels)+len(ps.MatchExpressions)) for k, v := range ps.MatchLabels { r, err := labels.NewRequirement(k, selection.Equals, []string{v}) if err != nil { return nil, err } - selector = selector.Add(*r) + requirements = append(requirements, *r) } for _, expr := range ps.MatchExpressions { var op selection.Operator @@ -64,8 +64,10 @@ if err != nil { return nil, err } - selector = selector.Add(*r) + requirements = append(requirements, *r) } + selector := labels.NewSelector() + selector = selector.Add(requirements...) return selector, nil } @@ -154,7 +156,7 @@ } selector := &LabelSelector{ - MatchLabels: make(map[string]string), + MatchLabels: make(map[string]string, len(ls)), } for label, value := range ls { selector.MatchLabels[label] = value diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/OWNERS libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/OWNERS --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/OWNERS 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/OWNERS 2021-12-26 00:45:51.000000000 +0000 @@ -8,7 +8,6 @@ - brendandburns - caesarxuchao - liggitt -- erictune - davidopp - sttts - quinton-hoole @@ -20,6 +19,5 @@ - dims - hongchaodeng - krousey -- mml - therc - kevin-wangzefeng diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go 2021-12-26 00:45:51.000000000 +0000 @@ -303,6 +303,7 @@ // OwnerReference contains enough information to let you identify an owning // object. An owning object must be in the same namespace as the dependent, or // be cluster-scoped, so there is no namespace field. +// +structType=atomic type OwnerReference struct { // API version of the referent. APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"` @@ -356,8 +357,6 @@ // assume bookmarks are returned at any specific interval, nor may they // assume the server will send any BOOKMARK event during a session. // If this is not a watch, this field is ignored. - // If the feature gate WatchBookmarks is not enabled in apiserver, - // this field is ignored. // +optional AllowWatchBookmarks bool `json:"allowWatchBookmarks,omitempty" protobuf:"varint,9,opt,name=allowWatchBookmarks"` @@ -1180,6 +1179,15 @@ // FieldsV1 holds the first JSON version format as described in the "FieldsV1" type. // +optional FieldsV1 *FieldsV1 `json:"fieldsV1,omitempty" protobuf:"bytes,7,opt,name=fieldsV1"` + + // Subresource is the name of the subresource used to update that object, or + // empty string if the object was updated through the main resource. The + // value of this field is used to distinguish between managers, even if they + // share the same name. For example, a status update will be distinct from a + // regular update using the same manager name. + // Note that the APIVersion field is not related to the Subresource field and + // it always corresponds to the version of the main resource. + Subresource string `json:"subresource,omitempty" protobuf:"bytes,8,opt,name=subresource"` } // ManagedFieldsOperationType is the type of operation which lead to a ManagedFieldsEntry being created. diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types_swagger_doc_generated.go 2021-12-26 00:45:51.000000000 +0000 @@ -209,7 +209,7 @@ "labelSelector": "A selector to restrict the list of returned objects by their labels. Defaults to everything.", "fieldSelector": "A selector to restrict the list of returned objects by their fields. Defaults to everything.", "watch": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.", - "allowWatchBookmarks": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored. If the feature gate WatchBookmarks is not enabled in apiserver, this field is ignored.", + "allowWatchBookmarks": "allowWatchBookmarks requests watch events with type \"BOOKMARK\". Servers that do not implement bookmarks may ignore this flag and bookmarks are sent at the server's discretion. Clients should not assume bookmarks are returned at any specific interval, nor may they assume the server will send any BOOKMARK event during a session. If this is not a watch, this field is ignored.", "resourceVersion": "resourceVersion sets a constraint on what resource versions a request may be served from. See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", "resourceVersionMatch": "resourceVersionMatch determines how resourceVersion is applied to list calls. It is highly recommended that resourceVersionMatch be set for list calls where resourceVersion is set See https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions for details.\n\nDefaults to unset", "timeoutSeconds": "Timeout for the list/watch call. This limits the duration of the call, regardless of any activity or inactivity.", @@ -222,13 +222,14 @@ } var map_ManagedFieldsEntry = map[string]string{ - "": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", - "manager": "Manager is an identifier of the workflow managing these fields.", - "operation": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", - "apiVersion": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", - "time": "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'", - "fieldsType": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", - "fieldsV1": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + "": "ManagedFieldsEntry is a workflow-id, a FieldSet and the group version of the resource that the fieldset applies to.", + "manager": "Manager is an identifier of the workflow managing these fields.", + "operation": "Operation is the type of operation which lead to this ManagedFieldsEntry being created. The only valid values for this field are 'Apply' and 'Update'.", + "apiVersion": "APIVersion defines the version of this resource that this field set applies to. The format is \"group/version\" just like the top-level APIVersion field. It is necessary to track the version of a field set because it cannot be automatically converted.", + "time": "Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply'", + "fieldsType": "FieldsType is the discriminator for the different fields format and version. There is currently only one possible value: \"FieldsV1\"", + "fieldsV1": "FieldsV1 holds the first JSON version format as described in the \"FieldsV1\" type.", + "subresource": "Subresource is the name of the subresource used to update that object, or empty string if the object was updated through the main resource. The value of this field is used to distinguish between managers, even if they share the same name. For example, a status update will be distinct from a regular update using the same manager name. Note that the APIVersion field is not related to the Subresource field and it always corresponds to the version of the main resource.", } func (ManagedFieldsEntry) SwaggerDoc() map[string]string { diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/labels/selector.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/labels/selector.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/labels/selector.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/labels/selector.go 2021-12-26 00:45:51.000000000 +0000 @@ -31,12 +31,15 @@ ) var ( - validRequirementOperators = []string{ + unaryOperators = []string{ + string(selection.Exists), string(selection.DoesNotExist), + } + binaryOperators = []string{ string(selection.In), string(selection.NotIn), string(selection.Equals), string(selection.DoubleEquals), string(selection.NotEquals), - string(selection.Exists), string(selection.DoesNotExist), string(selection.GreaterThan), string(selection.LessThan), } + validRequirementOperators = append(binaryOperators, unaryOperators...) ) // Requirements is AND of all requirements. @@ -140,7 +143,7 @@ // NewRequirement is the constructor for a Requirement. // If any of these rules is violated, an error is returned: -// (1) The operator can only be In, NotIn, Equals, DoubleEquals, NotEquals, Exists, or DoesNotExist. +// (1) The operator can only be In, NotIn, Equals, DoubleEquals, Gt, Lt, NotEquals, Exists, or DoesNotExist. // (2) If the operator is In or NotIn, the values set must be non-empty. // (3) If the operator is Equals, DoubleEquals, or NotEquals, the values set must contain one value. // (4) If the operator is Exists or DoesNotExist, the value set must be empty. @@ -364,13 +367,9 @@ // Add adds requirements to the selector. It copies the current selector returning a new one func (s internalSelector) Add(reqs ...Requirement) Selector { - var ret internalSelector - for ix := range s { - ret = append(ret, s[ix]) - } - for _, r := range reqs { - ret = append(ret, r) - } + ret := make(internalSelector, 0, len(s)+len(reqs)) + ret = append(ret, s...) + ret = append(ret, reqs...) sort.Sort(ByKey(ret)) return ret } @@ -753,7 +752,7 @@ case NotEqualsToken: op = selection.NotEquals default: - return "", fmt.Errorf("found '%s', expected: '=', '!=', '==', 'in', notin'", lit) + return "", fmt.Errorf("found '%s', expected: %v", lit, strings.Join(binaryOperators, ", ")) } return op, nil } diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/util/net/http.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/util/net/http.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/util/net/http.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/util/net/http.go 2021-12-26 00:45:51.000000000 +0000 @@ -113,6 +113,7 @@ t.Proxy = NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment) } // If no custom dialer is set, use the default context dialer + //lint:file-ignore SA1019 Keep supporting deprecated Dial method of custom transports if t.DialContext == nil && t.Dial == nil { t.DialContext = defaultTransport.DialContext } diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go --- libpod-3.2.1+ds1/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go 2021-12-26 00:45:51.000000000 +0000 @@ -181,7 +181,7 @@ // valid values). func NotSupported(field *Path, value interface{}, validValues []string) *Error { detail := "" - if validValues != nil && len(validValues) > 0 { + if len(validValues) > 0 { quotedValues := make([]string, len(validValues)) for i, v := range validValues { quotedValues[i] = strconv.Quote(v) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/code-of-conduct.md libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/code-of-conduct.md --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/code-of-conduct.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/code-of-conduct.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# Kubernetes Community Code of Conduct - -Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/CONTRIBUTING.md libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/CONTRIBUTING.md --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/CONTRIBUTING.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/CONTRIBUTING.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# Contributing Guidelines - -Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://github.com/kubernetes/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: - -_As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ - -## Getting Started - -We have full documentation on how to get started contributing here: - -- [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests -- [Kubernetes Contributor Guide](http://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](http://git.k8s.io/community/contributors/guide#contributing) -- [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers - -## Mentorship - -- [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! - -## Contact Information - -- [Slack](https://kubernetes.slack.com/messages/sig-architecture) -- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture) diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/.gitignore libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/.gitignore --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/.gitignore 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -# OSX leaves these everywhere on SMB shares -._* - -# OSX trash -.DS_Store - -# Eclipse files -.classpath -.project -.settings/** - -# Files generated by JetBrains IDEs, e.g. IntelliJ IDEA -.idea/ -*.iml - -# Vscode files -.vscode diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/go.mod libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/go.mod --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/go.mod 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/go.mod 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -module k8s.io/klog/v2 - -go 1.13 - -require github.com/go-logr/logr v0.4.0 diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/go.sum libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/go.sum --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/go.sum 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/go.sum 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/klog_file.go libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/klog_file.go --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/klog_file.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/klog_file.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ -// -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// File I/O for logs. - -package klog - -import ( - "errors" - "fmt" - "os" - "os/user" - "path/filepath" - "runtime" - "strings" - "sync" - "time" -) - -// MaxSize is the maximum size of a log file in bytes. -var MaxSize uint64 = 1024 * 1024 * 1800 - -// logDirs lists the candidate directories for new log files. -var logDirs []string - -func createLogDirs() { - if logging.logDir != "" { - logDirs = append(logDirs, logging.logDir) - } - logDirs = append(logDirs, os.TempDir()) -} - -var ( - pid = os.Getpid() - program = filepath.Base(os.Args[0]) - host = "unknownhost" - userName = "unknownuser" - userNameOnce sync.Once -) - -func init() { - if h, err := os.Hostname(); err == nil { - host = shortHostname(h) - } -} - -func getUserName() string { - userNameOnce.Do(func() { - // On Windows, the Go 'user' package requires netapi32.dll. - // This affects Windows Nano Server: - // https://github.com/golang/go/issues/21867 - // Fallback to using environment variables. - if runtime.GOOS == "windows" { - u := os.Getenv("USERNAME") - if len(u) == 0 { - return - } - // Sanitize the USERNAME since it may contain filepath separators. - u = strings.Replace(u, `\`, "_", -1) - - // user.Current().Username normally produces something like 'USERDOMAIN\USERNAME' - d := os.Getenv("USERDOMAIN") - if len(d) != 0 { - userName = d + "_" + u - } else { - userName = u - } - } else { - current, err := user.Current() - if err == nil { - userName = current.Username - } - } - }) - - return userName -} - -// shortHostname returns its argument, truncating at the first period. -// For instance, given "www.google.com" it returns "www". -func shortHostname(hostname string) string { - if i := strings.Index(hostname, "."); i >= 0 { - return hostname[:i] - } - return hostname -} - -// logName returns a new log file name containing tag, with start time t, and -// the name for the symlink for tag. -func logName(tag string, t time.Time) (name, link string) { - name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", - program, - host, - getUserName(), - tag, - t.Year(), - t.Month(), - t.Day(), - t.Hour(), - t.Minute(), - t.Second(), - pid) - return name, program + "." + tag -} - -var onceLogDirs sync.Once - -// create creates a new log file and returns the file and its filename, which -// contains tag ("INFO", "FATAL", etc.) and t. If the file is created -// successfully, create also attempts to update the symlink for that tag, ignoring -// errors. -// The startup argument indicates whether this is the initial startup of klog. -// If startup is true, existing files are opened for appending instead of truncated. -func create(tag string, t time.Time, startup bool) (f *os.File, filename string, err error) { - if logging.logFile != "" { - f, err := openOrCreate(logging.logFile, startup) - if err == nil { - return f, logging.logFile, nil - } - return nil, "", fmt.Errorf("log: unable to create log: %v", err) - } - onceLogDirs.Do(createLogDirs) - if len(logDirs) == 0 { - return nil, "", errors.New("log: no log dirs") - } - name, link := logName(tag, t) - var lastErr error - for _, dir := range logDirs { - fname := filepath.Join(dir, name) - f, err := openOrCreate(fname, startup) - if err == nil { - symlink := filepath.Join(dir, link) - os.Remove(symlink) // ignore err - os.Symlink(name, symlink) // ignore err - return f, fname, nil - } - lastErr = err - } - return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) -} - -// The startup argument indicates whether this is the initial startup of klog. -// If startup is true, existing files are opened for appending instead of truncated. -func openOrCreate(name string, startup bool) (*os.File, error) { - if startup { - f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) - return f, err - } - f, err := os.Create(name) - return f, err -} diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/klog.go libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/klog.go --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/klog.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/klog.go 1970-01-01 00:00:00.000000000 +0000 @@ -1,1600 +0,0 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ -// -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package klog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. -// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as -// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. -// -// Basic examples: -// -// klog.Info("Prepare to repel boarders") -// -// klog.Fatalf("Initialization failed: %s", err) -// -// See the documentation for the V function for an explanation of these examples: -// -// if klog.V(2) { -// klog.Info("Starting transaction...") -// } -// -// klog.V(2).Infoln("Processed", nItems, "elements") -// -// Log output is buffered and written periodically using Flush. Programs -// should call Flush before exiting to guarantee all log output is written. -// -// By default, all log statements write to standard error. -// This package provides several flags that modify this behavior. -// As a result, flag.Parse must be called before any logging is done. -// -// -logtostderr=true -// Logs are written to standard error instead of to files. -// -alsologtostderr=false -// Logs are written to standard error as well as to files. -// -stderrthreshold=ERROR -// Log events at or above this severity are logged to standard -// error as well as to files. -// -log_dir="" -// Log files will be written to this directory instead of the -// default temporary directory. -// -// Other flags provide aids to debugging. -// -// -log_backtrace_at="" -// When set to a file and line number holding a logging statement, -// such as -// -log_backtrace_at=gopherflakes.go:234 -// a stack trace will be written to the Info log whenever execution -// hits that statement. (Unlike with -vmodule, the ".go" must be -// present.) -// -v=0 -// Enable V-leveled logging at the specified level. -// -vmodule="" -// The syntax of the argument is a comma-separated list of pattern=N, -// where pattern is a literal file name (minus the ".go" suffix) or -// "glob" pattern and N is a V level. For instance, -// -vmodule=gopher*=3 -// sets the V level to 3 in all Go files whose names begin "gopher". -// -package klog - -import ( - "bufio" - "bytes" - "errors" - "flag" - "fmt" - "io" - stdLog "log" - "math" - "os" - "path/filepath" - "reflect" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" - - "github.com/go-logr/logr" -) - -// severity identifies the sort of log: info, warning etc. It also implements -// the flag.Value interface. The -stderrthreshold flag is of type severity and -// should be modified only through the flag.Value interface. The values match -// the corresponding constants in C++. -type severity int32 // sync/atomic int32 - -// These constants identify the log levels in order of increasing severity. -// A message written to a high-severity log file is also written to each -// lower-severity log file. -const ( - infoLog severity = iota - warningLog - errorLog - fatalLog - numSeverity = 4 -) - -const severityChar = "IWEF" - -var severityName = []string{ - infoLog: "INFO", - warningLog: "WARNING", - errorLog: "ERROR", - fatalLog: "FATAL", -} - -// get returns the value of the severity. -func (s *severity) get() severity { - return severity(atomic.LoadInt32((*int32)(s))) -} - -// set sets the value of the severity. -func (s *severity) set(val severity) { - atomic.StoreInt32((*int32)(s), int32(val)) -} - -// String is part of the flag.Value interface. -func (s *severity) String() string { - return strconv.FormatInt(int64(*s), 10) -} - -// Get is part of the flag.Getter interface. -func (s *severity) Get() interface{} { - return *s -} - -// Set is part of the flag.Value interface. -func (s *severity) Set(value string) error { - var threshold severity - // Is it a known name? - if v, ok := severityByName(value); ok { - threshold = v - } else { - v, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return err - } - threshold = severity(v) - } - logging.stderrThreshold.set(threshold) - return nil -} - -func severityByName(s string) (severity, bool) { - s = strings.ToUpper(s) - for i, name := range severityName { - if name == s { - return severity(i), true - } - } - return 0, false -} - -// OutputStats tracks the number of output lines and bytes written. -type OutputStats struct { - lines int64 - bytes int64 -} - -// Lines returns the number of lines written. -func (s *OutputStats) Lines() int64 { - return atomic.LoadInt64(&s.lines) -} - -// Bytes returns the number of bytes written. -func (s *OutputStats) Bytes() int64 { - return atomic.LoadInt64(&s.bytes) -} - -// Stats tracks the number of lines of output and number of bytes -// per severity level. Values must be read with atomic.LoadInt64. -var Stats struct { - Info, Warning, Error OutputStats -} - -var severityStats = [numSeverity]*OutputStats{ - infoLog: &Stats.Info, - warningLog: &Stats.Warning, - errorLog: &Stats.Error, -} - -// Level is exported because it appears in the arguments to V and is -// the type of the v flag, which can be set programmatically. -// It's a distinct type because we want to discriminate it from logType. -// Variables of type level are only changed under logging.mu. -// The -v flag is read only with atomic ops, so the state of the logging -// module is consistent. - -// Level is treated as a sync/atomic int32. - -// Level specifies a level of verbosity for V logs. *Level implements -// flag.Value; the -v flag is of type Level and should be modified -// only through the flag.Value interface. -type Level int32 - -// get returns the value of the Level. -func (l *Level) get() Level { - return Level(atomic.LoadInt32((*int32)(l))) -} - -// set sets the value of the Level. -func (l *Level) set(val Level) { - atomic.StoreInt32((*int32)(l), int32(val)) -} - -// String is part of the flag.Value interface. -func (l *Level) String() string { - return strconv.FormatInt(int64(*l), 10) -} - -// Get is part of the flag.Getter interface. -func (l *Level) Get() interface{} { - return *l -} - -// Set is part of the flag.Value interface. -func (l *Level) Set(value string) error { - v, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return err - } - logging.mu.Lock() - defer logging.mu.Unlock() - logging.setVState(Level(v), logging.vmodule.filter, false) - return nil -} - -// moduleSpec represents the setting of the -vmodule flag. -type moduleSpec struct { - filter []modulePat -} - -// modulePat contains a filter for the -vmodule flag. -// It holds a verbosity level and a file pattern to match. -type modulePat struct { - pattern string - literal bool // The pattern is a literal string - level Level -} - -// match reports whether the file matches the pattern. It uses a string -// comparison if the pattern contains no metacharacters. -func (m *modulePat) match(file string) bool { - if m.literal { - return file == m.pattern - } - match, _ := filepath.Match(m.pattern, file) - return match -} - -func (m *moduleSpec) String() string { - // Lock because the type is not atomic. TODO: clean this up. - logging.mu.Lock() - defer logging.mu.Unlock() - var b bytes.Buffer - for i, f := range m.filter { - if i > 0 { - b.WriteRune(',') - } - fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) - } - return b.String() -} - -// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the -// struct is not exported. -func (m *moduleSpec) Get() interface{} { - return nil -} - -var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") - -// Syntax: -vmodule=recordio=2,file=1,gfs*=3 -func (m *moduleSpec) Set(value string) error { - var filter []modulePat - for _, pat := range strings.Split(value, ",") { - if len(pat) == 0 { - // Empty strings such as from a trailing comma can be ignored. - continue - } - patLev := strings.Split(pat, "=") - if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { - return errVmoduleSyntax - } - pattern := patLev[0] - v, err := strconv.ParseInt(patLev[1], 10, 32) - if err != nil { - return errors.New("syntax error: expect comma-separated list of filename=N") - } - if v < 0 { - return errors.New("negative value for vmodule level") - } - if v == 0 { - continue // Ignore. It's harmless but no point in paying the overhead. - } - // TODO: check syntax of filter? - filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) - } - logging.mu.Lock() - defer logging.mu.Unlock() - logging.setVState(logging.verbosity, filter, true) - return nil -} - -// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters -// that require filepath.Match to be called to match the pattern. -func isLiteral(pattern string) bool { - return !strings.ContainsAny(pattern, `\*?[]`) -} - -// traceLocation represents the setting of the -log_backtrace_at flag. -type traceLocation struct { - file string - line int -} - -// isSet reports whether the trace location has been specified. -// logging.mu is held. -func (t *traceLocation) isSet() bool { - return t.line > 0 -} - -// match reports whether the specified file and line matches the trace location. -// The argument file name is the full path, not the basename specified in the flag. -// logging.mu is held. -func (t *traceLocation) match(file string, line int) bool { - if t.line != line { - return false - } - if i := strings.LastIndex(file, "/"); i >= 0 { - file = file[i+1:] - } - return t.file == file -} - -func (t *traceLocation) String() string { - // Lock because the type is not atomic. TODO: clean this up. - logging.mu.Lock() - defer logging.mu.Unlock() - return fmt.Sprintf("%s:%d", t.file, t.line) -} - -// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the -// struct is not exported -func (t *traceLocation) Get() interface{} { - return nil -} - -var errTraceSyntax = errors.New("syntax error: expect file.go:234") - -// Syntax: -log_backtrace_at=gopherflakes.go:234 -// Note that unlike vmodule the file extension is included here. -func (t *traceLocation) Set(value string) error { - if value == "" { - // Unset. - logging.mu.Lock() - defer logging.mu.Unlock() - t.line = 0 - t.file = "" - return nil - } - fields := strings.Split(value, ":") - if len(fields) != 2 { - return errTraceSyntax - } - file, line := fields[0], fields[1] - if !strings.Contains(file, ".") { - return errTraceSyntax - } - v, err := strconv.Atoi(line) - if err != nil { - return errTraceSyntax - } - if v <= 0 { - return errors.New("negative or zero value for level") - } - logging.mu.Lock() - defer logging.mu.Unlock() - t.line = v - t.file = file - return nil -} - -// flushSyncWriter is the interface satisfied by logging destinations. -type flushSyncWriter interface { - Flush() error - Sync() error - io.Writer -} - -// init sets up the defaults and runs flushDaemon. -func init() { - logging.stderrThreshold = errorLog // Default stderrThreshold is ERROR. - logging.setVState(0, nil, false) - logging.logDir = "" - logging.logFile = "" - logging.logFileMaxSizeMB = 1800 - logging.toStderr = true - logging.alsoToStderr = false - logging.skipHeaders = false - logging.addDirHeader = false - logging.skipLogHeaders = false - logging.oneOutput = false - go logging.flushDaemon() -} - -// InitFlags is for explicitly initializing the flags. -func InitFlags(flagset *flag.FlagSet) { - if flagset == nil { - flagset = flag.CommandLine - } - - flagset.StringVar(&logging.logDir, "log_dir", logging.logDir, "If non-empty, write log files in this directory") - flagset.StringVar(&logging.logFile, "log_file", logging.logFile, "If non-empty, use this log file") - flagset.Uint64Var(&logging.logFileMaxSizeMB, "log_file_max_size", logging.logFileMaxSizeMB, - "Defines the maximum size a log file can grow to. Unit is megabytes. "+ - "If the value is 0, the maximum file size is unlimited.") - flagset.BoolVar(&logging.toStderr, "logtostderr", logging.toStderr, "log to standard error instead of files") - flagset.BoolVar(&logging.alsoToStderr, "alsologtostderr", logging.alsoToStderr, "log to standard error as well as files") - flagset.Var(&logging.verbosity, "v", "number for the log level verbosity") - flagset.BoolVar(&logging.addDirHeader, "add_dir_header", logging.addDirHeader, "If true, adds the file directory to the header of the log messages") - flagset.BoolVar(&logging.skipHeaders, "skip_headers", logging.skipHeaders, "If true, avoid header prefixes in the log messages") - flagset.BoolVar(&logging.oneOutput, "one_output", logging.oneOutput, "If true, only write logs to their native severity level (vs also writing to each lower severity level)") - flagset.BoolVar(&logging.skipLogHeaders, "skip_log_headers", logging.skipLogHeaders, "If true, avoid headers when opening log files") - flagset.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") - flagset.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") - flagset.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") -} - -// Flush flushes all pending log I/O. -func Flush() { - logging.lockAndFlushAll() -} - -// loggingT collects all the global state of the logging setup. -type loggingT struct { - // Boolean flags. Not handled atomically because the flag.Value interface - // does not let us avoid the =true, and that shorthand is necessary for - // compatibility. TODO: does this matter enough to fix? Seems unlikely. - toStderr bool // The -logtostderr flag. - alsoToStderr bool // The -alsologtostderr flag. - - // Level flag. Handled atomically. - stderrThreshold severity // The -stderrthreshold flag. - - // freeList is a list of byte buffers, maintained under freeListMu. - freeList *buffer - // freeListMu maintains the free list. It is separate from the main mutex - // so buffers can be grabbed and printed to without holding the main lock, - // for better parallelization. - freeListMu sync.Mutex - - // mu protects the remaining elements of this structure and is - // used to synchronize logging. - mu sync.Mutex - // file holds writer for each of the log types. - file [numSeverity]flushSyncWriter - // pcs is used in V to avoid an allocation when computing the caller's PC. - pcs [1]uintptr - // vmap is a cache of the V Level for each V() call site, identified by PC. - // It is wiped whenever the vmodule flag changes state. - vmap map[uintptr]Level - // filterLength stores the length of the vmodule filter chain. If greater - // than zero, it means vmodule is enabled. It may be read safely - // using sync.LoadInt32, but is only modified under mu. - filterLength int32 - // traceLocation is the state of the -log_backtrace_at flag. - traceLocation traceLocation - // These flags are modified only under lock, although verbosity may be fetched - // safely using atomic.LoadInt32. - vmodule moduleSpec // The state of the -vmodule flag. - verbosity Level // V logging level, the value of the -v flag/ - - // If non-empty, overrides the choice of directory in which to write logs. - // See createLogDirs for the full list of possible destinations. - logDir string - - // If non-empty, specifies the path of the file to write logs. mutually exclusive - // with the log_dir option. - logFile string - - // When logFile is specified, this limiter makes sure the logFile won't exceeds a certain size. When exceeds, the - // logFile will be cleaned up. If this value is 0, no size limitation will be applied to logFile. - logFileMaxSizeMB uint64 - - // If true, do not add the prefix headers, useful when used with SetOutput - skipHeaders bool - - // If true, do not add the headers to log files - skipLogHeaders bool - - // If true, add the file directory to the header - addDirHeader bool - - // If set, all output will be redirected unconditionally to the provided logr.Logger - logr logr.Logger - - // If true, messages will not be propagated to lower severity log levels - oneOutput bool - - // If set, all output will be filtered through the filter. - filter LogFilter -} - -// buffer holds a byte Buffer for reuse. The zero value is ready for use. -type buffer struct { - bytes.Buffer - tmp [64]byte // temporary byte array for creating headers. - next *buffer -} - -var logging loggingT - -// setVState sets a consistent state for V logging. -// l.mu is held. -func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { - // Turn verbosity off so V will not fire while we are in transition. - l.verbosity.set(0) - // Ditto for filter length. - atomic.StoreInt32(&l.filterLength, 0) - - // Set the new filters and wipe the pc->Level map if the filter has changed. - if setFilter { - l.vmodule.filter = filter - l.vmap = make(map[uintptr]Level) - } - - // Things are consistent now, so enable filtering and verbosity. - // They are enabled in order opposite to that in V. - atomic.StoreInt32(&l.filterLength, int32(len(filter))) - l.verbosity.set(verbosity) -} - -// getBuffer returns a new, ready-to-use buffer. -func (l *loggingT) getBuffer() *buffer { - l.freeListMu.Lock() - b := l.freeList - if b != nil { - l.freeList = b.next - } - l.freeListMu.Unlock() - if b == nil { - b = new(buffer) - } else { - b.next = nil - b.Reset() - } - return b -} - -// putBuffer returns a buffer to the free list. -func (l *loggingT) putBuffer(b *buffer) { - if b.Len() >= 256 { - // Let big buffers die a natural death. - return - } - l.freeListMu.Lock() - b.next = l.freeList - l.freeList = b - l.freeListMu.Unlock() -} - -var timeNow = time.Now // Stubbed out for testing. - -/* -header formats a log header as defined by the C++ implementation. -It returns a buffer containing the formatted header and the user's file and line number. -The depth specifies how many stack frames above lives the source line to be identified in the log message. - -Log lines have this form: - Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... -where the fields are defined as follows: - L A single character, representing the log level (eg 'I' for INFO) - mm The month (zero padded; ie May is '05') - dd The day (zero padded) - hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds - threadid The space-padded thread ID as returned by GetTID() - file The file name - line The line number - msg The user-supplied message -*/ -func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { - _, file, line, ok := runtime.Caller(3 + depth) - if !ok { - file = "???" - line = 1 - } else { - if slash := strings.LastIndex(file, "/"); slash >= 0 { - path := file - file = path[slash+1:] - if l.addDirHeader { - if dirsep := strings.LastIndex(path[:slash], "/"); dirsep >= 0 { - file = path[dirsep+1:] - } - } - } - } - return l.formatHeader(s, file, line), file, line -} - -// formatHeader formats a log header using the provided file name and line number. -func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { - now := timeNow() - if line < 0 { - line = 0 // not a real line number, but acceptable to someDigits - } - if s > fatalLog { - s = infoLog // for safety. - } - buf := l.getBuffer() - if l.skipHeaders { - return buf - } - - // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. - // It's worth about 3X. Fprintf is hard. - _, month, day := now.Date() - hour, minute, second := now.Clock() - // Lmmdd hh:mm:ss.uuuuuu threadid file:line] - buf.tmp[0] = severityChar[s] - buf.twoDigits(1, int(month)) - buf.twoDigits(3, day) - buf.tmp[5] = ' ' - buf.twoDigits(6, hour) - buf.tmp[8] = ':' - buf.twoDigits(9, minute) - buf.tmp[11] = ':' - buf.twoDigits(12, second) - buf.tmp[14] = '.' - buf.nDigits(6, 15, now.Nanosecond()/1000, '0') - buf.tmp[21] = ' ' - buf.nDigits(7, 22, pid, ' ') // TODO: should be TID - buf.tmp[29] = ' ' - buf.Write(buf.tmp[:30]) - buf.WriteString(file) - buf.tmp[0] = ':' - n := buf.someDigits(1, line) - buf.tmp[n+1] = ']' - buf.tmp[n+2] = ' ' - buf.Write(buf.tmp[:n+3]) - return buf -} - -// Some custom tiny helper functions to print the log header efficiently. - -const digits = "0123456789" - -// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. -func (buf *buffer) twoDigits(i, d int) { - buf.tmp[i+1] = digits[d%10] - d /= 10 - buf.tmp[i] = digits[d%10] -} - -// nDigits formats an n-digit integer at buf.tmp[i], -// padding with pad on the left. -// It assumes d >= 0. -func (buf *buffer) nDigits(n, i, d int, pad byte) { - j := n - 1 - for ; j >= 0 && d > 0; j-- { - buf.tmp[i+j] = digits[d%10] - d /= 10 - } - for ; j >= 0; j-- { - buf.tmp[i+j] = pad - } -} - -// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. -func (buf *buffer) someDigits(i, d int) int { - // Print into the top, then copy down. We know there's space for at least - // a 10-digit number. - j := len(buf.tmp) - for { - j-- - buf.tmp[j] = digits[d%10] - d /= 10 - if d == 0 { - break - } - } - return copy(buf.tmp[i:], buf.tmp[j:]) -} - -func (l *loggingT) println(s severity, logr logr.Logger, filter LogFilter, args ...interface{}) { - buf, file, line := l.header(s, 0) - // if logr is set, we clear the generated header as we rely on the backing - // logr implementation to print headers - if logr != nil { - l.putBuffer(buf) - buf = l.getBuffer() - } - if filter != nil { - args = filter.Filter(args) - } - fmt.Fprintln(buf, args...) - l.output(s, logr, buf, file, line, false) -} - -func (l *loggingT) print(s severity, logr logr.Logger, filter LogFilter, args ...interface{}) { - l.printDepth(s, logr, filter, 1, args...) -} - -func (l *loggingT) printDepth(s severity, logr logr.Logger, filter LogFilter, depth int, args ...interface{}) { - buf, file, line := l.header(s, depth) - // if logr is set, we clear the generated header as we rely on the backing - // logr implementation to print headers - if logr != nil { - l.putBuffer(buf) - buf = l.getBuffer() - } - if filter != nil { - args = filter.Filter(args) - } - fmt.Fprint(buf, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, logr, buf, file, line, false) -} - -func (l *loggingT) printf(s severity, logr logr.Logger, filter LogFilter, format string, args ...interface{}) { - buf, file, line := l.header(s, 0) - // if logr is set, we clear the generated header as we rely on the backing - // logr implementation to print headers - if logr != nil { - l.putBuffer(buf) - buf = l.getBuffer() - } - if filter != nil { - format, args = filter.FilterF(format, args) - } - fmt.Fprintf(buf, format, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, logr, buf, file, line, false) -} - -// printWithFileLine behaves like print but uses the provided file and line number. If -// alsoLogToStderr is true, the log message always appears on standard error; it -// will also appear in the log file unless --logtostderr is set. -func (l *loggingT) printWithFileLine(s severity, logr logr.Logger, filter LogFilter, file string, line int, alsoToStderr bool, args ...interface{}) { - buf := l.formatHeader(s, file, line) - // if logr is set, we clear the generated header as we rely on the backing - // logr implementation to print headers - if logr != nil { - l.putBuffer(buf) - buf = l.getBuffer() - } - if filter != nil { - args = filter.Filter(args) - } - fmt.Fprint(buf, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, logr, buf, file, line, alsoToStderr) -} - -// if loggr is specified, will call loggr.Error, otherwise output with logging module. -func (l *loggingT) errorS(err error, loggr logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) { - if filter != nil { - msg, keysAndValues = filter.FilterS(msg, keysAndValues) - } - if loggr != nil { - loggr.Error(err, msg, keysAndValues...) - return - } - l.printS(err, errorLog, depth+1, msg, keysAndValues...) -} - -// if loggr is specified, will call loggr.Info, otherwise output with logging module. -func (l *loggingT) infoS(loggr logr.Logger, filter LogFilter, depth int, msg string, keysAndValues ...interface{}) { - if filter != nil { - msg, keysAndValues = filter.FilterS(msg, keysAndValues) - } - if loggr != nil { - loggr.Info(msg, keysAndValues...) - return - } - l.printS(nil, infoLog, depth+1, msg, keysAndValues...) -} - -// printS is called from infoS and errorS if loggr is not specified. -// set log severity by s -func (l *loggingT) printS(err error, s severity, depth int, msg string, keysAndValues ...interface{}) { - b := &bytes.Buffer{} - b.WriteString(fmt.Sprintf("%q", msg)) - if err != nil { - b.WriteByte(' ') - b.WriteString(fmt.Sprintf("err=%q", err.Error())) - } - kvListFormat(b, keysAndValues...) - l.printDepth(s, logging.logr, nil, depth+1, b) -} - -const missingValue = "(MISSING)" - -func kvListFormat(b *bytes.Buffer, keysAndValues ...interface{}) { - for i := 0; i < len(keysAndValues); i += 2 { - var v interface{} - k := keysAndValues[i] - if i+1 < len(keysAndValues) { - v = keysAndValues[i+1] - } else { - v = missingValue - } - b.WriteByte(' ') - - switch v.(type) { - case string, error: - b.WriteString(fmt.Sprintf("%s=%q", k, v)) - default: - if _, ok := v.(fmt.Stringer); ok { - b.WriteString(fmt.Sprintf("%s=%q", k, v)) - } else { - b.WriteString(fmt.Sprintf("%s=%+v", k, v)) - } - } - } -} - -// redirectBuffer is used to set an alternate destination for the logs -type redirectBuffer struct { - w io.Writer -} - -func (rb *redirectBuffer) Sync() error { - return nil -} - -func (rb *redirectBuffer) Flush() error { - return nil -} - -func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) { - return rb.w.Write(bytes) -} - -// SetLogger will set the backing logr implementation for klog. -// If set, all log lines will be suppressed from the regular Output, and -// redirected to the logr implementation. -// All log lines include the 'severity', 'file' and 'line' values attached as -// structured logging values. -// Use as: -// ... -// klog.SetLogger(zapr.NewLogger(zapLog)) -func SetLogger(logr logr.Logger) { - logging.logr = logr -} - -// SetOutput sets the output destination for all severities -func SetOutput(w io.Writer) { - logging.mu.Lock() - defer logging.mu.Unlock() - for s := fatalLog; s >= infoLog; s-- { - rb := &redirectBuffer{ - w: w, - } - logging.file[s] = rb - } -} - -// SetOutputBySeverity sets the output destination for specific severity -func SetOutputBySeverity(name string, w io.Writer) { - logging.mu.Lock() - defer logging.mu.Unlock() - sev, ok := severityByName(name) - if !ok { - panic(fmt.Sprintf("SetOutputBySeverity(%q): unrecognized severity name", name)) - } - rb := &redirectBuffer{ - w: w, - } - logging.file[sev] = rb -} - -// LogToStderr sets whether to log exclusively to stderr, bypassing outputs -func LogToStderr(stderr bool) { - logging.mu.Lock() - defer logging.mu.Unlock() - - logging.toStderr = stderr -} - -// output writes the data to the log files and releases the buffer. -func (l *loggingT) output(s severity, log logr.Logger, buf *buffer, file string, line int, alsoToStderr bool) { - l.mu.Lock() - if l.traceLocation.isSet() { - if l.traceLocation.match(file, line) { - buf.Write(stacks(false)) - } - } - data := buf.Bytes() - if log != nil { - // TODO: set 'severity' and caller information as structured log info - // keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line} - if s == errorLog { - l.logr.Error(nil, string(data)) - } else { - log.Info(string(data)) - } - } else if l.toStderr { - os.Stderr.Write(data) - } else { - if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { - os.Stderr.Write(data) - } - - if logging.logFile != "" { - // Since we are using a single log file, all of the items in l.file array - // will point to the same file, so just use one of them to write data. - if l.file[infoLog] == nil { - if err := l.createFiles(infoLog); err != nil { - os.Stderr.Write(data) // Make sure the message appears somewhere. - l.exit(err) - } - } - l.file[infoLog].Write(data) - } else { - if l.file[s] == nil { - if err := l.createFiles(s); err != nil { - os.Stderr.Write(data) // Make sure the message appears somewhere. - l.exit(err) - } - } - - if l.oneOutput { - l.file[s].Write(data) - } else { - switch s { - case fatalLog: - l.file[fatalLog].Write(data) - fallthrough - case errorLog: - l.file[errorLog].Write(data) - fallthrough - case warningLog: - l.file[warningLog].Write(data) - fallthrough - case infoLog: - l.file[infoLog].Write(data) - } - } - } - } - if s == fatalLog { - // If we got here via Exit rather than Fatal, print no stacks. - if atomic.LoadUint32(&fatalNoStacks) > 0 { - l.mu.Unlock() - timeoutFlush(10 * time.Second) - os.Exit(1) - } - // Dump all goroutine stacks before exiting. - trace := stacks(true) - // Write the stack trace for all goroutines to the stderr. - if l.toStderr || l.alsoToStderr || s >= l.stderrThreshold.get() || alsoToStderr { - os.Stderr.Write(trace) - } - // Write the stack trace for all goroutines to the files. - logExitFunc = func(error) {} // If we get a write error, we'll still exit below. - for log := fatalLog; log >= infoLog; log-- { - if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. - f.Write(trace) - } - } - l.mu.Unlock() - timeoutFlush(10 * time.Second) - os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. - } - l.putBuffer(buf) - l.mu.Unlock() - if stats := severityStats[s]; stats != nil { - atomic.AddInt64(&stats.lines, 1) - atomic.AddInt64(&stats.bytes, int64(len(data))) - } -} - -// timeoutFlush calls Flush and returns when it completes or after timeout -// elapses, whichever happens first. This is needed because the hooks invoked -// by Flush may deadlock when klog.Fatal is called from a hook that holds -// a lock. -func timeoutFlush(timeout time.Duration) { - done := make(chan bool, 1) - go func() { - Flush() // calls logging.lockAndFlushAll() - done <- true - }() - select { - case <-done: - case <-time.After(timeout): - fmt.Fprintln(os.Stderr, "klog: Flush took longer than", timeout) - } -} - -// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. -func stacks(all bool) []byte { - // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. - n := 10000 - if all { - n = 100000 - } - var trace []byte - for i := 0; i < 5; i++ { - trace = make([]byte, n) - nbytes := runtime.Stack(trace, all) - if nbytes < len(trace) { - return trace[:nbytes] - } - n *= 2 - } - return trace -} - -// logExitFunc provides a simple mechanism to override the default behavior -// of exiting on error. Used in testing and to guarantee we reach a required exit -// for fatal logs. Instead, exit could be a function rather than a method but that -// would make its use clumsier. -var logExitFunc func(error) - -// exit is called if there is trouble creating or writing log files. -// It flushes the logs and exits the program; there's no point in hanging around. -// l.mu is held. -func (l *loggingT) exit(err error) { - fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) - // If logExitFunc is set, we do that instead of exiting. - if logExitFunc != nil { - logExitFunc(err) - return - } - l.flushAll() - os.Exit(2) -} - -// syncBuffer joins a bufio.Writer to its underlying file, providing access to the -// file's Sync method and providing a wrapper for the Write method that provides log -// file rotation. There are conflicting methods, so the file cannot be embedded. -// l.mu is held for all its methods. -type syncBuffer struct { - logger *loggingT - *bufio.Writer - file *os.File - sev severity - nbytes uint64 // The number of bytes written to this file - maxbytes uint64 // The max number of bytes this syncBuffer.file can hold before cleaning up. -} - -func (sb *syncBuffer) Sync() error { - return sb.file.Sync() -} - -// CalculateMaxSize returns the real max size in bytes after considering the default max size and the flag options. -func CalculateMaxSize() uint64 { - if logging.logFile != "" { - if logging.logFileMaxSizeMB == 0 { - // If logFileMaxSizeMB is zero, we don't have limitations on the log size. - return math.MaxUint64 - } - // Flag logFileMaxSizeMB is in MB for user convenience. - return logging.logFileMaxSizeMB * 1024 * 1024 - } - // If "log_file" flag is not specified, the target file (sb.file) will be cleaned up when reaches a fixed size. - return MaxSize -} - -func (sb *syncBuffer) Write(p []byte) (n int, err error) { - if sb.nbytes+uint64(len(p)) >= sb.maxbytes { - if err := sb.rotateFile(time.Now(), false); err != nil { - sb.logger.exit(err) - } - } - n, err = sb.Writer.Write(p) - sb.nbytes += uint64(n) - if err != nil { - sb.logger.exit(err) - } - return -} - -// rotateFile closes the syncBuffer's file and starts a new one. -// The startup argument indicates whether this is the initial startup of klog. -// If startup is true, existing files are opened for appending instead of truncated. -func (sb *syncBuffer) rotateFile(now time.Time, startup bool) error { - if sb.file != nil { - sb.Flush() - sb.file.Close() - } - var err error - sb.file, _, err = create(severityName[sb.sev], now, startup) - if err != nil { - return err - } - if startup { - fileInfo, err := sb.file.Stat() - if err != nil { - return fmt.Errorf("file stat could not get fileinfo: %v", err) - } - // init file size - sb.nbytes = uint64(fileInfo.Size()) - } else { - sb.nbytes = 0 - } - sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) - - if sb.logger.skipLogHeaders { - return nil - } - - // Write header. - var buf bytes.Buffer - fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) - fmt.Fprintf(&buf, "Running on machine: %s\n", host) - fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) - fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") - n, err := sb.file.Write(buf.Bytes()) - sb.nbytes += uint64(n) - return err -} - -// bufferSize sizes the buffer associated with each log file. It's large -// so that log records can accumulate without the logging thread blocking -// on disk I/O. The flushDaemon will block instead. -const bufferSize = 256 * 1024 - -// createFiles creates all the log files for severity from sev down to infoLog. -// l.mu is held. -func (l *loggingT) createFiles(sev severity) error { - now := time.Now() - // Files are created in decreasing severity order, so as soon as we find one - // has already been created, we can stop. - for s := sev; s >= infoLog && l.file[s] == nil; s-- { - sb := &syncBuffer{ - logger: l, - sev: s, - maxbytes: CalculateMaxSize(), - } - if err := sb.rotateFile(now, true); err != nil { - return err - } - l.file[s] = sb - } - return nil -} - -const flushInterval = 5 * time.Second - -// flushDaemon periodically flushes the log file buffers. -func (l *loggingT) flushDaemon() { - for range time.NewTicker(flushInterval).C { - l.lockAndFlushAll() - } -} - -// lockAndFlushAll is like flushAll but locks l.mu first. -func (l *loggingT) lockAndFlushAll() { - l.mu.Lock() - l.flushAll() - l.mu.Unlock() -} - -// flushAll flushes all the logs and attempts to "sync" their data to disk. -// l.mu is held. -func (l *loggingT) flushAll() { - // Flush from fatal down, in case there's trouble flushing. - for s := fatalLog; s >= infoLog; s-- { - file := l.file[s] - if file != nil { - file.Flush() // ignore error - file.Sync() // ignore error - } - } -} - -// CopyStandardLogTo arranges for messages written to the Go "log" package's -// default logs to also appear in the Google logs for the named and lower -// severities. Subsequent changes to the standard log's default output location -// or format may break this behavior. -// -// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not -// recognized, CopyStandardLogTo panics. -func CopyStandardLogTo(name string) { - sev, ok := severityByName(name) - if !ok { - panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) - } - // Set a log format that captures the user's file and line: - // d.go:23: message - stdLog.SetFlags(stdLog.Lshortfile) - stdLog.SetOutput(logBridge(sev)) -} - -// logBridge provides the Write method that enables CopyStandardLogTo to connect -// Go's standard logs to the logs provided by this package. -type logBridge severity - -// Write parses the standard logging line and passes its components to the -// logger for severity(lb). -func (lb logBridge) Write(b []byte) (n int, err error) { - var ( - file = "???" - line = 1 - text string - ) - // Split "d.go:23: message" into "d.go", "23", and "message". - if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { - text = fmt.Sprintf("bad log format: %s", b) - } else { - file = string(parts[0]) - text = string(parts[2][1:]) // skip leading space - line, err = strconv.Atoi(string(parts[1])) - if err != nil { - text = fmt.Sprintf("bad line number: %s", b) - line = 1 - } - } - // printWithFileLine with alsoToStderr=true, so standard log messages - // always appear on standard error. - logging.printWithFileLine(severity(lb), logging.logr, logging.filter, file, line, true, text) - return len(b), nil -} - -// setV computes and remembers the V level for a given PC -// when vmodule is enabled. -// File pattern matching takes the basename of the file, stripped -// of its .go suffix, and uses filepath.Match, which is a little more -// general than the *? matching used in C++. -// l.mu is held. -func (l *loggingT) setV(pc uintptr) Level { - fn := runtime.FuncForPC(pc) - file, _ := fn.FileLine(pc) - // The file is something like /a/b/c/d.go. We want just the d. - if strings.HasSuffix(file, ".go") { - file = file[:len(file)-3] - } - if slash := strings.LastIndex(file, "/"); slash >= 0 { - file = file[slash+1:] - } - for _, filter := range l.vmodule.filter { - if filter.match(file) { - l.vmap[pc] = filter.level - return filter.level - } - } - l.vmap[pc] = 0 - return 0 -} - -// Verbose is a boolean type that implements Infof (like Printf) etc. -// See the documentation of V for more information. -type Verbose struct { - enabled bool - logr logr.Logger - filter LogFilter -} - -func newVerbose(level Level, b bool) Verbose { - if logging.logr == nil { - return Verbose{b, nil, logging.filter} - } - return Verbose{b, logging.logr.V(int(level)), logging.filter} -} - -// V reports whether verbosity at the call site is at least the requested level. -// The returned value is a struct of type Verbose, which implements Info, Infoln -// and Infof. These methods will write to the Info log if called. -// Thus, one may write either -// if glog.V(2).Enabled() { klog.Info("log this") } -// or -// klog.V(2).Info("log this") -// The second form is shorter but the first is cheaper if logging is off because it does -// not evaluate its arguments. -// -// Whether an individual call to V generates a log record depends on the setting of -// the -v and -vmodule flags; both are off by default. The V call will log if its level -// is less than or equal to the value of the -v flag, or alternatively if its level is -// less than or equal to the value of the -vmodule pattern matching the source file -// containing the call. -func V(level Level) Verbose { - // This function tries hard to be cheap unless there's work to do. - // The fast path is two atomic loads and compares. - - // Here is a cheap but safe test to see if V logging is enabled globally. - if logging.verbosity.get() >= level { - return newVerbose(level, true) - } - - // It's off globally but vmodule may still be set. - // Here is another cheap but safe test to see if vmodule is enabled. - if atomic.LoadInt32(&logging.filterLength) > 0 { - // Now we need a proper lock to use the logging structure. The pcs field - // is shared so we must lock before accessing it. This is fairly expensive, - // but if V logging is enabled we're slow anyway. - logging.mu.Lock() - defer logging.mu.Unlock() - if runtime.Callers(2, logging.pcs[:]) == 0 { - return newVerbose(level, false) - } - v, ok := logging.vmap[logging.pcs[0]] - if !ok { - v = logging.setV(logging.pcs[0]) - } - return newVerbose(level, v >= level) - } - return newVerbose(level, false) -} - -// Enabled will return true if this log level is enabled, guarded by the value -// of v. -// See the documentation of V for usage. -func (v Verbose) Enabled() bool { - return v.enabled -} - -// Info is equivalent to the global Info function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Info(args ...interface{}) { - if v.enabled { - logging.print(infoLog, v.logr, v.filter, args...) - } -} - -// Infoln is equivalent to the global Infoln function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Infoln(args ...interface{}) { - if v.enabled { - logging.println(infoLog, v.logr, v.filter, args...) - } -} - -// Infof is equivalent to the global Infof function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Infof(format string, args ...interface{}) { - if v.enabled { - logging.printf(infoLog, v.logr, v.filter, format, args...) - } -} - -// InfoS is equivalent to the global InfoS function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) InfoS(msg string, keysAndValues ...interface{}) { - if v.enabled { - logging.infoS(v.logr, v.filter, 0, msg, keysAndValues...) - } -} - -// InfoSDepth acts as InfoS but uses depth to determine which call frame to log. -// InfoSDepth(0, "msg") is the same as InfoS("msg"). -func InfoSDepth(depth int, msg string, keysAndValues ...interface{}) { - logging.infoS(logging.logr, logging.filter, depth, msg, keysAndValues...) -} - -// Deprecated: Use ErrorS instead. -func (v Verbose) Error(err error, msg string, args ...interface{}) { - if v.enabled { - logging.errorS(err, v.logr, v.filter, 0, msg, args...) - } -} - -// ErrorS is equivalent to the global Error function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) ErrorS(err error, msg string, keysAndValues ...interface{}) { - if v.enabled { - logging.errorS(err, v.logr, v.filter, 0, msg, keysAndValues...) - } -} - -// Info logs to the INFO log. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Info(args ...interface{}) { - logging.print(infoLog, logging.logr, logging.filter, args...) -} - -// InfoDepth acts as Info but uses depth to determine which call frame to log. -// InfoDepth(0, "msg") is the same as Info("msg"). -func InfoDepth(depth int, args ...interface{}) { - logging.printDepth(infoLog, logging.logr, logging.filter, depth, args...) -} - -// Infoln logs to the INFO log. -// Arguments are handled in the manner of fmt.Println; a newline is always appended. -func Infoln(args ...interface{}) { - logging.println(infoLog, logging.logr, logging.filter, args...) -} - -// Infof logs to the INFO log. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Infof(format string, args ...interface{}) { - logging.printf(infoLog, logging.logr, logging.filter, format, args...) -} - -// InfoS structured logs to the INFO log. -// The msg argument used to add constant description to the log line. -// The key/value pairs would be join by "=" ; a newline is always appended. -// -// Basic examples: -// >> klog.InfoS("Pod status updated", "pod", "kubedns", "status", "ready") -// output: -// >> I1025 00:15:15.525108 1 controller_utils.go:116] "Pod status updated" pod="kubedns" status="ready" -func InfoS(msg string, keysAndValues ...interface{}) { - logging.infoS(logging.logr, logging.filter, 0, msg, keysAndValues...) -} - -// Warning logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Warning(args ...interface{}) { - logging.print(warningLog, logging.logr, logging.filter, args...) -} - -// WarningDepth acts as Warning but uses depth to determine which call frame to log. -// WarningDepth(0, "msg") is the same as Warning("msg"). -func WarningDepth(depth int, args ...interface{}) { - logging.printDepth(warningLog, logging.logr, logging.filter, depth, args...) -} - -// Warningln logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Println; a newline is always appended. -func Warningln(args ...interface{}) { - logging.println(warningLog, logging.logr, logging.filter, args...) -} - -// Warningf logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Warningf(format string, args ...interface{}) { - logging.printf(warningLog, logging.logr, logging.filter, format, args...) -} - -// Error logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Error(args ...interface{}) { - logging.print(errorLog, logging.logr, logging.filter, args...) -} - -// ErrorDepth acts as Error but uses depth to determine which call frame to log. -// ErrorDepth(0, "msg") is the same as Error("msg"). -func ErrorDepth(depth int, args ...interface{}) { - logging.printDepth(errorLog, logging.logr, logging.filter, depth, args...) -} - -// Errorln logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Println; a newline is always appended. -func Errorln(args ...interface{}) { - logging.println(errorLog, logging.logr, logging.filter, args...) -} - -// Errorf logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Errorf(format string, args ...interface{}) { - logging.printf(errorLog, logging.logr, logging.filter, format, args...) -} - -// ErrorS structured logs to the ERROR, WARNING, and INFO logs. -// the err argument used as "err" field of log line. -// The msg argument used to add constant description to the log line. -// The key/value pairs would be join by "=" ; a newline is always appended. -// -// Basic examples: -// >> klog.ErrorS(err, "Failed to update pod status") -// output: -// >> E1025 00:15:15.525108 1 controller_utils.go:114] "Failed to update pod status" err="timeout" -func ErrorS(err error, msg string, keysAndValues ...interface{}) { - logging.errorS(err, logging.logr, logging.filter, 0, msg, keysAndValues...) -} - -// ErrorSDepth acts as ErrorS but uses depth to determine which call frame to log. -// ErrorSDepth(0, "msg") is the same as ErrorS("msg"). -func ErrorSDepth(depth int, err error, msg string, keysAndValues ...interface{}) { - logging.errorS(err, logging.logr, logging.filter, depth, msg, keysAndValues...) -} - -// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Fatal(args ...interface{}) { - logging.print(fatalLog, logging.logr, logging.filter, args...) -} - -// FatalDepth acts as Fatal but uses depth to determine which call frame to log. -// FatalDepth(0, "msg") is the same as Fatal("msg"). -func FatalDepth(depth int, args ...interface{}) { - logging.printDepth(fatalLog, logging.logr, logging.filter, depth, args...) -} - -// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). -// Arguments are handled in the manner of fmt.Println; a newline is always appended. -func Fatalln(args ...interface{}) { - logging.println(fatalLog, logging.logr, logging.filter, args...) -} - -// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Fatalf(format string, args ...interface{}) { - logging.printf(fatalLog, logging.logr, logging.filter, format, args...) -} - -// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. -// It allows Exit and relatives to use the Fatal logs. -var fatalNoStacks uint32 - -// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Exit(args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.print(fatalLog, logging.logr, logging.filter, args...) -} - -// ExitDepth acts as Exit but uses depth to determine which call frame to log. -// ExitDepth(0, "msg") is the same as Exit("msg"). -func ExitDepth(depth int, args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.printDepth(fatalLog, logging.logr, logging.filter, depth, args...) -} - -// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -func Exitln(args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.println(fatalLog, logging.logr, logging.filter, args...) -} - -// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Exitf(format string, args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.printf(fatalLog, logging.logr, logging.filter, format, args...) -} - -// LogFilter is a collection of functions that can filter all logging calls, -// e.g. for sanitization of arguments and prevent accidental leaking of secrets. -type LogFilter interface { - Filter(args []interface{}) []interface{} - FilterF(format string, args []interface{}) (string, []interface{}) - FilterS(msg string, keysAndValues []interface{}) (string, []interface{}) -} - -func SetLogFilter(filter LogFilter) { - logging.mu.Lock() - defer logging.mu.Unlock() - - logging.filter = filter -} - -// ObjectRef references a kubernetes object -type ObjectRef struct { - Name string `json:"name"` - Namespace string `json:"namespace,omitempty"` -} - -func (ref ObjectRef) String() string { - if ref.Namespace != "" { - return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name) - } - return ref.Name -} - -// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface -// this interface may expand in the future, but will always be a subset of the -// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface -type KMetadata interface { - GetName() string - GetNamespace() string -} - -// KObj returns ObjectRef from ObjectMeta -func KObj(obj KMetadata) ObjectRef { - if obj == nil { - return ObjectRef{} - } - if val := reflect.ValueOf(obj); val.Kind() == reflect.Ptr && val.IsNil() { - return ObjectRef{} - } - - return ObjectRef{ - Name: obj.GetName(), - Namespace: obj.GetNamespace(), - } -} - -// KRef returns ObjectRef from name and namespace -func KRef(namespace, name string) ObjectRef { - return ObjectRef{ - Name: name, - Namespace: namespace, - } -} diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/LICENSE libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/LICENSE --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/LICENSE 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/LICENSE 1970-01-01 00:00:00.000000000 +0000 @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/OWNERS libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/OWNERS --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/OWNERS 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/OWNERS 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -# See the OWNERS docs at https://go.k8s.io/owners -reviewers: - - jayunit100 - - hoegaarden - - andyxning - - neolit123 - - pohly - - yagonobre - - vincepri - - detiber -approvers: - - dims - - thockin - - justinsb - - tallclair - - piosz - - brancz - - DirectXMan12 - - lavalamp diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/README.md libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/README.md --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/README.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -klog -==== - -klog is a permanent fork of https://github.com/golang/glog. - -## Why was klog created? - -The decision to create klog was one that wasn't made lightly, but it was necessary due to some -drawbacks that are present in [glog](https://github.com/golang/glog). Ultimately, the fork was created due to glog not being under active development; this can be seen in the glog README: - -> The code in this repo [...] is not itself under development - -This makes us unable to solve many use cases without a fork. The factors that contributed to needing feature development are listed below: - - * `glog` [presents a lot "gotchas"](https://github.com/kubernetes/kubernetes/issues/61006) and introduces challenges in containerized environments, all of which aren't well documented. - * `glog` doesn't provide an easy way to test logs, which detracts from the stability of software using it - * A long term goal is to implement a logging interface that allows us to add context, change output format, etc. - -Historical context is available here: - - * https://github.com/kubernetes/kubernetes/issues/61006 - * https://github.com/kubernetes/kubernetes/issues/70264 - * https://groups.google.com/forum/#!msg/kubernetes-sig-architecture/wCWiWf3Juzs/hXRVBH90CgAJ - * https://groups.google.com/forum/#!msg/kubernetes-dev/7vnijOMhLS0/1oRiNtigBgAJ - ----- - -How to use klog -=============== -- Replace imports for `"github.com/golang/glog"` with `"k8s.io/klog/v2"` -- Use `klog.InitFlags(nil)` explicitly for initializing global flags as we no longer use `init()` method to register the flags -- You can now use `log_file` instead of `log_dir` for logging to a single file (See `examples/log_file/usage_log_file.go`) -- If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`) -- For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md)) - -**NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater. - -### Coexisting with klog/v2 - -See [this example](examples/coexist_klog_v1_and_v2/) to see how to coexist with both klog/v1 and klog/v2. - -### Coexisting with glog -This package can be used side by side with glog. [This example](examples/coexist_glog/coexist_glog.go) shows how to initialize and synchronize flags from the global `flag.CommandLine` FlagSet. In addition, the example makes use of stderr as combined output by setting `alsologtostderr` (or `logtostderr`) to `true`. - -## Community, discussion, contribution, and support - -Learn how to engage with the Kubernetes community on the [community page](http://kubernetes.io/community/). - -You can reach the maintainers of this project at: - -- [Slack](https://kubernetes.slack.com/messages/klog) -- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-sig-architecture) - -### Code of conduct - -Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). - ----- - -glog -==== - -Leveled execution logs for Go. - -This is an efficient pure Go implementation of leveled logs in the -manner of the open source C++ package - https://github.com/google/glog - -By binding methods to booleans it is possible to use the log package -without paying the expense of evaluating the arguments to the log. -Through the -vmodule flag, the package also provides fine-grained -control over logging at the file level. - -The comment from glog.go introduces the ideas: - - Package glog implements logging analogous to the Google-internal - C++ INFO/ERROR/V setup. It provides functions Info, Warning, - Error, Fatal, plus formatting variants such as Infof. It - also provides V-style logging controlled by the -v and - -vmodule=file=2 flags. - - Basic examples: - - glog.Info("Prepare to repel boarders") - - glog.Fatalf("Initialization failed: %s", err) - - See the documentation for the V function for an explanation - of these examples: - - if glog.V(2) { - glog.Info("Starting transaction...") - } - - glog.V(2).Infoln("Processed", nItems, "elements") - - -The repository contains an open source version of the log package -used inside Google. The master copy of the source lives inside -Google, not here. The code in this repo is for export only and is not itself -under development. Feature requests will be ignored. - -Send bug reports to golang-nuts@googlegroups.com. diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/RELEASE.md libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/RELEASE.md --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/RELEASE.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/RELEASE.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -# Release Process - -The `klog` is released on an as-needed basis. The process is as follows: - -1. An issue is proposing a new release with a changelog since the last release -1. All [OWNERS](OWNERS) must LGTM this release -1. An OWNER runs `git tag -s $VERSION` and inserts the changelog and pushes the tag with `git push $VERSION` -1. The release issue is closed -1. An announcement email is sent to `kubernetes-dev@googlegroups.com` with the subject `[ANNOUNCE] kubernetes-template-project $VERSION is released` diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/SECURITY_CONTACTS libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/SECURITY_CONTACTS --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/SECURITY_CONTACTS 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/SECURITY_CONTACTS 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -# Defined below are the security contacts for this repo. -# -# They are the contact point for the Product Security Committee to reach out -# to for triaging and handling of incoming issues. -# -# The below names agree to abide by the -# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy) -# and will be removed and replaced if they violate that agreement. -# -# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE -# INSTRUCTIONS AT https://kubernetes.io/security/ - -dims -thockin -justinsb -tallclair -piosz -brancz -DirectXMan12 -lavalamp diff -Nru libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/SECURITY.md libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/SECURITY.md --- libpod-3.2.1+ds1/vendor/k8s.io/klog/v2/SECURITY.md 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/vendor/k8s.io/klog/v2/SECURITY.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -# Security Policy - -## Security Announcements - -Join the [kubernetes-security-announce] group for security and vulnerability announcements. - -You can also subscribe to an RSS feed of the above using [this link][kubernetes-security-announce-rss]. - -## Reporting a Vulnerability - -Instructions for reporting a vulnerability can be found on the -[Kubernetes Security and Disclosure Information] page. - -## Supported Versions - -Information about supported Kubernetes versions can be found on the -[Kubernetes version and version skew support policy] page on the Kubernetes website. - -[kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce -[kubernetes-security-announce-rss]: https://groups.google.com/forum/feed/kubernetes-security-announce/msgs/rss_v2_0.xml?num=50 -[Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions -[Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability diff -Nru libpod-3.2.1+ds1/version/version.go libpod-3.4.4+ds1/version/version.go --- libpod-3.2.1+ds1/version/version.go 2021-06-14 18:04:22.000000000 +0000 +++ libpod-3.4.4+ds1/version/version.go 2021-12-26 00:45:51.000000000 +0000 @@ -27,7 +27,7 @@ // NOTE: remember to bump the version at the top // of the top-level README.md file when this is // bumped. -var Version = semver.MustParse("3.2.1") +var Version = semver.MustParse("3.4.4") // See https://docs.docker.com/engine/api/v1.40/ // libpod compat handlers are expected to honor docker API versions