summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml26
-rw-r--r--Makefile32
-rw-r--r--README.md12
-rw-r--r--RELEASE_NOTES.md6
-rw-r--r--cmd/podman/common/completion.go7
-rw-r--r--cmd/podman/common/create.go13
-rw-r--r--cmd/podman/common/create_opts.go6
-rw-r--r--cmd/podman/containers/checkpoint.go34
-rw-r--r--cmd/podman/containers/create.go20
-rw-r--r--cmd/podman/containers/port.go50
-rw-r--r--cmd/podman/containers/ps.go183
-rw-r--r--cmd/podman/containers/restore.go34
-rw-r--r--cmd/podman/images/load.go15
-rw-r--r--cmd/podman/images/pull.go2
-rw-r--r--cmd/podman/images/scp.go65
-rw-r--r--cmd/podman/images/sign.go5
-rw-r--r--cmd/podman/machine/list.go9
-rw-r--r--cmd/podman/machine/stop.go8
-rw-r--r--cmd/podman/play/kube.go8
-rw-r--r--cmd/podman/pods/create.go31
-rw-r--r--cmd/podman/root.go5
-rw-r--r--cmd/podman/system/connection/add.go7
-rw-r--r--cmd/podman/system/connection/list.go4
-rw-r--r--cmd/podman/system/connection/remove.go27
-rw-r--r--cmd/podman/system/connection/shared.go9
-rw-r--r--cmd/podman/system/unshare.go10
-rw-r--r--cmd/podman/system/version.go4
-rw-r--r--cmd/podman/validate/args.go3
-rw-r--r--cmd/rootlessport/main.go42
-rw-r--r--contrib/cirrus/lib.sh51
-rwxr-xr-xcontrib/cirrus/pr-should-include-tests8
-rwxr-xr-xcontrib/cirrus/runner.sh1
-rw-r--r--contrib/podmanimage/stable/Dockerfile9
-rw-r--r--contrib/podmanimage/testing/Dockerfile9
-rw-r--r--contrib/podmanimage/upstream/Dockerfile9
-rw-r--r--contrib/spec/podman.spec.in2
-rw-r--r--docs/source/markdown/podman-auto-update.1.md2
-rw-r--r--docs/source/markdown/podman-build.1.md14
-rw-r--r--docs/source/markdown/podman-completion.1.md4
-rw-r--r--docs/source/markdown/podman-container-checkpoint.1.md36
-rw-r--r--docs/source/markdown/podman-container-cleanup.1.md2
-rw-r--r--docs/source/markdown/podman-container-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-container-prune.1.md2
-rw-r--r--docs/source/markdown/podman-container-restore.1.md26
-rw-r--r--docs/source/markdown/podman-container-runlabel.1.md2
-rw-r--r--docs/source/markdown/podman-container.1.md2
-rw-r--r--docs/source/markdown/podman-create.1.md42
-rw-r--r--docs/source/markdown/podman-events.1.md2
-rw-r--r--docs/source/markdown/podman-exec.1.md2
-rw-r--r--docs/source/markdown/podman-export.1.md2
-rw-r--r--docs/source/markdown/podman-generate-kube.1.md58
-rw-r--r--docs/source/markdown/podman-generate-systemd.1.md2
-rw-r--r--docs/source/markdown/podman-generate.1.md2
-rw-r--r--docs/source/markdown/podman-healthcheck-run.1.md2
-rw-r--r--docs/source/markdown/podman-healthcheck.1.md2
-rw-r--r--docs/source/markdown/podman-history.1.md2
-rw-r--r--docs/source/markdown/podman-image-diff.1.md2
-rw-r--r--docs/source/markdown/podman-image-exists.1.md2
-rw-r--r--docs/source/markdown/podman-image-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-image-mount.1.md2
-rw-r--r--docs/source/markdown/podman-image-prune.1.md2
-rw-r--r--docs/source/markdown/podman-image-scp.1.md32
-rw-r--r--docs/source/markdown/podman-image-sign.1.md11
-rw-r--r--docs/source/markdown/podman-image-tree.1.md2
-rw-r--r--docs/source/markdown/podman-image-trust.1.md3
-rw-r--r--docs/source/markdown/podman-image-unmount.1.md2
-rw-r--r--docs/source/markdown/podman-image.1.md2
-rw-r--r--docs/source/markdown/podman-images.1.md2
-rw-r--r--docs/source/markdown/podman-import.1.md2
-rw-r--r--docs/source/markdown/podman-info.1.md2
-rw-r--r--docs/source/markdown/podman-init.1.md2
-rw-r--r--docs/source/markdown/podman-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-kill.1.md2
-rw-r--r--docs/source/markdown/podman-load.1.md6
-rw-r--r--docs/source/markdown/podman-login.1.md2
-rw-r--r--docs/source/markdown/podman-logout.1.md2
-rw-r--r--docs/source/markdown/podman-logs.1.md2
-rw-r--r--docs/source/markdown/podman-machine-init.1.md6
-rw-r--r--docs/source/markdown/podman-machine-list.1.md2
-rw-r--r--docs/source/markdown/podman-machine-rm.1.md2
-rw-r--r--docs/source/markdown/podman-machine-ssh.1.md4
-rw-r--r--docs/source/markdown/podman-machine-start.1.md2
-rw-r--r--docs/source/markdown/podman-machine-stop.1.md2
-rw-r--r--docs/source/markdown/podman-machine.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-add.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-annotate.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-create.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-exists.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-push.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-remove.1.md2
-rw-r--r--docs/source/markdown/podman-manifest-rm.1.md2
-rw-r--r--docs/source/markdown/podman-manifest.1.md2
-rw-r--r--docs/source/markdown/podman-mount.1.md2
-rw-r--r--docs/source/markdown/podman-network-connect.1.md2
-rw-r--r--docs/source/markdown/podman-network-disconnect.1.md2
-rw-r--r--docs/source/markdown/podman-network-exists.1.md2
-rw-r--r--docs/source/markdown/podman-network-prune.1.md2
-rw-r--r--docs/source/markdown/podman-network-reload.1.md2
-rw-r--r--docs/source/markdown/podman-network-rm.1.md2
-rw-r--r--docs/source/markdown/podman-network.1.md2
-rw-r--r--docs/source/markdown/podman-pause.1.md4
-rw-r--r--docs/source/markdown/podman-play-kube.1.md19
-rw-r--r--docs/source/markdown/podman-play.1.md2
-rw-r--r--docs/source/markdown/podman-pod-create.1.md5
-rw-r--r--docs/source/markdown/podman-pod-exists.1.md2
-rw-r--r--docs/source/markdown/podman-pod-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-pod-kill.1.md2
-rw-r--r--docs/source/markdown/podman-pod-logs.1.md2
-rw-r--r--docs/source/markdown/podman-pod-pause.1.md2
-rw-r--r--docs/source/markdown/podman-pod-prune.1.md2
-rw-r--r--docs/source/markdown/podman-pod-ps.1.md2
-rw-r--r--docs/source/markdown/podman-pod-restart.1.md2
-rw-r--r--docs/source/markdown/podman-pod-rm.1.md2
-rw-r--r--docs/source/markdown/podman-pod-start.1.md2
-rw-r--r--docs/source/markdown/podman-pod-stats.1.md2
-rw-r--r--docs/source/markdown/podman-pod-stop.1.md2
-rw-r--r--docs/source/markdown/podman-pod-top.1.md2
-rw-r--r--docs/source/markdown/podman-pod-unpause.1.md2
-rw-r--r--docs/source/markdown/podman-pod.1.md2
-rw-r--r--docs/source/markdown/podman-port.1.md2
-rw-r--r--docs/source/markdown/podman-ps.1.md2
-rw-r--r--docs/source/markdown/podman-pull.1.md2
-rw-r--r--docs/source/markdown/podman-push.1.md2
-rw-r--r--docs/source/markdown/podman-remote.1.md2
-rw-r--r--docs/source/markdown/podman-rename.1.md2
-rw-r--r--docs/source/markdown/podman-restart.1.md2
-rw-r--r--docs/source/markdown/podman-rm.1.md2
-rw-r--r--docs/source/markdown/podman-rmi.1.md2
-rw-r--r--docs/source/markdown/podman-run.1.md36
-rw-r--r--docs/source/markdown/podman-save.1.md2
-rw-r--r--docs/source/markdown/podman-search.1.md2
-rw-r--r--docs/source/markdown/podman-secret-create.1.md2
-rw-r--r--docs/source/markdown/podman-secret-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-secret-ls.1.md2
-rw-r--r--docs/source/markdown/podman-secret-rm.1.md2
-rw-r--r--docs/source/markdown/podman-secret.1.md2
-rw-r--r--docs/source/markdown/podman-start.1.md2
-rw-r--r--docs/source/markdown/podman-stats.1.md2
-rw-r--r--docs/source/markdown/podman-stop.1.md2
-rw-r--r--docs/source/markdown/podman-system-connection-add.1.md3
-rw-r--r--docs/source/markdown/podman-system-connection-default.1.md2
-rw-r--r--docs/source/markdown/podman-system-connection-list.1.md2
-rw-r--r--docs/source/markdown/podman-system-connection-remove.1.md10
-rw-r--r--docs/source/markdown/podman-system-connection-rename.1.md2
-rw-r--r--docs/source/markdown/podman-system-connection.1.md2
-rw-r--r--docs/source/markdown/podman-system-df.1.md2
-rw-r--r--docs/source/markdown/podman-system-migrate.1.md2
-rw-r--r--docs/source/markdown/podman-system-prune.1.md2
-rw-r--r--docs/source/markdown/podman-system-renumber.1.md2
-rw-r--r--docs/source/markdown/podman-system-reset.1.md2
-rw-r--r--docs/source/markdown/podman-system.1.md2
-rw-r--r--docs/source/markdown/podman-tag.1.md2
-rw-r--r--docs/source/markdown/podman-top.1.md2
-rw-r--r--docs/source/markdown/podman-unmount.1.md2
-rw-r--r--docs/source/markdown/podman-unpause.1.md2
-rw-r--r--docs/source/markdown/podman-unshare.1.md10
-rw-r--r--docs/source/markdown/podman-untag.1.md2
-rw-r--r--docs/source/markdown/podman-version.1.md2
-rw-r--r--docs/source/markdown/podman-volume-create.1.md4
-rw-r--r--docs/source/markdown/podman-volume-exists.1.md2
-rw-r--r--docs/source/markdown/podman-volume-export.1.md2
-rw-r--r--docs/source/markdown/podman-volume-import.1.md2
-rw-r--r--docs/source/markdown/podman-volume-inspect.1.md2
-rw-r--r--docs/source/markdown/podman-volume-ls.1.md2
-rw-r--r--docs/source/markdown/podman-volume-prune.1.md2
-rw-r--r--docs/source/markdown/podman-volume-rm.1.md2
-rw-r--r--docs/source/markdown/podman-volume.1.md2
-rw-r--r--docs/source/markdown/podman-wait.1.md2
-rw-r--r--docs/source/markdown/podman.1.md4
-rw-r--r--go.mod21
-rw-r--r--go.sum95
-rwxr-xr-xhack/install_catatonit.sh2
-rwxr-xr-xhack/xref-helpmsgs-manpages127
-rw-r--r--libpod/boltdb_state_internal.go9
-rw-r--r--libpod/common_test.go4
-rw-r--r--libpod/container.go2
-rw-r--r--libpod/container_api.go24
-rw-r--r--libpod/container_config.go16
-rw-r--r--libpod/container_internal.go126
-rw-r--r--libpod/container_internal_linux.go182
-rw-r--r--libpod/container_log_linux.go33
-rw-r--r--libpod/container_path_resolution.go2
-rw-r--r--libpod/define/checkpoint_restore.go32
-rw-r--r--libpod/define/info.go1
-rw-r--r--libpod/info.go3
-rw-r--r--libpod/kube.go208
-rw-r--r--libpod/lock/shm/shm_lock.go11
-rw-r--r--libpod/network/cni/cni_conversion.go40
-rw-r--r--libpod/network/cni/cni_types.go2
-rw-r--r--libpod/network/cni/config.go156
-rw-r--r--libpod/network/cni/network.go116
-rw-r--r--libpod/network/cni/run.go47
-rw-r--r--libpod/network/cni/run_test.go16
-rw-r--r--libpod/network/internal/util/bridge.go69
-rw-r--r--libpod/network/internal/util/create.go42
-rw-r--r--libpod/network/internal/util/interface.go19
-rw-r--r--libpod/network/internal/util/interfaces.go (renamed from libpod/network/util/interfaces.go)4
-rw-r--r--libpod/network/internal/util/ip.go70
-rw-r--r--libpod/network/internal/util/ip_test.go63
-rw-r--r--libpod/network/internal/util/parse.go37
-rw-r--r--libpod/network/internal/util/util.go123
-rw-r--r--libpod/network/internal/util/validate.go121
-rw-r--r--libpod/network/netavark/config.go210
-rw-r--r--libpod/network/netavark/config_test.go1123
-rw-r--r--libpod/network/netavark/const.go5
-rw-r--r--libpod/network/netavark/exec.go128
-rw-r--r--libpod/network/netavark/ipam.go368
-rw-r--r--libpod/network/netavark/ipam_test.go433
-rw-r--r--libpod/network/netavark/netavark_suite_test.go75
-rw-r--r--libpod/network/netavark/network.go305
-rw-r--r--libpod/network/netavark/run.go119
-rw-r--r--libpod/network/netavark/run_test.go693
-rw-r--r--libpod/network/netavark/testfiles/invalid/broken.json16
-rw-r--r--libpod/network/netavark/testfiles/invalid/invalid name.json19
-rw-r--r--libpod/network/netavark/testfiles/invalid/invalid_gateway.json19
-rw-r--r--libpod/network/netavark/testfiles/invalid/name_missmatch.json19
-rw-r--r--libpod/network/netavark/testfiles/invalid/wrongID.json19
-rw-r--r--libpod/network/netavark/testfiles/valid/bridge.json23
-rw-r--r--libpod/network/netavark/testfiles/valid/dualstack.json23
-rw-r--r--libpod/network/netavark/testfiles/valid/internal.json18
-rw-r--r--libpod/network/netavark/testfiles/valid/label.json22
-rw-r--r--libpod/network/netavark/testfiles/valid/mtu.json22
-rw-r--r--libpod/network/netavark/testfiles/valid/podman.json19
-rw-r--r--libpod/network/netavark/testfiles/valid/vlan.json22
-rw-r--r--libpod/network/types/network.go50
-rw-r--r--libpod/network/types/network_test.go82
-rw-r--r--libpod/network/util/ip.go67
-rw-r--r--libpod/network/util/ip_calc.go53
-rw-r--r--libpod/network/util/ip_test.go52
-rw-r--r--libpod/networking_linux.go371
-rw-r--r--libpod/networking_linux_test.go323
-rw-r--r--libpod/networking_slirp4netns.go72
-rw-r--r--libpod/oci.go11
-rw-r--r--libpod/oci_conmon_linux.go98
-rw-r--r--libpod/oci_missing.go8
-rw-r--r--libpod/oci_util.go169
-rw-r--r--libpod/options.go45
-rw-r--r--libpod/runtime.go52
-rw-r--r--libpod/runtime_ctr.go11
-rw-r--r--libpod/runtime_pod_linux.go40
-rw-r--r--libpod/state_test.go40
-rw-r--r--libpod/util.go22
-rw-r--r--nix/default-arm64.nix3
-rw-r--r--nix/default.nix3
-rw-r--r--pause/pause.c69
-rw-r--r--pkg/api/handlers/compat/containers_create.go2
-rw-r--r--pkg/api/handlers/compat/containers_top.go81
-rw-r--r--pkg/api/handlers/compat/images_build.go15
-rw-r--r--pkg/api/handlers/libpod/containers.go28
-rw-r--r--pkg/api/handlers/libpod/play.go2
-rw-r--r--pkg/api/handlers/libpod/pods.go101
-rw-r--r--pkg/api/handlers/libpod/volumes.go17
-rw-r--r--pkg/api/handlers/types.go12
-rw-r--r--pkg/api/handlers/utils/handler.go8
-rw-r--r--pkg/api/handlers/utils/handler_test.go48
-rw-r--r--pkg/api/server/handler_rid.go9
-rw-r--r--pkg/api/server/register_containers.go25
-rw-r--r--pkg/api/server/register_images.go8
-rw-r--r--pkg/api/server/register_pods.go15
-rw-r--r--pkg/api/server/swagger.go14
-rw-r--r--pkg/bindings/connection.go5
-rw-r--r--pkg/bindings/containers/attach.go4
-rw-r--r--pkg/bindings/containers/logs.go2
-rw-r--r--pkg/bindings/errors.go29
-rw-r--r--pkg/bindings/play/types.go2
-rw-r--r--pkg/bindings/play/types_kube_options.go15
-rw-r--r--pkg/bindings/pods/pods.go14
-rw-r--r--pkg/bindings/test/common_test.go17
-rw-r--r--pkg/bindings/test/containers_test.go7
-rw-r--r--pkg/bindings/test/pods_test.go26
-rw-r--r--pkg/cgroups/cgroups.go4
-rw-r--r--pkg/checkpoint/checkpoint_restore.go4
-rw-r--r--pkg/domain/entities/container_ps.go2
-rw-r--r--pkg/domain/entities/containers.go16
-rw-r--r--pkg/domain/entities/engine_image.go1
-rw-r--r--pkg/domain/entities/images.go6
-rw-r--r--pkg/domain/entities/play.go2
-rw-r--r--pkg/domain/entities/pods.go18
-rw-r--r--pkg/domain/entities/reports/prune.go6
-rw-r--r--pkg/domain/entities/system.go2
-rw-r--r--pkg/domain/entities/volumes.go4
-rw-r--r--pkg/domain/infra/abi/containers.go18
-rw-r--r--pkg/domain/infra/abi/generate.go3
-rw-r--r--pkg/domain/infra/abi/images.go68
-rw-r--r--pkg/domain/infra/abi/play.go34
-rw-r--r--pkg/domain/infra/abi/system.go10
-rw-r--r--pkg/domain/infra/runtime_libpod.go3
-rw-r--r--pkg/domain/infra/tunnel/containers.go2
-rw-r--r--pkg/domain/infra/tunnel/images.go7
-rw-r--r--pkg/domain/infra/tunnel/network.go2
-rw-r--r--pkg/domain/infra/tunnel/play.go3
-rw-r--r--pkg/domain/infra/tunnel/secrets.go4
-rw-r--r--pkg/domain/infra/tunnel/volumes.go2
-rw-r--r--pkg/errorhandling/errorhandling.go14
-rw-r--r--pkg/machine/config.go1
-rw-r--r--pkg/machine/ignition.go18
-rw-r--r--pkg/machine/qemu/config.go2
-rw-r--r--pkg/machine/qemu/machine.go4
-rw-r--r--pkg/rootless/rootless_linux.go2
-rw-r--r--pkg/rootlessport/rootlessport_linux.go2
-rw-r--r--pkg/specgen/generate/container_create.go6
-rw-r--r--pkg/specgen/generate/kube/kube.go24
-rw-r--r--pkg/specgen/generate/pod_create.go112
-rw-r--r--pkg/specgen/generate/ports.go589
-rw-r--r--pkg/specgen/generate/ports_bench_test.go197
-rw-r--r--pkg/specgen/generate/ports_test.go989
-rw-r--r--pkg/specgen/generate/storage.go3
-rw-r--r--pkg/specgen/podspecgen.go3
-rw-r--r--pkg/specgen/specgen.go10
-rw-r--r--pkg/specgenutil/specgen.go17
-rw-r--r--pkg/specgenutil/volumes.go12
-rw-r--r--pkg/systemd/dbus.go2
-rw-r--r--test/apiv2/10-images.at3
-rw-r--r--test/apiv2/40-pods.at4
-rw-r--r--test/apiv2/python/rest_api/test_v2_0_0_container.py103
-rw-r--r--test/apiv2/python/rest_api/test_v2_0_0_volume.py67
-rw-r--r--test/e2e/checkpoint_test.go183
-rw-r--r--test/e2e/common_test.go78
-rw-r--r--test/e2e/config/containers-netns.conf3
-rw-r--r--test/e2e/config/containers-netns2.conf3
-rw-r--r--test/e2e/generate_kube_test.go9
-rw-r--r--test/e2e/healthcheck_run_test.go12
-rw-r--r--test/e2e/image_scp_test.go26
-rw-r--r--test/e2e/libpod_suite_remote_test.go24
-rw-r--r--test/e2e/libpod_suite_test.go17
-rw-r--r--test/e2e/logs_test.go69
-rw-r--r--test/e2e/network_connect_disconnect_test.go2
-rw-r--r--test/e2e/play_kube_test.go56
-rw-r--r--test/e2e/pod_create_test.go18
-rw-r--r--test/e2e/push_test.go4
-rw-r--r--test/e2e/run_memory_test.go19
-rw-r--r--test/e2e/run_networking_test.go37
-rw-r--r--test/e2e/run_test.go18
-rw-r--r--test/e2e/system_connection_test.go395
-rw-r--r--test/e2e/system_reset_test.go2
-rw-r--r--test/e2e/systemd_test.go17
-rw-r--r--test/e2e/trust_test.go63
-rw-r--r--test/e2e/unshare_test.go2
-rw-r--r--test/e2e/version_test.go10
-rw-r--r--test/python/docker/compat/test_containers.py14
-rw-r--r--test/registries.conf6
-rw-r--r--test/system/001-basic.bats4
-rw-r--r--test/system/011-image.bats54
-rw-r--r--test/system/015-help.bats4
-rw-r--r--test/system/035-logs.bats28
-rw-r--r--test/system/070-build.bats29
-rw-r--r--test/system/120-load.bats20
-rw-r--r--test/system/130-kill.bats15
-rw-r--r--test/system/200-pod.bats49
-rw-r--r--test/system/272-system-connection.bats8
-rw-r--r--test/system/500-networking.bats27
-rw-r--r--test/system/700-play.bats6
-rw-r--r--test/upgrade/test-upgrade.bats20
-rw-r--r--test/utils/matchers.go153
-rw-r--r--test/utils/podmantest_test.go2
-rw-r--r--test/utils/utils.go28
-rw-r--r--troubleshooting.md4
-rw-r--r--utils/utils.go20
-rw-r--r--vendor/github.com/Microsoft/hcsshim/.gitignore39
-rw-r--r--vendor/github.com/Microsoft/hcsshim/.golangci.yml96
-rw-r--r--vendor/github.com/Microsoft/hcsshim/Makefile87
-rw-r--r--vendor/github.com/Microsoft/hcsshim/README.md82
-rw-r--r--vendor/github.com/Microsoft/hcsshim/go.mod26
-rw-r--r--vendor/github.com/Microsoft/hcsshim/go.sum777
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go6
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go8
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go14
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go15
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go15
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go17
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go16
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go16
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go15
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go18
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go16
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go18
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go15
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go1
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go30
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go4
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go4
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go2
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go23
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go25
-rw-r--r--vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go27
-rw-r--r--vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go3
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore5
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/Makefile56
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/README.md3
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/go.mod3
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/go.sum6
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go12
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go4
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go462
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.proto40
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go6
-rw-r--r--vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go54
-rw-r--r--vendor/github.com/containers/common/libimage/download.go46
-rw-r--r--vendor/github.com/containers/common/libimage/filters.go32
-rw-r--r--vendor/github.com/containers/common/libimage/image.go36
-rw-r--r--vendor/github.com/containers/common/libimage/import.go5
-rw-r--r--vendor/github.com/containers/common/libimage/inspect.go5
-rw-r--r--vendor/github.com/containers/common/libimage/runtime.go10
-rw-r--r--vendor/github.com/containers/common/libimage/search.go2
-rw-r--r--vendor/github.com/containers/common/pkg/config/config.go11
-rw-r--r--vendor/github.com/containers/common/pkg/config/containers.conf4
-rw-r--r--vendor/github.com/containers/common/pkg/config/default.go1
-rw-r--r--vendor/github.com/containers/common/pkg/download/download.go31
-rw-r--r--vendor/github.com/containers/common/pkg/flag/flag.go174
-rw-r--r--vendor/github.com/containers/common/pkg/seccomp/default_linux.go12
-rw-r--r--vendor/github.com/containers/common/pkg/seccomp/errno_list.go91
-rw-r--r--vendor/github.com/containers/common/pkg/seccomp/seccomp.json34
-rw-r--r--vendor/github.com/containers/common/pkg/seccomp/seccomp_linux.go58
-rw-r--r--vendor/github.com/containers/common/pkg/seccomp/types.go10
-rw-r--r--vendor/github.com/containers/storage/VERSION2
-rw-r--r--vendor/github.com/containers/storage/go.mod10
-rw-r--r--vendor/github.com/containers/storage/go.sum726
-rw-r--r--vendor/github.com/containers/storage/storage.conf6
-rw-r--r--vendor/github.com/containers/storage/store.go34
-rw-r--r--vendor/github.com/containers/storage/types/options.go14
-rw-r--r--vendor/github.com/godbus/dbus/v5/auth.go2
-rw-r--r--vendor/github.com/godbus/dbus/v5/conn.go22
-rw-r--r--vendor/github.com/godbus/dbus/v5/message.go4
-rw-r--r--vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go14
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/generate/generate.go81
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go14
-rw-r--r--vendor/github.com/opencontainers/runtime-tools/validate/validate.go2
-rw-r--r--vendor/golang.org/x/net/http2/README20
-rw-r--r--vendor/golang.org/x/net/http2/ascii.go4
-rw-r--r--vendor/golang.org/x/net/http2/server.go23
-rw-r--r--vendor/golang.org/x/net/http2/transport.go156
-rw-r--r--vendor/google.golang.org/grpc/.travis.yml42
-rw-r--r--vendor/google.golang.org/grpc/README.md2
-rw-r--r--vendor/google.golang.org/grpc/balancer_conn_wrappers.go63
-rw-r--r--vendor/google.golang.org/grpc/clientconn.go35
-rw-r--r--vendor/google.golang.org/grpc/go.mod11
-rw-r--r--vendor/google.golang.org/grpc/go.sum50
-rw-r--r--vendor/google.golang.org/grpc/internal/binarylog/sink.go41
-rw-r--r--vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go29
-rw-r--r--vendor/google.golang.org/grpc/internal/status/status.go14
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/controlbuf.go24
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/handler_server.go3
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/http2_client.go206
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/http2_server.go163
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/http_util.go224
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go2
-rw-r--r--vendor/google.golang.org/grpc/internal/transport/transport.go8
-rw-r--r--vendor/google.golang.org/grpc/metadata/metadata.go76
-rw-r--r--vendor/google.golang.org/grpc/picker_wrapper.go2
-rw-r--r--vendor/google.golang.org/grpc/resolver_conn_wrapper.go10
-rw-r--r--vendor/google.golang.org/grpc/rpc_util.go31
-rw-r--r--vendor/google.golang.org/grpc/server.go77
-rw-r--r--vendor/google.golang.org/grpc/stats/stats.go4
-rw-r--r--vendor/google.golang.org/grpc/stream.go57
-rw-r--r--vendor/google.golang.org/grpc/version.go2
-rw-r--r--vendor/google.golang.org/grpc/vet.sh28
-rw-r--r--vendor/google.golang.org/protobuf/encoding/prototext/decode.go3
-rw-r--r--vendor/google.golang.org/protobuf/internal/encoding/text/encode.go5
-rw-r--r--vendor/google.golang.org/protobuf/internal/impl/legacy_message.go7
-rw-r--r--vendor/google.golang.org/protobuf/internal/version/version.go4
-rw-r--r--vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go43
-rw-r--r--vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go82
-rw-r--r--vendor/modules.txt32
478 files changed, 14599 insertions, 3533 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index 9897a9f7f..091b37627 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -26,10 +26,10 @@ env:
####
FEDORA_NAME: "fedora-34"
PRIOR_FEDORA_NAME: "fedora-33"
- UBUNTU_NAME: "ubuntu-2104"
+ UBUNTU_NAME: "ubuntu-2110"
# Google-cloud VM Images
- IMAGE_SUFFIX: "c6431352024203264"
+ IMAGE_SUFFIX: "c4955591916388352"
FEDORA_CACHE_IMAGE_NAME: "fedora-${IMAGE_SUFFIX}"
PRIOR_FEDORA_CACHE_IMAGE_NAME: "prior-fedora-${IMAGE_SUFFIX}"
UBUNTU_CACHE_IMAGE_NAME: "ubuntu-${IMAGE_SUFFIX}"
@@ -148,11 +148,11 @@ build_task:
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
# ID for re-use of build output
_BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
- - 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: &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: &ubuntu_envvars
DISTRO_NV: ${UBUNTU_NAME}
VM_IMAGE_NAME: ${UBUNTU_CACHE_IMAGE_NAME}
@@ -402,7 +402,7 @@ unit_test_task:
- validate
matrix:
- env: *stdenvars
- - env: *priorfedora_envvars
+ #- env: *priorfedora_envvars
- env: *ubuntu_envvars
# Special-case: Rootless on latest Fedora (standard) VM
- name: "Rootless unit on $DISTRO_NV"
@@ -522,11 +522,11 @@ container_integration_test_task:
_BUILD_CACHE_HANDLE: ${FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
VM_IMAGE_NAME: ${FEDORA_CACHE_IMAGE_NAME}
CTR_FQIN: ${FEDORA_CONTAINER_FQIN}
- - env:
- DISTRO_NV: ${PRIOR_FEDORA_NAME}
- _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
- VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME}
- CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN}
+ # - env:
+ # DISTRO_NV: ${PRIOR_FEDORA_NAME}
+ # _BUILD_CACHE_HANDLE: ${PRIOR_FEDORA_NAME}-build-${CIRRUS_BUILD_ID}
+ # VM_IMAGE_NAME: ${PRIOR_FEDORA_CACHE_IMAGE_NAME}
+ # CTR_FQIN: ${PRIOR_FEDORA_CONTAINER_FQIN}
gce_instance: *standardvm
timeout_in: 90m
env:
diff --git a/Makefile b/Makefile
index 3b70c4f4f..47f30e5fa 100644
--- a/Makefile
+++ b/Makefile
@@ -62,10 +62,11 @@ PKG_MANAGER ?= $(shell command -v dnf yum|head -n1)
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)
@@ -185,6 +186,10 @@ ifdef HOMEBREW_PREFIX
endif
endif
+# For building pause/pause.c
+GCC ?= gcc
+PAUSE_CFLAGS = -Os -static -Wall -Werror -DVERSION=v$(RELEASE_VERSION)
+
###
### Primary entry-point targets
###
@@ -196,7 +201,7 @@ default: all
all: binaries docs
.PHONY: binaries
-binaries: podman podman-remote rootlessport ## Build podman, podman-remote and rootlessport binaries
+binaries: podman podman-remote rootlessport pause
# Extract text following double-# for targets, as their description for
# the `help` target. Otherwise These simple-substitutions are resolved
@@ -374,6 +379,12 @@ bin/rootlessport: .gopathok $(SOURCES) go.mod go.sum
.PHONY: rootlessport
rootlessport: bin/rootlessport
+bin/pause: pause/pause.c
+ $(GCC) $(PAUSE_CFLAGS) pause/pause.c -o bin/pause
+
+.PHONY: pause
+pause: bin/pause
+
###
### Secondary binary-build targets
###
@@ -733,7 +744,7 @@ install.remote-nobuild:
install.remote: podman-remote install.remote-nobuild
.PHONY: install.bin-nobuild
-install.bin-nobuild:
+install.bin-nobuild: install.pause
install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR)
install ${SELINUXOPT} -m 755 bin/podman $(DESTDIR)$(BINDIR)/podman
test -z "${SELINUXOPT}" || chcon --verbose --reference=$(DESTDIR)$(BINDIR)/podman bin/podman
@@ -787,8 +798,10 @@ install.docker-docs-nobuild:
.PHONY: install.docker-docs
install.docker-docs: docker-docs install.docker-docs-nobuild
-.PHONY: install.docker-full
-install.docker-full: install.docker install.docker-docs
+.PHONY: install.pause
+install.pause: pause
+ install ${SELINUXOPT} -m 755 -d $(DESTDIR)$(LIBEXECPODMAN)/pause
+ install ${SELINUXOPT} -m 755 bin/pause $(DESTDIR)$(LIBEXECPODMAN)/pause/pause
.PHONY: install.systemd
ifneq (,$(findstring systemd,$(BUILDTAGS)))
@@ -819,6 +832,9 @@ else
install.systemd:
endif
+.PHONY: install.pause
+install.pause: pause
+
.PHONY: install.tools
install.tools: .install.goimports .install.gitvalidation .install.md2man .install.ginkgo .install.golangci-lint .install.bats ## Install needed tools
diff --git a/README.md b/README.md
index 3697943ec..2b384239f 100644
--- a/README.md
+++ b/README.md
@@ -168,7 +168,11 @@ was replaced by the REST API. Varlink support has been removed as of the 3.0 rel
For more details, you can see [this blog](https://podman.io/blogs/2020/01/17/podman-new-api.html).
## Static Binary Builds
-The Cirrus CI integration within this repository contains a `static_build` job
-which produces a static Podman binary for testing purposes. Please note that
-this binary is not officially supported with respect to feature-completeness
-and functionality and should be only used for testing.
+The Cirrus CI integration within this repository contains a `Static_Build` job
+which produces static Podman binaries for testing purposes. Please note that
+these binaries are not officially supported with respect to feature-completeness
+and functionality and should be only used for testing. To download these binaries,
+load the build link with the commit SHA at
+[main](https://cirrus-ci.com/github/containers/podman/main) or
+`https://cirrus-ci.com/github/containers/podman/pull/<selected PR>`
+and open the artifacts folder within `Static Build`.
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index ef48df291..60d433953 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -113,7 +113,7 @@
### 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` commmand now supports Kubernetes liveness probes, which will be created as Podman healthchecks.
+- 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)).
@@ -369,7 +369,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
@@ -1014,7 +1014,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 --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go
index ea453a331..4cb29383a 100644
--- a/cmd/podman/common/completion.go
+++ b/cmd/podman/common/completion.go
@@ -1163,6 +1163,13 @@ func AutocompleteEventBackend(cmd *cobra.Command, args []string, toComplete stri
return types, cobra.ShellCompDirectiveNoFileComp
}
+// AutocompleteNetworkBackend - Autocomplete network backend options.
+// -> "cni", "netavark"
+func AutocompleteNetworkBackend(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
+ types := []string{"cni", "netavark"}
+ return types, cobra.ShellCompDirectiveNoFileComp
+}
+
// AutocompleteLogLevel - Autocomplete log level options.
// -> "trace", "debug", "info", "warn", "error", "fatal", "panic"
func AutocompleteLogLevel(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index a3ff37c19..4598e535d 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -5,6 +5,7 @@ import (
"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"
@@ -319,6 +320,9 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
"Kernel memory limit "+sizeWithUnitFormat,
)
_ = cmd.RegisterFlagCompletionFunc(kernelMemoryFlagName, completion.AutocompleteNone)
+ // kernel-memory is deprecated in the runtime spec.
+ _ = createFlags.MarkHidden("kernel-memory")
+
logDriverFlagName := "log-driver"
createFlags.StringVar(
&cf.LogDriver,
@@ -406,7 +410,7 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
createFlags.StringVar(
&cf.Variant,
variantFlagName, "",
- "Use _VARIANT_ instead of the running architecture variant for choosing images",
+ "Use `VARIANT` instead of the running architecture variant for choosing images",
)
_ = cmd.RegisterFlagCompletionFunc(variantFlagName, completion.AutocompleteNone)
@@ -586,12 +590,9 @@ func DefineCreateFlags(cmd *cobra.Command, cf *entities.ContainerCreateOptions,
)
_ = cmd.RegisterFlagCompletionFunc(timeoutFlagName, completion.AutocompleteNone)
- // Flag for TLS verification, so that `run` and `create` commands can make use of it.
- // Make sure to use `=` while using this flag i.e `--tls-verify=false/true`
- tlsVerifyFlagName := "tls-verify"
- createFlags.BoolVar(
+ commonFlag.OptionalBoolFlag(createFlags,
&cf.TLSVerify,
- tlsVerifyFlagName, true,
+ "tls-verify",
"Require HTTPS and verify certificates when contacting registries for pulling images",
)
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index 50d7c446d..6283eb28e 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -18,6 +18,7 @@ import (
"github.com/containers/podman/v3/pkg/specgen"
"github.com/docker/docker/api/types/mount"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func stringMaptoArray(m map[string]string) []string {
@@ -291,7 +292,7 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
Rm: cc.HostConfig.AutoRemove,
SecurityOpt: cc.HostConfig.SecurityOpt,
StopSignal: cc.Config.StopSignal,
- StorageOpt: stringMaptoArray(cc.HostConfig.StorageOpt),
+ StorageOpts: stringMaptoArray(cc.HostConfig.StorageOpt),
Sysctl: stringMaptoArray(cc.HostConfig.Sysctls),
Systemd: "true", // podman default
TmpFS: parsedTmp,
@@ -383,6 +384,9 @@ func ContainerCreateToContainerCLIOpts(cc handlers.CreateContainerConfig, rtc *c
if cc.HostConfig.Memory > 0 {
cliOpts.Memory = strconv.Itoa(int(cc.HostConfig.Memory))
}
+ if cc.HostConfig.KernelMemory > 0 {
+ logrus.Warnf("The --kernel-memory flag has been deprecated. May not work properly on your system.")
+ }
if cc.HostConfig.MemoryReservation > 0 {
cliOpts.MemoryReservation = strconv.Itoa(int(cc.HostConfig.MemoryReservation))
diff --git a/cmd/podman/containers/checkpoint.go b/cmd/podman/containers/checkpoint.go
index 4fa72d520..d92bc3e5e 100644
--- a/cmd/podman/containers/checkpoint.go
+++ b/cmd/podman/containers/checkpoint.go
@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"strings"
+ "time"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -40,6 +41,11 @@ var (
var checkpointOptions entities.CheckpointOptions
+type checkpointStatistics struct {
+ PodmanDuration int64 `json:"podman_checkpoint_duration"`
+ ContainerStatistics []*entities.CheckpointReport `json:"container_statistics"`
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: checkpointCommand,
@@ -63,11 +69,19 @@ func init() {
flags.StringP("compress", "c", "zstd", "Select compression algorithm (gzip, none, zstd) for checkpoint archive.")
_ = checkpointCommand.RegisterFlagCompletionFunc("compress", common.AutocompleteCheckpointCompressType)
+ flags.BoolVar(
+ &checkpointOptions.PrintStats,
+ "print-stats",
+ false,
+ "Display checkpoint statistics",
+ )
+
validate.AddLatestFlag(checkpointCommand, &checkpointOptions.Latest)
}
func checkpoint(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
+ podmanStart := time.Now()
if cmd.Flags().Changed("compress") {
if checkpointOptions.Export == "" {
return errors.Errorf("--compress can only be used with --export")
@@ -102,12 +116,30 @@ func checkpoint(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ podmanFinished := time.Now()
+
+ var statistics checkpointStatistics
+
for _, r := range responses {
if r.Err == nil {
- fmt.Println(r.Id)
+ if checkpointOptions.PrintStats {
+ statistics.ContainerStatistics = append(statistics.ContainerStatistics, r)
+ } else {
+ fmt.Println(r.Id)
+ }
} else {
errs = append(errs, r.Err)
}
}
+
+ if checkpointOptions.PrintStats {
+ statistics.PodmanDuration = podmanFinished.Sub(podmanStart).Microseconds()
+ j, err := json.MarshalIndent(statistics, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(j))
+ }
+
return errs.PrintErrors()
}
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index bfeeb7ebe..9b53a730f 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -303,6 +303,11 @@ func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (strin
}
}
+ skipTLSVerify := types.OptionalBoolUndefined
+ if cliVals.TLSVerify.Present() {
+ skipTLSVerify = types.NewOptionalBool(!cliVals.TLSVerify.Value())
+ }
+
pullReport, pullErr := registry.ImageEngine().Pull(registry.GetContext(), imageName, entities.ImagePullOptions{
Authfile: cliVals.Authfile,
Quiet: cliVals.Quiet,
@@ -311,7 +316,7 @@ func PullImage(imageName string, cliVals entities.ContainerCreateOptions) (strin
Variant: cliVals.Variant,
SignaturePolicy: cliVals.SignaturePolicy,
PullPolicy: pullPolicy,
- SkipTLSVerify: types.NewOptionalBool(!cliVals.TLSVerify), // If Flag changed for TLS Verification
+ SkipTLSVerify: skipTLSVerify,
})
if pullErr != nil {
return "", pullErr
@@ -371,16 +376,13 @@ func createPodIfNecessary(s *specgen.SpecGenerator, netOpts *entities.NetOptions
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
+ infraOpts := entities.NewInfraContainerCreateOptions()
+ infraOpts.Net = netOpts
+ infraOpts.Quiet = true
+ imageName := config.DefaultInfraImage
podGen.InfraImage = imageName
podGen.InfraContainerSpec = specgen.NewSpecGenerator(imageName, false)
- podGen.InfraContainerSpec.RawImageName = rawImageName
+ podGen.InfraContainerSpec.RawImageName = imageName
podGen.InfraContainerSpec.NetworkOptions = podGen.NetworkOptions
err = specgenutil.FillOutSpecGen(podGen.InfraContainerSpec, &infraOpts, []string{})
if err != nil {
diff --git a/cmd/podman/containers/port.go b/cmd/podman/containers/port.go
index f309390c3..0e582ae52 100644
--- a/cmd/podman/containers/port.go
+++ b/cmd/podman/containers/port.go
@@ -8,7 +8,6 @@ import (
"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/network/types"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@@ -73,7 +72,8 @@ func port(_ *cobra.Command, args []string) error {
var (
container string
err error
- userPort types.OCICNIPortMapping
+ userPort uint16
+ userProto string
)
if len(args) == 0 && !portOpts.Latest && !portOpts.All {
@@ -101,16 +101,12 @@ func port(_ *cobra.Command, args []string) error {
fields = append(fields, "tcp")
}
- portNum, err := strconv.Atoi(fields[0])
+ portNum, err := strconv.ParseUint(fields[0], 10, 16)
if err != nil {
return err
}
- userPort = types.OCICNIPortMapping{
- HostPort: 0,
- ContainerPort: int32(portNum),
- Protocol: fields[1],
- HostIP: "",
- }
+ userPort = uint16(portNum)
+ userProto = fields[1]
}
reports, err := registry.ContainerEngine().ContainerPort(registry.GetContext(), container, portOpts)
@@ -120,24 +116,36 @@ func port(_ *cobra.Command, args []string) error {
var found bool
// Iterate mappings
for _, report := range reports {
+ allPrefix := ""
+ if portOpts.All {
+ allPrefix = report.Id[:12] + "\t"
+ }
for _, v := range report.Ports {
hostIP := v.HostIP
// Set host IP to 0.0.0.0 if blank
if hostIP == "" {
hostIP = "0.0.0.0"
}
- if portOpts.All {
- fmt.Printf("%s\t", report.Id[:12])
- }
- // If not searching by port or port/proto, then dump what we see
- if port == "" {
- fmt.Printf("%d/%s -> %s:%d\n", v.ContainerPort, v.Protocol, hostIP, v.HostPort)
- continue
- }
- if v.ContainerPort == userPort.ContainerPort {
- fmt.Printf("%s:%d\n", hostIP, v.HostPort)
- found = true
- break
+ protocols := strings.Split(v.Protocol, ",")
+ for _, protocol := range protocols {
+ // If not searching by port or port/proto, then dump what we see
+ if port == "" {
+ for i := uint16(0); i < v.Range; i++ {
+ fmt.Printf("%s%d/%s -> %s:%d\n", allPrefix, v.ContainerPort+i, protocol, hostIP, v.HostPort+i)
+ }
+ continue
+ }
+ // check if the proto matches and if the port is in the range
+ // this is faster than looping over the range for no reason
+ if v.Protocol == userProto &&
+ v.ContainerPort <= userPort &&
+ v.ContainerPort+v.Range > userPort {
+ // we have to add the current range to the host port
+ hostPort := v.HostPort + userPort - v.ContainerPort
+ fmt.Printf("%s%s:%d\n", allPrefix, hostIP, hostPort)
+ found = true
+ break
+ }
}
}
if !found && port != "" {
diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go
index 9687cd5bd..712de327c 100644
--- a/cmd/podman/containers/ps.go
+++ b/cmd/podman/containers/ps.go
@@ -3,8 +3,6 @@ package containers
import (
"fmt"
"os"
- "sort"
- "strconv"
"strings"
"time"
@@ -477,174 +475,31 @@ func (l psReporter) UTS() string {
// portsToString converts the ports used to a string of the from "port1, port2"
// and also groups a continuous list of ports into a readable format.
-func portsToString(ports []types.OCICNIPortMapping) string {
+// The format is IP:HostPort(-Range)->ContainerPort(-Range)/Proto
+func portsToString(ports []types.PortMapping) string {
if len(ports) == 0 {
return ""
}
- // Sort the ports, so grouping continuous ports become easy.
- sort.Slice(ports, func(i, j int) bool {
- return comparePorts(ports[i], ports[j])
- })
-
- portGroups := [][]types.OCICNIPortMapping{}
- currentGroup := []types.OCICNIPortMapping{}
- for i, v := range ports {
- var prevPort, nextPort *int32
- if i > 0 {
- prevPort = &ports[i-1].ContainerPort
- }
- if i+1 < len(ports) {
- nextPort = &ports[i+1].ContainerPort
- }
-
- port := v.ContainerPort
-
- // Helper functions
- addToCurrentGroup := func(x types.OCICNIPortMapping) {
- currentGroup = append(currentGroup, x)
- }
-
- addToPortGroup := func(x types.OCICNIPortMapping) {
- portGroups = append(portGroups, []types.OCICNIPortMapping{x})
- }
-
- finishCurrentGroup := func() {
- portGroups = append(portGroups, currentGroup)
- currentGroup = []types.OCICNIPortMapping{}
- }
-
- // Single entry slice
- if prevPort == nil && nextPort == nil {
- addToPortGroup(v)
- }
-
- // Start of the slice with len > 0
- if prevPort == nil && nextPort != nil {
- isGroup := *nextPort-1 == port
-
- if isGroup {
- // Start with a group
- addToCurrentGroup(v)
- } else {
- // Start with single item
- addToPortGroup(v)
- }
-
- continue
- }
-
- // Middle of the slice with len > 0
- if prevPort != nil && nextPort != nil {
- currentIsGroup := *prevPort+1 == port
- nextIsGroup := *nextPort-1 == port
-
- if currentIsGroup {
- // Maybe in the middle of a group
- addToCurrentGroup(v)
-
- if !nextIsGroup {
- // End of a group
- finishCurrentGroup()
- }
- } else if nextIsGroup {
- // Start of a new group
- addToCurrentGroup(v)
- } else {
- // No group at all
- addToPortGroup(v)
- }
-
- continue
- }
-
- // End of the slice with len > 0
- if prevPort != nil && nextPort == nil {
- isGroup := *prevPort+1 == port
-
- if isGroup {
- // End group
- addToCurrentGroup(v)
- finishCurrentGroup()
- } else {
- // End single item
- addToPortGroup(v)
- }
- }
- }
-
- portDisplay := []string{}
- for _, group := range portGroups {
- if len(group) == 0 {
- // Usually should not happen, but better do not crash.
- continue
- }
-
- first := group[0]
-
- hostIP := first.HostIP
+ sb := &strings.Builder{}
+ for _, port := range ports {
+ hostIP := port.HostIP
if hostIP == "" {
hostIP = "0.0.0.0"
}
-
- // Single mappings
- if len(group) == 1 {
- portDisplay = append(portDisplay,
- fmt.Sprintf(
- "%s:%d->%d/%s",
- hostIP, first.HostPort, first.ContainerPort, first.Protocol,
- ),
- )
- continue
- }
-
- // Group mappings
- last := group[len(group)-1]
- portDisplay = append(portDisplay, formatGroup(
- fmt.Sprintf("%s/%s", hostIP, first.Protocol),
- first.HostPort, last.HostPort,
- first.ContainerPort, last.ContainerPort,
- ))
- }
- return strings.Join(portDisplay, ", ")
-}
-
-func comparePorts(i, j types.OCICNIPortMapping) bool {
- if i.ContainerPort != j.ContainerPort {
- return i.ContainerPort < j.ContainerPort
- }
-
- if i.HostIP != j.HostIP {
- return i.HostIP < j.HostIP
- }
-
- if i.HostPort != j.HostPort {
- return i.HostPort < j.HostPort
- }
-
- return i.Protocol < j.Protocol
-}
-
-// formatGroup returns the group in the format:
-// <IP:firstHost:lastHost->firstCtr:lastCtr/Proto>
-// e.g 0.0.0.0:1000-1006->2000-2006/tcp.
-func formatGroup(key string, firstHost, lastHost, firstCtr, lastCtr int32) string {
- parts := strings.Split(key, "/")
- groupType := parts[0]
- var ip string
- if len(parts) > 1 {
- ip = parts[0]
- groupType = parts[1]
- }
-
- group := func(first, last int32) string {
- group := strconv.Itoa(int(first))
- if first != last {
- group = fmt.Sprintf("%s-%d", group, last)
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ if port.Range > 1 {
+ fmt.Fprintf(sb, "%s:%d-%d->%d-%d/%s, ",
+ hostIP, port.HostPort, port.HostPort+port.Range-1,
+ port.ContainerPort, port.ContainerPort+port.Range-1, protocol)
+ } else {
+ fmt.Fprintf(sb, "%s:%d->%d/%s, ",
+ hostIP, port.HostPort,
+ port.ContainerPort, protocol)
+ }
}
- return group
}
- hostGroup := group(firstHost, lastHost)
- ctrGroup := group(firstCtr, lastCtr)
-
- return fmt.Sprintf("%s:%s->%s/%s", ip, hostGroup, ctrGroup, groupType)
+ display := sb.String()
+ // make sure to trim the last ", " of the string
+ return display[:len(display)-2]
}
diff --git a/cmd/podman/containers/restore.go b/cmd/podman/containers/restore.go
index 05214f32c..217adc887 100644
--- a/cmd/podman/containers/restore.go
+++ b/cmd/podman/containers/restore.go
@@ -3,6 +3,7 @@ package containers
import (
"context"
"fmt"
+ "time"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
@@ -39,6 +40,11 @@ var (
var restoreOptions entities.RestoreOptions
+type restoreStatistics struct {
+ PodmanDuration int64 `json:"podman_restore_duration"`
+ ContainerStatistics []*entities.RestoreReport `json:"container_statistics"`
+}
+
func init() {
registry.Commands = append(registry.Commands, registry.CliCommand{
Command: restoreCommand,
@@ -75,11 +81,19 @@ func init() {
flags.StringVar(&restoreOptions.Pod, "pod", "", "Restore container into existing Pod (only works with --import)")
_ = restoreCommand.RegisterFlagCompletionFunc("pod", common.AutocompletePodsRunning)
+ flags.BoolVar(
+ &restoreOptions.PrintStats,
+ "print-stats",
+ false,
+ "Display restore statistics",
+ )
+
validate.AddLatestFlag(restoreCommand, &restoreOptions.Latest)
}
func restore(cmd *cobra.Command, args []string) error {
var errs utils.OutputErrors
+ podmanStart := time.Now()
if rootless.IsRootless() {
return errors.New("restoring a container requires root")
}
@@ -132,12 +146,30 @@ func restore(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
+ podmanFinished := time.Now()
+
+ var statistics restoreStatistics
+
for _, r := range responses {
if r.Err == nil {
- fmt.Println(r.Id)
+ if restoreOptions.PrintStats {
+ statistics.ContainerStatistics = append(statistics.ContainerStatistics, r)
+ } else {
+ fmt.Println(r.Id)
+ }
} else {
errs = append(errs, r.Err)
}
}
+
+ if restoreOptions.PrintStats {
+ statistics.PodmanDuration = podmanFinished.Sub(podmanStart).Microseconds()
+ j, err := json.MarshalIndent(statistics, "", " ")
+ if err != nil {
+ return err
+ }
+ fmt.Println(string(j))
+ }
+
return errs.PrintErrors()
}
diff --git a/cmd/podman/images/load.go b/cmd/podman/images/load.go
index c7b7d6b3f..c39ae624e 100644
--- a/cmd/podman/images/load.go
+++ b/cmd/podman/images/load.go
@@ -9,6 +9,7 @@ import (
"strings"
"github.com/containers/common/pkg/completion"
+ "github.com/containers/common/pkg/download"
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/cmd/podman/validate"
"github.com/containers/podman/v3/pkg/domain/entities"
@@ -69,6 +70,20 @@ func loadFlags(cmd *cobra.Command) {
func load(cmd *cobra.Command, args []string) error {
if len(loadOpts.Input) > 0 {
+ // Download the input file if needed.
+ if strings.HasPrefix(loadOpts.Input, "https://") || strings.HasPrefix(loadOpts.Input, "http://") {
+ tmpdir, err := util.DefaultContainerConfig().ImageCopyTmpDir()
+ if err != nil {
+ return err
+ }
+ tmpfile, err := download.FromURL(tmpdir, loadOpts.Input)
+ if err != nil {
+ return err
+ }
+ defer os.Remove(tmpfile)
+ loadOpts.Input = tmpfile
+ }
+
if _, err := os.Stat(loadOpts.Input); err != nil {
return err
}
diff --git a/cmd/podman/images/pull.go b/cmd/podman/images/pull.go
index a4e3515db..a990d1626 100644
--- a/cmd/podman/images/pull.go
+++ b/cmd/podman/images/pull.go
@@ -92,7 +92,7 @@ func pullFlags(cmd *cobra.Command) {
_ = 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"
diff --git a/cmd/podman/images/scp.go b/cmd/podman/images/scp.go
index 176563440..5c9cadc7a 100644
--- a/cmd/podman/images/scp.go
+++ b/cmd/podman/images/scp.go
@@ -16,6 +16,7 @@ import (
"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/containers/podman/v3/pkg/rootless"
"github.com/docker/distribution/reference"
scpD "github.com/dtylman/scp"
"github.com/pkg/errors"
@@ -125,20 +126,39 @@ func scp(cmd *cobra.Command, args []string) (finalErr error) {
fmt.Println(rep)
// TODO: Add podman remote support
default: // else native load
+ scpOpts.Save.Format = "oci-archive"
+ _, err := os.Open(scpOpts.Save.Output)
+ if err != nil {
+ return err
+ }
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")
+ return errors.Wrapf(abiErr, "could not save image as specified")
}
- rep, err := abiEng.Load(context.Background(), scpOpts.Load)
- if err != nil {
- return err
+ if !rootless.IsRootless() && scpOpts.Rootless {
+ if scpOpts.User == "" {
+ scpOpts.User = os.Getenv("SUDO_USER")
+ if scpOpts.User == "" {
+ return errors.New("could not obtain root user, make sure the environmental variable SUDO_USER is set, and that this command is being run as root")
+ }
+ }
+ err := abiEng.Transfer(context.Background(), scpOpts)
+ if err != nil {
+ return err
+ }
+ } else {
+ rep, err := abiEng.Load(context.Background(), scpOpts.Load)
+ if err != nil {
+ return err
+ }
+ fmt.Println("Loaded image(s): " + strings.Join(rep.Names, ","))
}
- fmt.Println("Loaded image(s): " + strings.Join(rep.Names, ","))
}
+
return nil
}
@@ -154,7 +174,7 @@ func loadToRemote(localFile string, tag string, url *urlP.URL, iden string) (str
n, err := scpD.CopyTo(dial, localFile, remoteFile)
if err != nil {
- errOut := (strconv.Itoa(int(n)) + " Bytes copied before error")
+ errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
return " ", errors.Wrapf(err, errOut)
}
run := ""
@@ -167,7 +187,7 @@ func loadToRemote(localFile string, tag string, url *urlP.URL, iden string) (str
if err != nil {
return "", err
}
- return strings.TrimSuffix(out, "\n"), nil
+ return strings.TrimSuffix(string(out), "\n"), nil
}
// saveToRemote takes image information and remote connection information. it connects to the specified client
@@ -185,7 +205,7 @@ func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden strin
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 thie case...
+ 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
@@ -193,7 +213,7 @@ func saveToRemote(image, localFile string, tag string, uri *urlP.URL, iden strin
n, err := scpD.CopyFrom(dial, remoteFile, localFile)
connection.ExecRemoteCommand(dial, "rm "+remoteFile)
if err != nil {
- errOut := (strconv.Itoa(int(n)) + " Bytes copied before error")
+ errOut := strconv.Itoa(int(n)) + " Bytes copied before error"
return errors.Wrapf(err, errOut)
}
return nil
@@ -207,11 +227,7 @@ func makeRemoteFile(dial *ssh.Client) (string, error) {
if err != nil {
return "", err
}
- remoteFile = strings.TrimSuffix(remoteFile, "\n")
- if err != nil {
- return "", err
- }
- return remoteFile, nil
+ return strings.TrimSuffix(string(remoteFile), "\n"), nil
}
// createConnections takes a boolean determining which ssh client to dial
@@ -260,7 +276,13 @@ func parseArgs(args []string, cfg *config.Config) (map[string]config.Destination
cliConnections := []string{}
switch len(args) {
case 1:
- if strings.Contains(args[0], "::") {
+ if strings.Contains(args[0], "localhost") {
+ if strings.Split(args[0], "@")[0] != "root" {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "cannot transfer images from any user besides root using sudo")
+ }
+ scpOpts.Rootless = true
+ scpOpts.SourceImageName = strings.Split(args[0], "::")[1]
+ } else if strings.Contains(args[0], "::") {
scpOpts.FromRemote = true
cliConnections = append(cliConnections, args[0])
} else {
@@ -271,7 +293,18 @@ func parseArgs(args []string, cfg *config.Config) (map[string]config.Destination
scpOpts.SourceImageName = args[0]
}
case 2:
- if strings.Contains(args[0], "::") {
+ if strings.Contains(args[0], "localhost") || strings.Contains(args[1], "localhost") { // only supporting root to local using sudo at the moment
+ if strings.Split(args[0], "@")[0] != "root" {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "currently, transferring images to a user account is not supported")
+ }
+ if len(strings.Split(args[0], "::")) > 1 {
+ scpOpts.Rootless = true
+ scpOpts.User = strings.Split(args[1], "@")[0]
+ scpOpts.SourceImageName = strings.Split(args[0], "::")[1]
+ } else {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "currently, you cannot rename images during the transfer or transfer them to a user account")
+ }
+ } else 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
diff --git a/cmd/podman/images/sign.go b/cmd/podman/images/sign.go
index 96f214d0b..4c42a0bd6 100644
--- a/cmd/podman/images/sign.go
+++ b/cmd/podman/images/sign.go
@@ -3,6 +3,7 @@ package images
import (
"os"
+ "github.com/containers/common/pkg/auth"
"github.com/containers/common/pkg/completion"
"github.com/containers/podman/v3/cmd/podman/common"
"github.com/containers/podman/v3/cmd/podman/registry"
@@ -48,6 +49,10 @@ func init() {
flags.StringVar(&signOptions.CertDir, certDirFlagName, "", "`Pathname` of a directory containing TLS certificates and keys")
_ = signCommand.RegisterFlagCompletionFunc(certDirFlagName, completion.AutocompleteDefault)
flags.BoolVarP(&signOptions.All, "all", "a", false, "Sign all the manifests of the multi-architecture image")
+
+ authfileFlagName := "authfile"
+ flags.StringVar(&signOptions.Authfile, authfileFlagName, auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
+ _ = signCommand.RegisterFlagCompletionFunc(authfileFlagName, completion.AutocompleteDefault)
}
func sign(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/machine/list.go b/cmd/podman/machine/list.go
index 7e5459e08..d569f4db0 100644
--- a/cmd/podman/machine/list.go
+++ b/cmd/podman/machine/list.go
@@ -48,6 +48,7 @@ type machineReporter struct {
Created string
Running bool
LastUp string
+ Stream string
VMType string
CPUs uint64
Memory string
@@ -153,6 +154,13 @@ func strUint(u uint64) string {
return strconv.FormatUint(u, 10)
}
+func streamName(imageStream string) string {
+ if imageStream == "" {
+ return "default"
+ }
+ return imageStream
+}
+
func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
cfg, err := config.ReadCustomConfig()
if err != nil {
@@ -167,6 +175,7 @@ func toMachineFormat(vms []*machine.ListResponse) ([]*machineReporter, error) {
response.Running = vm.Running
response.LastUp = strTime(vm.LastUp)
response.Created = strTime(vm.CreatedAt)
+ response.Stream = streamName(vm.Stream)
response.VMType = vm.VMType
response.CPUs = vm.CPUs
response.Memory = strUint(vm.Memory * units.MiB)
diff --git a/cmd/podman/machine/stop.go b/cmd/podman/machine/stop.go
index 76ba85601..75666f734 100644
--- a/cmd/podman/machine/stop.go
+++ b/cmd/podman/machine/stop.go
@@ -3,6 +3,8 @@
package machine
import (
+ "fmt"
+
"github.com/containers/podman/v3/cmd/podman/registry"
"github.com/containers/podman/v3/pkg/machine"
"github.com/containers/podman/v3/pkg/machine/qemu"
@@ -46,5 +48,9 @@ func stop(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
- return vm.Stop(vmName, machine.StopOptions{})
+ if err := vm.Stop(vmName, machine.StopOptions{}); err != nil {
+ return err
+ }
+ fmt.Printf("Machine %q stopped successfully\n", vmName)
+ return nil
}
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index e6869efd3..581b29113 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -80,6 +80,14 @@ func init() {
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
_ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver)
+ logOptFlagName := "log-opt"
+ flags.StringSliceVar(
+ &kubeOptions.LogOptions,
+ logOptFlagName, []string{},
+ "Logging driver options",
+ )
+ _ = kubeCmd.RegisterFlagCompletionFunc(logOptFlagName, common.AutocompleteLogOpt)
+
flags.BoolVar(&kubeOptions.NoHosts, "no-hosts", false, "Do not create /etc/hosts within the pod's containers, instead use the version from the image")
flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go
index 7c2c72171..7399dd029 100644
--- a/cmd/podman/pods/create.go
+++ b/cmd/podman/pods/create.go
@@ -11,7 +11,6 @@ import (
"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"
@@ -47,7 +46,8 @@ var (
var (
createOptions entities.PodCreateOptions
- infraOptions entities.ContainerCreateOptions
+ infraOptions = entities.NewInfraContainerCreateOptions()
+ infraImage string
labels, labelFile []string
podIDFile string
replace bool
@@ -61,7 +61,6 @@ func init() {
})
flags := createCommand.Flags()
flags.SetInterspersed(false)
- infraOptions.IsInfra = true
common.DefineCreateFlags(createCommand, &infraOptions, true)
common.DefineNetFlags(createCommand)
@@ -72,7 +71,7 @@ func init() {
_ = createCommand.RegisterFlagCompletionFunc(nameFlagName, completion.AutocompleteNone)
infraImageFlagName := "infra-image"
- flags.String(infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod")
+ flags.StringVar(&infraImage, infraImageFlagName, containerConfig.Engine.InfraImage, "The image of the infra container to associate with the pod")
_ = createCommand.RegisterFlagCompletionFunc(infraImageFlagName, common.AutocompleteImages)
podIDFileFlagName := "pod-id-file"
@@ -110,14 +109,14 @@ func create(cmd *cobra.Command, args []string) error {
return errors.Wrapf(err, "unable to process labels")
}
- imageName = config.DefaultInfraImage
+ imageName = infraImage
img := imageName
if !createOptions.Infra {
if cmd.Flag("no-hosts").Changed {
return fmt.Errorf("cannot specify no-hosts without an infra container")
}
flags := cmd.Flags()
- createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false)
+ createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra)
if err != nil {
return err
}
@@ -134,7 +133,7 @@ func create(cmd *cobra.Command, args []string) error {
} else {
// reassign certain options for lbpod api, these need to be populated in spec
flags := cmd.Flags()
- infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, false)
+ infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, createOptions.Infra)
if err != nil {
return err
}
@@ -151,14 +150,6 @@ func create(cmd *cobra.Command, args []string) error {
return err
}
}
- if cmd.Flag("infra-image").Changed {
- // Only send content to server side if user changed defaults
- img, err = cmd.Flags().GetString("infra-image")
- imageName = img
- if err != nil {
- return err
- }
- }
podName = createOptions.Name
err = common.ContainerToPodOptions(&infraOptions, &createOptions)
if err != nil {
@@ -242,16 +233,6 @@ func create(cmd *cobra.Command, args []string) error {
}
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
diff --git a/cmd/podman/root.go b/cmd/podman/root.go
index 6da34050e..418a70e1e 100644
--- a/cmd/podman/root.go
+++ b/cmd/podman/root.go
@@ -357,6 +357,11 @@ func rootFlags(cmd *cobra.Command, opts *entities.PodmanConfig) {
pFlags.StringVar(&cfg.Engine.Namespace, namespaceFlagName, cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
_ = cmd.RegisterFlagCompletionFunc(namespaceFlagName, completion.AutocompleteNone)
+ networkBackendFlagName := "network-backend"
+ pFlags.StringVar(&cfg.Network.NetworkBackend, networkBackendFlagName, cfg.Network.NetworkBackend, `Network backend to use ("cni"|"netavark")`)
+ _ = cmd.RegisterFlagCompletionFunc(networkBackendFlagName, common.AutocompleteNetworkBackend)
+ pFlags.MarkHidden(networkBackendFlagName)
+
rootFlagName := "root"
pFlags.StringVar(&cfg.Engine.StaticDir, rootFlagName, "", "Path to the root directory in which data, including images, is stored")
_ = cmd.RegisterFlagCompletionFunc(rootFlagName, completion.AutocompleteDefault)
diff --git a/cmd/podman/system/connection/add.go b/cmd/podman/system/connection/add.go
index 290b3c245..ee237d7d0 100644
--- a/cmd/podman/system/connection/add.go
+++ b/cmd/podman/system/connection/add.go
@@ -226,12 +226,7 @@ func getUDS(cmd *cobra.Command, uri *url.URL, iden string) (string, error) {
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)
+ infoJSON, err := ExecRemoteCommand(dial, podman+" info --format=json")
if err != nil {
return "", err
}
diff --git a/cmd/podman/system/connection/list.go b/cmd/podman/system/connection/list.go
index a3290e3d6..f1f7657ad 100644
--- a/cmd/podman/system/connection/list.go
+++ b/cmd/podman/system/connection/list.go
@@ -53,10 +53,6 @@ func list(cmd *cobra.Command, _ []string) error {
return err
}
- if len(cfg.Engine.ServiceDestinations) == 0 {
- return nil
- }
-
hdrs := []map[string]string{{
"Identity": "Identity",
"Name": "Name",
diff --git a/cmd/podman/system/connection/remove.go b/cmd/podman/system/connection/remove.go
index 73bae4994..ffbea76c5 100644
--- a/cmd/podman/system/connection/remove.go
+++ b/cmd/podman/system/connection/remove.go
@@ -5,14 +5,14 @@ import (
"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/pkg/errors"
"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),
+ Use: "remove [options] NAME",
Aliases: []string{"rm"},
Long: `Delete named destination from podman configuration`,
Short: "Delete named destination",
@@ -21,6 +21,10 @@ var (
Example: `podman system connection remove devl
podman system connection rm devl`,
}
+
+ rmOpts = struct {
+ All bool
+ }{}
)
func init() {
@@ -28,14 +32,31 @@ func init() {
Command: rmCmd,
Parent: system.ConnectionCmd,
})
+
+ flags := rmCmd.Flags()
+ flags.BoolVarP(&rmOpts.All, "all", "a", false, "Remove all connections")
}
-func rm(_ *cobra.Command, args []string) error {
+func rm(cmd *cobra.Command, args []string) error {
cfg, err := config.ReadCustomConfig()
if err != nil {
return err
}
+ if rmOpts.All {
+ if cfg.Engine.ServiceDestinations != nil {
+ for k := range cfg.Engine.ServiceDestinations {
+ delete(cfg.Engine.ServiceDestinations, k)
+ }
+ }
+ cfg.Engine.ActiveService = ""
+ return cfg.Write()
+ }
+
+ if len(args) != 1 {
+ return errors.New("accepts 1 arg(s), received 0")
+ }
+
if cfg.Engine.ServiceDestinations != nil {
delete(cfg.Engine.ServiceDestinations, args[0])
}
diff --git a/cmd/podman/system/connection/shared.go b/cmd/podman/system/connection/shared.go
index 3fd7c59fb..714ae827d 100644
--- a/cmd/podman/system/connection/shared.go
+++ b/cmd/podman/system/connection/shared.go
@@ -9,10 +9,10 @@ import (
// 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) {
+func ExecRemoteCommand(dial *ssh.Client, run string) ([]byte, error) {
sess, err := dial.NewSession() // new ssh client session
if err != nil {
- return "", err
+ return nil, err
}
defer sess.Close()
@@ -21,8 +21,7 @@ func ExecRemoteCommand(dial *ssh.Client, run string) (string, error) {
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())
+ return nil, errors.Wrapf(err, bufferErr.String())
}
- out := buffer.String() // output from command
- return out, nil
+ return buffer.Bytes(), nil
}
diff --git a/cmd/podman/system/unshare.go b/cmd/podman/system/unshare.go
index 50230609e..9b777dd8f 100644
--- a/cmd/podman/system/unshare.go
+++ b/cmd/podman/system/unshare.go
@@ -10,6 +10,7 @@ import (
"github.com/containers/podman/v3/pkg/rootless"
"github.com/pkg/errors"
"github.com/spf13/cobra"
+ "github.com/spf13/pflag"
)
var (
@@ -34,7 +35,14 @@ func init() {
})
flags := unshareCommand.Flags()
flags.SetInterspersed(false)
- flags.BoolVar(&unshareOptions.RootlessCNI, "rootless-cni", false, "Join the rootless network namespace used for CNI networking")
+ flags.BoolVar(&unshareOptions.RootlessNetNS, "rootless-netns", false, "Join the rootless network namespace used for CNI and netavark networking")
+ // backwards compat still allow --rootless-cni
+ flags.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
+ if name == "rootless-cni" {
+ name = "rootless-netns"
+ }
+ return pflag.NormalizedName(name)
+ })
}
func unshare(cmd *cobra.Command, args []string) error {
diff --git a/cmd/podman/system/version.go b/cmd/podman/system/version.go
index 4502b156c..3443978d6 100644
--- a/cmd/podman/system/version.go
+++ b/cmd/podman/system/version.go
@@ -20,7 +20,7 @@ var (
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,
}
@@ -67,7 +67,7 @@ func version(cmd *cobra.Command, args []string) error {
}
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)
+ row = strings.ReplaceAll(row, ".Server.", ".")
tmpl, err := report.NewTemplate("version 1.0.0").Parse(row)
if err != nil {
return err
diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go
index fc07a6acc..6b5425a69 100644
--- a/cmd/podman/validate/args.go
+++ b/cmd/podman/validate/args.go
@@ -27,7 +27,8 @@ func SubCommandExists(cmd *cobra.Command, args []string) error {
}
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())
+ cmd.Help()
+ return errors.Errorf("missing command '%[1]s COMMAND'", cmd.CommandPath())
}
// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag
diff --git a/cmd/rootlessport/main.go b/cmd/rootlessport/main.go
index feb9f5c06..e691ce2fc 100644
--- a/cmd/rootlessport/main.go
+++ b/cmd/rootlessport/main.go
@@ -10,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "strings"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/podman/v3/libpod/network/types"
@@ -289,25 +290,30 @@ func handler(ctx context.Context, conn io.Reader, pm rkport.Manager) error {
return nil
}
-func exposePorts(pm rkport.Manager, portMappings []types.OCICNIPortMapping, childIP string) error {
+func exposePorts(pm rkport.Manager, portMappings []types.PortMapping, childIP string) error {
ctx := context.TODO()
- for _, i := range portMappings {
- hostIP := i.HostIP
- if hostIP == "" {
- hostIP = "0.0.0.0"
- }
- spec := rkport.Spec{
- Proto: i.Protocol,
- ParentIP: hostIP,
- ParentPort: int(i.HostPort),
- ChildPort: int(i.ContainerPort),
- ChildIP: childIP,
- }
- if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
- return err
- }
- if _, err := pm.AddPort(ctx, spec); err != nil {
- return err
+ for _, port := range portMappings {
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ hostIP := port.HostIP
+ if hostIP == "" {
+ hostIP = "0.0.0.0"
+ }
+ for i := uint16(0); i < port.Range; i++ {
+ spec := rkport.Spec{
+ Proto: protocol,
+ ParentIP: hostIP,
+ ParentPort: int(port.HostPort + i),
+ ChildPort: int(port.ContainerPort + i),
+ ChildIP: childIP,
+ }
+ if err := rkportutil.ValidatePortSpec(spec, nil); err != nil {
+ return err
+ }
+ if _, err := pm.AddPort(ctx, spec); err != nil {
+ return err
+ }
+ }
}
}
return nil
diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh
index 9a7bfba8f..cff8f4b3f 100644
--- a/contrib/cirrus/lib.sh
+++ b/contrib/cirrus/lib.sh
@@ -143,6 +143,8 @@ setup_rootless() {
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
@@ -164,29 +166,42 @@ setup_rootless() {
useradd -g $rootless_gid -u $rootless_uid --no-user-group --create-home $ROOTLESS_USER
chown -R $ROOTLESS_USER:$ROOTLESS_USER "$GOPATH" "$GOSRC"
- msg "creating ssh key pair for $USER"
- [[ -r "$HOME/.ssh/id_rsa" ]] || \
- ssh-keygen -P "" -f "$HOME/.ssh/id_rsa"
+ mkdir -p "$HOME/.ssh" "/home/$ROOTLESS_USER/.ssh"
- msg "Allowing ssh key for $ROOTLESS_USER"
- (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"
- # Makes debugging easier
- cat /root/.ssh/authorized_keys >> "/home/$ROOTLESS_USER/.ssh/authorized_keys"
+ msg "Creating ssh key pairs"
+ [[ -r "$HOME/.ssh/id_rsa" ]] || \
+ ssh-keygen -t rsa -P "" -f "$HOME/.ssh/id_rsa"
+ ssh-keygen -t ed25519 -P "" -f "/home/$ROOTLESS_USER/.ssh/id_ed25519"
+ ssh-keygen -t rsa -P "" -f "/home/$ROOTLESS_USER/.ssh/id_rsa"
- msg "Configuring subuid and subgid"
- grep -q "${ROOTLESS_USER}" /etc/subuid || \
- echo "${ROOTLESS_USER}:$[rootless_uid * 100]:65536" | \
- tee -a /etc/subuid >> /etc/subgid
+ msg "Setup authorized_keys"
+ cat $HOME/.ssh/*.pub /home/$ROOTLESS_USER/.ssh/*.pub >> $HOME/.ssh/authorized_keys
+ cat $HOME/.ssh/*.pub /home/$ROOTLESS_USER/.ssh/*.pub >> /home/$ROOTLESS_USER/.ssh/authorized_keys
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
+ lilto systemctl is-active sshd
+
+ msg "Configure ssh file permissions"
+ chmod -R 700 "$HOME/.ssh"
+ chmod -R 700 "/home/$ROOTLESS_USER/.ssh"
+ chown -R $ROOTLESS_USER:$ROOTLESS_USER "/home/$ROOTLESS_USER/.ssh"
+
+ msg " setup known_hosts for $USER"
+ ssh -q root@localhost \
+ -o UserKnownHostsFile=/root/.ssh/known_hosts \
+ -o UpdateHostKeys=yes \
+ -o StrictHostKeyChecking=no \
+ -o CheckHostIP=no \
+ true
+
+ msg " setup known_hosts for $ROOTLESS_USER"
+ su $ROOTLESS_USER -c "ssh -q $ROOTLESS_USER@localhost \
+ -o UserKnownHostsFile=/home/$ROOTLESS_USER/.ssh/known_hosts \
+ -o UpdateHostKeys=yes \
+ -o StrictHostKeyChecking=no \
+ -o CheckHostIP=no \
+ true"
}
install_test_configs() {
diff --git a/contrib/cirrus/pr-should-include-tests b/contrib/cirrus/pr-should-include-tests
index 4b6329311..8103df41d 100755
--- a/contrib/cirrus/pr-should-include-tests
+++ b/contrib/cirrus/pr-should-include-tests
@@ -12,9 +12,6 @@ fi
if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.NEW.TESTS.NEEDED ]]; then
exit 0
fi
-if [[ "${CIRRUS_CHANGE_MESSAGE}" =~ NO.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}
@@ -52,14 +49,11 @@ if [[ -z "$filtered_changes" ]]; then
exit 0
fi
-# One last chance: perhaps the developer included the magic '[NO (NEW) 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 NEW TESTS NEEDED]'; then
exit 0
fi
-if git log --format=%B ${base}..${head} | fgrep '[NO TESTS NEEDED]'; then
- exit 0
-fi
cat <<EOF
$(basename $0): PR does not include changes in the 'tests' directory
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh
index 22a66dd08..8ef2a6e64 100755
--- a/contrib/cirrus/runner.sh
+++ b/contrib/cirrus/runner.sh
@@ -117,6 +117,7 @@ exec_container() {
set -x
# shellcheck disable=SC2154
exec podman run --rm --privileged --net=host --cgroupns=host \
+ -v `mktemp -d -p /var/tmp`:/tmp:Z \
-v /dev/fuse:/dev/fuse \
-v "$GOPATH:$GOPATH:Z" \
--workdir "$GOSRC" \
diff --git a/contrib/podmanimage/stable/Dockerfile b/contrib/podmanimage/stable/Dockerfile
index f980d15ed..7950269d2 100644
--- a/contrib/podmanimage/stable/Dockerfile
+++ b/contrib/podmanimage/stable/Dockerfile
@@ -19,14 +19,15 @@ 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
-RUN mkdir -p /home/podman/.local/share/containers
+RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
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
+# 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 --git a/contrib/podmanimage/testing/Dockerfile b/contrib/podmanimage/testing/Dockerfile
index 58b11a20b..03da05806 100644
--- a/contrib/podmanimage/testing/Dockerfile
+++ b/contrib/podmanimage/testing/Dockerfile
@@ -19,14 +19,15 @@ 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
-RUN mkdir -p /home/podman/.local/share/containers
+RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
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
+# 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 --git a/contrib/podmanimage/upstream/Dockerfile b/contrib/podmanimage/upstream/Dockerfile
index 75de947ea..89ec6e39b 100644
--- a/contrib/podmanimage/upstream/Dockerfile
+++ b/contrib/podmanimage/upstream/Dockerfile
@@ -68,14 +68,15 @@ 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
-RUN mkdir -p /home/podman/.local/share/containers
+RUN mkdir -p /home/podman/.local/share/containers; chown podman:podman -R /home/podman
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
+# 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 --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in
index 295a953ef..474add1af 100644
--- a/contrib/spec/podman.spec.in
+++ b/contrib/spec/podman.spec.in
@@ -361,6 +361,7 @@ Man pages for the %{name} commands
Summary: Tests for %{name}
Requires: %{name} = %{epoch}:%{version}-%{release}
+Requires: gnupg
Requires: bats
Requires: jq
Requires: skopeo
@@ -528,6 +529,7 @@ export GOPATH=%{buildroot}/%{gopath}:$(pwd)/vendor:%{gopath}
%{_usr}/lib/tmpfiles.d/podman.conf
%dir %{_libexecdir}/%{name}
%{_libexecdir}/%{name}/rootlessport
+%{_libexecdir}/%{name}/pause/pause
%if 0%{?with_devel}
%files -n libpod-devel -f devel.file-list
diff --git a/docs/source/markdown/podman-auto-update.1.md b/docs/source/markdown/podman-auto-update.1.md
index 4952e09dc..755f46d70 100644
--- a/docs/source/markdown/podman-auto-update.1.md
+++ b/docs/source/markdown/podman-auto-update.1.md
@@ -141,4 +141,4 @@ $ podman auto-update
```
## SEE ALSO
-**[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)
+**[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)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index fd36166b6..40bd7d8aa 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -241,7 +241,7 @@ Note: if _host_device_ is a symbolic link then it will be resolved first.
The container will only store the major and minor numbers of the host device.
Note: if the user only has access rights via a group, accessing the device
-from inside a rootless container will fail. The **crun**(1) runtime offers a
+from inside a rootless container will fail. The **[crun(1)](https://github.com/containers/crun/tree/main/crun.1.md)** runtime offers a
workaround for this by adding the option
#### **--annotation run.oci.keep_original_groups=1**.
@@ -631,7 +631,7 @@ Sets the configuration for user namespaces when handling `RUN` instructions.
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*
@@ -774,6 +774,14 @@ 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.
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
@@ -1013,7 +1021,7 @@ If you are using `useradd` within your build script, you should pass the
useradd to stop creating the lastlog file.
## SEE ALSO
-podman(1), buildah(1), containers-certs.d(5), containers-registries.conf(5), crun(8), runc(8), useradd(8), podman-ps(1), podman-rm(1), Containerfile(5), containerignore(5)
+**[podman(1)](podman.1.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.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.conf.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[useradd(8)](https://www.unix.com/man-page/redhat/8/useradd)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[Containerfile(5)](https://github.com/containers/buildah/blob/main/docs/Containerfile.5.md)**, **[containerignore(5)](https://github.com/containers/buildah/blob/main/docs/containerignore.5.md)**
## HISTORY
Aug 2020, Additional options and .containerignore added by Dan Walsh `<dwalsh@redhat.com>`
diff --git a/docs/source/markdown/podman-completion.1.md b/docs/source/markdown/podman-completion.1.md
index 639332365..538bb9e60 100644
--- a/docs/source/markdown/podman-completion.1.md
+++ b/docs/source/markdown/podman-completion.1.md
@@ -40,7 +40,7 @@ Shell completion needs to be already enabled in the environment. The following c
**echo "autoload -U compinit; compinit" >> ~/.zshrc**
To make it available for all zsh sessions run:\
-**podman completion -f "${fpath[1]}/_podman zsh"**
+**podman completion -f "${fpath[1]}/_podman" zsh**
Once the shell is reloaded the auto-completion should be working.
@@ -61,4 +61,4 @@ 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**.
## SEE ALSO
-**[podman(1)](podman.1.md)**, zsh(1), fish(1), powershell(1)
+**[podman(1)](podman.1.md)**, **zsh(1)**, **fish(1)**, **powershell(1)**
diff --git a/docs/source/markdown/podman-container-checkpoint.1.md b/docs/source/markdown/podman-container-checkpoint.1.md
index 56fd848ac..1faa40a94 100644
--- a/docs/source/markdown/podman-container-checkpoint.1.md
+++ b/docs/source/markdown/podman-container-checkpoint.1.md
@@ -68,6 +68,40 @@ Dump the *container's* memory information only, leaving the *container* running.
operations will supersede prior dumps. It only works on `runc 1.0-rc3` or `higher`.\
The default is **false**.
+#### **--print-stats**
+
+Print out statistics about checkpointing the container(s). The output is
+rendered in a JSON array and contains information about how much time different
+checkpoint operations required. Many of the checkpoint statistics are created
+by CRIU and just passed through to Podman. The following information is provided
+in the JSON array:
+
+- **podman_checkpoint_duration**: Overall time (in microseconds) needed to create
+ all checkpoints.
+
+- **runtime_checkpoint_duration**: Time (in microseconds) the container runtime
+ needed to create the checkpoint.
+
+- **freezing_time**: Time (in microseconds) CRIU needed to pause (freeze) all
+ processes in the container (measured by CRIU).
+
+- **frozen_time**: Time (in microseconds) all processes in the container were
+ paused (measured by CRIU).
+
+- **memdump_time**: Time (in microseconds) needed to extract all required memory
+ pages from all container processes (measured by CRIU).
+
+- **memwrite_time**: Time (in microseconds) needed to write all required memory
+ pages to the corresponding checkpoint image files (measured by CRIU).
+
+- **pages_scanned**: Number of memory pages scanned to determine if they need
+ to be checkpointed (measured by CRIU).
+
+- **pages_written**: Number of memory pages actually written to the checkpoint
+ image files (measured by CRIU).
+
+The default is **false**.
+
#### **--tcp-established**
Checkpoint a *container* with established TCP connections. If the checkpoint
@@ -106,7 +140,7 @@ Dump the container's memory information of the latest container into an archive
```
## SEE ALSO
-**[podman(1)](podman.1.md)**, **[podman-container-restore(1)](podman-container-restore.1.md)**
+**[podman(1)](podman.1.md)**, **[podman-container-restore(1)](podman-container-restore.1.md)**, **criu(8)**
## HISTORY
September 2018, Originally compiled by Adrian Reber <areber@redhat.com>
diff --git a/docs/source/markdown/podman-container-cleanup.1.md b/docs/source/markdown/podman-container-cleanup.1.md
index 29e34f1cf..e58519ac3 100644
--- a/docs/source/markdown/podman-container-cleanup.1.md
+++ b/docs/source/markdown/podman-container-cleanup.1.md
@@ -51,7 +51,7 @@ $ podman container cleanup mywebserver myflaskserver 860a4b23
```
## SEE ALSO
-**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, conmon(8)
+**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
## HISTORY
Jun 2018, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-container-inspect.1.md b/docs/source/markdown/podman-container-inspect.1.md
index 72b7cef3b..54b3cb2ae 100644
--- a/docs/source/markdown/podman-container-inspect.1.md
+++ b/docs/source/markdown/podman-container-inspect.1.md
@@ -312,7 +312,7 @@ $ podman container inspect --latest --format {{.EffectiveCaps}}
```
## SEE ALSO
-**[podman(1)](podman.1.md)**,**[podman-container(1)](podman-container.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)**
+**[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 <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-container-prune.1.md b/docs/source/markdown/podman-container-prune.1.md
index 0f679ceff..494500ca1 100644
--- a/docs/source/markdown/podman-container-prune.1.md
+++ b/docs/source/markdown/podman-container-prune.1.md
@@ -71,7 +71,7 @@ Are you sure you want to continue? [y/N] y
```
## SEE ALSO
-**[podman(1)](podman.1.md)**, **[podman-ps](podman-ps.1.md)**
+**[podman(1)](podman.1.md)**, **[podman-ps(1)](podman-ps.1.md)**
## HISTORY
December 2018, Originally compiled by Brent Baude <bbaude@redhat.com>\
diff --git a/docs/source/markdown/podman-container-restore.1.md b/docs/source/markdown/podman-container-restore.1.md
index 856008cc0..962770ea0 100644
--- a/docs/source/markdown/podman-container-restore.1.md
+++ b/docs/source/markdown/podman-container-restore.1.md
@@ -102,6 +102,30 @@ from (see **[podman pod create --share](podman-pod-create.1.md#--share)**).
This option requires at least CRIU 3.16.
+#### **--print-stats**
+
+Print out statistics about restoring the container(s). The output is
+rendered in a JSON array and contains information about how much time different
+restore operations required. Many of the restore statistics are created
+by CRIU and just passed through to Podman. The following information is provided
+in the JSON array:
+
+- **podman_restore_duration**: Overall time (in microseconds) needed to restore
+ all checkpoints.
+
+- **runtime_restore_duration**: Time (in microseconds) the container runtime
+ needed to restore the checkpoint.
+
+- **forking_time**: Time (in microseconds) CRIU needed to create (fork) all
+ processes in the restored container (measured by CRIU).
+
+- **restore_time**: Time (in microseconds) CRIU needed to restore all processes
+ in the container (measured by CRIU).
+
+- **pages_restored**: Number of memory pages restored (measured by CRIU).
+
+The default is **false**.
+
#### **--publish**, **-p**=*port*
Replaces the ports that the *container* publishes, as configured during the
@@ -137,7 +161,7 @@ $ podman run --rm -p 2345:80 -d webserver
```
## SEE ALSO
-**[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)**
+**[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)**, **criu(8)**
## HISTORY
September 2018, Originally compiled by Adrian Reber <areber@redhat.com>
diff --git a/docs/source/markdown/podman-container-runlabel.1.md b/docs/source/markdown/podman-container-runlabel.1.md
index ec67a2687..7078b07e4 100644
--- a/docs/source/markdown/podman-container-runlabel.1.md
+++ b/docs/source/markdown/podman-container-runlabel.1.md
@@ -84,7 +84,7 @@ $ podman container runlabel --display run foobar
```
## SEE ALSO
-podman(1), crun(1), runc(1), containers-auth.json(5), containers-certs.d(5), containers-registries.conf(5)
+**[podman(1)](podman.1.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**, **[containers-auth.json(5)](https://github.com/containers/image/blob/main/docs/containers-auth.json.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**
## HISTORY
August 2021, Refinements by Valentin Rothberg (rothberg at redhat dot com)
diff --git a/docs/source/markdown/podman-container.1.md b/docs/source/markdown/podman-container.1.md
index c950347a9..540ebc723 100644
--- a/docs/source/markdown/podman-container.1.md
+++ b/docs/source/markdown/podman-container.1.md
@@ -48,4 +48,4 @@ The container command allows you to manage containers
| wait | [podman-wait(1)](podman-wait.1.md) | Wait on one or more containers to stop and print their exit codes. |
## SEE ALSO
-podman, podman-exec, podman-run
+**[podman(1)](podman.1.md)**, **[podman-exec(1)](podman-exec.1.md)**, **[podman-run(1)](podman-run.1.md)**
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index 0d4366dbe..b0d7b8f12 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -487,18 +487,6 @@ Default is to create a private IPC namespace (POSIX SysV IPC) for the container
`host`: use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
`ns:<path>` path to an IPC namespace to join.
-#### **--kernel-memory**=*number[unit]*
-
-Kernel memory limit (format: `<number>[<unit>]`, where unit = b (bytes), k (kilobytes), m (megabytes), or g (gigabytes))
-
-Constrains the kernel memory available to a container. If a limit of 0
-is specified (not using `--kernel-memory`), the container's kernel memory
-is not limited. If you specify a limit, it may be rounded up to a multiple
-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)
@@ -541,12 +529,6 @@ It supports the same keys as **podman inspect --format**.
This option is currently supported only by the **journald** log driver.
-`--log-opt tag="{{.ImageName}}"`
-
-It supports the same keys as `podman inspect --format`.
-
-It is currently supported only by the journald log driver.
-
#### **--mac-address**=*address*
Container MAC address (e.g. 92:d0:c6:0a:29:33)
@@ -656,6 +638,16 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ Options specific to devpts:
+
+ · uid: UID of the file owner (default 0).
+
+ · gid: GID of the file owner (default 0).
+
+ · mode: permission mask for the file (default 600).
+
+ · max: maximum number of PTYs (default 1048576).
+
#### **--name**=*name*
Assign a name to the container
@@ -1257,6 +1249,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.
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
@@ -1485,7 +1486,7 @@ Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released.
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.
@@ -1536,8 +1537,7 @@ page.
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-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), **personality**(2).
+**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
## HISTORY
October 2017, converted from Docker documentation to Podman by Dan Walsh for Podman `<dwalsh@redhat.com>`
diff --git a/docs/source/markdown/podman-events.1.md b/docs/source/markdown/podman-events.1.md
index 3c8b46884..7ca52e3e9 100644
--- a/docs/source/markdown/podman-events.1.md
+++ b/docs/source/markdown/podman-events.1.md
@@ -155,7 +155,7 @@ $ podman events --format json
```
## SEE ALSO
-podman(1), containers.conf(5)
+**[podman(1)](podman.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**
## HISTORY
March 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-exec.1.md b/docs/source/markdown/podman-exec.1.md
index d739e1f5d..c539e987b 100644
--- a/docs/source/markdown/podman-exec.1.md
+++ b/docs/source/markdown/podman-exec.1.md
@@ -114,7 +114,7 @@ $ podman exec --user root ctrID ls
```
## SEE ALSO
-podman(1), podman-run(1)
+**[podman(1)](podman.1.md)**, **[podman-run(1)](podman-run.1.md)**
## HISTORY
December 2017, Originally compiled by Brent Baude<bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-export.1.md b/docs/source/markdown/podman-export.1.md
index 63989a4db..b2ad4e907 100644
--- a/docs/source/markdown/podman-export.1.md
+++ b/docs/source/markdown/podman-export.1.md
@@ -41,7 +41,7 @@ $ podman export 883504668ec465463bc0fe7e63d53154ac3b696ea8d7b233748918664ea90e57
```
## SEE ALSO
-podman(1), podman-import(1)
+**[podman(1)](podman.1.md)**, **[podman-import(1)](podman-import.1.md)**
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-generate-kube.1.md b/docs/source/markdown/podman-generate-kube.1.md
index 9ae3941ec..41545455e 100644
--- a/docs/source/markdown/podman-generate-kube.1.md
+++ b/docs/source/markdown/podman-generate-kube.1.md
@@ -54,13 +54,7 @@ spec:
- 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
@@ -77,14 +71,14 @@ spec:
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: {}
```
@@ -106,31 +100,18 @@ spec:
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:
@@ -158,31 +139,18 @@ spec:
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
@@ -210,22 +178,9 @@ spec:
- 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: {}
@@ -242,7 +197,6 @@ spec:
- name: "8050"
nodePort: 31269
port: 8050
- protocol: TCP
targetPort: 0
selector:
app: demoweb
@@ -252,7 +206,7 @@ status:
```
## SEE ALSO
-podman(1), podman-container(1), podman-pod(1), podman-play-kube(1)
+**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-play-kube(1)](podman-play-kube.1.md)**
## HISTORY
December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/source/markdown/podman-generate-systemd.1.md b/docs/source/markdown/podman-generate-systemd.1.md
index fe5d1e46c..356ac0629 100644
--- a/docs/source/markdown/podman-generate-systemd.1.md
+++ b/docs/source/markdown/podman-generate-systemd.1.md
@@ -241,7 +241,7 @@ CONTAINER ID IMAGE COMMAND CREATED STATUS
bb310a0780ae docker.io/library/alpine:latest /bin/sh 3 minutes ago Created busy_moser
```
## SEE ALSO
-[**podman**(1)](podman.1.md), [**podman-container**(1)](podman-container.1.md), **systemctl**(1), **systemd.unit**(5), **systemd.service**(5), **conmon**(8).
+**[podman(1)](podman.1.md)**, **[podman-container(1)](podman-container.1.md)**, **systemctl(1)**, **systemd.unit(5)**, **systemd.service(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
## HISTORY
April 2020, Updated details and added use case to use generated .service files as root and non-root, by Sujil Shah (sushah at redhat dot com)
diff --git a/docs/source/markdown/podman-generate.1.md b/docs/source/markdown/podman-generate.1.md
index c2060d1a4..415a53875 100644
--- a/docs/source/markdown/podman-generate.1.md
+++ b/docs/source/markdown/podman-generate.1.md
@@ -18,4 +18,4 @@ The generate command will create structured output (like YAML) based on a contai
## SEE ALSO
-podman, podman-pod, podman-container
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-container(1)](podman-container.1.md)**
diff --git a/docs/source/markdown/podman-healthcheck-run.1.md b/docs/source/markdown/podman-healthcheck-run.1.md
index fa10d5dca..dbaf87a0b 100644
--- a/docs/source/markdown/podman-healthcheck-run.1.md
+++ b/docs/source/markdown/podman-healthcheck-run.1.md
@@ -33,7 +33,7 @@ $ podman healthcheck run mywebapp
```
## SEE ALSO
-podman-healthcheck(1)
+**[podman(1)](podman.1.md)**, **[podman-healthcheck(1)](podman-healthcheck.1.md)**
## HISTORY
Feb 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-healthcheck.1.md b/docs/source/markdown/podman-healthcheck.1.md
index 91c3e4345..eae71eba9 100644
--- a/docs/source/markdown/podman-healthcheck.1.md
+++ b/docs/source/markdown/podman-healthcheck.1.md
@@ -16,7 +16,7 @@ podman healthcheck is a set of subcommands that manage container healthchecks
| run | [podman-healthcheck-run(1)](podman-healthcheck-run.1.md) | Run a container healthcheck |
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
Feb 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-history.1.md b/docs/source/markdown/podman-history.1.md
index c9d01f105..4ab2547ea 100644
--- a/docs/source/markdown/podman-history.1.md
+++ b/docs/source/markdown/podman-history.1.md
@@ -91,7 +91,7 @@ $ podman history --format json debian
```
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-image-diff.1.md b/docs/source/markdown/podman-image-diff.1.md
index 933cd0f9a..c5c62c751 100644
--- a/docs/source/markdown/podman-image-diff.1.md
+++ b/docs/source/markdown/podman-image-diff.1.md
@@ -48,7 +48,7 @@ $ podman diff --format json redis:old redis:alpine
```
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**, **[podman-image(1)](podman-image.1.md)**
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
diff --git a/docs/source/markdown/podman-image-exists.1.md b/docs/source/markdown/podman-image-exists.1.md
index 5d5cb1647..b585d16f1 100644
--- a/docs/source/markdown/podman-image-exists.1.md
+++ b/docs/source/markdown/podman-image-exists.1.md
@@ -37,7 +37,7 @@ $
```
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**, **[podman-image(1)](podman-image.1.md)**
## HISTORY
November 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/source/markdown/podman-image-inspect.1.md b/docs/source/markdown/podman-image-inspect.1.md
index a4f81dfc0..eb8d75786 100644
--- a/docs/source/markdown/podman-image-inspect.1.md
+++ b/docs/source/markdown/podman-image-inspect.1.md
@@ -99,7 +99,7 @@ $ podman image inspect --format '{{ .Id }}' fedora
```
## SEE ALSO
-**[podman(1)](podman.1.md)**,**[podman-image(1)](podman-image.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)**
+**[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 <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-image-mount.1.md b/docs/source/markdown/podman-image-mount.1.md
index 8cb9684a7..453266f8c 100644
--- a/docs/source/markdown/podman-image-mount.1.md
+++ b/docs/source/markdown/podman-image-mount.1.md
@@ -73,4 +73,4 @@ podman image mount --format json
```
## SEE ALSO
-podman(1), podman-image-umount(1), mount(8), podman-unshare(1)
+**[podman(1)](podman.1.md)**, **[podman-image(1)](podman-image.1.md)**, **[podman-image-umount(1)](podman-image-umount.1.md)**, **[podman-unshare(1)](podman-unshare.1.md)**, **mount(8)**
diff --git a/docs/source/markdown/podman-image-prune.1.md b/docs/source/markdown/podman-image-prune.1.md
index 493332ec0..153e3bfe8 100644
--- a/docs/source/markdown/podman-image-prune.1.md
+++ b/docs/source/markdown/podman-image-prune.1.md
@@ -103,7 +103,7 @@ f9f0a8a58c9e02a2b3250b88cc5c95b1e10245ca2c4161d19376580aaa90f55c
```
## SEE ALSO
-podman(1), podman-images
+**[podman(1)](podman.1.md)**, **[podman-images(1)](podman-images.1.md)**
## HISTORY
December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/source/markdown/podman-image-scp.1.md b/docs/source/markdown/podman-image-scp.1.md
index 420452a4d..d39882417 100644
--- a/docs/source/markdown/podman-image-scp.1.md
+++ b/docs/source/markdown/podman-image-scp.1.md
@@ -8,7 +8,7 @@ podman-image-scp - Securely copy an image from one host to another
## 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.
+Note: `::` is used to specify the image name depending on if you are saving or loading. Images can also be transferred from rootful to rootless storage on the same machine without using sshd. This feature is not supported on the remote client.
**podman image scp [GLOBAL OPTIONS]**
@@ -62,8 +62,36 @@ Storing signatures
Loaded image(s): docker.io/library/alpine:latest
```
+```
+$ sudo podman image scp root@localhost::alpine username@localhost::
+Copying blob e2eb06d8af82 done
+Copying config 696d33ca15 done
+Writing manifest to image destination
+Storing signatures
+Getting image source signatures
+Copying blob 5eb901baf107 skipped: already exists
+Copying config 696d33ca15 done
+Writing manifest to image destination
+Storing signatures
+Loaded image(s): docker.io/library/alpine:latest
+```
+
+```
+$ sudo podman image scp root@localhost::alpine
+Copying blob e2eb06d8af82 done
+Copying config 696d33ca15 done
+Writing manifest to image destination
+Storing signatures
+Getting image source signatures
+Copying blob 5eb901baf107
+Copying config 696d33ca15 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)
+**[podman(1)](podman.1.md)**, **[podman-load(1)](podman-load.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-remote(1)](podman-remote.1.md)**, **[podman-system-connection-add(1)](podman-system-connection-add.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-transports(5)](https://github.com/containers/image/blob/main/docs/containers-transports.5.md)**
## HISTORY
July 2021, Originally written by Charlie Doern <cdoern@redhat.com>
diff --git a/docs/source/markdown/podman-image-sign.1.md b/docs/source/markdown/podman-image-sign.1.md
index 616aab8ff..5f23bbfaf 100644
--- a/docs/source/markdown/podman-image-sign.1.md
+++ b/docs/source/markdown/podman-image-sign.1.md
@@ -23,6 +23,13 @@ Print usage statement.
Sign all the manifests of the multi-architecture image (default false).
+#### **--authfile**=*path*
+
+Path of the authentication file. Default is ${XDG\_RUNTIME\_DIR}/containers/auth.json
+
+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.
@@ -41,6 +48,8 @@ Sign the busybox image with the identity of foo@bar.com with a user's keyring an
sudo podman image sign --sign-by foo@bar.com --directory /tmp/signatures docker://privateregistry.example.com/foobar
+ sudo podman image sign --authfile=/tmp/foobar.json --sign-by foo@bar.com --directory /tmp/signatures docker://privateregistry.example.com/foobar
+
## RELATED CONFIGURATION
The write (and read) location for signatures is defined in YAML-based
@@ -61,7 +70,7 @@ the signature will be written into sub-directories of
the signature will be 'read' from that same location on a pull-related function.
## SEE ALSO
-containers-certs.d(5), containers-registries.d(5)
+**[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**, **[containers-registries.d(5)](https://github.com/containers/image/blob/main/docs/containers-registries.d.5.md)**
## HISTORY
November 2018, Originally compiled by Qi Wang (qiwan at redhat dot com)
diff --git a/docs/source/markdown/podman-image-tree.1.md b/docs/source/markdown/podman-image-tree.1.md
index fe36929cc..78f1b1004 100644
--- a/docs/source/markdown/podman-image-tree.1.md
+++ b/docs/source/markdown/podman-image-tree.1.md
@@ -82,7 +82,7 @@ Image Layers
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
Feb 2019, Originally compiled by Kunal Kushwaha `<kushwaha_kunal_v7@lab.ntt.co.jp>`
diff --git a/docs/source/markdown/podman-image-trust.1.md b/docs/source/markdown/podman-image-trust.1.md
index 6e2c2287a..61b8966a7 100644
--- a/docs/source/markdown/podman-image-trust.1.md
+++ b/docs/source/markdown/podman-image-trust.1.md
@@ -85,8 +85,7 @@ Display trust as JSON
sudo podman image trust show --json
## SEE ALSO
-
-containers-policy.json(5)
+**[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**
## HISTORY
January 2019, updated by Tom Sweeney (tsweeney at redhat dot com)
diff --git a/docs/source/markdown/podman-image-unmount.1.md b/docs/source/markdown/podman-image-unmount.1.md
index 2fedb8f58..71ec5488f 100644
--- a/docs/source/markdown/podman-image-unmount.1.md
+++ b/docs/source/markdown/podman-image-unmount.1.md
@@ -40,4 +40,4 @@ podman image unmount imageID1 imageID2 imageID3
podman image unmount --all
## SEE ALSO
-podman(1), podman-image-mount(1), podman-container-mount(1)
+**[podman(1)](podman.1.md)**, **[podman-image-mount(1)](podman-image-mount.1.md)**, **[podman-container-mount(1)](podman-container-mount.1.md)**
diff --git a/docs/source/markdown/podman-image.1.md b/docs/source/markdown/podman-image.1.md
index 01024519f..356369007 100644
--- a/docs/source/markdown/podman-image.1.md
+++ b/docs/source/markdown/podman-image.1.md
@@ -37,4 +37,4 @@ The image command allows you to manage images
| untag | [podman-untag(1)](podman-untag.1.md) | Removes one or more names from a locally-stored image. |
## SEE ALSO
-podman
+**[podman(1)](podman.1.md)**
diff --git a/docs/source/markdown/podman-images.1.md b/docs/source/markdown/podman-images.1.md
index a346b7810..8db291fb4 100644
--- a/docs/source/markdown/podman-images.1.md
+++ b/docs/source/markdown/podman-images.1.md
@@ -188,7 +188,7 @@ docker.io/library/alpine latest 3fd9065eaf02 5 months ago 4.41 MB
```
## SEE ALSO
-podman(1), containers-storage.conf(5)
+**[podman(1)](podman.1.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**
## HISTORY
March 2017, Originally compiled by Dan Walsh `<dwalsh@redhat.com>`
diff --git a/docs/source/markdown/podman-import.1.md b/docs/source/markdown/podman-import.1.md
index 989e23b74..462e9eacf 100644
--- a/docs/source/markdown/podman-import.1.md
+++ b/docs/source/markdown/podman-import.1.md
@@ -107,7 +107,7 @@ db65d991f3bbf7f31ed1064db9a6ced7652e3f8166c4736aa9133dadd3c7acb3
```
## SEE ALSO
-podman(1), podman-export(1)
+**[podman(1)](podman.1.md)**, **[podman-export(1)](podman-export.1.md)**
## HISTORY
November 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md
index 4f873248d..4f09913b8 100644
--- a/docs/source/markdown/podman-info.1.md
+++ b/docs/source/markdown/podman-info.1.md
@@ -290,4 +290,4 @@ map[registries:[docker.io quay.io registry.fedoraproject.org registry.access.red
```
## SEE ALSO
-podman(1), containers-registries.conf(5), containers-storage.conf(5)
+**[podman(1)](podman.1.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**
diff --git a/docs/source/markdown/podman-init.1.md b/docs/source/markdown/podman-init.1.md
index 5f3636686..6d1d92e04 100644
--- a/docs/source/markdown/podman-init.1.md
+++ b/docs/source/markdown/podman-init.1.md
@@ -36,7 +36,7 @@ podman init test1
podman init --latest
## SEE ALSO
-podman(1), podman-start(1)
+**[podman(1)](podman.1.md)**, **[podman-start(1)](podman-start.1.md)**
## HISTORY
April 2019, Originally compiled by Matthew Heon <mheon@redhat.com>
diff --git a/docs/source/markdown/podman-inspect.1.md b/docs/source/markdown/podman-inspect.1.md
index 83dc5cbbe..259a6c992 100644
--- a/docs/source/markdown/podman-inspect.1.md
+++ b/docs/source/markdown/podman-inspect.1.md
@@ -162,7 +162,7 @@ myNetwork
```
## SEE ALSO
-**[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)**.
+**[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 <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-kill.1.md b/docs/source/markdown/podman-kill.1.md
index e5f1d4bbe..dc79a44dc 100644
--- a/docs/source/markdown/podman-kill.1.md
+++ b/docs/source/markdown/podman-kill.1.md
@@ -47,7 +47,7 @@ podman kill --cidfile /home/user/cidfile-1
podman kill --cidfile /home/user/cidfile-1 --cidfile ./cidfile-2
## SEE ALSO
-podman(1), podman-stop(1)
+**[podman(1)](podman.1.md)**, **[podman-stop(1)](podman-stop.1.md)**
## HISTORY
September 2017, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-load.1.md b/docs/source/markdown/podman-load.1.md
index 3ff03e9e7..d1735750f 100644
--- a/docs/source/markdown/podman-load.1.md
+++ b/docs/source/markdown/podman-load.1.md
@@ -28,7 +28,7 @@ Note: `:` is a restricted character and cannot be part of the file name.
#### **--input**, **-i**=*input*
-Read from archive file, default is STDIN.
+Load the specified input file instead of from stdin. The file can be on the local file system or on a server (e.g., https://server.com/archive.tar)
The remote client requires the use of this option.
@@ -49,7 +49,7 @@ $ podman load --quiet -i fedora.tar
```
```
-$ podman load -q -i fedora.tar
+$ podman load -q -i https://server.com/archive.tar
```
```
@@ -77,7 +77,7 @@ Loaded image: registry.fedoraproject.org/fedora:latest
```
## SEE ALSO
-podman(1), podman-save(1)
+**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**
## HISTORY
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-login.1.md b/docs/source/markdown/podman-login.1.md
index 2559f4b77..d7e0152a8 100644
--- a/docs/source/markdown/podman-login.1.md
+++ b/docs/source/markdown/podman-login.1.md
@@ -120,7 +120,7 @@ Login Succeeded!
```
## SEE ALSO
-podman(1), podman-logout(1), containers-auth.json(5), containers-certs.d(5), containers-registries.conf(5)
+**[podman(1)](podman.1.md)**, **[podman-logout(1)](podman-logout.1.md)**, **[containers-auth.json(5)](https://github.com/containers/image/blob/main/docs/containers-auth.json.5.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.conf.5.md)**
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-logout.1.md b/docs/source/markdown/podman-logout.1.md
index c35c4856b..e34c80e95 100644
--- a/docs/source/markdown/podman-logout.1.md
+++ b/docs/source/markdown/podman-logout.1.md
@@ -54,7 +54,7 @@ Remove login credentials for all registries
```
## SEE ALSO
-podman(1), podman-login(1), containers-auth.json(5)
+**[podman(1)](podman.1.md)**, **[podman-login(1)](podman-login.1.md)**, **[containers-auth.json(5)](https://github.com/containers/image/blob/main/docs/containers-auth.json.5.md)**
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-logs.1.md b/docs/source/markdown/podman-logs.1.md
index 6b3ef79d7..888279990 100644
--- a/docs/source/markdown/podman-logs.1.md
+++ b/docs/source/markdown/podman-logs.1.md
@@ -123,7 +123,7 @@ AH00558: httpd: Could not reliably determine the server's fully qualified domain
```
## SEE ALSO
-podman(1), podman-run(1), podman-container-rm(1)
+**[podman(1)](podman.1.md)**, **[podman-run(1)](podman-run.1.md)**, **[podman-container-rm(1)](podman-container-rm.1.md)**
## HISTORY
February 2018, Updated by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-machine-init.1.md b/docs/source/markdown/podman-machine-init.1.md
index f1fbd56ee..3abc7c9ea 100644
--- a/docs/source/markdown/podman-machine-init.1.md
+++ b/docs/source/markdown/podman-machine-init.1.md
@@ -18,6 +18,10 @@ tied to the Linux kernel.
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*
@@ -65,7 +69,7 @@ $ podman machine init --memory=1024 myvm
```
## SEE ALSO
-podman-machine(1)
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine-list.1.md b/docs/source/markdown/podman-machine-list.1.md
index cad7e614b..accdd760c 100644
--- a/docs/source/markdown/podman-machine-list.1.md
+++ b/docs/source/markdown/podman-machine-list.1.md
@@ -48,7 +48,7 @@ $ podman machine ls --format {{.Name}}\t{{.VMType}}\t{{.Created}}\t{{.LastUp}}\n
```
## SEE ALSO
-podman-machine(1)
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine-rm.1.md b/docs/source/markdown/podman-machine-rm.1.md
index 4da17fdcb..7c9eb65c5 100644
--- a/docs/source/markdown/podman-machine-rm.1.md
+++ b/docs/source/markdown/podman-machine-rm.1.md
@@ -59,7 +59,7 @@ Are you sure you want to continue? [y/N] y
```
## SEE ALSO
-podman-machine (1)
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine-ssh.1.md b/docs/source/markdown/podman-machine-ssh.1.md
index c4c732819..db0350961 100644
--- a/docs/source/markdown/podman-machine-ssh.1.md
+++ b/docs/source/markdown/podman-machine-ssh.1.md
@@ -9,7 +9,7 @@ podman\-machine\-ssh - SSH into a virtual machine
## 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.
@@ -49,7 +49,7 @@ $ podman machine ssh myvm rpm -q podman
```
## SEE ALSO
-podman-machine (1)
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine-start.1.md b/docs/source/markdown/podman-machine-start.1.md
index 3ab5e96ba..e55dcab13 100644
--- a/docs/source/markdown/podman-machine-start.1.md
+++ b/docs/source/markdown/podman-machine-start.1.md
@@ -32,7 +32,7 @@ $ podman machine start myvm
```
## SEE ALSO
-podman-machine (1)
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine-stop.1.md b/docs/source/markdown/podman-machine-stop.1.md
index fc31e0bee..9aa781561 100644
--- a/docs/source/markdown/podman-machine-stop.1.md
+++ b/docs/source/markdown/podman-machine-stop.1.md
@@ -29,7 +29,7 @@ $ podman machine stop myvm
```
## SEE ALSO
-podman-machine (1)
+**[podman(1)](podman.1.md)**, **[podman-machine(1)](podman-machine.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-machine.1.md b/docs/source/markdown/podman-machine.1.md
index 40f69c0ba..8d9e77ea5 100644
--- a/docs/source/markdown/podman-machine.1.md
+++ b/docs/source/markdown/podman-machine.1.md
@@ -21,7 +21,7 @@ podman\-machine - Manage Podman's virtual machine
| stop | [podman-machine-stop(1)](podman-machine-stop.1.md) | Stop a virtual machine |
## SEE ALSO
-podman(1), podman-machine-init(1), podman-machine-list(1), podman-machine-rm(1), podman-machine-ssh(1), podman-machine-start(1), podman-machine-stop(1)
+**[podman(1)](podman.1.md)**, **[podman-machine-init(1)](podman-machine-init.1.md)**, **[podman-machine-list(1)](podman-machine-list.1.md)**, **[podman-machine-rm(1)](podman-machine-rm.1.md)**, **[podman-machine-ssh(1)](podman-machine-ssh.1.md)**, **[podman-machine-start(1)](podman-machine-start.1.md)**, **[podman-machine-stop(1)](podman-machine-stop.1.md)**
## HISTORY
March 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-manifest-add.1.md b/docs/source/markdown/podman-manifest-add.1.md
index 376301589..47f3fa25f 100644
--- a/docs/source/markdown/podman-manifest-add.1.md
+++ b/docs/source/markdown/podman-manifest-add.1.md
@@ -132,4 +132,4 @@ podman manifest add --arch arm64 --variant v8 mylist:v1.11 docker://71c201d10fff
```
## SEE ALSO
-podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-manifest-remove(1), podman-rmi(1), containers-certs.d(5)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
diff --git a/docs/source/markdown/podman-manifest-annotate.1.md b/docs/source/markdown/podman-manifest-annotate.1.md
index 2126b71ec..2b8b70aa4 100644
--- a/docs/source/markdown/podman-manifest-annotate.1.md
+++ b/docs/source/markdown/podman-manifest-annotate.1.md
@@ -58,4 +58,4 @@ podman manifest annotate --arch arm64 --variant v8 mylist:v1.11 sha256:59eec8837
```
## SEE ALSO
-podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-rmi(1)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
diff --git a/docs/source/markdown/podman-manifest-create.1.md b/docs/source/markdown/podman-manifest-create.1.md
index 3a3d5e4b0..77a4b9db6 100644
--- a/docs/source/markdown/podman-manifest-create.1.md
+++ b/docs/source/markdown/podman-manifest-create.1.md
@@ -40,4 +40,4 @@ podman manifest create --all mylist:v1.11 docker://fedora
```
## SEE ALSO
-podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-manifest-remove(1), podman-rmi(1)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
diff --git a/docs/source/markdown/podman-manifest-exists.1.md b/docs/source/markdown/podman-manifest-exists.1.md
index e15110126..dd344046d 100644
--- a/docs/source/markdown/podman-manifest-exists.1.md
+++ b/docs/source/markdown/podman-manifest-exists.1.md
@@ -37,7 +37,7 @@ $
```
## SEE ALSO
-podman(1), podman-manifest-create(1), podman-manifest-remove(1)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
## HISTORY
January 2021, Originally compiled by Paul Holzinger `<paul.holzinger@web.de>`
diff --git a/docs/source/markdown/podman-manifest-inspect.1.md b/docs/source/markdown/podman-manifest-inspect.1.md
index a4c58bd13..b2c6b1935 100644
--- a/docs/source/markdown/podman-manifest-inspect.1.md
+++ b/docs/source/markdown/podman-manifest-inspect.1.md
@@ -21,4 +21,4 @@ podman manifest inspect mylist:v1.11
```
## SEE ALSO
-podman(1), podman-manifest(1), podman-manifest-create(1), podman-manifest-add(1), podman-manifest-push(1), podman-manifest-remove(1), podman-rmi(1)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
diff --git a/docs/source/markdown/podman-manifest-push.1.md b/docs/source/markdown/podman-manifest-push.1.md
index 2cf1cc375..b50b8748a 100644
--- a/docs/source/markdown/podman-manifest-push.1.md
+++ b/docs/source/markdown/podman-manifest-push.1.md
@@ -107,4 +107,4 @@ podman manifest push mylist:v1.11 docker://registry.example.org/mylist:v1.11
```
## SEE ALSO
-podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-remove(1), podman-rmi(1), containers-certs.d(5)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
diff --git a/docs/source/markdown/podman-manifest-remove.1.md b/docs/source/markdown/podman-manifest-remove.1.md
index c13714195..256d5a5b5 100644
--- a/docs/source/markdown/podman-manifest-remove.1.md
+++ b/docs/source/markdown/podman-manifest-remove.1.md
@@ -20,4 +20,4 @@ e604eabaaee4858232761b4fef84e2316ed8f93e15eceafce845966ee3400036 :sha256:cb8a924
```
## SEE ALSO
-podman(1), podman-manifest(1), podman-manifest-add(1), podman-manifest-create(1), podman-manifest-inspect(1), podman-manifest-push(1), podman-rmi(1)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**
diff --git a/docs/source/markdown/podman-manifest-rm.1.md b/docs/source/markdown/podman-manifest-rm.1.md
index a65f32936..af7fedefe 100644
--- a/docs/source/markdown/podman-manifest-rm.1.md
+++ b/docs/source/markdown/podman-manifest-rm.1.md
@@ -22,4 +22,4 @@ storage.conf is the storage configuration file for all tools using containers/st
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)
+**[podman(1)](podman.1.md)**, **[podman-manifest(1)](podman-manifest.1.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**
diff --git a/docs/source/markdown/podman-manifest.1.md b/docs/source/markdown/podman-manifest.1.md
index 964f89afe..5ff44c1d5 100644
--- a/docs/source/markdown/podman-manifest.1.md
+++ b/docs/source/markdown/podman-manifest.1.md
@@ -69,4 +69,4 @@ be performed like this:
## 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)
+**[podman(1)](podman.1.md)**, **[podman-manifest-add(1)](podman-manifest-add.1.md)**, **[podman-manifest-annotate(1)](podman-manifest-annotate.1.md)**, **[podman-manifest-create(1)](podman-manifest-create.1.md)**, **[podman-manifest-inspect(1)](podman-manifest-inspect.1.md)**, **[podman-manifest-push(1)](podman-manifest-push.1.md)**, **[podman-manifest-remove(1)](podman-manifest-remove.1.md)**
diff --git a/docs/source/markdown/podman-mount.1.md b/docs/source/markdown/podman-mount.1.md
index 110fb0500..a4ce25bce 100644
--- a/docs/source/markdown/podman-mount.1.md
+++ b/docs/source/markdown/podman-mount.1.md
@@ -73,4 +73,4 @@ a7060253093b /var/lib/containers/storage/overlay/0ff7d7ca68bed1ace424f9df154d2dd
```
## SEE ALSO
-podman(1), podman-umount(1), mount(8), podman-unshare(1)
+**[podman(1)](podman.1.md)**, **[podman-umount(1)](podman-umount.1.md)**, **[podman-unshare(1)](podman-unshare.1.md)**, **mount(8)**
diff --git a/docs/source/markdown/podman-network-connect.1.md b/docs/source/markdown/podman-network-connect.1.md
index 39893c676..b998d4b7e 100644
--- a/docs/source/markdown/podman-network-connect.1.md
+++ b/docs/source/markdown/podman-network-connect.1.md
@@ -30,7 +30,7 @@ podman network connect --alias web1 --alias web2 test web
```
## SEE ALSO
-podman(1), podman-network(1), podman-network-disconnect(1), podman-network-inspect(1)
+**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-disconnect(1)](podman-network-disconnect.1.md)**
## HISTORY
November 2020, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-network-disconnect.1.md b/docs/source/markdown/podman-network-disconnect.1.md
index a13aa6088..f3ca059a1 100644
--- a/docs/source/markdown/podman-network-disconnect.1.md
+++ b/docs/source/markdown/podman-network-disconnect.1.md
@@ -26,7 +26,7 @@ podman network disconnect test web
## SEE ALSO
-podman(1), podman-network(1), podman-network-connect(1)
+**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-connect(1)](podman-network-connect.1.md)**
## HISTORY
November 2020, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-network-exists.1.md b/docs/source/markdown/podman-network-exists.1.md
index 3ad3ccedb..ee62a830e 100644
--- a/docs/source/markdown/podman-network-exists.1.md
+++ b/docs/source/markdown/podman-network-exists.1.md
@@ -38,7 +38,7 @@ $
```
## SEE ALSO
-podman(1), podman-network-create(1), podman-network-rm(1)
+**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**
## HISTORY
January 2021, Originally compiled by Paul Holzinger `<paul.holzinger@web.de>`
diff --git a/docs/source/markdown/podman-network-prune.1.md b/docs/source/markdown/podman-network-prune.1.md
index e18ffaaea..d35decb1b 100644
--- a/docs/source/markdown/podman-network-prune.1.md
+++ b/docs/source/markdown/podman-network-prune.1.md
@@ -39,7 +39,7 @@ podman network prune
## SEE ALSO
-podman(1), podman-network(1), podman-network-remove(1)
+**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**, **[podman-network-rm(1)](podman-network-rm.1.md)**
## HISTORY
February 2021, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-network-reload.1.md b/docs/source/markdown/podman-network-reload.1.md
index 593265df6..f091c34dc 100644
--- a/docs/source/markdown/podman-network-reload.1.md
+++ b/docs/source/markdown/podman-network-reload.1.md
@@ -52,7 +52,7 @@ fe7e8eca56f844ec33af10f0aa3b31b44a172776e3277b9550a623ed5d96e72b
## SEE ALSO
-podman(1), podman-network(1)
+**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**
## HISTORY
December 2020, Originally compiled by Paul Holzinger <paul.holzinger@web.de>
diff --git a/docs/source/markdown/podman-network-rm.1.md b/docs/source/markdown/podman-network-rm.1.md
index b12e81810..12102ba5a 100644
--- a/docs/source/markdown/podman-network-rm.1.md
+++ b/docs/source/markdown/podman-network-rm.1.md
@@ -45,7 +45,7 @@ Deleted: fred
**125** The command fails for any other reason
## SEE ALSO
-podman(1), podman-network(1), podman-network-inspect(1)
+**[podman(1)](podman.1.md)**, **[podman-network(1)](podman-network.1.md)**
## HISTORY
August 2019, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-network.1.md b/docs/source/markdown/podman-network.1.md
index 885c957b6..29ee70139 100644
--- a/docs/source/markdown/podman-network.1.md
+++ b/docs/source/markdown/podman-network.1.md
@@ -24,4 +24,4 @@ The network command manages CNI networks for Podman.
| rm | [podman-network-rm(1)](podman-network-rm.1.md) | Remove one or more CNI networks |
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
diff --git a/docs/source/markdown/podman-pause.1.md b/docs/source/markdown/podman-pause.1.md
index 123362822..863be9ed4 100644
--- a/docs/source/markdown/podman-pause.1.md
+++ b/docs/source/markdown/podman-pause.1.md
@@ -31,11 +31,11 @@ podman pause 860a4b23
Pause all **running** containers.
```
-podman stop -a
+podman pause -a
```
## SEE ALSO
-podman(1), podman-unpause(1)
+**[podman(1)](podman.1.md)**, **[podman-unpause(1)](podman-unpause.1.md)**
## HISTORY
September 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index d4770a538..2438f7ad8 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -112,6 +112,23 @@ Assign a static ip address to the pod. This option can be specified several time
Set logging driver for all created containers.
+#### **--log-opt**=*name*=*value*
+
+Set custom logging configuration. The following *name*s are supported:
+
+- **path**: specify a path to the log file
+(e.g. **--log-opt path=/var/log/container/mycontainer.json**);
+
+- **max-size**: specify a max size of the log file
+(e.g. **--log-opt max-size=10mb**);
+
+- **tag**: specify a custom log tag for the container
+(e.g. **--log-opt tag="{{.ImageName}}"**.
+
+It supports the same keys as **podman inspect --format**.
+
+This option is currently supported only by the **journald** log driver.
+
#### **--mac-address**=*MAC address*
Assign a static mac address to the pod. This option can be specified several times when play kube creates more than one pod.
@@ -210,7 +227,7 @@ $ podman play kube demo.yml --network cni1,cni2
Please take into account that CNI networks must be created first using podman-network-create(1).
## SEE ALSO
-podman(1), podman-container(1), podman-pod(1), podman-generate-kube(1), podman-play(1), podman-network-create(1), containers-certs.d(5)
+**[podman(1)](podman.1.md)**, **[podman-play(1)](podman-play.1.md)**, **[podman-network-create(1)](podman-network-create.1.md)**, **[podman-generate-kube(1)](podman-generate-kube.1.md)**, **[containers-certs.d(5)](https://github.com/containers/image/blob/main/docs/containers-certs.d.5.md)**
## HISTORY
December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/source/markdown/podman-play.1.md b/docs/source/markdown/podman-play.1.md
index 39101ad31..e5f0d315e 100644
--- a/docs/source/markdown/podman-play.1.md
+++ b/docs/source/markdown/podman-play.1.md
@@ -17,4 +17,4 @@ file input. Containers will be automatically started.
| kube | [podman-play-kube(1)](podman-play-kube.1.md) | Create containers, pods or volumes based on Kubernetes YAML. |
## SEE ALSO
-podman, podman-pod(1), podman-container(1), podman-generate(1), podman-play(1), podman-play-kube(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-container(1)](podman-container.1.md)**, **[podman-generate(1)](podman-generate.1.md)**, **[podman-play-kube(1)](podman-play-kube.1.md)**
diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md
index 08ac19b8b..cca90c942 100644
--- a/docs/source/markdown/podman-pod-create.1.md
+++ b/docs/source/markdown/podman-pod-create.1.md
@@ -112,7 +112,7 @@ The command that will be run to start the infra container. Default: "/pause".
#### **--infra-image**=*image*
-The image that will be created for the infra container. Default: "k8s.gcr.io/pause:3.1".
+The custom image that will be used for the infra container. Unless specified, Podman builds a custom local image which does not require pulling down an image.
#### **--infra-name**=*name*
@@ -432,7 +432,8 @@ $ podman pod create --network slirp4netns:cidr=192.168.0.0/24
```
## SEE ALSO
-podman-pod(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
+
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-exists.1.md b/docs/source/markdown/podman-pod-exists.1.md
index 4c8714341..201dfbefd 100644
--- a/docs/source/markdown/podman-pod-exists.1.md
+++ b/docs/source/markdown/podman-pod-exists.1.md
@@ -31,7 +31,7 @@ $
```
## SEE ALSO
-podman-pod(1), podman(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
## HISTORY
December 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/source/markdown/podman-pod-inspect.1.md b/docs/source/markdown/podman-pod-inspect.1.md
index 0c58b099e..5147a2559 100644
--- a/docs/source/markdown/podman-pod-inspect.1.md
+++ b/docs/source/markdown/podman-pod-inspect.1.md
@@ -70,7 +70,7 @@ Valid placeholders for the Go template are listed below:
```
## SEE ALSO
-**[podman(1)](podman.1.md)**,**[podman-pod(1)](podman-pod.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)**
+**[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 <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-pod-kill.1.md b/docs/source/markdown/podman-pod-kill.1.md
index d4eef2307..973d8da88 100644
--- a/docs/source/markdown/podman-pod-kill.1.md
+++ b/docs/source/markdown/podman-pod-kill.1.md
@@ -37,7 +37,7 @@ podman pod kill --latest
podman pod kill --all
## SEE ALSO
-podman-pod(1), podman-pod-stop(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-pod-stop(1)](podman-pod-stop.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-logs.1.md b/docs/source/markdown/podman-pod-logs.1.md
index 5ccc69bb9..5adcd9df5 100644
--- a/docs/source/markdown/podman-pod-logs.1.md
+++ b/docs/source/markdown/podman-pod-logs.1.md
@@ -85,4 +85,4 @@ podman pod logs --until 30m myserver-pod-1
```
## SEE ALSO
-podman(1), podman-pod-start(1), podman-pod-rm(1), podman-logs(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-pod-rm(1)](podman-pod-rm.1.md)**, **[podman-logs(1)](podman-logs.1.md)**
diff --git a/docs/source/markdown/podman-pod-pause.1.md b/docs/source/markdown/podman-pod-pause.1.md
index 2c849a932..0e53b52be 100644
--- a/docs/source/markdown/podman-pod-pause.1.md
+++ b/docs/source/markdown/podman-pod-pause.1.md
@@ -26,7 +26,7 @@ podman pod pause mywebserverpod
podman pod pause 860a4b23
## SEE ALSO
-podman-pod(1), podman-pod-unpause(1), podman-pause(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-pod-unpause(1)](podman-pod-unpause.1.md)**, **[podman-pause(1)](podman-pause.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-prune.1.md b/docs/source/markdown/podman-pod-prune.1.md
index e90164c15..6cc9babf1 100644
--- a/docs/source/markdown/podman-pod-prune.1.md
+++ b/docs/source/markdown/podman-pod-prune.1.md
@@ -26,7 +26,7 @@ $ sudo podman pod prune
```
## SEE ALSO
-podman-pod(1), podman-pod-ps(1), podman-pod-rm(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
## HISTORY
April 2019, Originally compiled by Peter Hunt (pehunt at redhat dot com)
diff --git a/docs/source/markdown/podman-pod-ps.1.md b/docs/source/markdown/podman-pod-ps.1.md
index 41e06347e..b61f12e90 100644
--- a/docs/source/markdown/podman-pod-ps.1.md
+++ b/docs/source/markdown/podman-pod-ps.1.md
@@ -169,7 +169,7 @@ POD ID NAME STATUS CREATED INFRA ID NAMES
Print a list of pods
## SEE ALSO
-podman-pod(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-restart.1.md b/docs/source/markdown/podman-pod-restart.1.md
index 65d05f977..c63daa419 100644
--- a/docs/source/markdown/podman-pod-restart.1.md
+++ b/docs/source/markdown/podman-pod-restart.1.md
@@ -44,7 +44,7 @@ cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907
```
## SEE ALSO
-podman-pod(1), podman-pod-start(1), podman-restart(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-restart(1)](podman-restart.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-rm.1.md b/docs/source/markdown/podman-pod-rm.1.md
index fc834a69c..00c82a8f7 100644
--- a/docs/source/markdown/podman-pod-rm.1.md
+++ b/docs/source/markdown/podman-pod-rm.1.md
@@ -61,7 +61,7 @@ podman pod rm --pod-id-file /path/to/id/file
**125** The command fails for any other reason
## SEE ALSO
-podman-pod(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-start.1.md b/docs/source/markdown/podman-pod-start.1.md
index 40a71d879..7f3adc8ff 100644
--- a/docs/source/markdown/podman-pod-start.1.md
+++ b/docs/source/markdown/podman-pod-start.1.md
@@ -37,7 +37,7 @@ podman pod start --all
podman pod start --pod-id-file /path/to/id/file
## SEE ALSO
-podman-pod(1), podman-pod-stop(1), podman-start(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-pod-stop(1)](podman-pod-stop.1.md)**
## HISTORY
July 2018, Adapted from podman start man page by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-stats.1.md b/docs/source/markdown/podman-pod-stats.1.md
index 3158ffc1c..47d95983a 100644
--- a/docs/source/markdown/podman-pod-stats.1.md
+++ b/docs/source/markdown/podman-pod-stats.1.md
@@ -85,7 +85,7 @@ ID NAME MEM USAGE / LIMIT
```
## SEE ALSO
-podman-pod(1), podman(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**
## HISTORY
February 2019, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-pod-stop.1.md b/docs/source/markdown/podman-pod-stop.1.md
index 57abf608d..f2887b5a5 100644
--- a/docs/source/markdown/podman-pod-stop.1.md
+++ b/docs/source/markdown/podman-pod-stop.1.md
@@ -80,7 +80,7 @@ $ podman pod stop -a -t 1
```
## SEE ALSO
-podman-pod(1), podman-pod-start(1), podman-stop(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-pod-start(1)](podman-pod-start.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-top.1.md b/docs/source/markdown/podman-pod-top.1.md
index d374a0dca..42198c7fa 100644
--- a/docs/source/markdown/podman-pod-top.1.md
+++ b/docs/source/markdown/podman-pod-top.1.md
@@ -44,7 +44,7 @@ PID SECCOMP COMMAND %CPU
```
## SEE ALSO
-podman-pod(1), podman-top(1), ps(1), seccomp(2), proc(5), capabilities(7)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **ps(1)**, **seccomp(2)**, **proc(5)**, **capabilities(7)**
## HISTORY
August 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod-unpause.1.md b/docs/source/markdown/podman-pod-unpause.1.md
index 98022a50b..cd91a661b 100644
--- a/docs/source/markdown/podman-pod-unpause.1.md
+++ b/docs/source/markdown/podman-pod-unpause.1.md
@@ -26,7 +26,7 @@ podman pod unpause mywebserverpod
podman pod unpause 860a4b23
## SEE ALSO
-podman-pod(1), podman-pod-pause(1), podman-unpause(1)
+**[podman(1)](podman.1.md)**, **[podman-pod(1)](podman-pod.1.md)**, **[podman-pod-pause(1)](podman-pod-pause.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-pod.1.md b/docs/source/markdown/podman-pod.1.md
index 9de2442bd..71e4dcb59 100644
--- a/docs/source/markdown/podman-pod.1.md
+++ b/docs/source/markdown/podman-pod.1.md
@@ -30,7 +30,7 @@ podman pod is a set of subcommands that manage pods, or groups of containers.
| unpause | [podman-pod-unpause(1)](podman-pod-unpause.1.md) | Unpause one or more pods. |
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/source/markdown/podman-port.1.md b/docs/source/markdown/podman-port.1.md
index 7a84d181a..2881cbb7b 100644
--- a/docs/source/markdown/podman-port.1.md
+++ b/docs/source/markdown/podman-port.1.md
@@ -55,7 +55,7 @@ List the port mappings for a specific container for port 80 and the tcp protocol
#
```
## SEE ALSO
-podman(1), podman-inspect(1)
+**[podman(1)](podman.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)**
## HISTORY
January 2018, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-ps.1.md b/docs/source/markdown/podman-ps.1.md
index b1a944942..4bae3e6bd 100644
--- a/docs/source/markdown/podman-ps.1.md
+++ b/docs/source/markdown/podman-ps.1.md
@@ -194,7 +194,7 @@ f78620804e00 scratch buildah 2 hours ago storage
Print a list of containers
## SEE ALSO
-podman(1), buildah(1), crio(8)
+**[podman(1)](podman.1.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **[crio(8)](https://github.com/cri-o/cri-o/blob/main/docs/crio.8.md)**
## HISTORY
August 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-pull.1.md b/docs/source/markdown/podman-pull.1.md
index 7fd9732d6..d91571799 100644
--- a/docs/source/markdown/podman-pull.1.md
+++ b/docs/source/markdown/podman-pull.1.md
@@ -234,7 +234,7 @@ Storing signatures
```
## SEE ALSO
-**[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)**
+**[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.conf.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 <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-push.1.md b/docs/source/markdown/podman-push.1.md
index 089d169fb..a145e3194 100644
--- a/docs/source/markdown/podman-push.1.md
+++ b/docs/source/markdown/podman-push.1.md
@@ -154,4 +154,4 @@ Storing signatures
```
## SEE ALSO
-podman(1), podman-pull(1), podman-login(1), containers-certs.d(5), containers-transports(5)
+**[podman(1)](podman.1.md)**, **[podman-pull(1)](podman-pull.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.conf.5.md)**, **[containers-transports(5)](https://github.com/containers/image/blob/main/docs/containers-transports.5.md)**
diff --git a/docs/source/markdown/podman-remote.1.md b/docs/source/markdown/podman-remote.1.md
index fb77f3300..72c8d7226 100644
--- a/docs/source/markdown/podman-remote.1.md
+++ b/docs/source/markdown/podman-remote.1.md
@@ -177,4 +177,4 @@ Users can modify defaults by creating the `$HOME/.config/containers/containers.c
Podman uses builtin defaults if no containers.conf file is found.
## SEE ALSO
-`containers.conf(5)`
+**[podman(1)](podman.1.md)**, **[podman-system-service(1)](podman-system-service.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**
diff --git a/docs/source/markdown/podman-rename.1.md b/docs/source/markdown/podman-rename.1.md
index fdb0dac89..4017db505 100644
--- a/docs/source/markdown/podman-rename.1.md
+++ b/docs/source/markdown/podman-rename.1.md
@@ -35,4 +35,4 @@ $ podman container rename 6e7514b47180 databaseCtr
```
## SEE ALSO
-podman(1), podman-create(1), podman-run(1)
+**[podman(1)](podman.1.md)**
diff --git a/docs/source/markdown/podman-restart.1.md b/docs/source/markdown/podman-restart.1.md
index 9ffca7b58..bb8f13a92 100644
--- a/docs/source/markdown/podman-restart.1.md
+++ b/docs/source/markdown/podman-restart.1.md
@@ -60,7 +60,7 @@ $ podman restart --all
```
## SEE ALSO
-podman(1), podman-run(1), podman-start(1), podman-create(1)
+**[podman(1)](podman.1.md)**
## HISTORY
March 2018, Originally compiled by Matt Heon <mheon@redhat.com>
diff --git a/docs/source/markdown/podman-rm.1.md b/docs/source/markdown/podman-rm.1.md
index ad44fd894..c599c8687 100644
--- a/docs/source/markdown/podman-rm.1.md
+++ b/docs/source/markdown/podman-rm.1.md
@@ -91,7 +91,7 @@ $ podman rm -f --latest
**125** The command fails for any other reason
## SEE ALSO
-podman(1), podman-image-rm(1), podman-ps(1), podman-build(1), buildah(1), cri-o(1)
+**[podman(1)](podman.1.md)**, **[crio(8)](https://github.com/cri-o/cri-o/blob/main/docs/crio.8.md)**
## HISTORY
August 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
diff --git a/docs/source/markdown/podman-rmi.1.md b/docs/source/markdown/podman-rmi.1.md
index 4f3ec5541..5fe0efa18 100644
--- a/docs/source/markdown/podman-rmi.1.md
+++ b/docs/source/markdown/podman-rmi.1.md
@@ -53,7 +53,7 @@ $ podman rmi -a -f
**125** The command fails for any other reason
## SEE ALSO
-podman(1), skopeo-delete(1)
+**[podman(1)](podman.1.md)**, **[skopeo-delete(1)](https://github.com/containers/skopeo/blob/main/docs/skopeo-delete.1.md)**
## HISTORY
March 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 30a9fad9a..0fdd47a78 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -512,18 +512,6 @@ a private IPC namespace.
- **host**: use the host shared memory,semaphores and message queues inside the container. Note: the host mode gives the container full access to local shared memory and is therefore considered insecure.
- **ns:**_path_: path to an IPC namespace to join.
-#### **--kernel-memory**=_number_[_unit_]
-
-Kernel memory limit. A _unit_ can be **b** (bytes), **k** (kilobytes), **m** (megabytes), or **g** (gigabytes).
-
-Constrains the kernel memory available to a container. If a limit of 0
-is specified (not using *--kernel-memory*), the container's kernel memory
-is not limited. If you specify a limit, it may be rounded up to a multiple
-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.
@@ -677,6 +665,16 @@ Current supported mount TYPEs are **bind**, **volume**, **image**, **tmpfs** and
. U, chown: true or false (default). Change recursively the owner and group of the source volume based on the UID and GID of the container.
+ Options specific to devpts:
+
+ · uid: UID of the file owner (default 0).
+
+ · gid: GID of the file owner (default 0).
+
+ · mode: permission mask for the file (default 600).
+
+ · max: maximum number of PTYs (default 1048576).
+
#### **--name**=*name*
Assign a name to the container.
@@ -1316,6 +1314,15 @@ share the volume content. As a result, Podman labels the content with a shared
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
@@ -1835,7 +1842,7 @@ Note: RHEL7 and Centos 7 will not have this feature until RHEL7.7 is released.
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.
@@ -1884,8 +1891,7 @@ page.
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-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), **personality**(2).
+**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
## HISTORY
September 2018, updated by Kunal Kushwaha `<kushwaha_kunal_v7@lab.ntt.co.jp>`
diff --git a/docs/source/markdown/podman-save.1.md b/docs/source/markdown/podman-save.1.md
index 842bc8b41..3fe99e911 100644
--- a/docs/source/markdown/podman-save.1.md
+++ b/docs/source/markdown/podman-save.1.md
@@ -105,7 +105,7 @@ Storing signatures
```
## SEE ALSO
-podman(1), podman-load(1), containers.conf(5), containers-transports(5)
+**[podman(1)](podman.1.md)**, **[podman-load(1)](podman-load.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.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 <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-search.1.md b/docs/source/markdown/podman-search.1.md
index 911bbcb4b..9e166fcc2 100644
--- a/docs/source/markdown/podman-search.1.md
+++ b/docs/source/markdown/podman-search.1.md
@@ -144,7 +144,7 @@ Note: This works only with registries that implement the v2 API. If tried with a
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)
+**[podman(1)](podman.1.md)**, **[containers-registries(5)](https://github.com/containers/image/blob/main/docs/containers-registries.5.md)**
## HISTORY
January 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-secret-create.1.md b/docs/source/markdown/podman-secret-create.1.md
index 712ca4aaa..2d504c0ad 100644
--- a/docs/source/markdown/podman-secret-create.1.md
+++ b/docs/source/markdown/podman-secret-create.1.md
@@ -45,7 +45,7 @@ $ printf <secret> | podman secret create my_secret -
```
## SEE ALSO
-podman-secret (1)
+**[podman(1)](podman.1.md)**, **[podman-secret(1)](podman-secret.1.md)**
## HISTORY
January 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-secret-inspect.1.md b/docs/source/markdown/podman-secret-inspect.1.md
index 383db8375..df16ba6fa 100644
--- a/docs/source/markdown/podman-secret-inspect.1.md
+++ b/docs/source/markdown/podman-secret-inspect.1.md
@@ -32,7 +32,7 @@ $ podman secret inspect --format "{{.Name} {{.Scope}}" mysecret
```
## SEE ALSO
-podman-secret(1)
+**[podman(1)](podman.1.md)**, **[podman-secret(1)](podman-secret.1.md)**
## HISTORY
January 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-secret-ls.1.md b/docs/source/markdown/podman-secret-ls.1.md
index 6adbeca08..27576f026 100644
--- a/docs/source/markdown/podman-secret-ls.1.md
+++ b/docs/source/markdown/podman-secret-ls.1.md
@@ -28,7 +28,7 @@ $ podman secret ls --format "{{.Name}}"
```
## SEE ALSO
-podman-secret(1)
+**[podman(1)](podman.1.md)**, **[podman-secret(1)](podman-secret.1.md)**
## HISTORY
January 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-secret-rm.1.md b/docs/source/markdown/podman-secret-rm.1.md
index c6f5079ba..ceab3df60 100644
--- a/docs/source/markdown/podman-secret-rm.1.md
+++ b/docs/source/markdown/podman-secret-rm.1.md
@@ -33,7 +33,7 @@ $ podman secret rm mysecret1 mysecret2
```
## SEE ALSO
-podman-secret(1)
+**[podman(1)](podman.1.md)**, **[podman-secret(1)](podman-secret.1.md)**
## HISTORY
January 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-secret.1.md b/docs/source/markdown/podman-secret.1.md
index 125888cff..671a0c43b 100644
--- a/docs/source/markdown/podman-secret.1.md
+++ b/docs/source/markdown/podman-secret.1.md
@@ -19,7 +19,7 @@ podman secret is a set of subcommands that manage secrets.
| rm | [podman-secret-rm(1)](podman-secret-rm.1.md) | Remove one or more secrets |
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
January 2021, Originally compiled by Ashley Cui <acui@redhat.com>
diff --git a/docs/source/markdown/podman-start.1.md b/docs/source/markdown/podman-start.1.md
index a751a098b..b8ed181e0 100644
--- a/docs/source/markdown/podman-start.1.md
+++ b/docs/source/markdown/podman-start.1.md
@@ -78,7 +78,7 @@ podman start --interactive --attach 860a4b231279
podman start -i -l
## SEE ALSO
-podman(1), podman-create(1)
+**[podman(1)](podman.1.md)**
## HISTORY
November 2018, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-stats.1.md b/docs/source/markdown/podman-stats.1.md
index abd8fd530..bbb4bcb06 100644
--- a/docs/source/markdown/podman-stats.1.md
+++ b/docs/source/markdown/podman-stats.1.md
@@ -99,7 +99,7 @@ ID NAME MEM USAGE / LIMIT
```
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
July 2017, Originally compiled by Ryan Cole <rycole@redhat.com>
diff --git a/docs/source/markdown/podman-stop.1.md b/docs/source/markdown/podman-stop.1.md
index 2a3f60844..9a852cbae 100644
--- a/docs/source/markdown/podman-stop.1.md
+++ b/docs/source/markdown/podman-stop.1.md
@@ -59,7 +59,7 @@ $ podman stop -a
$ podman stop --latest
## SEE ALSO
-podman(1), podman-rm(1)
+**[podman(1)](podman.1.md)**, **[podman-rm(1)](podman-rm.1.md)**
## HISTORY
September 2018, Originally compiled by Brent Baude <bbaude@redhat.com>
diff --git a/docs/source/markdown/podman-system-connection-add.1.md b/docs/source/markdown/podman-system-connection-add.1.md
index fa763d7cc..76f222f2b 100644
--- a/docs/source/markdown/podman-system-connection-add.1.md
+++ b/docs/source/markdown/podman-system-connection-add.1.md
@@ -46,7 +46,8 @@ $ 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)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**
+
## HISTORY
June 2020, Originally compiled by Jhon Honce (jhonce at redhat dot com)
diff --git a/docs/source/markdown/podman-system-connection-default.1.md b/docs/source/markdown/podman-system-connection-default.1.md
index f324f8c01..49f165e81 100644
--- a/docs/source/markdown/podman-system-connection-default.1.md
+++ b/docs/source/markdown/podman-system-connection-default.1.md
@@ -14,7 +14,7 @@ Set named ssh destination as default destination for the Podman service.
$ podman system connection default production
```
## SEE ALSO
-podman-system(1) , podman-system-connection(1) , containers.conf(5)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**
## HISTORY
July 2020, Originally compiled by Jhon Honce (jhonce at redhat dot com)
diff --git a/docs/source/markdown/podman-system-connection-list.1.md b/docs/source/markdown/podman-system-connection-list.1.md
index 4dc85dd98..cc457860a 100644
--- a/docs/source/markdown/podman-system-connection-list.1.md
+++ b/docs/source/markdown/podman-system-connection-list.1.md
@@ -33,7 +33,7 @@ devl ssh://root@example.com:/run/podman/podman.sock ~/.ssh/id_rsa True
devl ssh://user@example.com:/run/user/1000/podman/podman.sock ~/.ssh/id_rsa False
```
## SEE ALSO
-podman-system(1) , containers.conf(5)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**
## HISTORY
July 2020, Originally compiled by Jhon Honce (jhonce at redhat dot com)
diff --git a/docs/source/markdown/podman-system-connection-remove.1.md b/docs/source/markdown/podman-system-connection-remove.1.md
index faa767176..4914ac9e9 100644
--- a/docs/source/markdown/podman-system-connection-remove.1.md
+++ b/docs/source/markdown/podman-system-connection-remove.1.md
@@ -4,17 +4,23 @@
podman\-system\-connection\-remove - Delete named destination
## SYNOPSIS
-**podman system connection remove** *name*
+**podman system connection remove** [*options*] *name*
## DESCRIPTION
Delete named ssh destination.
+## OPTIONS
+
+#### **--all**=*false*, **-a**
+
+Remove all connections.
+
## EXAMPLE
```
$ podman system connection remove production
```
## SEE ALSO
-podman-system(1) , podman-system-connection(1) , containers.conf(5)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**
## HISTORY
July 2020, Originally compiled by Jhon Honce (jhonce at redhat dot com)
diff --git a/docs/source/markdown/podman-system-connection-rename.1.md b/docs/source/markdown/podman-system-connection-rename.1.md
index 819cb697f..5b5930738 100644
--- a/docs/source/markdown/podman-system-connection-rename.1.md
+++ b/docs/source/markdown/podman-system-connection-rename.1.md
@@ -14,7 +14,7 @@ Rename ssh destination from *old* to *new*.
$ podman system connection rename laptop devel
```
## SEE ALSO
-podman-system(1) , podman-system-connection(1) , containers.conf(5)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[podman-system-connection(1)](podman-system-connection.1.md)**
## HISTORY
July 2020, Originally compiled by Jhon Honce (jhonce at redhat dot com)
diff --git a/docs/source/markdown/podman-system-connection.1.md b/docs/source/markdown/podman-system-connection.1.md
index b00a2aec3..df9d8d248 100644
--- a/docs/source/markdown/podman-system-connection.1.md
+++ b/docs/source/markdown/podman-system-connection.1.md
@@ -28,7 +28,7 @@ Name URI Identity Default
devl ssh://root@example.com/run/podman/podman.sock ~/.ssh/id_rsa true
```
## SEE ALSO
-podman-system(1) , containers.conf(5)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**
## HISTORY
June 2020, Originally compiled by Jhon Honce (jhonce at redhat dot com)
diff --git a/docs/source/markdown/podman-system-df.1.md b/docs/source/markdown/podman-system-df.1.md
index f3db2d020..eb2c79b96 100644
--- a/docs/source/markdown/podman-system-df.1.md
+++ b/docs/source/markdown/podman-system-df.1.md
@@ -51,7 +51,7 @@ Containers 5
Local Volumes 1
```
## SEE ALSO
-podman-system(1)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**
## HISTORY
March 2019, Originally compiled by Qi Wang (qiwan at redhat dot com)
diff --git a/docs/source/markdown/podman-system-migrate.1.md b/docs/source/markdown/podman-system-migrate.1.md
index 913f119b0..dbdd24132 100644
--- a/docs/source/markdown/podman-system-migrate.1.md
+++ b/docs/source/markdown/podman-system-migrate.1.md
@@ -33,7 +33,7 @@ This can be used after a system upgrade which changes the default OCI runtime to
There are no guarantees that the containers will continue to work under the new runtime, as some runtimes support differing options and configurations.
## SEE ALSO
-`podman(1)`, `containers.conf(5)`, `usermod(8)`
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **usermod(8)**
## HISTORY
April 2019, Originally compiled by Giuseppe Scrivano (gscrivan at redhat dot com)
diff --git a/docs/source/markdown/podman-system-prune.1.md b/docs/source/markdown/podman-system-prune.1.md
index 791503a2c..d8b218db2 100644
--- a/docs/source/markdown/podman-system-prune.1.md
+++ b/docs/source/markdown/podman-system-prune.1.md
@@ -46,7 +46,7 @@ Print usage statement
Prune volumes currently unused by any container
## SEE ALSO
-podman(1), podman-image-prune(1), podman-container-prune(1), podman-pod-prune(1), podman-volume-prune(1)
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**
## HISTORY
February 2019, Originally compiled by Dan Walsh (dwalsh at redhat dot com)
diff --git a/docs/source/markdown/podman-system-renumber.1.md b/docs/source/markdown/podman-system-renumber.1.md
index 51c085606..61e26242b 100644
--- a/docs/source/markdown/podman-system-renumber.1.md
+++ b/docs/source/markdown/podman-system-renumber.1.md
@@ -20,7 +20,7 @@ When all available locks are exhausted, no further containers and pods can be cr
If possible, avoid calling **podman system renumber** while there are other Podman processes running.
## SEE ALSO
-`podman(1)`, `containers.conf(5)`
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**
## HISTORY
February 2019, Originally compiled by Matt Heon (mheon at redhat dot com)
diff --git a/docs/source/markdown/podman-system-reset.1.md b/docs/source/markdown/podman-system-reset.1.md
index 744f917df..90bcb5f53 100644
--- a/docs/source/markdown/podman-system-reset.1.md
+++ b/docs/source/markdown/podman-system-reset.1.md
@@ -43,7 +43,7 @@ if the program does not exist. Users can run `podman info` to ensure Podman is
using fuse-overlayfs and the overlay driver.
## SEE ALSO
-`podman(1)`, `podman-system(1)`, `fuse-overlayfs(1)`, `containers-storage.conf(5)`
+**[podman(1)](podman.1.md)**, **[podman-system(1)](podman-system.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**
## HISTORY
November 2019, Originally compiled by Dan Walsh (dwalsh at redhat dot com)
diff --git a/docs/source/markdown/podman-system.1.md b/docs/source/markdown/podman-system.1.md
index 7b9081b84..ae18aca88 100644
--- a/docs/source/markdown/podman-system.1.md
+++ b/docs/source/markdown/podman-system.1.md
@@ -23,4 +23,4 @@ The system command allows you to manage the podman systems
| service | [podman-system-service(1)](podman-system-service.1.md) | Run an API service |
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
diff --git a/docs/source/markdown/podman-tag.1.md b/docs/source/markdown/podman-tag.1.md
index 7eab62028..23dd3b60b 100644
--- a/docs/source/markdown/podman-tag.1.md
+++ b/docs/source/markdown/podman-tag.1.md
@@ -30,7 +30,7 @@ $ podman tag httpd myregistryhost:5000/fedora/httpd:v2
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
December 2019, Update description to refer to 'name' instead of 'alias' by Sascha Grunert <sgrunert@suse.com>
diff --git a/docs/source/markdown/podman-top.1.md b/docs/source/markdown/podman-top.1.md
index 09e8f7423..d385cde28 100644
--- a/docs/source/markdown/podman-top.1.md
+++ b/docs/source/markdown/podman-top.1.md
@@ -102,7 +102,7 @@ root 1 0 0.000 1h2m12.497061672s ? 0s sleep 100000
```
## SEE ALSO
-podman(1), ps(1), seccomp(2), proc(5), capabilities(7)
+**[podman(1)](podman.1.md)**, **ps(1)**, **seccomp(2)**, **proc(5)**, **capabilities(7)**
## HISTORY
July 2018, Introduce format descriptors by Valentin Rothberg <vrothberg@suse.com>
diff --git a/docs/source/markdown/podman-unmount.1.md b/docs/source/markdown/podman-unmount.1.md
index 2832e5fdb..045ea5456 100644
--- a/docs/source/markdown/podman-unmount.1.md
+++ b/docs/source/markdown/podman-unmount.1.md
@@ -54,4 +54,4 @@ podman unmount containerID1 containerID2 containerID3
podman unmount --all
## SEE ALSO
-podman(1), podman-container-mount(1), podman-image-mount(1)
+**[podman(1)](podman.1.md)**, **[podman-container-mount(1)](podman-container-mount.1.md)**, **[podman-image-mount(1)](podman-image-mount.1.md)**
diff --git a/docs/source/markdown/podman-unpause.1.md b/docs/source/markdown/podman-unpause.1.md
index ae178647a..0eb1f1bb8 100644
--- a/docs/source/markdown/podman-unpause.1.md
+++ b/docs/source/markdown/podman-unpause.1.md
@@ -36,7 +36,7 @@ podman unpause -a
```
## SEE ALSO
-podman(1), podman-pause(1)
+**[podman(1)](podman.1.md)**, **[podman-pause(1)](podman-pause.1.md)**
## HISTORY
September 2017, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/docs/source/markdown/podman-unshare.1.md b/docs/source/markdown/podman-unshare.1.md
index 72821b6e5..5676b1be7 100644
--- a/docs/source/markdown/podman-unshare.1.md
+++ b/docs/source/markdown/podman-unshare.1.md
@@ -30,10 +30,10 @@ The unshare session defines two environment variables:
Print usage statement
-#### **--rootless-cni**
+#### **--rootless-netns**
-Join the rootless network namespace used for CNI networking. It can be used to
-connect to a rootless container via IP address (CNI networking). This is otherwise
+Join the rootless network namespace used for CNI and netavark networking. It can be used to
+connect to a rootless container via IP address (bridge networking). This is otherwise
not possible from the host network namespace.
_Note: Using this option with more than one unshare session can have unexpected results._
@@ -78,7 +78,7 @@ $ podman unshare cat /proc/self/uid_map /proc/self/gid_map
0 1000 1
1 10000 65536
-$ podman unshare --rootless-cni ip addr
+$ podman unshare --rootless-netns ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
@@ -105,4 +105,4 @@ $ podman unshare --rootless-cni ip addr
## SEE ALSO
-podman(1), podman-mount(1), namespaces(7), newuidmap(1), newgidmap(1), user\_namespaces(7)
+**[podman(1)](podman.1.md)**, **[podman-mount(1)](podman-mount.1.md)**, **namespaces(7)**, **newuidmap(1)**, **newgidmap(1)**, **user\_namespaces(7)**
diff --git a/docs/source/markdown/podman-untag.1.md b/docs/source/markdown/podman-untag.1.md
index 70053c940..8314e5f9b 100644
--- a/docs/source/markdown/podman-untag.1.md
+++ b/docs/source/markdown/podman-untag.1.md
@@ -29,7 +29,7 @@ $ podman untag httpd myregistryhost:5000/fedora/httpd:v2
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
December 2019, Originally compiled by Sascha Grunert <sgrunert@suse.com>
diff --git a/docs/source/markdown/podman-version.1.md b/docs/source/markdown/podman-version.1.md
index a54e66372..809ce64a6 100644
--- a/docs/source/markdown/podman-version.1.md
+++ b/docs/source/markdown/podman-version.1.md
@@ -40,7 +40,7 @@ $ podman version --format '{{.Client.Version}}'
```
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
November 2018, Added --format flag by Tomas Tomecek <ttomecek@redhat.com>
diff --git a/docs/source/markdown/podman-volume-create.1.md b/docs/source/markdown/podman-volume-create.1.md
index 9bf5a3d81..365a5acac 100644
--- a/docs/source/markdown/podman-volume-create.1.md
+++ b/docs/source/markdown/podman-volume-create.1.md
@@ -17,7 +17,7 @@ driver options can be set using the **--opt** flag.
#### **--driver**=*driver*
-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.
+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)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)** configuration file.
#### **--help**
@@ -86,7 +86,7 @@ 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), **xfs_quota**(8), `xfs_quota(8)`, `projects(5)`, `projid(5)`
+**[podman(1)](podman.1.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[podman-volume(1)](podman-volume.1.md)**, **mount(8)**, **xfs_quota(8)**, **xfs_quota(8)**, **projects(5)**, **projid(5)**
## HISTORY
January 2020, updated with information on volume plugins by Matthew Heon <mheon@redhat.com>
diff --git a/docs/source/markdown/podman-volume-exists.1.md b/docs/source/markdown/podman-volume-exists.1.md
index ead806bd9..dbdf0985c 100644
--- a/docs/source/markdown/podman-volume-exists.1.md
+++ b/docs/source/markdown/podman-volume-exists.1.md
@@ -37,7 +37,7 @@ $
```
## SEE ALSO
-podman(1), podman-volume-create(1), podman-volume-rm(1)
+**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**
## HISTORY
January 2021, Originally compiled by Paul Holzinger `<paul.holzinger@web.de>`
diff --git a/docs/source/markdown/podman-volume-export.1.md b/docs/source/markdown/podman-volume-export.1.md
index 7db1e421d..ed73e86d0 100644
--- a/docs/source/markdown/podman-volume-export.1.md
+++ b/docs/source/markdown/podman-volume-export.1.md
@@ -35,4 +35,4 @@ $ podman volume export myvol --output myvol.tar
```
## SEE ALSO
-podman-volume(1), podman-volume-import(1)
+**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**, **[podman-volume-import(1)](podman-volume-import.1.md)**
diff --git a/docs/source/markdown/podman-volume-import.1.md b/docs/source/markdown/podman-volume-import.1.md
index 88b7b1b5b..71956f43a 100644
--- a/docs/source/markdown/podman-volume-import.1.md
+++ b/docs/source/markdown/podman-volume-import.1.md
@@ -32,4 +32,4 @@ $ podman volume export myvol | podman volume import oldmyvol -
```
## SEE ALSO
-podman-volume(1), podman-volume-export(1)
+**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**, **[podman-volume-export(1)](podman-volume-export.1.md)**
diff --git a/docs/source/markdown/podman-volume-inspect.1.md b/docs/source/markdown/podman-volume-inspect.1.md
index 4595ccda6..dda83e558 100644
--- a/docs/source/markdown/podman-volume-inspect.1.md
+++ b/docs/source/markdown/podman-volume-inspect.1.md
@@ -40,7 +40,7 @@ $ podman volume inspect --format "{{.Driver}} {{.Scope}}" myvol
```
## SEE ALSO
-**[podman(1)](podman.1.md)**,**[podman-volume(1)](podman-volume.1.md)**, **[podman-inspect(1)](podman-inspect.1.md)**
+**[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 <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-volume-ls.1.md b/docs/source/markdown/podman-volume-ls.1.md
index 6c80ec152..4f5ce6888 100644
--- a/docs/source/markdown/podman-volume-ls.1.md
+++ b/docs/source/markdown/podman-volume-ls.1.md
@@ -59,7 +59,7 @@ $ podman volume ls --filter label=key=value
```
## SEE ALSO
-podman-volume(1)
+**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**
## HISTORY
November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-volume-prune.1.md b/docs/source/markdown/podman-volume-prune.1.md
index cfdfc1a44..012567957 100644
--- a/docs/source/markdown/podman-volume-prune.1.md
+++ b/docs/source/markdown/podman-volume-prune.1.md
@@ -44,7 +44,7 @@ $ podman volume prune --filter label=mylabel=mylabelvalue
```
## SEE ALSO
-podman-volume(1)
+**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**
## HISTORY
November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-volume-rm.1.md b/docs/source/markdown/podman-volume-rm.1.md
index 26cc605a2..8274eaaf3 100644
--- a/docs/source/markdown/podman-volume-rm.1.md
+++ b/docs/source/markdown/podman-volume-rm.1.md
@@ -52,7 +52,7 @@ $ podman volume rm --force myvol
**125** The command fails for any other reason
## SEE ALSO
-podman-volume(1)
+**[podman(1)](podman.1.md)**, **[podman-volume(1)](podman-volume.1.md)**
## HISTORY
November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-volume.1.md b/docs/source/markdown/podman-volume.1.md
index 64b37c28c..d8bc11aad 100644
--- a/docs/source/markdown/podman-volume.1.md
+++ b/docs/source/markdown/podman-volume.1.md
@@ -23,7 +23,7 @@ podman volume is a set of subcommands that manage volumes.
| rm | [podman-volume-rm(1)](podman-volume-rm.1.md) | Remove one or more volumes. |
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
November 2018, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
diff --git a/docs/source/markdown/podman-wait.1.md b/docs/source/markdown/podman-wait.1.md
index fac017fe6..7ee53c57e 100644
--- a/docs/source/markdown/podman-wait.1.md
+++ b/docs/source/markdown/podman-wait.1.md
@@ -53,7 +53,7 @@ $ podman wait mywebserver myftpserver
```
## SEE ALSO
-podman(1)
+**[podman(1)](podman.1.md)**
## HISTORY
September 2017, Originally compiled by Brent Baude<bbaude@redhat.com>
diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md
index beb6e26d8..daa8212c5 100644
--- a/docs/source/markdown/podman.1.md
+++ b/docs/source/markdown/podman.1.md
@@ -370,7 +370,7 @@ Podman can also be used as non-root user. When podman runs in rootless mode, a u
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
@@ -400,7 +400,7 @@ The Network File System (NFS) and other distributed file systems (for example: L
For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/podman/blob/master/troubleshooting.md).
## SEE ALSO
-**containers-mounts.conf**(5), **containers-registries.conf**(5), **containers-storage.conf**(5), **buildah**(1), **containers.conf**(5), **oci-hooks**(5), **containers-policy.json**(5), **crun**(8), **runc**(8), **subuid**(5), **subgid**(5), **slirp4netns**(1), **conmon**(8).
+**[containers-mounts.conf(5)](https://github.com/containers/common/blob/main/docs/containers-mounts.conf.5.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **oci-hooks(5)**, **[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
## HISTORY
Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com>
diff --git a/go.mod b/go.mod
index e6d30a12b..05ad8990b 100644
--- a/go.mod
+++ b/go.mod
@@ -7,31 +7,31 @@ require (
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-20210922093614-c31748bec9f2
- github.com/checkpoint-restore/go-criu/v5 v5.1.0
+ github.com/checkpoint-restore/go-criu/v5 v5.2.0
github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.0.1
github.com/containers/buildah v1.23.1
- github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e
+ github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.16.1
github.com/containers/ocicrypt v1.1.2
github.com/containers/psgo v1.7.1
- github.com/containers/storage v1.37.0
+ github.com/containers/storage v1.37.1-0.20211014130921-5c5bf639ed01
github.com/coreos/go-systemd/v22 v22.3.2
github.com/coreos/stream-metadata-go v0.0.0-20210225230131-70edb9eb47b3
github.com/cyphar/filepath-securejoin v0.2.3
github.com/davecgh/go-spew v1.1.1
github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f
github.com/docker/distribution v2.7.1+incompatible
- github.com/docker/docker v20.10.9+incompatible
+ github.com/docker/docker v20.10.10+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/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.5
+ github.com/godbus/dbus/v5 v5.0.6
github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33
@@ -45,12 +45,12 @@ require (
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6
github.com/mrunalp/fileutils v0.5.0
github.com/onsi/ginkgo v1.16.5
- github.com/onsi/gomega v1.16.0
+ github.com/onsi/gomega v1.17.0
github.com/opencontainers/go-digest v1.0.0
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/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7
github.com/opencontainers/selinux v1.9.1
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
@@ -67,7 +67,10 @@ require (
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-20210910150752-751e447fb3d0
+ gopkg.in/fsnotify.v1 v1.4.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
- k8s.io/api v0.22.2
- k8s.io/apimachinery v0.22.2
+ k8s.io/api v0.22.3
+ k8s.io/apimachinery v0.22.3
)
+
+replace github.com/onsi/gomega => github.com/onsi/gomega v1.16.0
diff --git a/go.sum b/go.sum
index 5a6661162..f3a7dcffd 100644
--- a/go.sum
+++ b/go.sum
@@ -77,13 +77,17 @@ github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn
github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
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.22 h1:CulZ3GW8sNJExknToo+RWD+U+6ZM5kkNfuxywSDPd08=
+github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
github.com/Microsoft/hcsshim v0.8.22/go.mod h1:91uVCVzvX2QD16sMCenoxxXo6L1wJnLMX2PSufFMtF0=
+github.com/Microsoft/hcsshim v0.9.0 h1:BBgYMxl5YZDZVIijz02AlDINpYZOzQqRNCl9CZM13vk=
+github.com/Microsoft/hcsshim v0.9.0/go.mod h1:VBJWdC71NSWPlEo7lwde1aL21748J8B6Sdgno7NqEGE=
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=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
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.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
@@ -135,8 +139,8 @@ github.com/checkpoint-restore/checkpointctl v0.0.0-20210922093614-c31748bec9f2/g
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/checkpoint-restore/go-criu/v5 v5.2.0 h1:QwsRK9EdBr2kQr44DqSdBrP4dULp2+4EkqounYQOnF8=
+github.com/checkpoint-restore/go-criu/v5 v5.2.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
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=
@@ -153,6 +157,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
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/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=
@@ -192,8 +197,9 @@ github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09Zvgq
github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
github.com/containerd/containerd v1.5.4/go.mod h1:sx18RgvW6ABJ4iYUw7Q5x7bgFOAB9B6G7+yO0XBc4zw=
-github.com/containerd/containerd v1.5.5 h1:q1gxsZsGZ8ddVe98yO6pR21b5xQSMiR61lD0W96pgQo=
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=
@@ -222,6 +228,7 @@ github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJ
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.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
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=
@@ -251,8 +258,8 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB
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.46.1-0.20211008123044-d846f5aaec0e h1:lYazDued7KBcMq5IJzRIbX47SSLRg/yYxvM/P9LaVhE=
-github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e/go.mod h1:ggZks97KCmjBcHvNTCyLc17SqdjSYoeexW7rnRt9H9Y=
+github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358 h1:dK2AgGBdWspdQNw28Wc4peY25QeyYV4H9ViQaFaQ9XQ=
+github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358/go.mod h1:bu8gizEkgAz6gXHvUw2cMtI5ErxB+fn/hv49RWk5N1A=
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.16.0/go.mod h1:XgTpfAPLRGOd1XYyCU5cISFr777bLmOerCSpt/v7+Q4=
@@ -270,8 +277,9 @@ github.com/containers/psgo v1.7.1/go.mod h1:mWGpFzW73qWFA+blhF6l7GuKzbrACkYgr/aj
github.com/containers/storage v1.23.5/go.mod h1:ha26Q6ngehFNhf3AWoXldvAvwI4jFe3ETQAf/CeZPyM=
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/containers/storage v1.37.1-0.20211014130921-5c5bf639ed01 h1:bCJAZyAq5rLWwlDAsRNuk7hVqnAugvTJrYqZWjy9POk=
+github.com/containers/storage v1.37.1-0.20211014130921-5c5bf639ed01/go.mod h1:4QYCZMzSvM3XZtyrdMqC3jW/u5/MXAo8hjwyzug9noc=
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=
@@ -321,13 +329,16 @@ github.com/digitalocean/go-qemu v0.0.0-20210209191958-152a1535e49f/go.mod h1:Iet
github.com/disiqueira/gotree/v3 v3.0.2 h1:ik5iuLQQoufZBNPY518dXhiO5056hyNBIK9lWhkNRq8=
github.com/disiqueira/gotree/v3 v3.0.2/go.mod h1:ZuyjE4+mUQZlbpkI24AmruZKhg3VHEgPLDY8Qk+uUu8=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/distribution v2.7.1+incompatible 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.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v20.10.8+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k=
-github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.10+incompatible h1:GKkP0T7U4ks6X3lmmHKC2QDprnpRJor2Z5a8m62R9ZM=
+github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
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=
@@ -361,6 +372,7 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
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/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=
@@ -392,11 +404,15 @@ github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
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=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -408,8 +424,8 @@ github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68Fp
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/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.5 h1:9Eg0XUhQxtkV8ykTMKtMMYY72g4NgxtRq4jgh4Ih5YM=
-github.com/godbus/dbus/v5 v5.0.5/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+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=
@@ -435,6 +451,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
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/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
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=
@@ -469,6 +486,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
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-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
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=
@@ -506,6 +524,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
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.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
@@ -569,6 +588,7 @@ 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/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
@@ -612,11 +632,13 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
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/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
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-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
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=
@@ -637,12 +659,14 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC
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.6/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.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=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
@@ -711,23 +735,17 @@ github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:v
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
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.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-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.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-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.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
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=
@@ -758,8 +776,9 @@ github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.m
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
-github.com/opencontainers/runtime-tools v0.9.0 h1:FYgwVsKRI/H9hU32MJ/4MLOzXWodKK5zsQavY8NPMkU=
github.com/opencontainers/runtime-tools v0.9.0/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7 h1:6JHkPc2wUOsj2XBpYzyvmCL5Y/fA3TFaomYv/Iggt1g=
+github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
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=
@@ -836,6 +855,7 @@ github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiB
github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
+github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20200616122406-847368b35ebf h1:b0+ZBD3rohnkQ4q5duD1+RyTXTg9yk+qTOPMSQtapO0=
@@ -973,6 +993,7 @@ 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=
@@ -1068,12 +1089,10 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
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=
@@ -1086,8 +1105,9 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
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 h1:ADo5wSpq2gqaCGQWzk7S5vd//0iyyLeAratkEoG5dLE=
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a/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=
@@ -1136,6 +1156,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1229,10 +1250,12 @@ golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxb
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=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -1248,6 +1271,7 @@ golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -1271,8 +1295,10 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs
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-20200505023115-26f46d2f7ef8/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-20200616133436-c1934b75d054/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=
@@ -1280,6 +1306,7 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
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-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
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=
@@ -1287,6 +1314,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
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.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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=
@@ -1353,6 +1381,7 @@ google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfG
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-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
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=
@@ -1394,8 +1423,9 @@ google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA5
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 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
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=
@@ -1407,8 +1437,9 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
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=
@@ -1463,19 +1494,20 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
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.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
-k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
+k8s.io/api v0.22.3 h1:wOoES2GoSkUsdped2RB4zYypPqWtvprGoKCENTOOjP4=
+k8s.io/api v0.22.3/go.mod h1:azgiXFiXqiWyLCfI62/eYBOu19rj2LKmIhFPP4+33fs=
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.22.2 h1:ejz6y/zNma8clPVfNDLnPbleBo6MpoFy/HBiBqCouVk=
-k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
+k8s.io/apimachinery v0.22.3 h1:mrvBG5CZnEfwgpVqWcrRKvdsYECTrhAR6cApAgdsflk=
+k8s.io/apimachinery v0.22.3/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=
k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
+k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
@@ -1484,12 +1516,16 @@ k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
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.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
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-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
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=
@@ -1499,6 +1535,7 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
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.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
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.2 h1:Hr/htKFmJEbtMgS/UD0N+gtgctAqz81t3nu+sPzynno=
diff --git a/hack/install_catatonit.sh b/hack/install_catatonit.sh
index 8837db3a8..0a02b75ab 100755
--- a/hack/install_catatonit.sh
+++ b/hack/install_catatonit.sh
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
BASE_PATH="/usr/libexec/podman"
CATATONIT_PATH="${BASE_PATH}/catatonit"
-CATATONIT_VERSION="v0.1.4"
+CATATONIT_VERSION="v0.1.7"
set -e
if [ -f $CATATONIT_PATH ]; then
diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages
index af54f05f3..6a2d627bb 100755
--- a/hack/xref-helpmsgs-manpages
+++ b/hack/xref-helpmsgs-manpages
@@ -54,6 +54,11 @@ option is listed in the appropriate man page and vice-versa.
$ME invokes '\$PODMAN' (default: $Default_Podman).
+In the spirit of shoehorning functionality where it wasn't intended,
+$ME also checks the SEE ALSO section of each man page
+to ensure that references and links are properly formatted
+and valid.
+
Exit status is zero if no inconsistencies found, one otherwise
OPTIONS:
@@ -286,6 +291,9 @@ sub podman_man {
elsif ($line =~ /^\#\#\s+(SUB)?COMMANDS/) {
$section = 'commands';
}
+ elsif ($line =~ /^\#\#\s+SEE\s+ALSO/) {
+ $section = 'see-also';
+ }
elsif ($line =~ /^\#\#[^#]/) {
$section = '';
}
@@ -340,6 +348,11 @@ sub podman_man {
}
}
}
+
+ # It's easy to make mistakes in the SEE ALSO elements.
+ elsif ($section eq 'see-also') {
+ _check_seealso_links( "$subpath:$.", $line );
+ }
}
close $fh;
@@ -427,5 +440,119 @@ sub podman_rst {
# END data gathering
###############################################################################
+# BEGIN sanity checking of SEE ALSO links
+
+##########################
+# _check_seealso_links # Check formatting and link validity.
+##########################
+sub _check_seealso_links {
+ my $path = shift;
+ my $line = shift;
+
+ return if ! $line;
+
+ # Line must be a comma-separated list of man page references, e.g.
+ # **foo(1)**, **[podman-bar(1)](podman-bar.1.md)**, **[xxx(8)](http...)**
+ TOKEN:
+ for my $token (split /,\s+/, $line) {
+ # Elements must be separated by comma and space. (We don't do further
+ # checks here, so it's possible for the dev to add the space and then
+ # have us fail on the next iteration. I choose not to address that.)
+ if ($token =~ /,/) {
+ warn "$ME: $path: please add space after comma: '$token'\n";
+ ++$Errs;
+ next TOKEN;
+ }
+
+ # Each token must be of the form '**something**'
+ if ($token !~ s/^\*\*(.*)\*\*$/$1/) {
+ if ($token =~ /\*\*/) {
+ warn "$ME: $path: '$token' has asterisks in the wrong place\n";
+ }
+ else {
+ warn "$ME: $path: '$token' should be bracketed by '**'\n";
+ }
+ ++$Errs;
+ next TOKEN;
+ }
+
+ # Is it a markdown link?
+ if ($token =~ /^\[(\S+)\]\((\S+)\)$/) {
+ my ($name, $link) = ($1, $2);
+ if ($name =~ /^(.*)\((\d)\)$/) {
+ my ($base, $section) = ($1, $2);
+ if (-e "$Markdown_Path/$base.$section.md" || -e "$Markdown_Path/links/$base.$section") {
+ if ($link ne "$base.$section.md") {
+ warn "$ME: $path: inconsistent link $name -> $link, expected $base.$section.md\n";
+ ++$Errs;
+ }
+ }
+ else {
+ if (! _is_valid_external_link($base, $section, $link)) {
+ warn "$ME: $path: invalid link $name -> $link\n";
+ ++$Errs;
+ }
+ }
+ }
+ else {
+ warn "$ME: $path: could not parse '$name' as 'manpage(N)'\n";
+ ++$Errs;
+ }
+ }
+
+ # Not a markdown link; it must be a plain man reference, e.g. 'foo(5)'
+ elsif ($token =~ m!^(\S+)\((\d+)\)$!) {
+ my ($base, $section) = ($1, $2);
+
+ # Unadorned 'podman-foo(1)' must be a link.
+ if (-e "$Markdown_Path/$base.$section.md" || -e "$Markdown_Path/links/$base.$section") {
+ warn "$ME: $path: '$token' should be '[$token]($base.$section.md)'\n";
+ ++$Errs;
+ }
+
+ # Link to man page foo(5) but without a link. This is not an error
+ # but Ed may sometimes want to see those on a manual test run.
+ warn "$ME: $path: plain '$token' would be so much nicer as a link\n"
+ if $verbose;
+ }
+ else {
+ warn "$ME: $path: invalid token '$token'\n";
+ ++$Errs;
+ }
+ }
+}
+
+#############################
+# _is_valid_external_link # Tries to validate links to external man pages
+#############################
+#
+# This performs no actual fetches, so we can't actually check for 404.
+# All we do is ensure that links conform to standard patterns. This is
+# good for catching things like 'conmon(8)' pointing to a .5 URL, or
+# linking to .md instead of .html.
+#
+# FIXME: we could actually rewrite this so as to offer hints on what to fix.
+# That's a lot of work, and a lot of convoluted code, for questionable ROI.
+#
+sub _is_valid_external_link {
+ my ($base, $section, $link) = @_;
+
+ return 1 if $link =~ m!^https://github\.com/\S+/blob/(main|master)(/.*)?/\Q$base\E\.$section\.md!;
+
+ return 1 if $link =~ m!^https://.*unix\.com/man-page/(linux|redhat)/$section/$base$!;
+ return 1 if $link eq "https://man7\.org/linux/man-pages/man$section/$base\.$section\.html";
+
+ if ($base =~ /systemd/) {
+ return 1 if $link eq "https://www.freedesktop.org/software/systemd/man/$base.html";
+ }
+
+ return;
+}
+
+
+
+
+# END sanity checking of SEE ALSO links
+###############################################################################
1;
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go
index 3e3c17a9e..e71d82736 100644
--- a/libpod/boltdb_state_internal.go
+++ b/libpod/boltdb_state_internal.go
@@ -384,6 +384,15 @@ func (s *BoltState) getContainerConfigFromDB(id []byte, config *ContainerConfig,
return errors.Wrapf(err, "error unmarshalling container %s config", string(id))
}
+ // convert ports to the new format if needed
+ if len(config.ContainerNetworkConfig.OldPortMappings) > 0 && len(config.ContainerNetworkConfig.PortMappings) == 0 {
+ config.ContainerNetworkConfig.PortMappings = ocicniPortsToNetTypesPorts(config.ContainerNetworkConfig.OldPortMappings)
+ // keep the OldPortMappings in case an user has to downgrade podman
+
+ // indicate the the config was modified and should be written back to the db when possible
+ config.rewrite = true
+ }
+
return nil
}
diff --git a/libpod/common_test.go b/libpod/common_test.go
index 4662a33bd..67e29c265 100644
--- a/libpod/common_test.go
+++ b/libpod/common_test.go
@@ -41,18 +41,20 @@ func getTestContainer(id, name string, manager lock.Manager) (*Container, error)
ContainerNetworkConfig: ContainerNetworkConfig{
DNSServer: []net.IP{net.ParseIP("192.168.1.1"), net.ParseIP("192.168.2.2")},
DNSSearch: []string{"example.com", "example.example.com"},
- PortMappings: []types.OCICNIPortMapping{
+ PortMappings: []types.PortMapping{
{
HostPort: 80,
ContainerPort: 90,
Protocol: "tcp",
HostIP: "192.168.3.3",
+ Range: 1,
},
{
HostPort: 100,
ContainerPort: 110,
Protocol: "udp",
HostIP: "192.168.4.4",
+ Range: 1,
},
},
},
diff --git a/libpod/container.go b/libpod/container.go
index 4d15c04c5..86989a02f 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -465,7 +465,7 @@ func (c *Container) NewNetNS() bool {
// PortMappings returns the ports that will be mapped into a container if
// a new network namespace is created
// If NewNetNS() is false, this value is unused
-func (c *Container) PortMappings() ([]types.OCICNIPortMapping, error) {
+func (c *Container) PortMappings() ([]types.PortMapping, error) {
// First check if the container belongs to a network namespace (like a pod)
if len(c.config.NetNsCtr) > 0 {
netNsCtr, err := c.runtime.GetContainer(c.config.NetNsCtr)
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 38223316e..a41bb03df 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -794,21 +794,29 @@ type ContainerCheckpointOptions struct {
// container no PID 1 will be in the namespace and that is not
// possible.
Pod string
+ // PrintStats tells the API to fill out the statistics about
+ // how much time each component in the stack requires to
+ // checkpoint a container.
+ PrintStats bool
}
// Checkpoint checkpoints a container
-func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
+// The return values *define.CRIUCheckpointRestoreStatistics and int64 (time
+// the runtime needs to checkpoint the container) are only set if
+// options.PrintStats is set to true. Not setting options.PrintStats to true
+// will return nil and 0.
+func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointOptions) (*define.CRIUCheckpointRestoreStatistics, int64, error) {
logrus.Debugf("Trying to checkpoint container %s", c.ID())
if options.TargetFile != "" {
if err := c.prepareCheckpointExport(); err != nil {
- return err
+ return nil, 0, err
}
}
if options.WithPrevious {
if err := c.canWithPrevious(); err != nil {
- return err
+ return nil, 0, err
}
}
@@ -817,14 +825,18 @@ func (c *Container) Checkpoint(ctx context.Context, options ContainerCheckpointO
defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
- return err
+ return nil, 0, err
}
}
return c.checkpoint(ctx, options)
}
// Restore restores a container
-func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) error {
+// The return values *define.CRIUCheckpointRestoreStatistics and int64 (time
+// the runtime needs to restore the container) are only set if
+// options.PrintStats is set to true. Not setting options.PrintStats to true
+// will return nil and 0.
+func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOptions) (*define.CRIUCheckpointRestoreStatistics, int64, error) {
if options.Pod == "" {
logrus.Debugf("Trying to restore container %s", c.ID())
} else {
@@ -835,7 +847,7 @@ func (c *Container) Restore(ctx context.Context, options ContainerCheckpointOpti
defer c.lock.Unlock()
if err := c.syncContainer(); err != nil {
- return err
+ return nil, 0, err
}
}
defer c.newContainerEvent(events.Restore)
diff --git a/libpod/container_config.go b/libpod/container_config.go
index 54d102a71..412be835f 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -78,6 +78,11 @@ type ContainerConfig struct {
// These containers must be started before this container is started.
Dependencies []string
+ // rewrite is an internal bool to indicate that the config was modified after
+ // a read from the db, e.g. to migrate config fields after an upgrade.
+ // This field should never be written to the db, the json tag ensures this.
+ rewrite bool `json:"-"`
+
// embedded sub-configs
ContainerRootFSConfig
ContainerSecurityConfig
@@ -153,6 +158,8 @@ type ContainerRootFSConfig struct {
Secrets []*ContainerSecret `json:"secrets,omitempty"`
// SecretPath is the secrets location in storage
SecretsPath string `json:"secretsPath"`
+ // StorageOpts to be used when creating rootfs
+ StorageOpts map[string]string `json:"storageOpts"`
// 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"`
@@ -226,11 +233,16 @@ type ContainerNetworkConfig struct {
// StaticMAC is a static MAC to request for the container.
// This cannot be set unless CreateNetNS is set.
// If not set, the container will be dynamically assigned a MAC by CNI.
- StaticMAC net.HardwareAddr `json:"staticMAC"`
+ StaticMAC types.HardwareAddr `json:"staticMAC"`
// PortMappings are the ports forwarded to the container's network
// namespace
// These are not used unless CreateNetNS is true
- PortMappings []types.OCICNIPortMapping `json:"portMappings,omitempty"`
+ PortMappings []types.PortMapping `json:"newPortMappings,omitempty"`
+ // OldPortMappings are the ports forwarded to the container's network
+ // namespace. As of podman 4.0 this field is deprecated, use PortMappings
+ // instead. The db will convert the old ports to the new structure for you.
+ // These are not used unless CreateNetNS is true
+ OldPortMappings []types.OCICNIPortMapping `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,
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 43f5398a2..4bf15be86 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -39,6 +39,7 @@ import (
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
)
const (
@@ -290,7 +291,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (_ bool, retErr err
// setup slirp4netns again because slirp4netns will die when conmon exits
if c.config.NetMode.IsSlirp4netns() {
- err := c.runtime.setupSlirp4netns(c)
+ err := c.runtime.setupSlirp4netns(c, c.state.NetNS)
if err != nil {
return false, err
}
@@ -299,7 +300,7 @@ func (c *Container) handleRestartPolicy(ctx context.Context) (_ bool, retErr err
// 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())
+ err := c.runtime.setupRootlessPortMappingViaRLK(c, c.state.NetNS.Path(), c.state.NetworkStatus)
if err != nil {
return false, err
}
@@ -445,10 +446,24 @@ func (c *Container) setupStorage(ctx context.Context) error {
},
LabelOpts: c.config.LabelOpts,
}
- 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.
+
+ nopts := len(c.config.StorageOpts)
+ if nopts > 0 {
+ options.StorageOpt = make(map[string]string, nopts)
+ for _, opt := range c.config.StorageOpts {
+ split2 := strings.SplitN(opt, "=", 2)
+ if len(split2) > 2 {
+ return errors.Wrapf(define.ErrInvalidArg, "invalid storage options %q for %s", opt, c.ID())
+ }
+ options.StorageOpt[split2[0]] = split2[1]
+ }
+ }
+ if c.restoreFromCheckpoint && c.config.ProcessLabel != "" && c.config.MountLabel != "" {
+ // If restoring from a checkpoint, the root file-system needs
+ // to be mounted with the same SELinux labels as it was mounted
+ // previously. But only if both labels have been set. For
+ // privileged containers or '--ipc host' only ProcessLabel will
+ // be set and so we will skip it for cases like that.
if options.Flags == nil {
options.Flags = make(map[string]interface{})
}
@@ -482,9 +497,27 @@ func (c *Container) setupStorage(ctx context.Context) error {
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")
}
// only reconfig IDMappings if layer was mounted from storage
@@ -653,6 +686,19 @@ func (c *Container) refresh() error {
c.state.NetworkStatus = nil
c.state.NetworkStatusOld = nil
+ // Rewrite the config if necessary.
+ // Podman 4.0 uses a new port format in the config.
+ // getContainerConfigFromDB() already converted the old ports to the new one
+ // but it did not write the config to the db back for performance reasons.
+ // If a rewrite must happen the config.rewrite field is set to true.
+ if c.config.rewrite {
+ // SafeRewriteContainerConfig must be used with care. Make sure to not change config fields by accident.
+ if err := c.runtime.state.SafeRewriteContainerConfig(c, "", "", c.config); err != nil {
+ return errors.Wrapf(err, "failed to rewrite the config for container %s", c.config.ID)
+ }
+ c.config.rewrite = false
+ }
+
if err := c.save(); err != nil {
return errors.Wrapf(err, "error refreshing state for container %s", c.ID())
}
@@ -954,9 +1000,6 @@ func (c *Container) completeNetworkSetup() error {
if err := c.syncContainer(); err != nil {
return err
}
- if c.config.NetMode.IsSlirp4netns() {
- return c.runtime.setupSlirp4netns(c)
- }
if err := c.runtime.setupNetNS(c); err != nil {
return err
}
@@ -1046,7 +1089,7 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
}
// With the spec complete, do an OCI create
- if err := c.ociRuntime.CreateContainer(c, nil); err != nil {
+ if _, err = c.ociRuntime.CreateContainer(c, nil); err != nil {
// Fedora 31 is carrying a patch to display improved error
// messages to better handle the V2 transition. This is NOT
// upstream in any OCI runtime.
@@ -1499,8 +1542,8 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
mountPoint := c.config.Rootfs
// Check if overlay has to be created on top of Rootfs
if c.config.RootfsOverlay {
- overlayDest := c.runtime.store.GraphRoot()
- contentDir, err := overlay.GenerateStructure(c.runtime.store.GraphRoot(), c.ID(), "rootfs", c.RootUID(), c.RootGID())
+ overlayDest := c.runtime.RunRoot()
+ contentDir, err := overlay.GenerateStructure(overlayDest, c.ID(), "rootfs", c.RootUID(), c.RootGID())
if err != nil {
return "", errors.Wrapf(err, "rootfs-overlay: failed to create TempDir in the %s directory", overlayDest)
}
@@ -1550,14 +1593,49 @@ func (c *Container) mountStorage() (_ string, deferredErr error) {
}()
}
+ 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.
- mtab := filepath.Join(mountPoint, "/etc/mtab")
- if err := idtools.MkdirAllAs(filepath.Dir(mtab), 0755, c.RootUID(), c.RootGID()); err != nil {
- return "", errors.Wrap(err, "error creating mtab directory")
+ err = unix.Symlinkat("/proc/mounts", etcInTheContainerFd, "mtab")
+ if err != nil && !os.IsExist(err) {
+ return "", errors.Wrap(err, "creating /etc/mtab symlink")
}
- if err = os.Symlink("/proc/mounts", mtab); err != nil && !os.IsExist(err) {
- return "", err
+ // 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
@@ -1725,11 +1803,11 @@ func (c *Container) cleanupStorage() error {
// umount rootfs overlay if it was created
if c.config.RootfsOverlay {
- overlayBasePath := filepath.Dir(c.config.StaticDir)
- overlayBasePath = filepath.Join(overlayBasePath, "rootfs")
+ overlayBasePath := filepath.Dir(c.state.Mountpoint)
if err := overlay.Unmount(overlayBasePath); err != nil {
- // If the container can't remove content report the error
- logrus.Errorf("Failed to cleanup overlay mounts for %s: %v", c.ID(), err)
+ if cleanupErr != nil {
+ logrus.Errorf("Failed to cleanup overlay mounts for %s: %v", c.ID(), err)
+ }
cleanupErr = err
}
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 27cc318b4..4a15d495f 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -21,6 +21,7 @@ import (
"time"
metadata "github.com/checkpoint-restore/checkpointctl/lib"
+ "github.com/checkpoint-restore/go-criu/v5/stats"
cdi "github.com/container-orchestrated-devices/container-device-interface/pkg"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/buildah/pkg/chrootuser"
@@ -107,15 +108,6 @@ func (c *Container) prepare() error {
c.state.NetNS = netNS
c.state.NetworkStatus = networkStatus
}
-
- // handle rootless network namespace setup
- if noNetNS && !c.config.PostConfigureNetNS {
- if rootless.IsRootless() {
- createNetNSErr = c.runtime.setupRootlessNetNS(c)
- } else if c.config.NetMode.IsSlirp4netns() {
- createNetNSErr = c.runtime.setupSlirp4netns(c)
- }
- }
}()
// Mount storage if not mounted
go func() {
@@ -322,7 +314,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) {
return nil, err
}
- g := generate.Generator{Config: c.config.Spec}
+ g := generate.NewFromSpec(c.config.Spec)
// If network namespace was requested, add it now
if c.config.CreateNetNS {
@@ -1013,6 +1005,7 @@ func (c *Container) exportCheckpoint(options ContainerCheckpointOptions) error {
metadata.ConfigDumpFile,
metadata.SpecDumpFile,
metadata.NetworkStatusFile,
+ stats.StatsDump,
}
if c.LogDriver() == define.KubernetesLogging ||
@@ -1136,25 +1129,26 @@ func (c *Container) checkpointRestoreSupported(version int) error {
return nil
}
-func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) error {
+func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointOptions) (*define.CRIUCheckpointRestoreStatistics, int64, error) {
if err := c.checkpointRestoreSupported(criu.MinCriuVersion); err != nil {
- return err
+ return nil, 0, err
}
if c.state.State != define.ContainerStateRunning {
- return errors.Wrapf(define.ErrCtrStateInvalid, "%q is not running, cannot checkpoint", c.state.State)
+ return nil, 0, errors.Wrapf(define.ErrCtrStateInvalid, "%q is not running, cannot checkpoint", c.state.State)
}
if c.AutoRemove() && options.TargetFile == "" {
- return errors.Errorf("cannot checkpoint containers that have been started with '--rm' unless '--export' is used")
+ return nil, 0, errors.Errorf("cannot checkpoint containers that have been started with '--rm' unless '--export' is used")
}
if err := crutils.CRCreateFileWithLabel(c.bundlePath(), "dump.log", c.MountLabel()); err != nil {
- return err
+ return nil, 0, err
}
- if err := c.ociRuntime.CheckpointContainer(c, options); err != nil {
- return err
+ runtimeCheckpointDuration, err := c.ociRuntime.CheckpointContainer(c, options)
+ if err != nil {
+ return nil, 0, err
}
// Save network.status. This is needed to restore the container with
@@ -1162,7 +1156,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
// with one interface.
// FIXME: will this break something?
if _, err := metadata.WriteJSONFile(c.getNetworkStatus(), c.bundlePath(), metadata.NetworkStatusFile); err != nil {
- return err
+ return nil, 0, err
}
defer c.newContainerEvent(events.Checkpoint)
@@ -1172,13 +1166,13 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
if options.WithPrevious {
os.Remove(path.Join(c.CheckpointPath(), "parent"))
if err := os.Symlink("../pre-checkpoint", path.Join(c.CheckpointPath(), "parent")); err != nil {
- return err
+ return nil, 0, err
}
}
if options.TargetFile != "" {
if err := c.exportCheckpoint(options); err != nil {
- return err
+ return nil, 0, err
}
}
@@ -1190,14 +1184,41 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
// Cleanup Storage and Network
if err := c.cleanup(ctx); err != nil {
- return err
+ return nil, 0, err
}
}
+ criuStatistics, err := func() (*define.CRIUCheckpointRestoreStatistics, error) {
+ if !options.PrintStats {
+ return nil, nil
+ }
+ statsDirectory, err := os.Open(c.bundlePath())
+ if err != nil {
+ return nil, errors.Wrapf(err, "Not able to open %q", c.bundlePath())
+ }
+
+ dumpStatistics, err := stats.CriuGetDumpStats(statsDirectory)
+ if err != nil {
+ return nil, errors.Wrap(err, "Displaying checkpointing statistics not possible")
+ }
+
+ return &define.CRIUCheckpointRestoreStatistics{
+ FreezingTime: dumpStatistics.GetFreezingTime(),
+ FrozenTime: dumpStatistics.GetFrozenTime(),
+ MemdumpTime: dumpStatistics.GetMemdumpTime(),
+ MemwriteTime: dumpStatistics.GetMemwriteTime(),
+ PagesScanned: dumpStatistics.GetPagesScanned(),
+ PagesWritten: dumpStatistics.GetPagesWritten(),
+ }, nil
+ }()
+ if err != nil {
+ return nil, 0, err
+ }
+
if !options.Keep && !options.PreCheckPoint {
cleanup := []string{
"dump.log",
- "stats-dump",
+ stats.StatsDump,
metadata.ConfigDumpFile,
metadata.SpecDumpFile,
}
@@ -1210,7 +1231,7 @@ func (c *Container) checkpoint(ctx context.Context, options ContainerCheckpointO
}
c.state.FinishedTime = time.Now()
- return c.save()
+ return criuStatistics, runtimeCheckpointDuration, c.save()
}
func (c *Container) importCheckpoint(input string) error {
@@ -1219,7 +1240,8 @@ func (c *Container) importCheckpoint(input string) error {
}
// Make sure the newly created config.json exists on disk
- g := generate.Generator{Config: c.config.Spec}
+ g := generate.NewFromSpec(c.config.Spec)
+
if err := c.saveSpec(g.Config); err != nil {
return errors.Wrap(err, "saving imported container specification for restore failed")
}
@@ -1242,7 +1264,7 @@ func (c *Container) importPreCheckpoint(input string) error {
return nil
}
-func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (retErr error) {
+func (c *Container) restore(ctx context.Context, options ContainerCheckpointOptions) (criuStatistics *define.CRIUCheckpointRestoreStatistics, runtimeRestoreDuration int64, retErr error) {
minCriuVersion := func() int {
if options.Pod == "" {
return criu.MinCriuVersion
@@ -1250,37 +1272,37 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
return criu.PodCriuVersion
}()
if err := c.checkpointRestoreSupported(minCriuVersion); err != nil {
- return err
+ return nil, 0, err
}
if options.Pod != "" && !crutils.CRRuntimeSupportsPodCheckpointRestore(c.ociRuntime.Path()) {
- return errors.Errorf("runtime %s does not support pod restore", c.ociRuntime.Path())
+ return nil, 0, 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())
+ return nil, 0, errors.Wrapf(define.ErrCtrStateInvalid, "container %s is running or paused, cannot restore", c.ID())
}
if options.ImportPrevious != "" {
if err := c.importPreCheckpoint(options.ImportPrevious); err != nil {
- return err
+ return nil, 0, err
}
}
if options.TargetFile != "" {
if err := c.importCheckpoint(options.TargetFile); err != nil {
- return err
+ return nil, 0, err
}
}
// Let's try to stat() CRIU's inventory file. If it does not exist, it makes
// no sense to try a restore. This is a minimal check if a checkpoint exist.
if _, err := os.Stat(filepath.Join(c.CheckpointPath(), "inventory.img")); os.IsNotExist(err) {
- return errors.Wrapf(err, "a complete checkpoint for this container cannot be found, cannot restore")
+ return nil, 0, errors.Wrapf(err, "a complete checkpoint for this container cannot be found, cannot restore")
}
if err := crutils.CRCreateFileWithLabel(c.bundlePath(), "restore.log", c.MountLabel()); err != nil {
- return err
+ return nil, 0, err
}
// If a container is restored multiple times from an exported checkpoint with
@@ -1317,7 +1339,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// container with the same networks settings as during checkpointing.
aliases, err := c.GetAllNetworkAliases()
if err != nil {
- return err
+ return nil, 0, err
}
netOpts := make(map[string]types.PerNetworkOptions, len(netStatus))
for network, status := range netStatus {
@@ -1342,7 +1364,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
if perNetOpts.InterfaceName == "" {
eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(network)
if !exists {
- return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network)
+ return nil, 0, errors.Errorf("no network interface name for container %s on network %s", c.config.ID, network)
}
perNetOpts.InterfaceName = eth
}
@@ -1360,7 +1382,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}()
if err := c.prepare(); err != nil {
- return err
+ return nil, 0, err
}
// Read config
@@ -1369,7 +1391,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
g, err := generate.NewFromFile(jsonPath)
if err != nil {
logrus.Debugf("generate.NewFromFile failed with %v", err)
- return err
+ return nil, 0, err
}
// Restoring from an import means that we are doing migration
@@ -1385,7 +1407,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), netNSPath); err != nil {
- return err
+ return nil, 0, err
}
}
@@ -1394,23 +1416,23 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// 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)
+ return nil, 0, 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)
+ return nil, 0, 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())
+ return nil, 0, 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())
+ return nil, 0, errors.Wrapf(err, "Error starting infrastructure container %s status", infraContainer.ID())
}
}
infraContainer.lock.Unlock()
@@ -1418,56 +1440,56 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
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)
+ return nil, 0, 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
+ return nil, 0, 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)
+ return nil, 0, 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
+ return nil, 0, 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)
+ return nil, 0, 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
+ return nil, 0, 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)
+ return nil, 0, 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
+ return nil, 0, 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)
+ return nil, 0, 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
+ return nil, 0, err
}
}
}
if err := c.makeBindMounts(); err != nil {
- return err
+ return nil, 0, err
}
if options.TargetFile != "" {
@@ -1489,12 +1511,12 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// Cleanup for a working restore.
if err := c.removeConmonFiles(); err != nil {
- return err
+ return nil, 0, err
}
// Save the OCI spec to disk
if err := c.saveSpec(g.Config); err != nil {
- return err
+ return nil, 0, err
}
// When restoring from an imported archive, allow restoring the content of volumes.
@@ -1505,24 +1527,24 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
volumeFile, err := os.Open(volumeFilePath)
if err != nil {
- return errors.Wrapf(err, "failed to open volume file %s", volumeFilePath)
+ return nil, 0, errors.Wrapf(err, "failed to open volume file %s", volumeFilePath)
}
defer volumeFile.Close()
volume, err := c.runtime.GetVolume(v.Name)
if err != nil {
- return errors.Wrapf(err, "failed to retrieve volume %s", v.Name)
+ return nil, 0, errors.Wrapf(err, "failed to retrieve volume %s", v.Name)
}
mountPoint, err := volume.MountPoint()
if err != nil {
- return err
+ return nil, 0, err
}
if mountPoint == "" {
- return errors.Wrapf(err, "unable to import volume %s as it is not mounted", volume.Name())
+ return nil, 0, errors.Wrapf(err, "unable to import volume %s as it is not mounted", volume.Name())
}
if err := archive.UntarUncompressed(volumeFile, mountPoint, nil); err != nil {
- return errors.Wrapf(err, "Failed to extract volume %s to %s", volumeFilePath, mountPoint)
+ return nil, 0, errors.Wrapf(err, "Failed to extract volume %s to %s", volumeFilePath, mountPoint)
}
}
}
@@ -1530,21 +1552,49 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
// Before actually restarting the container, apply the root file-system changes
if !options.IgnoreRootfs {
if err := crutils.CRApplyRootFsDiffTar(c.bundlePath(), c.state.Mountpoint); err != nil {
- return err
+ return nil, 0, err
}
if err := crutils.CRRemoveDeletedFiles(c.ID(), c.bundlePath(), c.state.Mountpoint); err != nil {
- return err
+ return nil, 0, err
}
}
- if err := c.ociRuntime.CreateContainer(c, &options); err != nil {
- return err
+ runtimeRestoreDuration, err = c.ociRuntime.CreateContainer(c, &options)
+ if err != nil {
+ return nil, 0, err
+ }
+
+ criuStatistics, err = func() (*define.CRIUCheckpointRestoreStatistics, error) {
+ if !options.PrintStats {
+ return nil, nil
+ }
+ statsDirectory, err := os.Open(c.bundlePath())
+ if err != nil {
+ return nil, errors.Wrapf(err, "Not able to open %q", c.bundlePath())
+ }
+
+ restoreStatistics, err := stats.CriuGetRestoreStats(statsDirectory)
+ if err != nil {
+ return nil, errors.Wrap(err, "Displaying restore statistics not possible")
+ }
+
+ return &define.CRIUCheckpointRestoreStatistics{
+ PagesCompared: restoreStatistics.GetPagesCompared(),
+ PagesSkippedCow: restoreStatistics.GetPagesSkippedCow(),
+ ForkingTime: restoreStatistics.GetForkingTime(),
+ RestoreTime: restoreStatistics.GetRestoreTime(),
+ PagesRestored: restoreStatistics.GetPagesRestored(),
+ }, nil
+ }()
+ if err != nil {
+ return nil, 0, err
}
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
@@ -1562,8 +1612,8 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
cleanup := [...]string{
"restore.log",
"dump.log",
- "stats-dump",
- "stats-restore",
+ stats.StatsDump,
+ stats.StatsRestore,
metadata.NetworkStatusFile,
metadata.RootFsDiffTar,
metadata.DeletedFilesFile,
@@ -1577,7 +1627,7 @@ func (c *Container) restore(ctx context.Context, options ContainerCheckpointOpti
}
}
- return c.save()
+ return criuStatistics, runtimeRestoreDuration, c.save()
}
// Retrieves a container's "root" net namespace container dependency.
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 562169ce2..4029d0af7 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -121,7 +121,24 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
}()
tailQueue := []*logs.LogLine{} // needed for options.Tail
- doTail := options.Tail > 0
+ 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 := ""
for {
select {
@@ -152,16 +169,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
// Hit the end of the journal (so far?).
if cursor == lastReadCursor {
if doTail {
- // Flush *once* we hit the end of the journal.
- startIndex := int64(len(tailQueue)-1) - options.Tail
- if startIndex < 0 {
- startIndex = 0
- }
- for i := startIndex; i < int64(len(tailQueue)); i++ {
- logChannel <- tailQueue[i]
- }
- tailQueue = nil
- doTail = false
+ doTailFunc()
}
// Unless we follow, quit.
if !options.Follow {
@@ -194,6 +202,9 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
return
}
if status == events.Exited {
+ if doTail {
+ doTailFunc()
+ }
return
}
continue
diff --git a/libpod/container_path_resolution.go b/libpod/container_path_resolution.go
index bb2ef1a73..7db23b783 100644
--- a/libpod/container_path_resolution.go
+++ b/libpod/container_path_resolution.go
@@ -161,7 +161,7 @@ func isPathOnBindMount(c *Container, containerPath string) bool {
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 --git a/libpod/define/checkpoint_restore.go b/libpod/define/checkpoint_restore.go
new file mode 100644
index 000000000..536bdde9a
--- /dev/null
+++ b/libpod/define/checkpoint_restore.go
@@ -0,0 +1,32 @@
+package define
+
+// This contains values reported by CRIU during
+// checkpointing or restoring.
+// All names are the same as reported by CRIU.
+type CRIUCheckpointRestoreStatistics struct {
+ // Checkpoint values
+ // Time required to freeze/pause/quiesce the processes
+ FreezingTime uint32 `json:"freezing_time,omitempty"`
+ // Time the processes are actually not running during checkpointing
+ FrozenTime uint32 `json:"frozen_time,omitempty"`
+ // Time required to extract memory pages from the processes
+ MemdumpTime uint32 `json:"memdump_time,omitempty"`
+ // Time required to write memory pages to disk
+ MemwriteTime uint32 `json:"memwrite_time,omitempty"`
+ // Number of memory pages CRIU analyzed
+ PagesScanned uint64 `json:"pages_scanned,omitempty"`
+ // Number of memory pages written
+ PagesWritten uint64 `json:"pages_written,omitempty"`
+
+ // Restore values
+ // Number of pages compared during restore
+ PagesCompared uint64 `json:"pages_compared,omitempty"`
+ // Number of COW pages skipped during restore
+ PagesSkippedCow uint64 `json:"pages_skipped_cow,omitempty"`
+ // Time required to fork processes
+ ForkingTime uint32 `json:"forking_time,omitempty"`
+ // Time required to restore
+ RestoreTime uint32 `json:"restore_time,omitempty"`
+ // Number of memory pages restored
+ PagesRestored uint64 `json:"pages_restored,omitempty"`
+}
diff --git a/libpod/define/info.go b/libpod/define/info.go
index 61f2f4c75..15400991f 100644
--- a/libpod/define/info.go
+++ b/libpod/define/info.go
@@ -39,6 +39,7 @@ type HostInfo struct {
LogDriver string `json:"logDriver"`
MemFree int64 `json:"memFree"`
MemTotal int64 `json:"memTotal"`
+ NetworkBackend string `json:"networkBackend"`
OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"`
OS string `json:"os"`
// RemoteSocket returns the UNIX domain socket the Podman service is listening on
diff --git a/libpod/info.go b/libpod/info.go
index a2fd18491..a1db5763a 100644
--- a/libpod/info.go
+++ b/libpod/info.go
@@ -131,6 +131,7 @@ func (r *Runtime) hostInfo() (*define.HostInfo, error) {
Kernel: kv,
MemFree: mi.MemFree,
MemTotal: mi.MemTotal,
+ NetworkBackend: r.config.Network.NetworkBackend,
OS: runtime.GOOS,
Security: define.SecurityInfo{
AppArmorEnabled: apparmor.IsEnabled(),
@@ -332,7 +333,7 @@ func readKernelVersion() (string, error) {
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
diff --git a/libpod/kube.go b/libpod/kube.go
index d47f47f1c..850f2493c 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -11,6 +11,7 @@ import (
"strings"
"time"
+ "github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/env"
@@ -74,7 +75,7 @@ func (p *Pod) GenerateForKube(ctx context.Context) (*v1.Pod, []v1.ServicePort, e
Hostnames: []string{hostSli[0]},
})
}
- ports, err = ocicniPortMappingToContainerPort(infraContainer.config.PortMappings)
+ ports, err = portMappingToContainerPort(infraContainer.config.PortMappings)
if err != nil {
return nil, servicePorts, err
}
@@ -166,9 +167,83 @@ func (v *Volume) GenerateForKube() *v1.PersistentVolumeClaim {
}
}
+// 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 {
+ service := YAMLService{}
selector := make(map[string]string)
selector["app"] = pod.Labels["app"]
ports := servicePorts
@@ -332,7 +407,7 @@ func newPodObject(podName string, annotations map[string]string, initCtrs, conta
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{
@@ -447,16 +522,11 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
kubeVolumes = append(kubeVolumes, volumes...)
}
- envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env)
- if err != nil {
- return kubeContainer, kubeVolumes, nil, annotations, err
- }
-
portmappings, err := c.PortMappings()
if err != nil {
return kubeContainer, kubeVolumes, nil, annotations, err
}
- ports, err := ocicniPortMappingToContainerPort(portmappings)
+ ports, err := portMappingToContainerPort(portmappings)
if err != nil {
return kubeContainer, kubeVolumes, nil, annotations, err
}
@@ -473,11 +543,23 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
kubeContainer.Name = removeUnderscores(c.Name())
_, image := c.Image()
+
+ // The infra container may have been created with an overlay root FS
+ // instead of an infra image. If so, set the imageto the default K8s
+ // pause one and make sure it's in the storage by pulling it down if
+ // missing.
+ if image == "" && c.IsInfra() {
+ image = config.DefaultInfraImage
+ if _, err := c.runtime.libimageRuntime.Pull(ctx, image, config.PullPolicyMissing, nil); err != nil {
+ return kubeContainer, nil, nil, nil, err
+ }
+ }
+
kubeContainer.Image = image
kubeContainer.Stdin = c.Stdin()
img, _, err := c.runtime.libimageRuntime.LookupImage(image, nil)
if err != nil {
- return kubeContainer, kubeVolumes, nil, annotations, err
+ return kubeContainer, kubeVolumes, nil, annotations, fmt.Errorf("looking up image %q of container %q: %w", image, c.ID(), err)
}
imgData, err := img.Inspect(ctx, nil)
if err != nil {
@@ -489,15 +571,23 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
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
}
- kubeContainer.WorkingDir = c.WorkingDir()
+ envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env, imgData.Config.Env)
+ if err != nil {
+ return kubeContainer, kubeVolumes, nil, annotations, err
+ }
+ kubeContainer.Env = envVariables
+
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
@@ -572,37 +662,49 @@ func containerToV1Container(ctx context.Context, c *Container) (v1.Container, []
return kubeContainer, kubeVolumes, &dns, annotations, nil
}
-// ocicniPortMappingToContainerPort takes an ocicni portmapping and converts
+// portMappingToContainerPort takes an portmapping and converts
// it to a v1.ContainerPort format for kube output
-func ocicniPortMappingToContainerPort(portMappings []types.OCICNIPortMapping) ([]v1.ContainerPort, error) {
+func portMappingToContainerPort(portMappings []types.PortMapping) ([]v1.ContainerPort, error) {
containerPorts := make([]v1.ContainerPort, 0, len(portMappings))
for _, p := range portMappings {
- var protocol v1.Protocol
- switch strings.ToUpper(p.Protocol) {
- case "TCP":
- // 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:
- return containerPorts, errors.Errorf("unknown network protocol %s", p.Protocol)
- }
- cp := v1.ContainerPort{
- // Name will not be supported
- HostPort: p.HostPort,
- HostIP: p.HostIP,
- ContainerPort: p.ContainerPort,
- Protocol: protocol,
- }
- containerPorts = append(containerPorts, cp)
+ protocols := strings.Split(p.Protocol, ",")
+ for _, proto := range protocols {
+ var protocol v1.Protocol
+ switch strings.ToUpper(proto) {
+ case "TCP":
+ // 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
+ case "SCTP":
+ protocol = v1.ProtocolSCTP
+ default:
+ return containerPorts, errors.Errorf("unknown network protocol %s", p.Protocol)
+ }
+ for i := uint16(0); i < p.Range; i++ {
+ cp := v1.ContainerPort{
+ // Name will not be supported
+ HostPort: int32(p.HostPort + i),
+ HostIP: p.HostIP,
+ ContainerPort: int32(p.ContainerPort + i),
+ Protocol: protocol,
+ }
+ containerPorts = append(containerPorts, cp)
+ }
+ }
}
return containerPorts, nil
}
// 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 {
@@ -611,6 +713,9 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) {
if defaultEnv[split[0]] == split[1] {
continue
}
+ if imageMap[split[0]] == split[1] {
+ continue
+ }
ev := v1.EnvVar{
Name: split[0],
Value: split[1],
@@ -808,33 +913,42 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
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 --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go
index 322e92a8f..fea02a619 100644
--- a/libpod/lock/shm/shm_lock.go
+++ b/libpod/lock/shm/shm_lock.go
@@ -130,8 +130,17 @@ func (locks *SHMLocks) AllocateSemaphore() (uint32, error) {
// 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 --git a/libpod/network/cni/cni_conversion.go b/libpod/network/cni/cni_conversion.go
index 01e149114..70d259b60 100644
--- a/libpod/network/cni/cni_conversion.go
+++ b/libpod/network/cni/cni_conversion.go
@@ -14,6 +14,7 @@ import (
"time"
"github.com/containernetworking/cni/libcni"
+ internalutil "github.com/containers/podman/v3/libpod/network/internal/util"
"github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/libpod/network/util"
pkgutil "github.com/containers/podman/v3/pkg/util"
@@ -156,10 +157,7 @@ func convertIPAMConfToNetwork(network *types.Network, ipam ipamConfig, confPath
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
- }
+ util.NormalizeIP(&gateway)
} else if !network.Internal {
// only add a gateway address if the network is not internal
gateway, err = util.FirstIPInSubnet(sub)
@@ -244,13 +242,13 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
for k, v := range network.Options {
switch k {
case "mtu":
- mtu, err = parseMTU(v)
+ mtu, err = internalutil.ParseMTU(v)
if err != nil {
return nil, "", err
}
case "vlan":
- vlan, err = parseVlan(v)
+ vlan, err = internalutil.ParseVlan(v)
if err != nil {
return nil, "", err
}
@@ -339,36 +337,6 @@ func (n *cniNetwork) createCNIConfigListFromNetwork(network *types.Network, writ
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 {
diff --git a/libpod/network/cni/cni_types.go b/libpod/network/cni/cni_types.go
index 87beceff3..c70cb92b6 100644
--- a/libpod/network/cni/cni_types.go
+++ b/libpod/network/cni/cni_types.go
@@ -176,7 +176,7 @@ func newIPAMLocalHostRange(subnet types.IPNet, leaseRange *types.LeaseRange, gw
Subnet: subnet.String(),
}
- // an user provided a range, we add it here
+ // a user provided a range, we add it here
if leaseRange != nil {
if leaseRange.StartIP != nil {
hostRange.RangeStart = leaseRange.StartIP.String()
diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go
index 3df155637..5d587da23 100644
--- a/libpod/network/cni/config.go
+++ b/libpod/network/cni/config.go
@@ -7,8 +7,8 @@ import (
"os"
"github.com/containers/podman/v3/libpod/define"
+ internalutil "github.com/containers/podman/v3/libpod/network/internal/util"
"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"
@@ -47,32 +47,9 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create")
}
- if newNetwork.Labels == nil {
- newNetwork.Labels = map[string]string{}
- }
- if newNetwork.Options == nil {
- newNetwork.Options = map[string]string{}
- }
- if newNetwork.IPAMOptions == nil {
- newNetwork.IPAMOptions = map[string]string{}
- }
-
- var name string
- var err error
- // validate the name when given
- if newNetwork.Name != "" {
- if !define.NameRegex.MatchString(newNetwork.Name) {
- return nil, errors.Wrapf(define.RegexError, "network name %s invalid", newNetwork.Name)
- }
- if _, ok := n.networks[newNetwork.Name]; ok {
- return nil, errors.Wrapf(define.ErrNetworkExists, "network name %s already used", newNetwork.Name)
- }
- } else {
- name, err = n.getFreeDeviceName()
- if err != nil {
- return nil, err
- }
- newNetwork.Name = name
+ err := internalutil.CommonNetworkCreate(n, &newNetwork)
+ if err != nil {
+ return nil, err
}
// Only get the used networks for validation if we do not create the default network.
@@ -84,7 +61,7 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
// fail because it thinks the network is used on the host.
var usedNetworks []*net.IPNet
if !defaultNet {
- usedNetworks, err = n.getUsedSubnets()
+ usedNetworks, err = internalutil.GetUsedSubnets(n)
if err != nil {
return nil, err
}
@@ -92,11 +69,7 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
switch newNetwork.Driver {
case types.BridgeNetworkDriver:
- // if the name was created with getFreeDeviceName set the interface to it as well
- if name != "" && newNetwork.NetworkInterface == "" {
- newNetwork.NetworkInterface = name
- }
- err = n.createBridge(&newNetwork, usedNetworks)
+ err = internalutil.CreateBridge(n, &newNetwork, usedNetworks)
if err != nil {
return nil, err
}
@@ -109,14 +82,9 @@ func (n *cniNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*
return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver)
}
- for i := range newNetwork.Subnets {
- err := validateSubnet(&newNetwork.Subnets[i], !newNetwork.Internal, usedNetworks)
- if err != nil {
- return nil, err
- }
- if util.IsIPv6(newNetwork.Subnets[i].Subnet.IP) {
- newNetwork.IPv6Enabled = true
- }
+ err = internalutil.ValidateSubnets(&newNetwork, usedNetworks)
+ if err != nil {
+ return nil, err
}
// generate the network ID
@@ -223,7 +191,7 @@ func createIPMACVLAN(network *types.Network) error {
return errors.New("internal is not supported with macvlan")
}
if network.NetworkInterface != "" {
- interfaceNames, err := util.GetLiveNetworkNames()
+ interfaceNames, err := internalutil.GetLiveNetworkNames()
if err != nil {
return err
}
@@ -238,107 +206,3 @@ func createIPMACVLAN(network *types.Network) error {
}
return nil
}
-
-func (n *cniNetwork) createBridge(network *types.Network, usedNetworks []*net.IPNet) 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(usedNetworks)
- 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(usedNetworks)
- if err != nil {
- return err
- }
- network.Subnets = append(network.Subnets, *freeSubnet)
- }
- if !ipv6 {
- freeSubnet, err := n.getFreeIPv6NetworkSubnet(usedNetworks)
- 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, usedNetworks []*net.IPNet) error {
- if s == nil {
- return errors.New("subnet is nil")
- }
- if s.Subnet.IP == nil {
- return errors.New("subnet ip 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")
- }
-
- // check that the new subnet does not conflict with existing ones
- if util.NetworkIntersectsWithNetworks(net, usedNetworks) {
- return errors.Errorf("subnet %s is already used on the host or by another config", net.String())
- }
-
- 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 --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go
index a37a84373..3e9cdaa47 100644
--- a/libpod/network/cni/network.go
+++ b/libpod/network/cni/network.go
@@ -6,8 +6,6 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "fmt"
- "net"
"os"
"strings"
"time"
@@ -15,8 +13,6 @@ import (
"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"
@@ -242,111 +238,29 @@ func getNetworkIDFromName(name string) string {
return hex.EncodeToString(hash[:])
}
-// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet
-func (n *cniNetwork) getFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
- // 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, usedNetworks); !intersectsConfig {
- logrus.Debugf("found free ipv4 network subnet %s", network.String())
- return &types.Subnet{
- Subnet: types.IPNet{IPNet: *network},
- }, nil
- }
- var err error
- network, err = util.NextSubnet(network)
- if err != nil {
- return nil, err
- }
- }
-}
+// Implement the NetUtil interface for easy code sharing with other network interfaces.
-// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet
-func (n *cniNetwork) getFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
- // 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, usedNetworks); !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))
+// ForEach call the given function for each network
+func (n *cniNetwork) ForEach(run func(types.Network)) {
for _, val := range n.networks {
- for i := range val.libpodNet.Subnets {
- subnets = append(subnets, &val.libpodNet.Subnets[i].Subnet.IPNet)
- }
+ run(*val.libpodNet)
}
- // 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")
+// Len return the number of networks
+func (n *cniNetwork) Len() int {
+ return len(n.networks)
}
-// 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
+// DefaultInterfaceName return the default cni bridge name, must be suffixed with a number.
+func (n *cniNetwork) DefaultInterfaceName() string {
+ return cniDeviceName
}
-// 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)
- }
+func (n *cniNetwork) Network(nameOrID string) (*types.Network, error) {
+ network, err := n.getNetwork(nameOrID)
+ if err != nil {
+ return nil, err
}
- return names
+ return network.libpodNet, err
}
diff --git a/libpod/network/cni/run.go b/libpod/network/cni/run.go
index bd873f89b..667ed3ab1 100644
--- a/libpod/network/cni/run.go
+++ b/libpod/network/cni/run.go
@@ -13,6 +13,7 @@ import (
types040 "github.com/containernetworking/cni/pkg/types/040"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/internal/util"
"github.com/containers/podman/v3/libpod/network/types"
"github.com/hashicorp/go-multierror"
"github.com/pkg/errors"
@@ -30,24 +31,9 @@ func (n *cniNetwork) Setup(namespacePath string, options types.SetupOptions) (ma
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
- }
+ err = util.ValidateSetupOptions(n, namespacePath, options)
+ if err != nil {
+ return nil, err
}
// set the loopback adapter up in the container netns
@@ -160,7 +146,7 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
return result, err
}
interfaces[cniInt.Name] = types.NetInterface{
- MacAddress: mac,
+ MacAddress: types.HardwareAddr(mac),
Networks: []types.NetAddress{{
Subnet: types.IPNet{IPNet: ip.Address},
Gateway: ip.Gateway,
@@ -172,23 +158,6 @@ func CNIResultToStatus(res cnitypes.Result) (types.StatusBlock, error) {
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)
- }
- return nil
-}
-
func getRuntimeConfig(netns, conName, conID, networkName string, ports []cniPortMapEntry, opts types.PerNetworkOptions) *libcni.RuntimeConf {
rt := &libcni.RuntimeConf{
ContainerID: conID,
@@ -196,10 +165,8 @@ func getRuntimeConfig(netns, conName, conID, networkName string, ports []cniPort
IfName: opts.InterfaceName,
Args: [][2]string{
{"IgnoreUnknown", "1"},
- // FIXME: Should we set the K8S args?
- //{"K8S_POD_NAMESPACE", conName},
- //{"K8S_POD_INFRA_CONTAINER_ID", conID},
- // K8S_POD_NAME is used by dnsname to get the container name
+ // 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{}{},
diff --git a/libpod/network/cni/run_test.go b/libpod/network/cni/run_test.go
index 965203c2a..6c54f82ef 100644
--- a/libpod/network/cni/run_test.go
+++ b/libpod/network/cni/run_test.go
@@ -398,7 +398,7 @@ var _ = Describe("run CNI", func() {
i, err := net.InterfaceByName(intName1)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName1))
- Expect(i.HardwareAddr).To(Equal(macInt1))
+ Expect(i.HardwareAddr).To(Equal((net.HardwareAddr)(macInt1)))
addrs, err := i.Addrs()
Expect(err).To(BeNil())
subnet := &net.IPNet{
@@ -448,7 +448,7 @@ var _ = Describe("run CNI", func() {
i, err := net.InterfaceByName(intName1)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName1))
- Expect(i.HardwareAddr).To(Equal(macInt1))
+ Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1)))
addrs, err := i.Addrs()
Expect(err).To(BeNil())
subnet := &net.IPNet{
@@ -460,7 +460,7 @@ var _ = Describe("run CNI", func() {
i, err = net.InterfaceByName(intName2)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName2))
- Expect(i.HardwareAddr).To(Equal(macInt2))
+ Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt2)))
addrs, err = i.Addrs()
Expect(err).To(BeNil())
subnet = &net.IPNet{
@@ -600,7 +600,7 @@ var _ = Describe("run CNI", func() {
i, err := net.InterfaceByName(intName1)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName1))
- Expect(i.HardwareAddr).To(Equal(macInt1))
+ Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt1)))
addrs, err := i.Addrs()
Expect(err).To(BeNil())
subnet := &net.IPNet{
@@ -612,7 +612,7 @@ var _ = Describe("run CNI", func() {
i, err = net.InterfaceByName(intName2)
Expect(err).To(BeNil())
Expect(i.Name).To(Equal(intName2))
- Expect(i.HardwareAddr).To(Equal(macInt2))
+ Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macInt2)))
addrs, err = i.Addrs()
Expect(err).To(BeNil())
subnet = &net.IPNet{
@@ -690,7 +690,7 @@ var _ = Describe("run CNI", func() {
netName: {
InterfaceName: interfaceName,
StaticIPs: []net.IP{ip1, ip2},
- StaticMAC: mac,
+ StaticMAC: types.HardwareAddr(mac),
},
},
},
@@ -708,7 +708,7 @@ var _ = Describe("run CNI", func() {
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))
+ Expect(res[netName].Interfaces[interfaceName].MacAddress).To(Equal(types.HardwareAddr(mac)))
// default network has no dns
Expect(res[netName].DNSServerIPs).To(BeEmpty())
Expect(res[netName].DNSSearchDomains).To(BeEmpty())
@@ -1232,7 +1232,7 @@ var _ = Describe("run CNI", func() {
}
_, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
Expect(err).To(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("network somenet: network not found"))
+ Expect(err.Error()).To(ContainSubstring("unable to find network with name or ID somenet: network not found"))
})
})
diff --git a/libpod/network/internal/util/bridge.go b/libpod/network/internal/util/bridge.go
new file mode 100644
index 000000000..476557050
--- /dev/null
+++ b/libpod/network/internal/util/bridge.go
@@ -0,0 +1,69 @@
+package util
+
+import (
+ "net"
+
+ "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"
+)
+
+func CreateBridge(n NetUtil, network *types.Network, usedNetworks []*net.IPNet) error {
+ if network.NetworkInterface != "" {
+ bridges := GetBridgeInterfaceNames(n)
+ 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 = GetFreeDeviceName(n)
+ if err != nil {
+ return err
+ }
+ }
+
+ if network.IPAMOptions["driver"] != types.DHCPIPAMDriver {
+ if len(network.Subnets) == 0 {
+ freeSubnet, err := GetFreeIPv4NetworkSubnet(usedNetworks)
+ 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 := GetFreeIPv4NetworkSubnet(usedNetworks)
+ if err != nil {
+ return err
+ }
+ network.Subnets = append(network.Subnets, *freeSubnet)
+ }
+ if !ipv6 {
+ freeSubnet, err := GetFreeIPv6NetworkSubnet(usedNetworks)
+ if err != nil {
+ return err
+ }
+ network.Subnets = append(network.Subnets, *freeSubnet)
+ }
+ }
+ network.IPAMOptions["driver"] = types.HostLocalIPAMDriver
+ }
+ return nil
+}
diff --git a/libpod/network/internal/util/create.go b/libpod/network/internal/util/create.go
new file mode 100644
index 000000000..cecfd7133
--- /dev/null
+++ b/libpod/network/internal/util/create.go
@@ -0,0 +1,42 @@
+package util
+
+import (
+ "github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/pkg/errors"
+)
+
+func CommonNetworkCreate(n NetUtil, network *types.Network) error {
+ if network.Labels == nil {
+ network.Labels = map[string]string{}
+ }
+ if network.Options == nil {
+ network.Options = map[string]string{}
+ }
+ if network.IPAMOptions == nil {
+ network.IPAMOptions = map[string]string{}
+ }
+
+ var name string
+ var err error
+ // validate the name when given
+ if network.Name != "" {
+ if !define.NameRegex.MatchString(network.Name) {
+ return errors.Wrapf(define.RegexError, "network name %s invalid", network.Name)
+ }
+ if _, err := n.Network(network.Name); err == nil {
+ return errors.Wrapf(define.ErrNetworkExists, "network name %s already used", network.Name)
+ }
+ } else {
+ name, err = GetFreeDeviceName(n)
+ if err != nil {
+ return err
+ }
+ network.Name = name
+ // also use the name as interface name when we create a bridge network
+ if network.Driver == types.BridgeNetworkDriver && network.NetworkInterface == "" {
+ network.NetworkInterface = name
+ }
+ }
+ return nil
+}
diff --git a/libpod/network/internal/util/interface.go b/libpod/network/internal/util/interface.go
new file mode 100644
index 000000000..4b01a09b8
--- /dev/null
+++ b/libpod/network/internal/util/interface.go
@@ -0,0 +1,19 @@
+package util
+
+import "github.com/containers/podman/v3/libpod/network/types"
+
+// This is a helper package to allow code sharing between the different
+// network interfaces.
+
+// NetUtil is a helper interface which all network interfaces should implement to allow easy code sharing
+type NetUtil interface {
+ // ForEach eaxecutes the given function for each network
+ ForEach(func(types.Network))
+ // Len returns the number of networks
+ Len() int
+ // DefaultInterfaceName return the default interface name, this will be suffixed by a number
+ DefaultInterfaceName() string
+ // Network returns the network with the given name or ID.
+ // It returns an error if the network is not found
+ Network(nameOrID string) (*types.Network, error)
+}
diff --git a/libpod/network/util/interfaces.go b/libpod/network/internal/util/interfaces.go
index dc2bd601d..20819f756 100644
--- a/libpod/network/util/interfaces.go
+++ b/libpod/network/internal/util/interfaces.go
@@ -2,9 +2,9 @@ package util
import "net"
-// GetLiveNetworkSubnets returns a slice of subnets representing what the system
+// getLiveNetworkSubnets returns a slice of subnets representing what the system
// has defined as network interfaces
-func GetLiveNetworkSubnets() ([]*net.IPNet, error) {
+func getLiveNetworkSubnets() ([]*net.IPNet, error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, err
diff --git a/libpod/network/internal/util/ip.go b/libpod/network/internal/util/ip.go
new file mode 100644
index 000000000..7fe35d3d4
--- /dev/null
+++ b/libpod/network/internal/util/ip.go
@@ -0,0 +1,70 @@
+package util
+
+import (
+ "crypto/rand"
+ "net"
+
+ "github.com/pkg/errors"
+)
+
+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
+}
+
+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 --git a/libpod/network/internal/util/ip_test.go b/libpod/network/internal/util/ip_test.go
new file mode 100644
index 000000000..eaed769d7
--- /dev/null
+++ b/libpod/network/internal/util/ip_test.go
@@ -0,0 +1,63 @@
+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 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 --git a/libpod/network/internal/util/parse.go b/libpod/network/internal/util/parse.go
new file mode 100644
index 000000000..1f68df0bb
--- /dev/null
+++ b/libpod/network/internal/util/parse.go
@@ -0,0 +1,37 @@
+package util
+
+import (
+ "strconv"
+
+ "github.com/pkg/errors"
+)
+
+// 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
+}
diff --git a/libpod/network/internal/util/util.go b/libpod/network/internal/util/util.go
new file mode 100644
index 000000000..bf9d70aba
--- /dev/null
+++ b/libpod/network/internal/util/util.go
@@ -0,0 +1,123 @@
+package util
+
+import (
+ "errors"
+ "fmt"
+ "net"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/pkg/util"
+ "github.com/sirupsen/logrus"
+)
+
+// GetBridgeInterfaceNames returns all bridge interface names
+// already used by network configs
+func GetBridgeInterfaceNames(n NetUtil) []string {
+ names := make([]string, 0, n.Len())
+ n.ForEach(func(net types.Network) {
+ if net.Driver == types.BridgeNetworkDriver {
+ names = append(names, net.NetworkInterface)
+ }
+ })
+ return names
+}
+
+// GetUsedNetworkNames returns all network names already used
+// by network configs
+func GetUsedNetworkNames(n NetUtil) []string {
+ names := make([]string, 0, n.Len())
+ n.ForEach(func(net types.Network) {
+ if net.Driver == types.BridgeNetworkDriver {
+ names = append(names, net.NetworkInterface)
+ }
+ })
+ return names
+}
+
+// GetFreeDeviceName returns a free device name which can
+// be used for new configs as name and bridge interface name.
+// The base name is suffixed by a number
+func GetFreeDeviceName(n NetUtil) (string, error) {
+ bridgeNames := GetBridgeInterfaceNames(n)
+ netNames := GetUsedNetworkNames(n)
+ liveInterfaces, err := 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", n.DefaultInterfaceName(), i)
+ if !util.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")
+}
+
+// GetUsedSubnets returns a list of all used subnets by network
+// configs and interfaces on the host.
+func GetUsedSubnets(n NetUtil) ([]*net.IPNet, error) {
+ // first, load all used subnets from network configs
+ subnets := make([]*net.IPNet, 0, n.Len())
+ n.ForEach(func(n types.Network) {
+ for i := range n.Subnets {
+ subnets = append(subnets, &n.Subnets[i].Subnet.IPNet)
+ }
+ })
+ // second, load networks from the current system
+ liveSubnets, err := getLiveNetworkSubnets()
+ if err != nil {
+ return nil, err
+ }
+ return append(subnets, liveSubnets...), nil
+}
+
+// GetFreeIPv6NetworkSubnet returns a unused ipv4 subnet
+func GetFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
+ // 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 := NetworkIntersectsWithNetworks(network, usedNetworks); !intersectsConfig {
+ logrus.Debugf("found free ipv4 network subnet %s", network.String())
+ return &types.Subnet{
+ Subnet: types.IPNet{IPNet: *network},
+ }, nil
+ }
+ var err error
+ network, err = NextSubnet(network)
+ if err != nil {
+ return nil, err
+ }
+ }
+}
+
+// GetFreeIPv6NetworkSubnet returns a unused ipv6 subnet
+func GetFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
+ // 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 := getRandomIPv6Subnet()
+ if err != nil {
+ return nil, err
+ }
+ if intersectsConfig := NetworkIntersectsWithNetworks(&network, usedNetworks); !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")
+}
diff --git a/libpod/network/internal/util/validate.go b/libpod/network/internal/util/validate.go
new file mode 100644
index 000000000..62c3f3951
--- /dev/null
+++ b/libpod/network/internal/util/validate.go
@@ -0,0 +1,121 @@
+package util
+
+import (
+ "net"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/libpod/network/util"
+ "github.com/pkg/errors"
+)
+
+// 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, usedNetworks []*net.IPNet) error {
+ if s == nil {
+ return errors.New("subnet is nil")
+ }
+ if s.Subnet.IP == nil {
+ return errors.New("subnet ip 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")
+ }
+
+ // check that the new subnet does not conflict with existing ones
+ if NetworkIntersectsWithNetworks(net, usedNetworks) {
+ return errors.Errorf("subnet %s is already used on the host or by another config", net.String())
+ }
+
+ 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)
+ }
+ util.NormalizeIP(&s.Gateway)
+ } 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 {
+ if !s.Subnet.Contains(s.LeaseRange.StartIP) {
+ return errors.Errorf("lease range start ip %s not in subnet %s", s.LeaseRange.StartIP, &s.Subnet)
+ }
+ util.NormalizeIP(&s.LeaseRange.StartIP)
+ }
+ if s.LeaseRange.EndIP != nil {
+ if !s.Subnet.Contains(s.LeaseRange.EndIP) {
+ return errors.Errorf("lease range end ip %s not in subnet %s", s.LeaseRange.EndIP, &s.Subnet)
+ }
+ util.NormalizeIP(&s.LeaseRange.EndIP)
+ }
+ }
+ return nil
+}
+
+// ValidateSubnets will validate the subnets for this network.
+// It also sets the gateway if the gateway is empty and it sets
+// IPv6Enabled to true if at least one subnet is ipv6.
+func ValidateSubnets(network *types.Network, usedNetworks []*net.IPNet) error {
+ for i := range network.Subnets {
+ err := ValidateSubnet(&network.Subnets[i], !network.Internal, usedNetworks)
+ if err != nil {
+ return err
+ }
+ if util.IsIPv6(network.Subnets[i].Subnet.IP) {
+ network.IPv6Enabled = true
+ }
+ }
+ return nil
+}
+
+func ValidateSetupOptions(n NetUtil, namespacePath string, options types.SetupOptions) error {
+ if namespacePath == "" {
+ return errors.New("namespacePath is empty")
+ }
+ if options.ContainerID == "" {
+ return errors.New("ContainerID is empty")
+ }
+ if len(options.Networks) == 0 {
+ return errors.New("must specify at least one network")
+ }
+ for name, netOpts := range options.Networks {
+ network, err := n.Network(name)
+ if err != nil {
+ return err
+ }
+ err = validatePerNetworkOpts(network, netOpts)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// validatePerNetworkOpts checks that all given static ips are in a subnet on this network
+func validatePerNetworkOpts(network *types.Network, netOpts types.PerNetworkOptions) error {
+ if netOpts.InterfaceName == "" {
+ return errors.Errorf("interface name on network %s is empty", network.Name)
+ }
+outer:
+ for _, ip := range netOpts.StaticIPs {
+ for _, s := range network.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.Name)
+ }
+ return nil
+}
diff --git a/libpod/network/netavark/config.go b/libpod/network/netavark/config.go
new file mode 100644
index 000000000..5cab76710
--- /dev/null
+++ b/libpod/network/netavark/config.go
@@ -0,0 +1,210 @@
+// +build linux
+
+package netavark
+
+import (
+ "encoding/json"
+ "net"
+ "os"
+ "path/filepath"
+ "time"
+
+ "github.com/containers/podman/v3/libpod/define"
+ internalutil "github.com/containers/podman/v3/libpod/network/internal/util"
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/storage/pkg/stringid"
+ "github.com/pkg/errors"
+)
+
+// NetworkCreate will take a partial filled Network and fill the
+// missing fields. It creates the Network and returns the full Network.
+func (n *netavarkNetwork) 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, false)
+ if err != nil {
+ return types.Network{}, err
+ }
+ // add the new network to the map
+ n.networks[network.Name] = network
+ return *network, nil
+}
+
+func (n *netavarkNetwork) networkCreate(newNetwork types.Network, defaultNet bool) (*types.Network, error) {
+ // if no driver is set use the default one
+ if newNetwork.Driver == "" {
+ newNetwork.Driver = types.DefaultNetworkDriver
+ }
+ if !defaultNet {
+ // 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 newNetwork.ID != "" {
+ return nil, errors.Wrap(define.ErrInvalidArg, "ID can not be set for network create")
+ }
+
+ // generate random network ID
+ var i int
+ for i = 0; i < 1000; i++ {
+ id := stringid.GenerateNonCryptoID()
+ if _, err := n.getNetwork(id); err != nil {
+ newNetwork.ID = id
+ break
+ }
+ }
+ if i == 1000 {
+ return nil, errors.New("failed to create random network ID")
+ }
+ }
+
+ err := internalutil.CommonNetworkCreate(n, &newNetwork)
+ if err != nil {
+ return nil, err
+ }
+
+ // Only get the used networks for validation if we do not create the default network.
+ // The default network should not be validated against used subnets, we have to ensure
+ // that this network can always be created even when a subnet is already used on the host.
+ // This could happen if you run a container on this net, then the cni interface will be
+ // created on the host and "block" this subnet from being used again.
+ // Therefore the next podman command tries to create the default net again and it would
+ // fail because it thinks the network is used on the host.
+ var usedNetworks []*net.IPNet
+ if !defaultNet {
+ usedNetworks, err = internalutil.GetUsedSubnets(n)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ switch newNetwork.Driver {
+ case types.BridgeNetworkDriver:
+ err = internalutil.CreateBridge(n, &newNetwork, usedNetworks)
+ if err != nil {
+ return nil, err
+ }
+ // validate the given options, we do not need them but just check to make sure they are valid
+ for key, value := range newNetwork.Options {
+ switch key {
+ case "mtu":
+ _, err = internalutil.ParseMTU(value)
+ if err != nil {
+ return nil, err
+ }
+
+ case "vlan":
+ _, err = internalutil.ParseVlan(value)
+ if err != nil {
+ return nil, err
+ }
+
+ default:
+ return nil, errors.Errorf("unsupported network option %s", key)
+ }
+ }
+
+ default:
+ return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported driver %s", newNetwork.Driver)
+ }
+
+ err = internalutil.ValidateSubnets(&newNetwork, usedNetworks)
+ if err != nil {
+ return nil, err
+ }
+
+ // FIXME: If we have a working solution for internal networks with dns this check should be removed.
+ if newNetwork.DNSEnabled && newNetwork.Internal {
+ return nil, errors.New("cannot set internal and dns enabled")
+ }
+
+ newNetwork.Created = time.Now()
+
+ if !defaultNet {
+ confPath := filepath.Join(n.networkConfigDir, newNetwork.Name+".json")
+ f, err := os.Create(confPath)
+ if err != nil {
+ return nil, err
+ }
+ enc := json.NewEncoder(f)
+ enc.SetIndent("", " ")
+ err = enc.Encode(newNetwork)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return &newNetwork, nil
+}
+
+// NetworkRemove will remove the Network with the given name or ID.
+// It does not ensure that the network is unused.
+func (n *netavarkNetwork) 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.Name == n.defaultNetwork {
+ return errors.Errorf("default network %s cannot be removed", n.defaultNetwork)
+ }
+
+ file := filepath.Join(n.networkConfigDir, network.Name+".json")
+ // make sure to not error for ErrNotExist
+ if err := os.Remove(file); err != nil && !errors.Is(err, os.ErrNotExist) {
+ return err
+ }
+ delete(n.networks, network.Name)
+ return nil
+}
+
+// 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 *netavarkNetwork) 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) {
+ continue outer
+ }
+ }
+ networks = append(networks, *net)
+ }
+ return networks, nil
+}
+
+// NetworkInspect will return the Network with the given name or ID.
+func (n *netavarkNetwork) 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, nil
+}
diff --git a/libpod/network/netavark/config_test.go b/libpod/network/netavark/config_test.go
new file mode 100644
index 000000000..ee4a825f1
--- /dev/null
+++ b/libpod/network/netavark/config_test.go
@@ -0,0 +1,1123 @@
+// +build linux
+
+package netavark_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
+ networkConfDir string
+ logBuffer bytes.Buffer
+ )
+
+ BeforeEach(func() {
+ var err error
+ networkConfDir, err = ioutil.TempDir("", "podman_netavark_test")
+ if err != nil {
+ Fail("Failed to create tmpdir")
+
+ }
+ logBuffer = bytes.Buffer{}
+ logrus.SetOutput(&logBuffer)
+ })
+
+ JustBeforeEach(func() {
+ var err error
+ libpodNet, err = getNetworkInterface(networkConfDir, false)
+ if err != nil {
+ Fail("Failed to create NewCNINetworkInterface")
+ }
+ })
+
+ AfterEach(func() {
+ os.RemoveAll(networkConfDir)
+ })
+
+ 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].ID).To(Equal("2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9"))
+ Expect(networks[0].NetworkInterface).To(Equal("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(networkConfDir, network1.Name+".json")
+ 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())
+ EqualNetwork(network2, network1)
+
+ // inspect by ID
+ network2, err = libpodNet.NetworkInspect(network1.ID)
+ Expect(err).To(BeNil())
+ EqualNetwork(network2, network1)
+
+ // inspect by partial ID
+ network2, err = libpodNet.NetworkInspect(network1.ID[:10])
+ Expect(err).To(BeNil())
+ EqualNetwork(network2, network1)
+
+ // create a new interface to force a config load from disk
+ libpodNet, err = getNetworkInterface(networkConfDir, false)
+ Expect(err).To(BeNil())
+
+ network2, err = libpodNet.NetworkInspect(network1.Name)
+ Expect(err).To(BeNil())
+ EqualNetwork(network2, 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(networkConfDir, network1.Name+".json")).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: "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("podman2"))
+ Expect(network1.Driver).To(Equal("bridge"))
+
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("bridge name podman2 already in use"))
+ })
+
+ 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())
+
+ // reload configs from disk
+ libpodNet, err = getNetworkInterface(networkConfDir, false)
+ Expect(err).To(BeNil())
+ // check the the networks are identical
+ network2, err := libpodNet.NetworkInspect(network1.Name)
+ Expect(err).To(BeNil())
+ EqualNetwork(network2, network1)
+ })
+
+ 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))
+
+ err = libpodNet.NetworkRemove(network1.Name)
+ Expect(err).To(BeNil())
+
+ 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(networkConfDir, network1.Name+".json")).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))
+
+ err = libpodNet.NetworkRemove(network1.Name)
+ Expect(err).To(BeNil())
+
+ 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 ip is nil"))
+ })
+
+ 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(networkConfDir, network1.Name+".json")
+ Expect(path).To(BeARegularFile())
+ grepInFile(path, `"dns_enabled": true`)
+ })
+
+ 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(networkConfDir, network1.Name+".json")
+ 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 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(networkConfDir, network1.Name+".json")
+ 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,
+ }
+ _, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("cannot set internal and dns enabled"))
+ })
+
+ It("network inspect partial ID", func() {
+ network := types.Network{Name: "net4"}
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+ Expect(network1.ID).To(HaveLen(64))
+
+ network2, err := libpodNet.NetworkInspect(network1.ID[:10])
+ Expect(err).ToNot(HaveOccurred())
+ EqualNetwork(network2, network1)
+ })
+
+ 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"))
+ })
+
+ It("network create with same subnet", func() {
+ subnet := "10.0.0.0/24"
+ n, _ := types.ParseCIDR(subnet)
+ subnet2 := "10.10.0.0/24"
+ n2, _ := types.ParseCIDR(subnet2)
+ network := types.Network{Subnets: []types.Subnet{{Subnet: n}, {Subnet: n2}}}
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+ Expect(network1.Subnets).To(HaveLen(2))
+ network = types.Network{Subnets: []types.Subnet{{Subnet: n}}}
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("subnet 10.0.0.0/24 is already used on the host or by another config"))
+ network = types.Network{Subnets: []types.Subnet{{Subnet: n2}}}
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config"))
+ })
+ })
+
+ 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(networkConfDir, 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(7))
+ // 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(7))
+
+ nets[0].Name = "myname"
+ nets, err = libpodNet.NetworkList()
+ Expect(err).To(BeNil())
+ Expect(nets).To(HaveLen(7))
+ 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("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("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("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("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("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("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("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("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"},
+ }
+ filterFuncs, err := util.GenerateNetworkFilters(filters)
+ Expect(err).To(BeNil())
+
+ networks, err := libpodNet.NetworkList(filterFuncs...)
+ Expect(err).To(BeNil())
+ Expect(networks).To(HaveLen(7))
+ Expect(networks).To(ConsistOf(HaveNetworkName("internal"), HaveNetworkName("bridge"),
+ HaveNetworkName("mtu"), HaveNetworkName("vlan"), HaveNetworkName("podman"),
+ HaveNetworkName("label"), 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("create bridge network with used interface name", func() {
+ network := types.Network{
+ NetworkInterface: "podman9",
+ }
+ _, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("bridge name 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(networkConfDir, 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(1))
+ logString := logBuffer.String()
+ Expect(logString).To(ContainSubstring("Error reading network config file \\\"%s/broken.json\\\": unexpected EOF", networkConfDir))
+ Expect(logString).To(ContainSubstring("Network config \\\"%s/invalid name.json\\\" has invalid name: \\\"invalid name\\\", skipping: names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: invalid argument", networkConfDir))
+ Expect(logString).To(ContainSubstring("Network config name \\\"name_miss\\\" does not match file name \\\"name_missmatch.json\\\", skipping"))
+ Expect(logString).To(ContainSubstring("Network config \\\"%s/wrongID.json\\\" could not be parsed, skipping: invalid network ID \\\"someID\\\"", networkConfDir))
+ Expect(logString).To(ContainSubstring("Network config \\\"%s/invalid_gateway.json\\\" could not be parsed, skipping: gateway 10.89.100.1 not in subnet 10.89.9.0/24", networkConfDir))
+ })
+
+ })
+
+})
+
+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))
+}
+
+// EqualNetwork must be used because comparing the time with deep equal does not work
+func EqualNetwork(net1, net2 types.Network) {
+ ExpectWithOffset(1, net1.Created.Equal(net2.Created)).To(BeTrue(), "net1 created: %v is not equal net2 created: %v", net1.Created, net2.Created)
+ net1.Created = time.Time{}
+ net2.Created = time.Time{}
+ ExpectWithOffset(1, net1).To(Equal(net2))
+}
diff --git a/libpod/network/netavark/const.go b/libpod/network/netavark/const.go
new file mode 100644
index 000000000..9709315c6
--- /dev/null
+++ b/libpod/network/netavark/const.go
@@ -0,0 +1,5 @@
+// +build linux
+
+package netavark
+
+const defaultBridgeName = "podman"
diff --git a/libpod/network/netavark/exec.go b/libpod/network/netavark/exec.go
new file mode 100644
index 000000000..d6458eeb4
--- /dev/null
+++ b/libpod/network/netavark/exec.go
@@ -0,0 +1,128 @@
+package netavark
+
+import (
+ "encoding/json"
+ "errors"
+ "os"
+ "os/exec"
+ "strconv"
+
+ "github.com/sirupsen/logrus"
+)
+
+type netavarkError struct {
+ exitCode int
+ // Set the json key to "error" so we can directly unmarshal into this struct
+ Msg string `json:"error"`
+ err error
+}
+
+func (e *netavarkError) Error() string {
+ ec := ""
+ // only add the exit code the the error message if we have at least info log level
+ // the normal user does not need to care about the number
+ if e.exitCode > 0 && logrus.IsLevelEnabled(logrus.InfoLevel) {
+ ec = " (exit code " + strconv.Itoa(e.exitCode) + ")"
+ }
+ msg := "netavark" + ec
+ if len(msg) > 0 {
+ msg += ": " + e.Msg
+ }
+ if e.err != nil {
+ msg += ": " + e.err.Error()
+ }
+ return msg
+}
+
+func (e *netavarkError) Unwrap() error {
+ return e.err
+}
+
+func newNetavarkError(msg string, err error) error {
+ return &netavarkError{
+ Msg: msg,
+ err: err,
+ }
+}
+
+// getRustLogEnv returns the RUST_LOG env var based on the current logrus level
+func getRustLogEnv() string {
+ level := logrus.GetLevel().String()
+ // rust env_log uses warn instead of warning
+ if level == "warning" {
+ level = "warn"
+ }
+ // the rust netlink library is very verbose
+ // make sure to only log netavark logs
+ return "RUST_LOG=netavark=" + level
+}
+
+// execNetavark will execute netavark with the following arguments
+// It takes the path to the binary, the list of args and an interface which is
+// marshaled to json and send via stdin to netavark. The result interface is
+// used to marshal the netavark output into it. This can be nil.
+// All errors return by this function should be of the type netavarkError
+// to provide a helpful error message.
+func execNetavark(binary string, args []string, stdin, result interface{}) error {
+ stdinR, stdinW, err := os.Pipe()
+ if err != nil {
+ return newNetavarkError("failed to create stdin pipe", err)
+ }
+ defer stdinR.Close()
+
+ stdoutR, stdoutW, err := os.Pipe()
+ if err != nil {
+ return newNetavarkError("failed to create stdout pipe", err)
+ }
+ defer stdoutR.Close()
+ defer stdoutW.Close()
+
+ cmd := exec.Command(binary, args...)
+ // connect the pipes to stdin and stdout
+ cmd.Stdin = stdinR
+ cmd.Stdout = stdoutW
+ // connect stderr to the podman stderr for logging
+ cmd.Stderr = os.Stderr
+ // set the netavark log level to the same as the podman
+ cmd.Env = append(os.Environ(), getRustLogEnv())
+ // if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics
+ if logrus.IsLevelEnabled(logrus.DebugLevel) {
+ cmd.Env = append(cmd.Env, "RUST_BACKTRACE=1")
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ return newNetavarkError("failed to start process", err)
+ }
+ err = json.NewEncoder(stdinW).Encode(stdin)
+ stdinW.Close()
+ if err != nil {
+ return newNetavarkError("failed to encode stdin data", err)
+ }
+
+ dec := json.NewDecoder(stdoutR)
+
+ err = cmd.Wait()
+ stdoutW.Close()
+ if err != nil {
+ exitError := &exec.ExitError{}
+ if errors.As(err, &exitError) {
+ ne := &netavarkError{}
+ // lets disallow unknown fields to make sure we do not get some unexpected stuff
+ dec.DisallowUnknownFields()
+ // this will unmarshal the error message into the error struct
+ ne.err = dec.Decode(ne)
+ ne.exitCode = exitError.ExitCode()
+ return ne
+ }
+ return newNetavarkError("unexpected failure during execution", err)
+ }
+
+ if result != nil {
+ err = dec.Decode(result)
+ if err != nil {
+ return newNetavarkError("failed to decode result", err)
+ }
+ }
+ return nil
+}
diff --git a/libpod/network/netavark/ipam.go b/libpod/network/netavark/ipam.go
new file mode 100644
index 000000000..db46ee652
--- /dev/null
+++ b/libpod/network/netavark/ipam.go
@@ -0,0 +1,368 @@
+package netavark
+
+import (
+ "encoding/json"
+ "fmt"
+ "net"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/libpod/network/util"
+ "github.com/pkg/errors"
+ "go.etcd.io/bbolt"
+)
+
+// IPAM boltdb structure
+// Each network has their own bucket with the network name as bucket key.
+// Inside the network bucket there is an ID bucket which maps the container ID (key)
+// to a json array of ip addresses (value).
+// The network bucket also has a bucket for each subnet, the subnet is used as key.
+// Inside the subnet bucket an ip is used as key and the container ID as value.
+
+const (
+ idBucket = "ids"
+ // lastIP this is used as key to store the last allocated ip
+ // note that this string should not be 4 or 16 byte long
+ lastIP = "lastIP"
+)
+
+var (
+ idBucketKey = []byte(idBucket)
+ lastIPKey = []byte(lastIP)
+)
+
+type ipamError struct {
+ msg string
+ cause error
+}
+
+func (e *ipamError) Error() string {
+ msg := "IPAM error"
+ if e.msg != "" {
+ msg += ": " + e.msg
+ }
+ if e.cause != nil {
+ msg += ": " + e.cause.Error()
+ }
+ return msg
+}
+
+func newIPAMError(cause error, msg string, args ...interface{}) *ipamError {
+ return &ipamError{
+ msg: fmt.Sprintf(msg, args...),
+ cause: cause,
+ }
+}
+
+// openDB will open the ipam database
+// Note that the caller has to Close it.
+func (n *netavarkNetwork) openDB() (*bbolt.DB, error) {
+ db, err := bbolt.Open(n.ipamDBPath, 0600, nil)
+ if err != nil {
+ return nil, newIPAMError(err, "failed to open database %s", n.ipamDBPath)
+ }
+ return db, nil
+}
+
+// allocIPs will allocate ips for the the container. It will change the
+// NetworkOptions in place. When static ips are given it will validate
+// that these are free to use and will allocate them to the container.
+func (n *netavarkNetwork) allocIPs(opts *types.NetworkOptions) error {
+ db, err := n.openDB()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ err = db.Update(func(tx *bbolt.Tx) error {
+ for netName, netOpts := range opts.Networks {
+ network := n.networks[netName]
+ if network == nil {
+ return newIPAMError(nil, "could not find network %q", netName)
+ }
+
+ // check if we have to alloc ips
+ if !requiresIPAMAlloc(network) {
+ continue
+ }
+
+ // create/get network bucket
+ netBkt, err := tx.CreateBucketIfNotExists([]byte(netName))
+ if err != nil {
+ return newIPAMError(err, "failed to create/get network bucket for network %s", netName)
+ }
+
+ // requestIPs is the list of ips which should be used for this container
+ requestIPs := make([]net.IP, 0, len(network.Subnets))
+
+ for _, subnet := range network.Subnets {
+ subnetBkt, err := netBkt.CreateBucketIfNotExists([]byte(subnet.Subnet.String()))
+ if err != nil {
+ return newIPAMError(err, "failed to create/get subnet bucket for network %s", netName)
+ }
+
+ // search for a static ip which matches the current subnet
+ // in this case the user wants this one and we should not assign a free one
+ var ip net.IP
+ for _, staticIP := range netOpts.StaticIPs {
+ if subnet.Subnet.Contains(staticIP) {
+ ip = staticIP
+ break
+ }
+ }
+
+ // when static ip is requested for this network
+ if ip != nil {
+ // convert to 4 byte ipv4 if needed
+ util.NormalizeIP(&ip)
+ id := subnetBkt.Get(ip)
+ if id != nil {
+ return newIPAMError(nil, "requested ip address %s is already allocated to container ID %s", ip.String(), string(id))
+ }
+ } else {
+ ip, err = getFreeIPFromBucket(subnetBkt, subnet)
+ if err != nil {
+ return err
+ }
+ err = subnetBkt.Put(lastIPKey, ip)
+ if err != nil {
+ return newIPAMError(err, "failed to store last ip in database")
+ }
+ }
+
+ err = subnetBkt.Put(ip, []byte(opts.ContainerID))
+ if err != nil {
+ return newIPAMError(err, "failed to store ip in database")
+ }
+
+ requestIPs = append(requestIPs, ip)
+ }
+
+ idsBucket, err := netBkt.CreateBucketIfNotExists(idBucketKey)
+ if err != nil {
+ return newIPAMError(err, "failed to create/get id bucket for network %s", netName)
+ }
+
+ ipsBytes, err := json.Marshal(requestIPs)
+ if err != nil {
+ return newIPAMError(err, "failed to marshal ips")
+ }
+
+ err = idsBucket.Put([]byte(opts.ContainerID), ipsBytes)
+ if err != nil {
+ return newIPAMError(err, "failed to store ips in database")
+ }
+
+ netOpts.StaticIPs = requestIPs
+ opts.Networks[netName] = netOpts
+ }
+ return nil
+ })
+ return err
+}
+
+func getFreeIPFromBucket(bucket *bbolt.Bucket, subnet types.Subnet) (net.IP, error) {
+ var rangeStart net.IP
+ var rangeEnd net.IP
+ if subnet.LeaseRange != nil {
+ rangeStart = subnet.LeaseRange.StartIP
+ rangeEnd = subnet.LeaseRange.EndIP
+ }
+
+ if rangeStart == nil {
+ // let start with the first ip in subnet
+ rangeStart = util.NextIP(subnet.Subnet.IP)
+ }
+
+ if rangeEnd == nil {
+ lastIP, err := util.LastIPInSubnet(&subnet.Subnet.IPNet)
+ // this error should never happen but lets check anyways to prevent panics
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to get lastIP")
+ }
+ // ipv4 uses the last ip in a subnet for broadcast so we cannot use it
+ if util.IsIPv4(lastIP) {
+ lastIP = util.PrevIP(lastIP)
+ }
+ rangeEnd = lastIP
+ }
+
+ lastIPByte := bucket.Get(lastIPKey)
+ curIP := net.IP(lastIPByte)
+ if curIP == nil {
+ curIP = rangeStart
+ } else {
+ curIP = util.NextIP(curIP)
+ }
+
+ // store the start ip to make sure we know when we looped over all available ips
+ startIP := curIP
+
+ for {
+ // skip the gateway
+ if subnet.Gateway != nil {
+ if util.Cmp(curIP, subnet.Gateway) == 0 {
+ curIP = util.NextIP(curIP)
+ continue
+ }
+ }
+
+ // if we are at the end we need to jump back to the start ip
+ if util.Cmp(curIP, rangeEnd) > 0 {
+ if util.Cmp(rangeStart, startIP) == 0 {
+ return nil, newIPAMError(nil, "failed to find free IP in range: %s - %s", rangeStart.String(), rangeEnd.String())
+ }
+ curIP = rangeStart
+ continue
+ }
+
+ // check if ip is already used by another container
+ // if not return it
+ if bucket.Get(curIP) == nil {
+ return curIP, nil
+ }
+
+ curIP = util.NextIP(curIP)
+
+ if util.Cmp(curIP, startIP) == 0 {
+ return nil, newIPAMError(nil, "failed to find free IP in range: %s - %s", rangeStart.String(), rangeEnd.String())
+ }
+ }
+}
+
+// getAssignedIPs will read the ipam database and will fill in the used ips for this container.
+// It will change the NetworkOptions in place.
+func (n *netavarkNetwork) getAssignedIPs(opts *types.NetworkOptions) error {
+ db, err := n.openDB()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ err = db.View(func(tx *bbolt.Tx) error {
+ for netName, netOpts := range opts.Networks {
+ network := n.networks[netName]
+ if network == nil {
+ return newIPAMError(nil, "could not find network %q", netName)
+ }
+
+ // check if we have to alloc ips
+ if !requiresIPAMAlloc(network) {
+ continue
+ }
+ // get network bucket
+ netBkt := tx.Bucket([]byte(netName))
+ if netBkt == nil {
+ return newIPAMError(nil, "failed to get network bucket for network %s", netName)
+ }
+
+ idBkt := netBkt.Bucket(idBucketKey)
+ if idBkt == nil {
+ return newIPAMError(nil, "failed to get id bucket for network %s", netName)
+ }
+
+ ipJSON := idBkt.Get([]byte(opts.ContainerID))
+ if ipJSON == nil {
+ return newIPAMError(nil, "failed to get ips for container ID %s on network %s", opts.ContainerID, netName)
+ }
+
+ // assignedIPs is the list of ips which should be used for this container
+ assignedIPs := make([]net.IP, 0, len(network.Subnets))
+
+ err = json.Unmarshal(ipJSON, &assignedIPs)
+ if err != nil {
+ return newIPAMError(err, "failed to unmarshal ips from database")
+ }
+
+ for i := range assignedIPs {
+ util.NormalizeIP(&assignedIPs[i])
+ }
+
+ netOpts.StaticIPs = assignedIPs
+ opts.Networks[netName] = netOpts
+ }
+ return nil
+ })
+ return err
+}
+
+// deallocIPs will release the ips in the network options from the DB so that
+// they can be reused by other containers. It expects that the network options
+// are already filled with the correct ips. Use getAssignedIPs() for this.
+func (n *netavarkNetwork) deallocIPs(opts *types.NetworkOptions) error {
+ db, err := n.openDB()
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ err = db.Update(func(tx *bbolt.Tx) error {
+ for netName, netOpts := range opts.Networks {
+ network := n.networks[netName]
+ if network == nil {
+ return newIPAMError(nil, "could not find network %q", netName)
+ }
+
+ // check if we have to alloc ips
+ if !requiresIPAMAlloc(network) {
+ continue
+ }
+ // get network bucket
+ netBkt := tx.Bucket([]byte(netName))
+ if netBkt == nil {
+ return newIPAMError(nil, "failed to get network bucket for network %s", netName)
+ }
+
+ for _, subnet := range network.Subnets {
+ subnetBkt := netBkt.Bucket([]byte(subnet.Subnet.String()))
+ if subnetBkt == nil {
+ return newIPAMError(nil, "failed to get subnet bucket for network %s", netName)
+ }
+
+ // search for a static ip which matches the current subnet
+ // in this case the user wants this one and we should not assign a free one
+ var ip net.IP
+ for _, staticIP := range netOpts.StaticIPs {
+ if subnet.Subnet.Contains(staticIP) {
+ ip = staticIP
+ break
+ }
+ }
+ if ip == nil {
+ return newIPAMError(nil, "failed to find ip for subnet %s on network %s", subnet.Subnet.String(), netName)
+ }
+ util.NormalizeIP(&ip)
+
+ err = subnetBkt.Delete(ip)
+ if err != nil {
+ return newIPAMError(err, "failed to remove ip %s from subnet bucket for network %s", ip.String(), netName)
+ }
+ }
+
+ idBkt := netBkt.Bucket(idBucketKey)
+ if idBkt == nil {
+ return newIPAMError(nil, "failed to get id bucket for network %s", netName)
+ }
+
+ err = idBkt.Delete([]byte(opts.ContainerID))
+ if err != nil {
+ return newIPAMError(err, "failed to remove allocated ips for container ID %s on network %s", opts.ContainerID, netName)
+ }
+ }
+ return nil
+ })
+ return err
+}
+
+// requiresIPAMAlloc return true when we have to allocate ips for this network
+// it checks the ipam driver and if subnets are set
+func requiresIPAMAlloc(network *types.Network) bool {
+ // only do host allocation when driver is set to HostLocalIPAMDriver or unset
+ switch network.IPAMOptions["driver"] {
+ case "", types.HostLocalIPAMDriver:
+ default:
+ return false
+ }
+
+ // no subnets == no ips to assign
+ return len(network.Subnets) > 0
+}
diff --git a/libpod/network/netavark/ipam_test.go b/libpod/network/netavark/ipam_test.go
new file mode 100644
index 000000000..4b3947501
--- /dev/null
+++ b/libpod/network/netavark/ipam_test.go
@@ -0,0 +1,433 @@
+package netavark
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "net"
+ "os"
+ "path/filepath"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ "github.com/sirupsen/logrus"
+)
+
+var _ = Describe("IPAM", func() {
+ var (
+ networkInterface *netavarkNetwork
+ networkConfDir string
+ logBuffer bytes.Buffer
+ )
+
+ BeforeEach(func() {
+ var err error
+ networkConfDir, err = ioutil.TempDir("", "podman_netavark_test")
+ if err != nil {
+ Fail("Failed to create tmpdir")
+
+ }
+ logBuffer = bytes.Buffer{}
+ logrus.SetOutput(&logBuffer)
+ })
+
+ JustBeforeEach(func() {
+ libpodNet, err := NewNetworkInterface(InitConfig{
+ NetworkConfigDir: networkConfDir,
+ IPAMDBPath: filepath.Join(networkConfDir, "ipam.db"),
+ LockFile: filepath.Join(networkConfDir, "netavark.lock"),
+ })
+ if err != nil {
+ Fail("Failed to create NewCNINetworkInterface")
+ }
+
+ networkInterface = libpodNet.(*netavarkNetwork)
+ // run network list to force a network load
+ networkInterface.NetworkList()
+ })
+
+ AfterEach(func() {
+ os.RemoveAll(networkConfDir)
+ })
+
+ It("simple ipam alloc", func() {
+ netName := types.DefaultNetworkName
+ for i := 2; i < 100; i++ {
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err := networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.88.0.%d", i)).To4()))
+ }
+ })
+
+ It("ipam try to alloc same ip", func() {
+ netName := types.DefaultNetworkName
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err := networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.88.0.2").To4()))
+
+ opts = &types.NetworkOptions{
+ ContainerID: "otherID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {StaticIPs: []net.IP{net.ParseIP("10.88.0.2")}},
+ },
+ }
+ err = networkInterface.allocIPs(opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: requested ip address 10.88.0.2 is already allocated to container ID someContainerID"))
+ })
+
+ It("ipam try to alloc more ips as in range", func() {
+ s, _ := types.ParseCIDR("10.0.0.1/24")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ LeaseRange: &types.LeaseRange{
+ StartIP: net.ParseIP("10.0.0.10"),
+ EndIP: net.ParseIP("10.0.0.20"),
+ },
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ for i := 10; i < 21; i++ {
+ opts := &types.NetworkOptions{
+ ContainerID: fmt.Sprintf("someContainerID-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4()))
+ }
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID-22",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ // now this should fail because all free ips are already assigned
+ err = networkInterface.allocIPs(opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.10 - 10.0.0.20"))
+ })
+
+ It("ipam basic setup", func() {
+ netName := types.DefaultNetworkName
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ expectedIP := net.ParseIP("10.88.0.2").To4()
+
+ err := networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP))
+
+ // remove static ips from opts
+ netOpts := opts.Networks[netName]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName] = netOpts
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP))
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: requested ip address 10.88.0.2 is already allocated to container ID someContainerID"))
+
+ // dealloc the ip
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(expectedIP))
+ })
+
+ It("ipam dual stack", func() {
+ s1, _ := types.ParseCIDR("10.0.0.0/26")
+ s2, _ := types.ParseCIDR("fd80::/24")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s1,
+ },
+ {
+ Subnet: s2,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2")))
+
+ // remove static ips from opts
+ netOpts := opts.Networks[netName]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName] = netOpts
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2")))
+
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ // try to alloc the same again
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(2))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks[netName].StaticIPs[1]).To(Equal(net.ParseIP("fd80::2")))
+ })
+
+ It("ipam with two networks", func() {
+ s, _ := types.ParseCIDR("10.0.0.0/24")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName1 := network.Name
+
+ s, _ = types.ParseCIDR("10.0.1.0/24")
+ network, err = networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName2 := network.Name
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName1: {},
+ netName2: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName1))
+ Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks).To(HaveKey(netName2))
+ Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4()))
+
+ // remove static ips from opts
+ netOpts := opts.Networks[netName1]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName1] = netOpts
+ netOpts = opts.Networks[netName2]
+ netOpts.StaticIPs = nil
+ opts.Networks[netName2] = netOpts
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName1))
+ Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks).To(HaveKey(netName2))
+ Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4()))
+
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+
+ // try to alloc the same again
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName1))
+ Expect(opts.Networks[netName1].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName1].StaticIPs[0]).To(Equal(net.ParseIP("10.0.0.2").To4()))
+ Expect(opts.Networks).To(HaveKey(netName2))
+ Expect(opts.Networks[netName2].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName2].StaticIPs[0]).To(Equal(net.ParseIP("10.0.1.2").To4()))
+ })
+
+ It("ipam alloc more ips as in subnet", func() {
+ s, _ := types.ParseCIDR("10.0.0.0/26")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ for i := 2; i < 64; i++ {
+ opts := &types.NetworkOptions{
+ ContainerID: fmt.Sprintf("id-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+ err = networkInterface.allocIPs(opts)
+ if i < 63 {
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4()))
+ } else {
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.1 - 10.0.0.62"))
+ }
+ }
+ })
+
+ It("ipam alloc -> dealloc -> alloc", func() {
+ s, _ := types.ParseCIDR("10.0.0.0/27")
+ network, err := networkInterface.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {
+ Subnet: s,
+ },
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ for i := 2; i < 10; i++ {
+ opts := types.NetworkOptions{
+ ContainerID: fmt.Sprintf("id-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+ err = networkInterface.allocIPs(&opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", i)).To4()))
+
+ err = networkInterface.deallocIPs(&opts)
+ Expect(err).ToNot(HaveOccurred())
+ }
+
+ for i := 0; i < 30; i++ {
+ opts := types.NetworkOptions{
+ ContainerID: fmt.Sprintf("id-%d", i),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+ err = networkInterface.allocIPs(&opts)
+ if i < 29 {
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(1))
+ // The (i+8)%29+2 part looks cryptic but it is actually simple, we already have 8 ips allocated above
+ // so we expect the 8 available ip. We have 29 assignable ip addresses in this subnet because "i"+8 can
+ // be greater than 30 we have to modulo by 29 to go back to the beginning. Also the first free ip is
+ // network address + 2, so we have to add 2 to the result
+ Expect(opts.Networks[netName].StaticIPs[0]).To(Equal(net.ParseIP(fmt.Sprintf("10.0.0.%d", (i+8)%29+2)).To4()))
+ } else {
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(Equal("IPAM error: failed to find free IP in range: 10.0.0.1 - 10.0.0.30"))
+ }
+ }
+ })
+
+ It("ipam with dhcp driver should not set ips", func() {
+ network, err := networkInterface.NetworkCreate(types.Network{
+ IPAMOptions: map[string]string{
+ "driver": types.DHCPIPAMDriver,
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+
+ opts := &types.NetworkOptions{
+ ContainerID: "someContainerID",
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {},
+ },
+ }
+
+ err = networkInterface.allocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(0))
+
+ err = networkInterface.getAssignedIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(opts.Networks).To(HaveKey(netName))
+ Expect(opts.Networks[netName].StaticIPs).To(HaveLen(0))
+
+ // dealloc the ip
+ err = networkInterface.deallocIPs(opts)
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+})
diff --git a/libpod/network/netavark/netavark_suite_test.go b/libpod/network/netavark/netavark_suite_test.go
new file mode 100644
index 000000000..6063a54e3
--- /dev/null
+++ b/libpod/network/netavark/netavark_suite_test.go
@@ -0,0 +1,75 @@
+// +build linux
+
+package netavark_test
+
+import (
+ "fmt"
+ "net"
+ "os"
+ "path/filepath"
+ "reflect"
+ "testing"
+
+ "github.com/containers/podman/v3/libpod/network/netavark"
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/libpod/network/util"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+ gomegaTypes "github.com/onsi/gomega/types"
+)
+
+func TestNetavark(t *testing.T) {
+ RegisterFailHandler(Fail)
+ RunSpecs(t, "Netavark Suite")
+}
+
+var netavarkBinary string
+
+func init() {
+ netavarkBinary = os.Getenv("NETAVARK_BINARY")
+ if netavarkBinary == "" {
+ netavarkBinary = "/usr/libexec/podman/netavark"
+ }
+}
+
+func getNetworkInterface(confDir string, machine bool) (types.ContainerNetwork, error) {
+ return netavark.NewNetworkInterface(netavark.InitConfig{
+ NetworkConfigDir: confDir,
+ IsMachine: machine,
+ NetavarkBinary: netavarkBinary,
+ IPAMDBPath: filepath.Join(confDir, "ipam.db"),
+ LockFile: filepath.Join(confDir, "netavark.lock"),
+ })
+}
+
+// EqualSubnet is a custom GomegaMatcher to match a subnet
+// This makes sure to not use the 16 bytes ip representation.
+func EqualSubnet(subnet *net.IPNet) gomegaTypes.GomegaMatcher {
+ return &equalSubnetMatcher{
+ expected: subnet,
+ }
+}
+
+type equalSubnetMatcher struct {
+ expected *net.IPNet
+}
+
+func (m *equalSubnetMatcher) Match(actual interface{}) (bool, error) {
+ util.NormalizeIP(&m.expected.IP)
+
+ subnet, ok := actual.(*net.IPNet)
+ if !ok {
+ return false, fmt.Errorf("EqualSubnet expects a *net.IPNet")
+ }
+ util.NormalizeIP(&subnet.IP)
+
+ return reflect.DeepEqual(subnet, m.expected), nil
+}
+
+func (m *equalSubnetMatcher) FailureMessage(actual interface{}) string {
+ return fmt.Sprintf("Expected subnet %#v to equal subnet %#v", actual, m.expected)
+}
+
+func (m *equalSubnetMatcher) NegatedFailureMessage(actual interface{}) string {
+ return fmt.Sprintf("Expected subnet %#v not to equal subnet %#v", actual, m.expected)
+}
diff --git a/libpod/network/netavark/network.go b/libpod/network/netavark/network.go
new file mode 100644
index 000000000..cc6fb423c
--- /dev/null
+++ b/libpod/network/netavark/network.go
@@ -0,0 +1,305 @@
+// +build linux
+
+package netavark
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "time"
+
+ "github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/internal/util"
+ "github.com/containers/podman/v3/libpod/network/types"
+ pkgutil "github.com/containers/podman/v3/pkg/util"
+ "github.com/containers/storage/pkg/lockfile"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+type netavarkNetwork struct {
+ // networkConfigDir is directory where the network config files are stored.
+ networkConfigDir string
+
+ // netavarkBinary is the path to the netavark binary.
+ netavarkBinary string
+
+ // defaultNetwork is the name for the default network.
+ defaultNetwork string
+ // defaultSubnet is the default subnet for the default network.
+ defaultSubnet types.IPNet
+
+ // ipamDBPath is the path to the ip allocation bolt db
+ ipamDBPath string
+
+ // isMachine describes whenever podman runs in a podman machine environment.
+ isMachine bool
+
+ // lock is a internal lock for critical operations
+ lock lockfile.Locker
+
+ // modTime is the timestamp when the config dir was modified
+ modTime time.Time
+
+ // networks is a map with loaded networks, the key is the network name
+ networks map[string]*types.Network
+}
+
+type InitConfig struct {
+ // NetworkConfigDir is directory where the network config files are stored.
+ NetworkConfigDir string
+
+ // NetavarkBinary is the path to the netavark binary.
+ NetavarkBinary string
+
+ // IPAMDBPath is the path to the ipam database. This should be on a tmpfs.
+ // If empty defaults to XDG_RUNTIME_DIR/netavark/ipam.db or /run/netavark/ipam.db as root.
+ IPAMDBPath 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
+}
+
+// NewNetworkInterface creates the ContainerNetwork interface for the netavark backend.
+// Note: The networks are not loaded from disk until a method is called.
+func NewNetworkInterface(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")
+ }
+
+ ipamdbPath := conf.IPAMDBPath
+ if ipamdbPath == "" {
+ runDir, err := pkgutil.GetRuntimeDir()
+ if err != nil {
+ return nil, err
+ }
+ // as root runtimeDir is empty so use /run
+ if runDir == "" {
+ runDir = "/run"
+ }
+ ipamdbPath = filepath.Join(runDir, "netavark")
+ if err := os.MkdirAll(ipamdbPath, 0700); err != nil {
+ return nil, errors.Wrap(err, "failed to create ipam db path")
+ }
+ ipamdbPath = filepath.Join(ipamdbPath, "ipam.db")
+ }
+
+ if err := os.MkdirAll(conf.NetworkConfigDir, 0755); err != nil {
+ return nil, err
+ }
+
+ n := &netavarkNetwork{
+ networkConfigDir: conf.NetworkConfigDir,
+ netavarkBinary: conf.NetavarkBinary,
+ ipamDBPath: ipamdbPath,
+ defaultNetwork: defaultNetworkName,
+ defaultSubnet: defaultNet,
+ isMachine: conf.IsMachine,
+ lock: lock,
+ }
+
+ return n, nil
+}
+
+// Drivers will return the list of supported network drivers
+// for this interface.
+func (n *netavarkNetwork) Drivers() []string {
+ return []string{types.BridgeNetworkDriver}
+}
+
+func (n *netavarkNetwork) loadNetworks() error {
+ // check the mod time of the config dir
+ f, err := os.Stat(n.networkConfigDir)
+ if err != nil {
+ return err
+ }
+ modTime := f.ModTime()
+
+ // skip loading networks if they are already loaded and
+ // if the config dir was not modified since the last call
+ if n.networks != nil && modTime.Equal(n.modTime) {
+ return nil
+ }
+ // make sure the remove all networks before we reload them
+ n.networks = nil
+ n.modTime = modTime
+
+ files, err := ioutil.ReadDir(n.networkConfigDir)
+ if err != nil && !errors.Is(err, os.ErrNotExist) {
+ return err
+ }
+
+ networks := make(map[string]*types.Network, len(files))
+ for _, f := range files {
+ if f.IsDir() {
+ continue
+ }
+ if filepath.Ext(f.Name()) != ".json" {
+ continue
+ }
+
+ path := filepath.Join(n.networkConfigDir, f.Name())
+ file, err := os.Open(path)
+ if err != nil {
+ // do not log ENOENT errors
+ if !errors.Is(err, os.ErrNotExist) {
+ logrus.Warnf("Error loading network config file %q: %v", path, err)
+ }
+ continue
+ }
+ network := new(types.Network)
+ err = json.NewDecoder(file).Decode(network)
+ if err != nil {
+ logrus.Warnf("Error reading network config file %q: %v", path, err)
+ continue
+ }
+
+ // check that the filename matches the network name
+ if network.Name+".json" != f.Name() {
+ logrus.Warnf("Network config name %q does not match file name %q, skipping", network.Name, f.Name())
+ continue
+ }
+
+ if !define.NameRegex.MatchString(network.Name) {
+ logrus.Warnf("Network config %q has invalid name: %q, skipping: %v", path, network.Name, define.RegexError)
+ continue
+ }
+
+ err = parseNetwork(network)
+ if err != nil {
+ logrus.Warnf("Network config %q could not be parsed, skipping: %v", path, err)
+ continue
+ }
+
+ logrus.Debugf("Successfully loaded network %s: %v", network.Name, network)
+ networks[network.Name] = network
+ }
+
+ // 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 parseNetwork(network *types.Network) error {
+ if network.Labels == nil {
+ network.Labels = map[string]string{}
+ }
+ if network.Options == nil {
+ network.Options = map[string]string{}
+ }
+ if network.IPAMOptions == nil {
+ network.IPAMOptions = map[string]string{}
+ }
+
+ if len(network.ID) != 64 {
+ return errors.Errorf("invalid network ID %q", network.ID)
+ }
+
+ return util.ValidateSubnets(network, nil)
+}
+
+func (n *netavarkNetwork) createDefaultNetwork() (*types.Network, error) {
+ net := types.Network{
+ Name: n.defaultNetwork,
+ NetworkInterface: defaultBridgeName + "0",
+ // Important do not change this ID
+ ID: "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9",
+ Driver: types.BridgeNetworkDriver,
+ Subnets: []types.Subnet{
+ {Subnet: n.defaultSubnet},
+ },
+ }
+ return n.networkCreate(net, true)
+}
+
+// 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 *netavarkNetwork) getNetwork(nameOrID string) (*types.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 *types.Network
+ for _, val := range n.networks {
+ // This should not happen because we already looked up the map by name but check anyway.
+ if val.Name == nameOrID {
+ return val, nil
+ }
+
+ if strings.HasPrefix(val.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)
+}
+
+// Implement the NetUtil interface for easy code sharing with other network interfaces.
+
+// ForEach call the given function for each network
+func (n *netavarkNetwork) ForEach(run func(types.Network)) {
+ for _, val := range n.networks {
+ run(*val)
+ }
+}
+
+// Len return the number of networks
+func (n *netavarkNetwork) Len() int {
+ return len(n.networks)
+}
+
+// DefaultInterfaceName return the default cni bridge name, must be suffixed with a number.
+func (n *netavarkNetwork) DefaultInterfaceName() string {
+ return defaultBridgeName
+}
+
+func (n *netavarkNetwork) Network(nameOrID string) (*types.Network, error) {
+ network, err := n.getNetwork(nameOrID)
+ if err != nil {
+ return nil, err
+ }
+ return network, nil
+}
diff --git a/libpod/network/netavark/run.go b/libpod/network/netavark/run.go
new file mode 100644
index 000000000..2f839151e
--- /dev/null
+++ b/libpod/network/netavark/run.go
@@ -0,0 +1,119 @@
+// +build linux
+
+package netavark
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/containers/podman/v3/libpod/network/internal/util"
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+type netavarkOptions struct {
+ types.NetworkOptions
+ Networks map[string]*types.Network `json:"network_info"`
+}
+
+// Setup will setup the container network namespace. It returns
+// a map of StatusBlocks, the key is the network name.
+func (n *netavarkNetwork) 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
+ }
+
+ err = util.ValidateSetupOptions(n, namespacePath, options)
+ if err != nil {
+ return nil, err
+ }
+
+ // allocate IPs in the IPAM db
+ err = n.allocIPs(&options.NetworkOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ netavarkOpts, err := n.convertNetOpts(options.NetworkOptions)
+ if err != nil {
+ return nil, errors.Wrap(err, "failed to convert net opts")
+ }
+
+ // trace output to get the json
+ if logrus.IsLevelEnabled(logrus.TraceLevel) {
+ b, err := json.Marshal(&netavarkOpts)
+ if err != nil {
+ return nil, err
+ }
+ // show the full netavark command so we can easily reproduce errors from the cli
+ logrus.Tracef("netavark command: printf '%s' | %s setup %s", string(b), n.netavarkBinary, namespacePath)
+ }
+
+ result := map[string]types.StatusBlock{}
+ err = execNetavark(n.netavarkBinary, []string{"setup", namespacePath}, netavarkOpts, &result)
+
+ if len(result) != len(options.Networks) {
+ logrus.Errorf("unexpected netavark result: %v", result)
+ return nil, fmt.Errorf("unexpected netavark result length, want (%d), got (%d) networks", len(options.Networks), len(result))
+ }
+
+ return result, err
+}
+
+// Teardown will teardown the container network namespace.
+func (n *netavarkNetwork) Teardown(namespacePath string, options types.TeardownOptions) error {
+ n.lock.Lock()
+ defer n.lock.Unlock()
+ err := n.loadNetworks()
+ if err != nil {
+ return err
+ }
+
+ // get IPs from the IPAM db
+ err = n.getAssignedIPs(&options.NetworkOptions)
+ if err != nil {
+ // when there is an error getting the ips we should still continue
+ // to call teardown for netavark to prevent leaking network interfaces
+ logrus.Error(err)
+ }
+
+ netavarkOpts, err := n.convertNetOpts(options.NetworkOptions)
+ if err != nil {
+ return errors.Wrap(err, "failed to convert net opts")
+ }
+
+ retErr := execNetavark(n.netavarkBinary, []string{"teardown", namespacePath}, netavarkOpts, nil)
+
+ // when netavark returned an error we still free the used ips
+ // otherwise we could end up in a state where block the ips forever
+ err = n.deallocIPs(&netavarkOpts.NetworkOptions)
+ if err != nil {
+ if retErr != nil {
+ logrus.Error(err)
+ } else {
+ retErr = err
+ }
+ }
+
+ return retErr
+}
+
+func (n *netavarkNetwork) convertNetOpts(opts types.NetworkOptions) (*netavarkOptions, error) {
+ netavarkOptions := netavarkOptions{
+ NetworkOptions: opts,
+ Networks: make(map[string]*types.Network, len(opts.Networks)),
+ }
+
+ for network := range opts.Networks {
+ net, err := n.getNetwork(network)
+ if err != nil {
+ return nil, err
+ }
+ netavarkOptions.Networks[network] = net
+ }
+ return &netavarkOptions, nil
+}
diff --git a/libpod/network/netavark/run_test.go b/libpod/network/netavark/run_test.go
new file mode 100644
index 000000000..3279203cc
--- /dev/null
+++ b/libpod/network/netavark/run_test.go
@@ -0,0 +1,693 @@
+// +build linux
+
+package netavark_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 (
+ "io/ioutil"
+ "net"
+ "os"
+ "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"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/containers/podman/v3/libpod/network/util"
+ "github.com/containers/podman/v3/pkg/netns"
+ "github.com/containers/podman/v3/pkg/rootless"
+ "github.com/containers/storage/pkg/stringid"
+)
+
+var _ = Describe("run netavark", func() {
+ var (
+ libpodNet types.ContainerNetwork
+ confDir string
+ netNSTest ns.NetNS
+ netNSContainer ns.NetNS
+ )
+
+ // 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()
+ // 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() {
+ if _, ok := os.LookupEnv("NETAVARK_BINARY"); !ok {
+ Skip("NETAVARK_BINARY not set skip run tests")
+ }
+
+ // set the logrus settings
+ logrus.SetLevel(logrus.TraceLevel)
+ // disable extra quotes so we can easily copy the netavark command
+ logrus.SetFormatter(&logrus.TextFormatter{DisableQuote: true})
+ logrus.SetOutput(os.Stderr)
+ // 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
+ confDir, err = ioutil.TempDir("", "podman_netavark_test")
+ if err != nil {
+ Fail("Failed to create tmpdir")
+ }
+
+ 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(confDir, false)
+ if err != nil {
+ Fail("Failed to create NewCNINetworkInterface")
+ }
+ })
+
+ AfterEach(func() {
+ logrus.SetFormatter(&logrus.TextFormatter{})
+ logrus.SetLevel(logrus.InfoLevel)
+ os.RemoveAll(confDir)
+
+ netns.UnmountNS(netNSTest)
+ netNSTest.Close()
+
+ netns.UnmountNS(netNSContainer)
+ netNSContainer.Close()
+ })
+
+ It("test basic setup", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ opts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: "someID",
+ ContainerName: "someName",
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), opts)
+ Expect(err).ToNot(HaveOccurred())
+ 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))
+ ip := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(ip.String()).To(ContainSubstring("10.88.0."))
+ gw := res[defNet].Interfaces[intName].Networks[0].Gateway
+ util.NormalizeIP(&gw)
+ Expect(gw.String()).To(Equal("10.88.0.1"))
+ macAddress := res[defNet].Interfaces[intName].MacAddress
+ Expect(macAddress).To(HaveLen(6))
+ // default network has no dns
+ Expect(res[defNet].DNSServerIPs).To(BeEmpty())
+ Expect(res[defNet].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(intName)
+ Expect(err).To(BeNil())
+ Expect(i.Name).To(Equal(intName))
+ Expect(i.HardwareAddr).To(Equal(net.HardwareAddr(macAddress)))
+ addrs, err := i.Addrs()
+ Expect(err).To(BeNil())
+ subnet := &net.IPNet{
+ IP: ip,
+ Mask: net.CIDRMask(16, 32),
+ }
+ Expect(addrs).To(ContainElements(EqualSubnet(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())
+
+ // default bridge name
+ bridgeName := "podman0"
+ // check settings on the host side
+ i, err := net.InterfaceByName(bridgeName)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(bridgeName))
+ addrs, err := i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ // test that the gateway ip is assigned to the interface
+ subnet := &net.IPNet{
+ IP: gw,
+ Mask: net.CIDRMask(16, 32),
+ }
+ Expect(addrs).To(ContainElements(EqualSubnet(subnet)))
+
+ wg := &sync.WaitGroup{}
+ expected := stringid.GenerateNonCryptoID()
+ // now check ip connectivity
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ wg.Add(1)
+ runNetListener(wg, "tcp", "0.0.0.0", 5000, expected)
+ return nil
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ conn, err := net.Dial("tcp", ip.String()+":5000")
+ Expect(err).To(BeNil())
+ _, err = conn.Write([]byte(expected))
+ Expect(err).To(BeNil())
+ conn.Close()
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(opts))
+ Expect(err).ToNot(HaveOccurred())
+ wg.Wait()
+ })
+ })
+
+ It("setup two containers", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ setupOpts1 := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts1)
+ Expect(err).ToNot(HaveOccurred())
+ 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))
+ ip1 := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(ip1.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+
+ setupOpts2 := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {InterfaceName: intName},
+ },
+ },
+ }
+
+ netNSContainer2, err := netns.NewNS()
+ Expect(err).ToNot(HaveOccurred())
+ defer netns.UnmountNS(netNSContainer2)
+ defer netNSContainer2.Close()
+
+ res, err = libpodNet.Setup(netNSContainer2.Path(), setupOpts2)
+ Expect(err).ToNot(HaveOccurred())
+ 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))
+ ip2 := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(ip2.String()).To(ContainSubstring("10.88.0."))
+ Expect(res[defNet].Interfaces[intName].MacAddress).To(HaveLen(6))
+ Expect(ip1.Equal(ip2)).To(BeFalse(), "IP1 %s should not be equal to IP2 %s", ip1.String(), ip2.String())
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts1))
+ Expect(err).ToNot(HaveOccurred())
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts2))
+ Expect(err).ToNot(HaveOccurred())
+ })
+ })
+
+ It("setup dualstack network", func() {
+ runTest(func() {
+ s1, _ := types.ParseCIDR("10.0.0.1/24")
+ s2, _ := types.ParseCIDR("fd10:88:a::/64")
+ network, err := libpodNet.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: s1}, {Subnet: s2},
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName := network.Name
+ intName := "eth0"
+
+ setupOpts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: stringid.GenerateNonCryptoID(),
+ Networks: map[string]types.PerNetworkOptions{
+ netName: {InterfaceName: intName},
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), setupOpts)
+ Expect(err).ToNot(HaveOccurred())
+ 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(2))
+ ip1 := res[netName].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(ip1.String()).To(ContainSubstring("10.0.0."))
+ gw1 := res[netName].Interfaces[intName].Networks[0].Gateway
+ Expect(gw1.String()).To(Equal("10.0.0.1"))
+ ip2 := res[netName].Interfaces[intName].Networks[1].Subnet.IP
+ Expect(ip2.String()).To(ContainSubstring("fd10:88:a::"))
+ gw2 := res[netName].Interfaces[intName].Networks[0].Gateway
+ Expect(gw2.String()).To(Equal("fd10:88:a::1"))
+ Expect(res[netName].Interfaces[intName].MacAddress).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(intName)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(intName))
+ addrs, err := i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ subnet1 := s1.IPNet
+ subnet1.IP = ip1
+ subnet2 := s2.IPNet
+ subnet2.IP = ip2
+ Expect(addrs).To(ContainElements(EqualSubnet(&subnet1), EqualSubnet(&subnet2)))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).ToNot(HaveOccurred())
+ 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).ToNot(HaveOccurred())
+
+ bridgeName := network.NetworkInterface
+ // check settings on the host side
+ i, err := net.InterfaceByName(bridgeName)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(bridgeName))
+ addrs, err := i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ // test that the gateway ip is assigned to the interface
+ subnet1 := s1.IPNet
+ subnet1.IP = gw1
+ subnet2 := s2.IPNet
+ subnet2.IP = gw2
+ Expect(addrs).To(ContainElements(EqualSubnet(&subnet1), EqualSubnet(&subnet2)))
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).ToNot(HaveOccurred())
+ })
+ })
+
+ It("setup two networks", func() {
+ runTest(func() {
+ s1, _ := types.ParseCIDR("10.0.0.1/24")
+ network1, err := libpodNet.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: s1},
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName1 := network1.Name
+ intName1 := "eth0"
+
+ s2, _ := types.ParseCIDR("10.1.0.0/24")
+ network2, err := libpodNet.NetworkCreate(types.Network{
+ Subnets: []types.Subnet{
+ {Subnet: s2},
+ },
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ netName2 := network2.Name
+ intName2 := "eth1"
+
+ 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).ToNot(HaveOccurred())
+ Expect(res).To(HaveLen(2))
+ Expect(res).To(HaveKey(netName1))
+ Expect(res).To(HaveKey(netName2))
+ Expect(res[netName1].Interfaces).To(HaveKey(intName1))
+ Expect(res[netName2].Interfaces).To(HaveKey(intName2))
+ Expect(res[netName1].Interfaces[intName1].Networks).To(HaveLen(1))
+ ip1 := res[netName1].Interfaces[intName1].Networks[0].Subnet.IP
+ Expect(ip1.String()).To(ContainSubstring("10.0.0."))
+ gw1 := res[netName1].Interfaces[intName1].Networks[0].Gateway
+ Expect(gw1.String()).To(Equal("10.0.0.1"))
+ ip2 := res[netName2].Interfaces[intName2].Networks[0].Subnet.IP
+ Expect(ip2.String()).To(ContainSubstring("10.1.0."))
+ gw2 := res[netName2].Interfaces[intName2].Networks[0].Gateway
+ Expect(gw2.String()).To(Equal("10.1.0.1"))
+ mac1 := res[netName1].Interfaces[intName1].MacAddress
+ Expect(mac1).To(HaveLen(6))
+ mac2 := res[netName2].Interfaces[intName2].MacAddress
+ Expect(mac2).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).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(intName1))
+ addrs, err := i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ subnet1 := s1.IPNet
+ subnet1.IP = ip1
+ Expect(addrs).To(ContainElements(EqualSubnet(&subnet1)))
+
+ i, err = net.InterfaceByName(intName2)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(intName2))
+ addrs, err = i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ subnet2 := s2.IPNet
+ subnet2.IP = ip2
+ Expect(addrs).To(ContainElements(EqualSubnet(&subnet2)))
+
+ // check loopback adapter
+ i, err = net.InterfaceByName("lo")
+ Expect(err).ToNot(HaveOccurred())
+ 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).ToNot(HaveOccurred())
+
+ bridgeName1 := network1.NetworkInterface
+ // check settings on the host side
+ i, err := net.InterfaceByName(bridgeName1)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(bridgeName1))
+ addrs, err := i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ // test that the gateway ip is assigned to the interface
+ subnet1 := s1.IPNet
+ subnet1.IP = gw1
+ Expect(addrs).To(ContainElements(EqualSubnet(&subnet1)))
+
+ bridgeName2 := network2.NetworkInterface
+ // check settings on the host side
+ i, err = net.InterfaceByName(bridgeName2)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(i.Name).To(Equal(bridgeName2))
+ addrs, err = i.Addrs()
+ Expect(err).ToNot(HaveOccurred())
+ // test that the gateway ip is assigned to the interface
+ subnet2 := s2.IPNet
+ subnet2.IP = gw2
+ Expect(addrs).To(ContainElements(EqualSubnet(&subnet2)))
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(setupOpts))
+ Expect(err).ToNot(HaveOccurred())
+ })
+ })
+
+ 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("simple teardown", func() {
+ runTest(func() {
+ defNet := types.DefaultNetworkName
+ intName := "eth0"
+ opts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: "someID",
+ ContainerName: "someName",
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+ res, err := libpodNet.Setup(netNSContainer.Path(), opts)
+ Expect(err).ToNot(HaveOccurred())
+ 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))
+ ip := res[defNet].Interfaces[intName].Networks[0].Subnet.IP
+ Expect(ip.String()).To(ContainSubstring("10.88.0."))
+ gw := res[defNet].Interfaces[intName].Networks[0].Gateway
+ Expect(gw.String()).To(Equal("10.88.0.1"))
+ macAddress := res[defNet].Interfaces[intName].MacAddress
+ Expect(macAddress).To(HaveLen(6))
+
+ err = libpodNet.Teardown(netNSContainer.Path(), types.TeardownOptions(opts))
+ Expect(err).ToNot(HaveOccurred())
+ err = netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+ // check that the container interface is removed
+ _, err := net.InterfaceByName(intName)
+ Expect(err).To(HaveOccurred())
+ return nil
+ })
+ Expect(err).ToNot(HaveOccurred())
+
+ // default bridge name
+ bridgeName := "podman0"
+ // check that bridge interface was removed
+ _, err = net.InterfaceByName(bridgeName)
+ Expect(err).To(HaveOccurred())
+ })
+ })
+
+ It("test netavark error", func() {
+ runTest(func() {
+ intName := "eth0"
+ err := netNSContainer.Do(func(_ ns.NetNS) error {
+ defer GinkgoRecover()
+
+ attr := netlink.NewLinkAttrs()
+ attr.Name = "eth0"
+ err := netlink.LinkAdd(&netlink.Bridge{LinkAttrs: attr})
+ Expect(err).ToNot(HaveOccurred())
+ return nil
+ })
+ Expect(err).ToNot(HaveOccurred())
+ defNet := types.DefaultNetworkName
+ opts := types.SetupOptions{
+ NetworkOptions: types.NetworkOptions{
+ ContainerID: "someID",
+ ContainerName: "someName",
+ Networks: map[string]types.PerNetworkOptions{
+ defNet: {
+ InterfaceName: intName,
+ },
+ },
+ },
+ }
+ _, err = libpodNet.Setup(netNSContainer.Path(), opts)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("interface eth0 already exists on container namespace"))
+ })
+ })
+})
+
+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()
+ defer ln.Close()
+ conn, err := ln.Accept()
+ Expect(err).To(BeNil())
+ defer conn.Close()
+ conn.SetDeadline(time.Now().Add(1 * time.Second))
+ data, err := ioutil.ReadAll(conn)
+ Expect(err).To(BeNil())
+ Expect(string(data)).To(Equal(expectedData))
+ }()
+ 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()
+ defer conn.Close()
+ 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))
+ }()
+ default:
+ Fail("unsupported protocol")
+ }
+}
diff --git a/libpod/network/netavark/testfiles/invalid/broken.json b/libpod/network/netavark/testfiles/invalid/broken.json
new file mode 100644
index 000000000..8968ddc73
--- /dev/null
+++ b/libpod/network/netavark/testfiles/invalid/broken.json
@@ -0,0 +1,16 @@
+{
+ "name": "bridge",
+ "id": "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121",
+ "driver": "bridge",
+ "network_interface": "podman9",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.8.0/24",
+ "gateway": "10.89.8.1",
+ "lease_range": {
+ "start_ip": "10.89.8.20",
+ "end_ip": "10.89.8.50"
+ }
+ }
+ ],
diff --git a/libpod/network/netavark/testfiles/invalid/invalid name.json b/libpod/network/netavark/testfiles/invalid/invalid name.json
new file mode 100644
index 000000000..02b441279
--- /dev/null
+++ b/libpod/network/netavark/testfiles/invalid/invalid name.json
@@ -0,0 +1,19 @@
+{
+ "name": "invalid name",
+ "id": "6839f44f0fd01c5c5830856b66a1d7ce46842dd8798be0addf96f7255ce9f889",
+ "driver": "bridge",
+ "network_interface": "podman9",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.8.0/24",
+ "gateway": "10.89.8.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": true,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/invalid/invalid_gateway.json b/libpod/network/netavark/testfiles/invalid/invalid_gateway.json
new file mode 100644
index 000000000..6e3a83156
--- /dev/null
+++ b/libpod/network/netavark/testfiles/invalid/invalid_gateway.json
@@ -0,0 +1,19 @@
+{
+ "name": "invalid_gateway",
+ "id": "49be6e401e7f8b9844afb969dcbc96e78205ed86ec1e5a46150bd4ab4fdd5686",
+ "driver": "bridge",
+ "network_interface": "podman9",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.9.0/24",
+ "gateway": "10.89.100.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": true,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/invalid/name_missmatch.json b/libpod/network/netavark/testfiles/invalid/name_missmatch.json
new file mode 100644
index 000000000..a3142d8bb
--- /dev/null
+++ b/libpod/network/netavark/testfiles/invalid/name_missmatch.json
@@ -0,0 +1,19 @@
+{
+ "name": "name_miss",
+ "id": "3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f",
+ "driver": "bridge",
+ "network_interface": "podman8",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.7.0/24",
+ "gateway": "10.89.7.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": true,
+ "dns_enabled": false,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/invalid/wrongID.json b/libpod/network/netavark/testfiles/invalid/wrongID.json
new file mode 100644
index 000000000..7c1446306
--- /dev/null
+++ b/libpod/network/netavark/testfiles/invalid/wrongID.json
@@ -0,0 +1,19 @@
+{
+ "name": "wrongID",
+ "id": "someID",
+ "driver": "bridge",
+ "network_interface": "podman1",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.0.0/24",
+ "gateway": "10.89.0.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": false,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/bridge.json b/libpod/network/netavark/testfiles/valid/bridge.json
new file mode 100644
index 000000000..f4ec82188
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/bridge.json
@@ -0,0 +1,23 @@
+{
+ "name": "bridge",
+ "id": "17f29b073143d8cd97b5bbe492bdeffec1c5fee55cc1fe2112c8b9335f8b6121",
+ "driver": "bridge",
+ "network_interface": "podman9",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.8.0/24",
+ "gateway": "10.89.8.1",
+ "lease_range": {
+ "start_ip": "10.89.8.20",
+ "end_ip": "10.89.8.50"
+ }
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": true,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/dualstack.json b/libpod/network/netavark/testfiles/valid/dualstack.json
new file mode 100644
index 000000000..bb4168f3a
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/dualstack.json
@@ -0,0 +1,23 @@
+{
+ "name": "dualstack",
+ "id": "6839f44f0fd01c5c5830856b66a1d7ce46842dd8798be0addf96f7255ce9f889",
+ "driver": "bridge",
+ "network_interface": "podman21",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "fd10:88:a::/64",
+ "gateway": "fd10:88:a::1"
+ },
+ {
+ "subnet": "10.89.19.0/24",
+ "gateway": "10.89.19.10"
+ }
+ ],
+ "ipv6_enabled": true,
+ "internal": false,
+ "dns_enabled": true,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/internal.json b/libpod/network/netavark/testfiles/valid/internal.json
new file mode 100644
index 000000000..3ccdd3889
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/internal.json
@@ -0,0 +1,18 @@
+{
+ "name": "internal",
+ "id": "3bed2cb3a3acf7b6a8ef408420cc682d5520e26976d354254f528c965612054f",
+ "driver": "bridge",
+ "network_interface": "podman8",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.7.0/24"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": true,
+ "dns_enabled": false,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/label.json b/libpod/network/netavark/testfiles/valid/label.json
new file mode 100644
index 000000000..c4ed637ec
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/label.json
@@ -0,0 +1,22 @@
+{
+ "name": "label",
+ "id": "1aca80e8b55c802f7b43740da2990e1b5735bbb323d93eb5ebda8395b04025e2",
+ "driver": "bridge",
+ "network_interface": "podman15",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.13.0/24",
+ "gateway": "10.89.13.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": true,
+ "labels": {
+ "mykey": "value"
+ },
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/mtu.json b/libpod/network/netavark/testfiles/valid/mtu.json
new file mode 100644
index 000000000..53fa4c9bc
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/mtu.json
@@ -0,0 +1,22 @@
+{
+ "name": "mtu",
+ "id": "49be6e401e7f8b9844afb969dcbc96e78205ed86ec1e5a46150bd4ab4fdd5686",
+ "driver": "bridge",
+ "network_interface": "podman13",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.11.0/24",
+ "gateway": "10.89.11.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": true,
+ "options": {
+ "mtu": "1500"
+ },
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/podman.json b/libpod/network/netavark/testfiles/valid/podman.json
new file mode 100644
index 000000000..19acddc83
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/podman.json
@@ -0,0 +1,19 @@
+{
+ "name": "podman",
+ "id": "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9",
+ "driver": "bridge",
+ "network_interface": "podman0",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.88.0.0/16",
+ "gateway": "10.88.0.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": false,
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/netavark/testfiles/valid/vlan.json b/libpod/network/netavark/testfiles/valid/vlan.json
new file mode 100644
index 000000000..30c88ec49
--- /dev/null
+++ b/libpod/network/netavark/testfiles/valid/vlan.json
@@ -0,0 +1,22 @@
+{
+ "name": "vlan",
+ "id": "c3b258168c41c0bce97616716bef315eeed33eb1142904bfe7f32eb392c7cf80",
+ "driver": "bridge",
+ "network_interface": "podman14",
+ "created": "2021-10-06T18:50:54.25770461+02:00",
+ "subnets": [
+ {
+ "subnet": "10.89.12.0/24",
+ "gateway": "10.89.12.1"
+ }
+ ],
+ "ipv6_enabled": false,
+ "internal": false,
+ "dns_enabled": true,
+ "options": {
+ "vlan": "5"
+ },
+ "ipam_options": {
+ "driver": "host-local"
+ }
+}
diff --git a/libpod/network/types/network.go b/libpod/network/types/network.go
index 657c1ca6a..ba5e018fd 100644
--- a/libpod/network/types/network.go
+++ b/libpod/network/types/network.go
@@ -1,6 +1,7 @@
package types
import (
+ "encoding/json"
"net"
"time"
)
@@ -94,6 +95,51 @@ func (n *IPNet) UnmarshalText(text []byte) error {
return nil
}
+// HardwareAddr is the same as net.HardwareAddr except
+// that it adds the json marshal/unmarshal methods.
+// This allows us to read the mac from a json string
+// and a byte array.
+// swagger:model MacAddress
+type HardwareAddr net.HardwareAddr
+
+func (h *HardwareAddr) String() string {
+ return (*net.HardwareAddr)(h).String()
+}
+
+func (h *HardwareAddr) MarshalText() ([]byte, error) {
+ return []byte((*net.HardwareAddr)(h).String()), nil
+}
+
+func (h *HardwareAddr) UnmarshalJSON(text []byte) error {
+ if len(text) == 0 {
+ *h = nil
+ return nil
+ }
+
+ // if the json string start with a quote we got a string
+ // unmarshal the string and parse the mac from this string
+ if string(text[0]) == `"` {
+ var macString string
+ err := json.Unmarshal(text, &macString)
+ if err == nil {
+ mac, err := net.ParseMAC(macString)
+ if err == nil {
+ *h = HardwareAddr(mac)
+ return nil
+ }
+ }
+ }
+ // not a string or got an error fallback to the normal parsing
+ mac := make(net.HardwareAddr, 0, 6)
+ // use the standard json unmarshal for backwards compat
+ err := json.Unmarshal(text, &mac)
+ if err != nil {
+ return err
+ }
+ *h = HardwareAddr(mac)
+ return nil
+}
+
type Subnet struct {
// Subnet for this Network in CIDR form.
// swagger:strfmt string
@@ -134,7 +180,7 @@ 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"`
+ MacAddress HardwareAddr `json:"mac_address"`
}
// NetAddress contains the subnet and gateway.
@@ -157,7 +203,7 @@ type PerNetworkOptions struct {
// Optional.
Aliases []string `json:"aliases,omitempty"`
// StaticMac for this container. Optional.
- StaticMAC net.HardwareAddr `json:"static_mac,omitempty"`
+ StaticMAC HardwareAddr `json:"static_mac,omitempty"`
// InterfaceName for this container. Required.
InterfaceName string `json:"interface_name"`
}
diff --git a/libpod/network/types/network_test.go b/libpod/network/types/network_test.go
new file mode 100644
index 000000000..91ee93692
--- /dev/null
+++ b/libpod/network/types/network_test.go
@@ -0,0 +1,82 @@
+package types_test
+
+import (
+ "encoding/json"
+ "reflect"
+ "testing"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+)
+
+func TestUnmarshalMacAddress(t *testing.T) {
+ tests := []struct {
+ name string
+ json string
+ want types.HardwareAddr
+ wantErr bool
+ }{
+ {
+ name: "mac as string with colon",
+ json: `"52:54:00:1c:2e:46"`,
+ want: types.HardwareAddr{0x52, 0x54, 0x00, 0x1c, 0x2e, 0x46},
+ },
+ {
+ name: "mac as string with dash",
+ json: `"52-54-00-1c-2e-46"`,
+ want: types.HardwareAddr{0x52, 0x54, 0x00, 0x1c, 0x2e, 0x46},
+ },
+ {
+ name: "mac as byte array",
+ json: `[82, 84, 0, 28, 46, 70]`,
+ want: types.HardwareAddr{0x52, 0x54, 0x00, 0x1c, 0x2e, 0x46},
+ },
+ {
+ name: "null value",
+ json: `null`,
+ want: nil,
+ },
+ {
+ name: "mac as base64",
+ json: `"qrvM3e7/"`,
+ want: types.HardwareAddr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+ },
+ {
+ name: "invalid string",
+ json: `"52:54:00:1c:2e`,
+ wantErr: true,
+ },
+ {
+ name: "invalid array",
+ json: `[82, 84, 0, 28, 46`,
+ wantErr: true,
+ },
+
+ {
+ name: "invalid value",
+ json: `ab`,
+ wantErr: true,
+ },
+ {
+ name: "invalid object",
+ json: `{}`,
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ test := tt
+ t.Run(test.name, func(t *testing.T) {
+ mac := types.HardwareAddr{}
+ err := json.Unmarshal([]byte(test.json), &mac)
+ if (err != nil) != test.wantErr {
+ t.Errorf("types.HardwareAddress Unmarshal() error = %v, wantErr %v", err, test.wantErr)
+ return
+ }
+ if test.wantErr {
+ return
+ }
+ if !reflect.DeepEqual(mac, test.want) {
+ t.Errorf("types.HardwareAddress Unmarshal() got = %v, want %v", mac, test.want)
+ }
+ })
+ }
+}
diff --git a/libpod/network/util/ip.go b/libpod/network/util/ip.go
index b2ba92735..e82b4a781 100644
--- a/libpod/network/util/ip.go
+++ b/libpod/network/util/ip.go
@@ -1,10 +1,7 @@
package util
import (
- "crypto/rand"
"net"
-
- "github.com/pkg/errors"
)
// IsIPv6 returns true if netIP is IPv6.
@@ -17,40 +14,6 @@ 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
@@ -84,30 +47,10 @@ func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) { //nolint:interfacer
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
+// NormalizeIP will transform the given ip to the 4 byte len ipv4 if possible
+func NormalizeIP(ip *net.IP) {
+ ipv4 := ip.To4()
+ if ipv4 != nil {
+ *ip = ipv4
}
- // 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 --git a/libpod/network/util/ip_calc.go b/libpod/network/util/ip_calc.go
new file mode 100644
index 000000000..a27ddf78b
--- /dev/null
+++ b/libpod/network/util/ip_calc.go
@@ -0,0 +1,53 @@
+// Copyright 2015 CNI authors
+//
+// 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 util
+
+import (
+ "math/big"
+ "net"
+)
+
+// NextIP returns IP incremented by 1
+func NextIP(ip net.IP) net.IP {
+ i := ipToInt(ip)
+ return intToIP(i.Add(i, big.NewInt(1)))
+}
+
+// PrevIP returns IP decremented by 1
+func PrevIP(ip net.IP) net.IP {
+ i := ipToInt(ip)
+ return intToIP(i.Sub(i, big.NewInt(1)))
+}
+
+// Cmp compares two IPs, returning the usual ordering:
+// a < b : -1
+// a == b : 0
+// a > b : 1
+func Cmp(a, b net.IP) int {
+ aa := ipToInt(a)
+ bb := ipToInt(b)
+ return aa.Cmp(bb)
+}
+
+func ipToInt(ip net.IP) *big.Int {
+ if v := ip.To4(); v != nil {
+ return big.NewInt(0).SetBytes(v)
+ }
+ return big.NewInt(0).SetBytes(ip.To16())
+}
+
+func intToIP(i *big.Int) net.IP {
+ return net.IP(i.Bytes())
+}
diff --git a/libpod/network/util/ip_test.go b/libpod/network/util/ip_test.go
index c26ad140a..63ac555f0 100644
--- a/libpod/network/util/ip_test.go
+++ b/libpod/network/util/ip_test.go
@@ -1,9 +1,7 @@
package util
import (
- "fmt"
"net"
- "reflect"
"testing"
)
@@ -12,34 +10,6 @@ func parseCIDR(n string) *net.IPNet {
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
@@ -101,25 +71,3 @@ func TestLastIPInSubnet(t *testing.T) {
})
}
}
-
-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 --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index e792a410c..fc91155fa 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -11,6 +11,7 @@ import (
"os/exec"
"path/filepath"
"regexp"
+ "sort"
"strconv"
"strings"
"syscall"
@@ -41,8 +42,11 @@ const (
// default slirp4ns subnet
defaultSlirp4netnsSubnet = "10.0.2.0/24"
- // rootlessCNINSName is the file name for the rootless network namespace bind mount
- rootlessCNINSName = "rootless-cni-ns"
+ // rootlessNetNsName is the file name for the rootless network namespace bind mount
+ rootlessNetNsName = "rootless-netns"
+
+ // rootlessNetNsSilrp4netnsPidFile is the name of the rootless netns slirp4netns pid file
+ rootlessNetNsSilrp4netnsPidFile = "rootless-netns-slirp4netns.pid"
// persistentCNIDir is the directory where the CNI files are stored
persistentCNIDir = "/var/lib/cni"
@@ -88,10 +92,7 @@ func (c *Container) getNetworkOptions() (types.NetworkOptions, error) {
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
- // TODO remove ocicni PortMappings from container config and store as types PortMappings
- if len(c.config.PortMappings) > 0 {
- opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings)
- }
+ opts.PortMappings = c.config.PortMappings
networks, _, err := c.networks()
if err != nil {
return opts, err
@@ -136,21 +137,21 @@ func (c *Container) getNetworkOptions() (types.NetworkOptions, error) {
return opts, nil
}
-type RootlessCNI struct {
+type RootlessNetNS struct {
ns ns.NetNS
dir string
Lock lockfile.Locker
}
-// getPath will join the given path to the rootless cni dir
-func (r *RootlessCNI) getPath(path string) string {
+// getPath will join the given path to the rootless netns dir
+func (r *RootlessNetNS) getPath(path string) string {
return filepath.Join(r.dir, path)
}
-// Do - run the given function in the rootless cni ns.
+// Do - run the given function in the rootless netns.
// 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 {
+func (r *RootlessNetNS) 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.
@@ -161,11 +162,11 @@ func (r *RootlessCNI) Do(toRun func() error) error {
// 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
+ // 1. XDG_RUNTIME_DIR -> XDG_RUNTIME_DIR/rootless-netns/XDG_RUNTIME_DIR
+ // 2. /run/systemd -> XDG_RUNTIME_DIR/rootless-netns/run/systemd (only if it exists)
+ // 3. XDG_RUNTIME_DIR/rootless-netns/resolv.conf -> /etc/resolv.conf or XDG_RUNTIME_DIR/rootless-netns/run/symlink/target
+ // 4. XDG_RUNTIME_DIR/rootless-netns/var/lib/cni -> /var/lib/cni (if /var/lib/cni does not exists use the parent dir)
+ // 5. XDG_RUNTIME_DIR/rootless-netns/run -> /run
// Create a new mount namespace,
// this must happen inside the netns thread.
@@ -174,16 +175,16 @@ func (r *RootlessCNI) Do(toRun func() error) error {
return errors.Wrapf(err, "cannot create a new mount namespace")
}
- netNsDir, err := netns.GetNSRunDir()
+ xdgRuntimeDir, err := util.GetRuntimeDir()
if err != nil {
- return errors.Wrap(err, "could not get network namespace directory")
+ return errors.Wrap(err, "could not get runtime directory")
}
- newNetNsDir := r.getPath(netNsDir)
+ newXDGRuntimeDir := r.getPath(xdgRuntimeDir)
// 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, "")
+ err = unix.Mount(xdgRuntimeDir, newXDGRuntimeDir, "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")
+ return errors.Wrap(err, "failed to mount runtime directory for rootless netns")
}
// 2. Also keep /run/systemd if it exists.
@@ -194,7 +195,7 @@ func (r *RootlessCNI) Do(toRun func() error) error {
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")
+ return errors.Wrap(err, "failed to mount /run/systemd directory for rootless netns")
}
}
@@ -242,25 +243,25 @@ func (r *RootlessCNI) Do(toRun func() error) error {
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)
+ return errors.Wrapf(err, "failed to mount tmpfs on %q for rootless netns", 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")
+ return errors.Wrap(err, "failed to create rootless-netns 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")
+ return errors.Wrap(err, "failed to create rootless-netns 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")
+ return errors.Wrap(err, "failed to mount resolv.conf for rootless netns")
}
// 4. CNI plugins need access to /var/lib/cni and /run
@@ -285,14 +286,14 @@ func (r *RootlessCNI) Do(toRun func() error) error {
// make sure to mount var first
err = unix.Mount(varDir, varTarget, "none", unix.MS_BIND, "")
if err != nil {
- return errors.Wrapf(err, "failed to mount %s for rootless cni", varTarget)
+ return errors.Wrapf(err, "failed to mount %s for rootless netns", varTarget)
}
// 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")
+ return errors.Wrap(err, "failed to mount /run for rootless netns")
}
// run the given function in the correct namespace
@@ -302,10 +303,11 @@ func (r *RootlessCNI) Do(toRun func() error) error {
return err
}
-// Cleanup the rootless cni namespace if needed.
+// Cleanup the rootless network 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 {
+// Cleanup() will try to lock RootlessNetNS, therefore you have to call
+// it with an unlocked lock.
+func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
_, err := os.Stat(r.dir)
if os.IsNotExist(err) {
// the directory does not exists no need for cleanup
@@ -314,8 +316,25 @@ func (r *RootlessCNI) Cleanup(runtime *Runtime) error {
r.Lock.Lock()
defer r.Lock.Unlock()
running := func(c *Container) bool {
+ // no bridge => no need to check
+ if !c.config.NetMode.IsBridge() {
+ return false
+ }
+
// 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
+ // locking is a problem because cleanup is called after net teardown
+ // at this stage the container is already locked.
+ // also do not try to lock only containers which are not currently in net
+ // teardown because this will result in an ABBA deadlock between the rootless
+ // cni lock and the container lock
+ // because we need to get the state we have to sync otherwise this will not
+ // work because the state is empty by default
+ // I do not like this but I do not see a better way at moment
+ err := c.syncContainer()
+ if err != nil {
+ return false
+ }
+
state := c.state.State
return state == define.ContainerStateRunning
}
@@ -323,101 +342,89 @@ func (r *RootlessCNI) Cleanup(runtime *Runtime) error {
if err != nil {
return err
}
- cleanup := true
- for _, ctr := range ctrs {
- if ctr.config.NetMode.IsBridge() {
- cleanup = false
- }
+ // no cleanup if we found containers
+ if len(ctrs) > 0 {
+ return nil
}
- if cleanup {
- // 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(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)
- if err != nil {
- return err
- }
- // make the following errors not fatal
- err = r.ns.Close()
- if err != nil {
- logrus.Error(err)
- }
- b, err := ioutil.ReadFile(r.getPath("rootless-cni-slirp4netns.pid"))
- if err == nil {
- var i int
- i, err = strconv.Atoi(string(b))
- if err == nil {
- // kill the slirp process so we do not leak it
- err = syscall.Kill(i, syscall.SIGTERM)
- }
- }
- if err != nil {
- logrus.Errorf("Failed to kill slirp4netns process: %s", err)
- }
- err = os.RemoveAll(r.dir)
- if err != nil {
- logrus.Error(err)
- }
- } else if err != nil && !os.IsNotExist(err) {
- logrus.Errorf("Could not read rootless cni directory, skipping cleanup: %s", err)
+ logrus.Debug("Cleaning up rootless network namespace")
+ err = netns.UnmountNS(r.ns)
+ if err != nil {
+ return err
+ }
+ // make the following errors not fatal
+ err = r.ns.Close()
+ if err != nil {
+ logrus.Error(err)
+ }
+ b, err := ioutil.ReadFile(r.getPath(rootlessNetNsSilrp4netnsPidFile))
+ if err == nil {
+ var i int
+ i, err = strconv.Atoi(string(b))
+ if err == nil {
+ // kill the slirp process so we do not leak it
+ err = syscall.Kill(i, syscall.SIGTERM)
}
}
+ if err != nil {
+ logrus.Errorf("Failed to kill slirp4netns process: %s", err)
+ }
+ err = os.RemoveAll(r.dir)
+ if err != nil {
+ logrus.Error(err)
+ }
return nil
}
-// 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.
+// GetRootlessNetNs returns the rootless netns object. If create is set to true
+// the rootless network 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) {
+func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
if !rootless.IsRootless() {
return nil, nil
}
- var rootlessCNINS *RootlessCNI
+ var rootlessNetNS *RootlessNetNS
runDir, err := util.GetRuntimeDir()
if err != nil {
return nil, err
}
- lfile := filepath.Join(runDir, "rootless-cni.lock")
+ lfile := filepath.Join(runDir, "rootless-netns.lock")
lock, err := lockfile.GetLockfile(lfile)
if err != nil {
- return nil, errors.Wrap(err, "failed to get rootless-cni lockfile")
+ return nil, errors.Wrap(err, "failed to get rootless-netns lockfile")
}
lock.Lock()
defer func() {
- // In case of an error (early exit) rootlessCNINS will be nil.
+ // In case of an error (early exit) rootlessNetNS will be nil.
// Make sure to unlock otherwise we could deadlock.
- if rootlessCNINS == nil {
+ if rootlessNetNS == nil {
lock.Unlock()
}
}()
- cniDir := filepath.Join(runDir, "rootless-cni")
- err = os.MkdirAll(cniDir, 0700)
+ rootlessNetNsDir := filepath.Join(runDir, rootlessNetNsName)
+ err = os.MkdirAll(rootlessNetNsDir, 0700)
if err != nil {
- return nil, errors.Wrap(err, "could not create rootless-cni directory")
+ return nil, errors.Wrap(err, "could not create rootless-netns directory")
}
nsDir, err := netns.GetNSRunDir()
if err != nil {
return nil, err
}
- path := filepath.Join(nsDir, rootlessCNINSName)
+ path := filepath.Join(nsDir, rootlessNetNsName)
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")
+ return nil, errors.Wrap(err, "error getting rootless network namespace")
}
// create a new namespace
- logrus.Debug("creating rootless cni network namespace")
- ns, err = netns.NewNSWithName(rootlessCNINSName)
+ logrus.Debug("creating rootless network namespace")
+ ns, err = netns.NewNSWithName(rootlessNetNsName)
if err != nil {
- return nil, errors.Wrap(err, "error creating rootless cni network namespace")
+ return nil, errors.Wrap(err, "error creating rootless network namespace")
}
// setup slirp4netns here
path := r.config.Engine.NetworkCmdPath
@@ -467,7 +474,7 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
// Leak one end of the pipe in slirp4netns
cmd.ExtraFiles = append(cmd.ExtraFiles, syncW)
- logPath := filepath.Join(r.config.Engine.TmpDir, "slirp4netns-rootless-cni.log")
+ logPath := filepath.Join(r.config.Engine.TmpDir, "slirp4netns-rootless-netns.log")
logFile, err := os.Create(logPath)
if err != nil {
return nil, errors.Wrapf(err, "failed to open slirp4netns log file %s", logPath)
@@ -486,9 +493,9 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
// 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)
+ err = ioutil.WriteFile(filepath.Join(rootlessNetNsDir, rootlessNetNsSilrp4netnsPidFile), []byte(pid), 0700)
if err != nil {
- errors.Wrap(err, "unable to write rootless-cni slirp4netns pid file")
+ errors.Wrap(err, "unable to write rootless-netns slirp4netns pid file")
}
defer func() {
@@ -529,43 +536,43 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
dnsOptions := resolvconf.GetOptions(conf.Content)
nameServers := resolvconf.GetNameservers(conf.Content)
- _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), append([]string{resolveIP.String()}, nameServers...), searchDomains, dnsOptions)
+ _, err = resolvconf.Build(filepath.Join(rootlessNetNsDir, "resolv.conf"), append([]string{resolveIP.String()}, nameServers...), searchDomains, dnsOptions)
if err != nil {
- return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf")
+ return nil, errors.Wrap(err, "failed to create rootless netns resolv.conf")
}
// 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)
+ err = os.MkdirAll(filepath.Join(rootlessNetNsDir, persistentCNIDir), 0700)
if err != nil {
- return nil, errors.Wrap(err, "could not create rootless-cni var directory")
+ return nil, errors.Wrap(err, "could not create rootless-netns var directory")
}
- runDir := filepath.Join(cniDir, "run")
+ runDir := filepath.Join(rootlessNetNsDir, "run")
err = os.MkdirAll(runDir, 0700)
if err != nil {
- return nil, errors.Wrap(err, "could not create rootless-cni run directory")
+ return nil, errors.Wrap(err, "could not create rootless-netns 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")
+ return nil, errors.Wrap(err, "could not create relabel rootless-netns 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")
+ return nil, errors.Wrap(err, "could not create rootless-netns 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)
+ // relative to the rootless-netns location
+ err = os.MkdirAll(filepath.Join(rootlessNetNsDir, nsDir), 0700)
if err != nil {
- return nil, errors.Wrap(err, "could not create rootless-cni netns directory")
+ return nil, errors.Wrap(err, "could not create rootless-netns 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.
+ // The CNI plugins and netavark 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 networking 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")
@@ -574,14 +581,14 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) {
os.Setenv("PATH", path)
}
- // Important set rootlessCNINS as last step.
+ // Important set rootlessNetNS as last step.
// Do not return any errors after this.
- rootlessCNINS = &RootlessCNI{
+ rootlessNetNS = &RootlessNetNS{
ns: ns,
- dir: cniDir,
+ dir: rootlessNetNsDir,
Lock: lock,
}
- return rootlessCNINS, nil
+ return rootlessNetNS, nil
}
// setPrimaryMachineIP is used for podman-machine and it sets
@@ -603,14 +610,14 @@ func setPrimaryMachineIP() error {
}
// setUpNetwork will set up the the networks, on error it will also tear down the cni
-// networks. If rootless it will join/create the rootless cni namespace.
+// networks. If rootless it will join/create the rootless network namespace.
func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string]types.StatusBlock, error) {
if r.config.MachineEnabled() {
if err := setPrimaryMachineIP(); err != nil {
return nil, err
}
}
- rootlessCNINS, err := r.GetRootlessCNINetNs(true)
+ rootlessNetNS, err := r.GetRootlessNetNs(true)
if err != nil {
return nil, err
}
@@ -619,11 +626,11 @@ func (r *Runtime) setUpNetwork(ns string, opts types.NetworkOptions) (map[string
results, err = r.network.Setup(ns, types.SetupOptions{NetworkOptions: opts})
return err
}
- // rootlessCNINS is nil if we are root
- if rootlessCNINS != nil {
- // execute the cni setup in the rootless net ns
- err = rootlessCNINS.Do(setUpPod)
- rootlessCNINS.Lock.Unlock()
+ // rootlessNetNS is nil if we are root
+ if rootlessNetNS != nil {
+ // execute the setup in the rootless net ns
+ err = rootlessNetNS.Do(setUpPod)
+ rootlessNetNS.Lock.Unlock()
} else {
err = setUpPod()
}
@@ -644,6 +651,9 @@ func getCNIPodName(c *Container) string {
// Create and configure a new network namespace for a container
func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (map[string]types.StatusBlock, error) {
+ if ctr.config.NetMode.IsSlirp4netns() {
+ return nil, r.setupSlirp4netns(ctr, ctrNS)
+ }
networks, _, err := ctr.networks()
if err != nil {
return nil, err
@@ -658,7 +668,24 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (map[string]typ
if err != nil {
return nil, err
}
- return r.setUpNetwork(ctrNS.Path(), netOpts)
+ netStatus, err := r.setUpNetwork(ctrNS.Path(), netOpts)
+ if err != nil {
+ return nil, err
+ }
+
+ // setup rootless port forwarder when rootless with ports and the network status is empty,
+ // if this is called from network reload the network status will not be empty and we should
+ // not setup port because they are still active
+ if rootless.IsRootless() && len(ctr.config.PortMappings) > 0 && ctr.getNetworkStatus() == nil {
+ // set up port forwarder for rootless netns
+ netnsPath := ctrNS.Path()
+ // TODO: support slirp4netns port forwarder as well
+ // make sure to fix this in container.handleRestartPolicy() as well
+ // Important we have to call this after r.setUpNetwork() so that
+ // we can use the proper netStatus
+ err = r.setupRootlessPortMappingViaRLK(ctr, netnsPath, netStatus)
+ }
+ return netStatus, err
}
// Create and configure a new network namespace for a container
@@ -681,31 +708,10 @@ func (r *Runtime) createNetNS(ctr *Container) (n ns.NetNS, q map[string]types.St
logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
var networkStatus map[string]types.StatusBlock
- if !ctr.config.NetMode.IsSlirp4netns() {
- networkStatus, err = r.configureNetNS(ctr, ctrNS)
- }
+ networkStatus, err = r.configureNetNS(ctr, ctrNS)
return ctrNS, networkStatus, err
}
-// Configure the network namespace for a rootless container
-func (r *Runtime) setupRootlessNetNS(ctr *Container) error {
- if ctr.config.NetMode.IsSlirp4netns() {
- return r.setupSlirp4netns(ctr)
- }
- networks, _, err := ctr.networks()
- if err != nil {
- return err
- }
- if len(networks) > 0 && len(ctr.config.PortMappings) > 0 {
- // 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
-}
-
// Configure the network namespace using the container process
func (r *Runtime) setupNetNS(ctr *Container) error {
nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
@@ -719,7 +725,7 @@ func (r *Runtime) setupNetNS(ctr *Container) error {
if err != nil {
return err
}
- nsPath = filepath.Join(nsPath, fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]))
+ nsPath = filepath.Join(nsPath, fmt.Sprintf("netns-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]))
if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil {
return err
@@ -777,10 +783,10 @@ func (r *Runtime) closeNetNS(ctr *Container) error {
return nil
}
-// Tear down a container's CNI network configuration and joins the
+// Tear down a container's network configuration and joins the
// rootless net ns as rootless user
func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error {
- rootlessCNINS, err := r.GetRootlessCNINetNs(false)
+ rootlessNetNS, err := r.GetRootlessNetNs(false)
if err != nil {
return err
}
@@ -789,13 +795,13 @@ func (r *Runtime) teardownNetwork(ns string, opts types.NetworkOptions) error {
return errors.Wrapf(err, "error tearing down network namespace configuration for container %s", opts.ContainerID)
}
- // rootlessCNINS is nil if we are root
- if rootlessCNINS != nil {
+ // rootlessNetNS is nil if we are root
+ if rootlessNetNS != nil {
// execute the cni setup in the rootless net ns
- err = rootlessCNINS.Do(tearDownPod)
- rootlessCNINS.Lock.Unlock()
+ err = rootlessNetNS.Do(tearDownPod)
+ rootlessNetNS.Lock.Unlock()
if err == nil {
- err = rootlessCNINS.Cleanup(r)
+ err = rootlessNetNS.Cleanup(r)
}
} else {
err = tearDownPod()
@@ -1200,9 +1206,7 @@ func (c *Container) NetworkDisconnect(nameOrID, netName string, force bool) erro
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
- if len(c.config.PortMappings) > 0 {
- opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings)
- }
+ opts.PortMappings = c.config.PortMappings
eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
if !exists {
return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
@@ -1294,9 +1298,7 @@ func (c *Container) NetworkConnect(nameOrID, netName string, aliases []string) e
ContainerID: c.config.ID,
ContainerName: getCNIPodName(c),
}
- if len(c.config.PortMappings) > 0 {
- opts.PortMappings = ocicniPortsToNetTypesPorts(c.config.PortMappings)
- }
+ opts.PortMappings = c.config.PortMappings
eth, exists := c.state.NetInterfaceDescriptions.getInterfaceByName(netName)
if !exists {
return errors.Errorf("no network interface name for container %s on network %s", c.config.ID, netName)
@@ -1364,16 +1366,67 @@ func (r *Runtime) normalizeNetworkName(nameOrID string) (string, error) {
return net.Name, nil
}
+// ocicniPortsToNetTypesPorts convert the old port format to the new one
+// while deduplicating ports into ranges
func ocicniPortsToNetTypesPorts(ports []types.OCICNIPortMapping) []types.PortMapping {
+ if len(ports) == 0 {
+ return nil
+ }
+
newPorts := make([]types.PortMapping, 0, len(ports))
- for _, port := range ports {
- newPorts = append(newPorts, types.PortMapping{
- HostIP: port.HostIP,
- HostPort: uint16(port.HostPort),
- ContainerPort: uint16(port.ContainerPort),
- Protocol: port.Protocol,
- Range: 1,
- })
+
+ // first sort the ports
+ sort.Slice(ports, func(i, j int) bool {
+ return compareOCICNIPorts(ports[i], ports[j])
+ })
+
+ // we already check if the slice is empty so we can use the first element
+ currentPort := types.PortMapping{
+ HostIP: ports[0].HostIP,
+ HostPort: uint16(ports[0].HostPort),
+ ContainerPort: uint16(ports[0].ContainerPort),
+ Protocol: ports[0].Protocol,
+ Range: 1,
+ }
+
+ for i := 1; i < len(ports); i++ {
+ if ports[i].HostIP == currentPort.HostIP &&
+ ports[i].Protocol == currentPort.Protocol &&
+ ports[i].HostPort-int32(currentPort.Range) == int32(currentPort.HostPort) &&
+ ports[i].ContainerPort-int32(currentPort.Range) == int32(currentPort.ContainerPort) {
+ currentPort.Range = currentPort.Range + 1
+ } else {
+ newPorts = append(newPorts, currentPort)
+ currentPort = types.PortMapping{
+ HostIP: ports[i].HostIP,
+ HostPort: uint16(ports[i].HostPort),
+ ContainerPort: uint16(ports[i].ContainerPort),
+ Protocol: ports[i].Protocol,
+ Range: 1,
+ }
+ }
}
+ newPorts = append(newPorts, currentPort)
return newPorts
}
+
+// compareOCICNIPorts will sort the ocicni ports by
+// 1) host ip
+// 2) protocol
+// 3) hostPort
+// 4) container port
+func compareOCICNIPorts(i, j types.OCICNIPortMapping) bool {
+ if i.HostIP != j.HostIP {
+ return i.HostIP < j.HostIP
+ }
+
+ if i.Protocol != j.Protocol {
+ return i.Protocol < j.Protocol
+ }
+
+ if i.HostPort != j.HostPort {
+ return i.HostPort < j.HostPort
+ }
+
+ return i.ContainerPort < j.ContainerPort
+}
diff --git a/libpod/networking_linux_test.go b/libpod/networking_linux_test.go
new file mode 100644
index 000000000..06bf05723
--- /dev/null
+++ b/libpod/networking_linux_test.go
@@ -0,0 +1,323 @@
+package libpod
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_ocicniPortsToNetTypesPorts(t *testing.T) {
+ tests := []struct {
+ name string
+ arg []types.OCICNIPortMapping
+ want []types.PortMapping
+ }{
+ {
+ name: "no ports",
+ arg: nil,
+ want: nil,
+ },
+ {
+ name: "empty ports",
+ arg: []types.OCICNIPortMapping{},
+ want: nil,
+ },
+ {
+ name: "single port",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "two separate ports",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 9000,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 9000,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "two ports joined",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ },
+ },
+ {
+ name: "three ports with different container port are not joined",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 79,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8082,
+ ContainerPort: 82,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 79,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 8082,
+ ContainerPort: 82,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "three ports joined (not sorted)",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8082,
+ ContainerPort: 82,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 3,
+ },
+ },
+ },
+ {
+ name: "different protocols ports are not joined",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "udp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "udp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "different host ip ports are not joined",
+ arg: []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ HostIP: "192.168.1.1",
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ HostIP: "192.168.1.2",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ HostIP: "192.168.1.1",
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ Range: 1,
+ HostIP: "192.168.1.2",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ result := ocicniPortsToNetTypesPorts(tt.arg)
+ assert.Equal(t, tt.want, result, "ports do not match")
+ })
+ }
+}
+
+func benchmarkOCICNIPortsToNetTypesPorts(b *testing.B, ports []types.OCICNIPortMapping) {
+ for n := 0; n < b.N; n++ {
+ ocicniPortsToNetTypesPorts(ports)
+ }
+}
+
+func Benchmark_ocicniPortsToNetTypesPortsNoPorts(b *testing.B) {
+ benchmarkOCICNIPortsToNetTypesPorts(b, nil)
+}
+
+func Benchmark_ocicniPortsToNetTypesPorts1(b *testing.B) {
+ benchmarkOCICNIPortsToNetTypesPorts(b, []types.OCICNIPortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ })
+}
+
+func Benchmark_ocicniPortsToNetTypesPorts10(b *testing.B) {
+ ports := make([]types.OCICNIPortMapping, 0, 10)
+ for i := int32(8080); i < 8090; i++ {
+ ports = append(ports, types.OCICNIPortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkOCICNIPortsToNetTypesPorts(b, ports)
+}
+
+func Benchmark_ocicniPortsToNetTypesPorts100(b *testing.B) {
+ ports := make([]types.OCICNIPortMapping, 0, 100)
+ for i := int32(8080); i < 8180; i++ {
+ ports = append(ports, types.OCICNIPortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkOCICNIPortsToNetTypesPorts(b, ports)
+}
+
+func Benchmark_ocicniPortsToNetTypesPorts1k(b *testing.B) {
+ ports := make([]types.OCICNIPortMapping, 0, 1000)
+ for i := int32(8080); i < 9080; i++ {
+ ports = append(ports, types.OCICNIPortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkOCICNIPortsToNetTypesPorts(b, ports)
+}
+
+func Benchmark_ocicniPortsToNetTypesPorts10k(b *testing.B) {
+ ports := make([]types.OCICNIPortMapping, 0, 30000)
+ for i := int32(8080); i < 18080; i++ {
+ ports = append(ports, types.OCICNIPortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkOCICNIPortsToNetTypesPorts(b, ports)
+}
+
+func Benchmark_ocicniPortsToNetTypesPorts1m(b *testing.B) {
+ ports := make([]types.OCICNIPortMapping, 0, 1000000)
+ for j := 0; j < 20; j++ {
+ for i := int32(1); i <= 50000; i++ {
+ ports = append(ports, types.OCICNIPortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ HostIP: fmt.Sprintf("192.168.1.%d", j),
+ })
+ }
+ }
+ b.ResetTimer()
+ benchmarkOCICNIPortsToNetTypesPorts(b, ports)
+}
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index ffd53ec2b..674075e23 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -16,6 +16,8 @@ import (
"syscall"
"time"
+ "github.com/containernetworking/plugins/pkg/ns"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/errorhandling"
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/rootlessport"
@@ -37,9 +39,9 @@ type slirpFeatures struct {
type slirp4netnsCmdArg struct {
Proto string `json:"proto,omitempty"`
HostAddr string `json:"host_addr"`
- HostPort int32 `json:"host_port"`
+ HostPort uint16 `json:"host_port"`
GuestAddr string `json:"guest_addr"`
- GuestPort int32 `json:"guest_port"`
+ GuestPort uint16 `json:"guest_port"`
}
type slirp4netnsCmd struct {
@@ -58,6 +60,8 @@ type slirp4netnsNetworkOptions struct {
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()
@@ -204,7 +208,7 @@ func createBasicSlirp4netnsCmdArgs(options *slirp4netnsNetworkOptions, features
}
// setupSlirp4netns can be called in rootful as well as in rootless
-func (r *Runtime) setupSlirp4netns(ctr *Container) error {
+func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
path := r.config.Engine.NetworkCmdPath
if path == "" {
var err error
@@ -260,7 +264,7 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
if err != nil {
return errors.Wrapf(err, "failed to create rootless network sync pipe")
}
- netnsPath = ctr.state.NetNS.Path()
+ netnsPath = netns.Path()
cmdArgs = append(cmdArgs, "--netns-type=path", netnsPath, "tap0")
} else {
defer errorhandling.CloseQuiet(ctr.rootlessSlirpSyncR)
@@ -297,6 +301,39 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
}
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")
}
@@ -310,6 +347,9 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
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)
@@ -327,7 +367,7 @@ func (r *Runtime) setupSlirp4netns(ctr *Container) error {
if netOptions.isSlirpHostForward {
return r.setupRootlessPortMappingViaSlirp(ctr, cmd, apiSocket)
}
- return r.setupRootlessPortMappingViaRLK(ctr, netnsPath)
+ return r.setupRootlessPortMappingViaRLK(ctr, netnsPath, nil)
}
return nil
@@ -440,7 +480,7 @@ func waitForSync(syncR *os.File, cmd *exec.Cmd, logFile io.ReadSeeker, timeout t
return nil
}
-func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string) error {
+func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath string, netStatus map[string]types.StatusBlock) error {
syncR, syncW, err := os.Pipe()
if err != nil {
return errors.Wrapf(err, "failed to open pipe")
@@ -467,7 +507,7 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin
}
}
- childIP := getRootlessPortChildIP(ctr)
+ childIP := getRootlessPortChildIP(ctr, netStatus)
cfg := rootlessport.Config{
Mappings: ctr.config.PortMappings,
NetNSPath: netnsPath,
@@ -492,9 +532,7 @@ func (r *Runtime) setupRootlessPortMappingViaRLK(ctr *Container, netnsPath strin
cmd.Args = []string{rootlessport.BinaryName}
// Leak one end of the pipe in rootlessport process, the other will be sent to conmon
- if ctr.rootlessPortSyncR != nil {
- defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR)
- }
+ defer errorhandling.CloseQuiet(ctr.rootlessPortSyncR)
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessPortSyncR, syncW)
cmd.Stdin = cfgR
@@ -610,7 +648,7 @@ func (r *Runtime) setupRootlessPortMappingViaSlirp(ctr *Container, cmd *exec.Cmd
return nil
}
-func getRootlessPortChildIP(c *Container) string {
+func getRootlessPortChildIP(c *Container, netStatus map[string]types.StatusBlock) string {
if c.config.NetMode.IsSlirp4netns() {
slirp4netnsIP, err := GetSlirp4netnsIP(c.slirp4netnsSubnet)
if err != nil {
@@ -620,7 +658,7 @@ func getRootlessPortChildIP(c *Container) string {
}
var ipv6 net.IP
- for _, status := range c.getNetworkStatus() {
+ for _, status := range netStatus {
for _, netInt := range status.Interfaces {
for _, netAddress := range netInt.Networks {
ipv4 := netAddress.Subnet.IP.To4()
@@ -640,15 +678,15 @@ func getRootlessPortChildIP(c *Container) string {
// 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 {
- childIP := getRootlessPortChildIP(c)
+ if len(c.config.PortMappings) == 0 {
+ return nil
+ }
+ childIP := getRootlessPortChildIP(c, c.state.NetworkStatus)
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
+ return errors.Wrap(err, "could not reload rootless port mappings, port forwarding may no longer work correctly")
}
defer conn.Close()
enc := json.NewEncoder(conn)
diff --git a/libpod/oci.go b/libpod/oci.go
index c92d9a077..f45c1a105 100644
--- a/libpod/oci.go
+++ b/libpod/oci.go
@@ -23,7 +23,10 @@ type OCIRuntime interface {
Path() string
// CreateContainer creates the container in the OCI runtime.
- CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error
+ // The returned int64 contains the microseconds needed to restore
+ // the given container if it is a restore and if restoreOptions.PrintStats
+ // is true. In all other cases the returned int64 is 0.
+ CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (int64, error)
// UpdateContainerStatus updates the status of the given container.
UpdateContainerStatus(ctr *Container) error
// StartContainer starts the given container.
@@ -101,8 +104,10 @@ type OCIRuntime interface {
// CheckpointContainer checkpoints the given container.
// Some OCI runtimes may not support this - if SupportsCheckpoint()
// returns false, this is not implemented, and will always return an
- // error.
- CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error
+ // error. If CheckpointOptions.PrintStats is true the first return parameter
+ // contains the number of microseconds the runtime needed to checkpoint
+ // the given container.
+ CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) (int64, error)
// CheckConmonRunning verifies that the given container's Conmon
// instance is still running. Runtimes without Conmon, or systems where
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index 1719b2dfa..533a0d78b 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -183,35 +183,39 @@ func hasCurrentUserMapped(ctr *Container) bool {
}
// CreateContainer creates a container.
-func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
+func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (int64, error) {
// always make the run dir accessible to the current user so that the PID files can be read without
// being in the rootless user namespace.
if err := makeAccessible(ctr.state.RunDir, 0, 0); err != nil {
- return err
+ return 0, err
}
if !hasCurrentUserMapped(ctr) {
for _, i := range []string{ctr.state.RunDir, ctr.runtime.config.Engine.TmpDir, ctr.config.StaticDir, ctr.state.Mountpoint, ctr.runtime.config.Engine.VolumePath} {
if err := makeAccessible(i, ctr.RootUID(), ctr.RootGID()); err != nil {
- return err
+ return 0, err
}
}
// if we are running a non privileged container, be sure to umount some kernel paths so they are not
// bind mounted inside the container at all.
if !ctr.config.Privileged && !rootless.IsRootless() {
- ch := make(chan error)
+ type result struct {
+ restoreDuration int64
+ err error
+ }
+ ch := make(chan result)
go func() {
runtime.LockOSThread()
- err := func() error {
+ restoreDuration, err := func() (int64, error) {
fd, err := os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid()))
if err != nil {
- return err
+ return 0, err
}
defer errorhandling.CloseQuiet(fd)
// create a new mountns on the current thread
if err = unix.Unshare(unix.CLONE_NEWNS); err != nil {
- return err
+ return 0, err
}
defer func() {
if err := unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS); err != nil {
@@ -224,12 +228,12 @@ func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *Conta
// changes are propagated to the host.
err = unix.Mount("/sys", "/sys", "none", unix.MS_REC|unix.MS_SLAVE, "")
if err != nil {
- return errors.Wrapf(err, "cannot make /sys slave")
+ return 0, errors.Wrapf(err, "cannot make /sys slave")
}
mounts, err := pmount.GetMounts()
if err != nil {
- return err
+ return 0, err
}
for _, m := range mounts {
if !strings.HasPrefix(m.Mountpoint, "/sys/kernel") {
@@ -237,15 +241,18 @@ func (r *ConmonOCIRuntime) CreateContainer(ctr *Container, restoreOptions *Conta
}
err = unix.Unmount(m.Mountpoint, 0)
if err != nil && !os.IsNotExist(err) {
- return errors.Wrapf(err, "cannot unmount %s", m.Mountpoint)
+ return 0, errors.Wrapf(err, "cannot unmount %s", m.Mountpoint)
}
}
return r.createOCIContainer(ctr, restoreOptions)
}()
- ch <- err
+ ch <- result{
+ restoreDuration: restoreDuration,
+ err: err,
+ }
}()
- err := <-ch
- return err
+ r := <-ch
+ return r.restoreDuration, r.err
}
}
return r.createOCIContainer(ctr, restoreOptions)
@@ -760,9 +767,9 @@ func (r *ConmonOCIRuntime) AttachResize(ctr *Container, newSize define.TerminalS
}
// CheckpointContainer checkpoints the given container.
-func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error {
+func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) (int64, error) {
if err := label.SetSocketLabel(ctr.ProcessLabel()); err != nil {
- return err
+ return 0, err
}
// imagePath is used by CRIU to store the actual checkpoint files
imagePath := ctr.CheckpointPath()
@@ -802,14 +809,25 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container
}
runtimeDir, err := util.GetRuntimeDir()
if err != nil {
- return err
+ return 0, err
}
if err = os.Setenv("XDG_RUNTIME_DIR", runtimeDir); err != nil {
- return errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
+ return 0, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR")
}
args = append(args, ctr.ID())
logrus.Debugf("the args to checkpoint: %s %s", r.path, strings.Join(args, " "))
- return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
+
+ runtimeCheckpointStarted := time.Now()
+ err = utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...)
+
+ runtimeCheckpointDuration := func() int64 {
+ if options.PrintStats {
+ return time.Since(runtimeCheckpointStarted).Microseconds()
+ }
+ return 0
+ }()
+
+ return runtimeCheckpointDuration, err
}
func (r *ConmonOCIRuntime) CheckConmonRunning(ctr *Container) (bool, error) {
@@ -984,23 +1002,23 @@ func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) {
}
// createOCIContainer generates this container's main conmon instance and prepares it for starting
-func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
+func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (int64, error) {
var stderrBuf bytes.Buffer
runtimeDir, err := util.GetRuntimeDir()
if err != nil {
- return err
+ return 0, err
}
parentSyncPipe, childSyncPipe, err := newPipe()
if err != nil {
- return errors.Wrapf(err, "error creating socket pair")
+ return 0, errors.Wrapf(err, "error creating socket pair")
}
defer errorhandling.CloseQuiet(parentSyncPipe)
childStartPipe, parentStartPipe, err := newPipe()
if err != nil {
- return errors.Wrapf(err, "error creating socket pair for start pipe")
+ return 0, errors.Wrapf(err, "error creating socket pair for start pipe")
}
defer errorhandling.CloseQuiet(parentStartPipe)
@@ -1012,12 +1030,12 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
logTag, err := r.getLogTag(ctr)
if err != nil {
- return err
+ return 0, err
}
if ctr.config.CgroupsMode == cgroupSplit {
- if err := utils.MoveUnderCgroupSubtree("supervisor"); err != nil {
- return err
+ if err := utils.MoveUnderCgroupSubtree("runtime"); err != nil {
+ return 0, err
}
}
@@ -1068,7 +1086,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
} else {
fds, err := strconv.Atoi(val)
if err != nil {
- return fmt.Errorf("converting LISTEN_FDS=%s: %w", val, err)
+ return 0, fmt.Errorf("converting LISTEN_FDS=%s: %w", val, err)
}
preserveFDs = uint(fds)
}
@@ -1149,7 +1167,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
ports, err := bindPorts(ctr.config.PortMappings)
if err != nil {
- return err
+ return 0, err
}
filesToClose = append(filesToClose, ports...)
@@ -1165,12 +1183,12 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if havePortMapping {
ctr.rootlessPortSyncR, ctr.rootlessPortSyncW, err = os.Pipe()
if err != nil {
- return errors.Wrapf(err, "failed to create rootless port sync pipe")
+ return 0, errors.Wrapf(err, "failed to create rootless port sync pipe")
}
}
ctr.rootlessSlirpSyncR, ctr.rootlessSlirpSyncW, err = os.Pipe()
if err != nil {
- return errors.Wrapf(err, "failed to create rootless network sync pipe")
+ return 0, errors.Wrapf(err, "failed to create rootless network sync pipe")
}
} else {
if ctr.rootlessSlirpSyncR != nil {
@@ -1189,22 +1207,25 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
cmd.ExtraFiles = append(cmd.ExtraFiles, ctr.rootlessPortSyncW)
}
}
-
+ var runtimeRestoreStarted time.Time
+ if restoreOptions != nil {
+ runtimeRestoreStarted = time.Now()
+ }
err = startCommandGivenSelinux(cmd, ctr)
// regardless of whether we errored or not, we no longer need the children pipes
childSyncPipe.Close()
childStartPipe.Close()
if err != nil {
- return err
+ return 0, err
}
if err := r.moveConmonToCgroupAndSignal(ctr, cmd, parentStartPipe); err != nil {
- return err
+ return 0, err
}
/* Wait for initial setup and fork, and reap child */
err = cmd.Wait()
if err != nil {
- return err
+ return 0, err
}
pid, err := readConmonPipeData(parentSyncPipe, ociLog)
@@ -1212,7 +1233,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
if err2 := r.DeleteContainer(ctr); err2 != nil {
logrus.Errorf("Removing container %s from runtime after creation failed", ctr.ID())
}
- return err
+ return 0, err
}
ctr.state.PID = pid
@@ -1238,13 +1259,20 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
}
}
+ runtimeRestoreDuration := func() int64 {
+ if restoreOptions != nil && restoreOptions.PrintStats {
+ return time.Since(runtimeRestoreStarted).Microseconds()
+ }
+ return 0
+ }()
+
// These fds were passed down to the runtime. Close them
// and not interfere
for _, f := range filesToClose {
errorhandling.CloseQuiet(f)
}
- return nil
+ return runtimeRestoreDuration, nil
}
// configureConmonEnv gets the environment values to add to conmon's exec struct
diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go
index fcf2ffca8..65ff818b4 100644
--- a/libpod/oci_missing.go
+++ b/libpod/oci_missing.go
@@ -66,8 +66,8 @@ func (r *MissingRuntime) Path() string {
}
// CreateContainer is not available as the runtime is missing
-func (r *MissingRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) error {
- return r.printError()
+func (r *MissingRuntime) CreateContainer(ctr *Container, restoreOptions *ContainerCheckpointOptions) (int64, error) {
+ return 0, r.printError()
}
// UpdateContainerStatus is not available as the runtime is missing
@@ -153,8 +153,8 @@ func (r *MissingRuntime) ExecUpdateStatus(ctr *Container, sessionID string) (boo
}
// CheckpointContainer is not available as the runtime is missing
-func (r *MissingRuntime) CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error {
- return r.printError()
+func (r *MissingRuntime) CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) (int64, error) {
+ return 0, r.printError()
}
// CheckConmonRunning is not available as the runtime is missing
diff --git a/libpod/oci_util.go b/libpod/oci_util.go
index c1afc0d20..6d99d5836 100644
--- a/libpod/oci_util.go
+++ b/libpod/oci_util.go
@@ -32,93 +32,108 @@ func createUnitName(prefix string, name string) string {
}
// Bind ports to keep them closed on the host
-func bindPorts(ports []types.OCICNIPortMapping) ([]*os.File, error) {
+func bindPorts(ports []types.PortMapping) ([]*os.File, error) {
var files []*os.File
- notifySCTP := false
- for _, i := range ports {
- isV6 := net.ParseIP(i.HostIP).To4() == nil
- if i.HostIP == "" {
+ sctpWarning := true
+ for _, port := range ports {
+ isV6 := net.ParseIP(port.HostIP).To4() == nil
+ if port.HostIP == "" {
isV6 = false
}
- switch i.Protocol {
- case "udp":
- var (
- addr *net.UDPAddr
- err error
- )
- if isV6 {
- addr, err = net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort))
- } else {
- addr, err = net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort))
- }
- if err != nil {
- return nil, errors.Wrapf(err, "cannot resolve the UDP address")
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ for i := uint16(0); i < port.Range; i++ {
+ f, err := bindPort(protocol, port.HostIP, port.HostPort+i, isV6, &sctpWarning)
+ if err != nil {
+ return files, err
+ }
+ if f != nil {
+ files = append(files, f)
+ }
}
+ }
+ }
+ return files, nil
+}
- proto := "udp4"
- if isV6 {
- proto = "udp6"
- }
- server, err := net.ListenUDP(proto, addr)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot listen on the UDP port")
- }
- f, err := server.File()
- if err != nil {
- 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)
- }
+func bindPort(protocol, hostIP string, port uint16, isV6 bool, sctpWarning *bool) (*os.File, error) {
+ var file *os.File
+ switch protocol {
+ case "udp":
+ var (
+ addr *net.UDPAddr
+ err error
+ )
+ if isV6 {
+ addr, err = net.ResolveUDPAddr("udp6", fmt.Sprintf("[%s]:%d", hostIP, port))
+ } else {
+ addr, err = net.ResolveUDPAddr("udp4", fmt.Sprintf("%s:%d", hostIP, port))
+ }
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot resolve the UDP address")
+ }
- case "tcp":
- var (
- addr *net.TCPAddr
- err error
- )
- if isV6 {
- addr, err = net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", i.HostIP, i.HostPort))
- } else {
- addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort))
- }
- if err != nil {
- return nil, errors.Wrapf(err, "cannot resolve the TCP address")
- }
+ proto := "udp4"
+ if isV6 {
+ proto = "udp6"
+ }
+ server, err := net.ListenUDP(proto, addr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot listen on the UDP port")
+ }
+ file, err = server.File()
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot get file for UDP socket")
+ }
+ // 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)
+ }
- proto := "tcp4"
- if isV6 {
- proto = "tcp6"
- }
- server, err := net.ListenTCP(proto, addr)
- if err != nil {
- return nil, errors.Wrapf(err, "cannot listen on the TCP port")
- }
- f, err := server.File()
- if err != nil {
- 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 "tcp":
+ var (
+ addr *net.TCPAddr
+ err error
+ )
+ if isV6 {
+ addr, err = net.ResolveTCPAddr("tcp6", fmt.Sprintf("[%s]:%d", hostIP, port))
+ } else {
+ addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", hostIP, port))
+ }
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot resolve the TCP address")
+ }
- case "sctp":
- if !notifySCTP {
- notifySCTP = true
- logrus.Warnf("Port reservation for SCTP is not supported")
- }
- default:
- return nil, fmt.Errorf("unknown protocol %s", i.Protocol)
+ proto := "tcp4"
+ if isV6 {
+ proto = "tcp6"
+ }
+ server, err := net.ListenTCP(proto, addr)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot listen on the TCP port")
+ }
+ file, err = server.File()
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot get file for TCP socket")
+ }
+ // 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 *sctpWarning {
+ logrus.Info("Port reservation for SCTP is not supported")
+ *sctpWarning = false
+ }
+ default:
+ return nil, fmt.Errorf("unknown protocol %s", protocol)
}
- return files, nil
+ return file, nil
}
func getOCIRuntimeError(runtimeMsg string) error {
diff --git a/libpod/options.go b/libpod/options.go
index 553af43fd..0cc4c784c 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -227,6 +227,19 @@ func WithNetworkCmdPath(path string) RuntimeOption {
}
}
+// WithNetworkBackend specifies the name of the network backend.
+func WithNetworkBackend(name string) RuntimeOption {
+ return func(rt *Runtime) error {
+ if rt.valid {
+ return define.ErrRuntimeFinalized
+ }
+
+ rt.config.Network.NetworkBackend = name
+
+ return nil
+ }
+}
+
// WithCgroupManager specifies the manager implementation name which is used to
// handle cgroups for containers.
// Current valid values are "cgroupfs" and "systemd".
@@ -310,6 +323,17 @@ func WithCDI(devices []string) CtrCreateOption {
}
}
+// WithStorageOpts sets the devices to check for for CDI configuration.
+func WithStorageOpts(storageOpts map[string]string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ ctr.config.StorageOpts = storageOpts
+ return nil
+ }
+}
+
// WithDefaultMountsFile sets the file to look at for default mounts (mainly
// secrets).
// Note we are not saving this in the database as it is for testing purposes
@@ -958,7 +982,7 @@ func WithUserNSFrom(nsCtr *Container) CtrCreateOption {
if err := JSONDeepCopy(nsCtr.IDMappings(), &ctr.config.IDMappings); err != nil {
return err
}
- g := generate.Generator{Config: ctr.config.Spec}
+ g := generate.NewFromSpec(ctr.config.Spec)
g.ClearLinuxUIDMappings()
for _, uidmap := range nsCtr.config.IDMappings.UIDMap {
@@ -1040,7 +1064,7 @@ func WithDependencyCtrs(ctrs []*Container) CtrCreateOption {
// namespace with a minimal configuration.
// An optional array of port mappings can be provided.
// Conflicts with WithNetNSFrom().
-func WithNetNS(portMappings []nettypes.OCICNIPortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
+func WithNetNS(portMappings []nettypes.PortMapping, exposedPorts map[uint16][]string, postConfigureNetNS bool, netmode string, networks []string) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
@@ -1093,7 +1117,7 @@ func WithNetworkOptions(options map[string][]string) CtrCreateOption {
// It cannot be set unless WithNetNS has already been passed.
// Further, it cannot be set if additional CNI networks to join have been
// specified.
-func WithStaticMAC(mac net.HardwareAddr) CtrCreateOption {
+func WithStaticMAC(mac nettypes.HardwareAddr) CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
@@ -2073,21 +2097,6 @@ func WithInfraContainer() PodCreateOption {
}
}
-// WithInfraContainerPorts tells the pod to add port bindings to the pause container
-func WithInfraContainerPorts(bindings []nettypes.OCICNIPortMapping, 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.
// The option can potentially cause data loss when used on a container that must survive a machine reboot.
func WithVolatile() CtrCreateOption {
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 855f3a9f9..c751df79b 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -28,6 +28,7 @@ import (
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/libpod/lock"
"github.com/containers/podman/v3/libpod/network/cni"
+ "github.com/containers/podman/v3/libpod/network/netavark"
nettypes "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/libpod/plugin"
"github.com/containers/podman/v3/libpod/shutdown"
@@ -483,16 +484,45 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
}
}
- netInterface, err := cni.NewCNINetworkInterface(cni.InitConfig{
- CNIConfigDir: runtime.config.Network.NetworkConfigDir,
- CNIPluginDirs: runtime.config.Network.CNIPluginDirs,
- DefaultNetwork: runtime.config.Network.DefaultNetwork,
- DefaultSubnet: runtime.config.Network.DefaultSubnet,
- IsMachine: runtime.config.Engine.MachineEnabled,
- LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni.lock"),
- })
- if err != nil {
- return errors.Wrapf(err, "could not create network interface")
+ var netInterface nettypes.ContainerNetwork
+
+ switch runtime.config.Network.NetworkBackend {
+ case "", "cni":
+ netInterface, err = cni.NewCNINetworkInterface(cni.InitConfig{
+ CNIConfigDir: runtime.config.Network.NetworkConfigDir,
+ CNIPluginDirs: runtime.config.Network.CNIPluginDirs,
+ DefaultNetwork: runtime.config.Network.DefaultNetwork,
+ DefaultSubnet: runtime.config.Network.DefaultSubnet,
+ IsMachine: runtime.config.Engine.MachineEnabled,
+ LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "cni.lock"),
+ })
+ if err != nil {
+ return errors.Wrapf(err, "could not create network interface")
+ }
+ if runtime.config.Network.NetworkBackend == "" {
+ // set backend to cni so that podman info can display it
+ runtime.config.Network.NetworkBackend = "cni"
+ }
+
+ case "netavark":
+ netavarkBin, err := runtime.config.FindHelperBinary("netavark", false)
+ if err != nil {
+ return err
+ }
+
+ netInterface, err = netavark.NewNetworkInterface(netavark.InitConfig{
+ NetavarkBinary: netavarkBin,
+ NetworkConfigDir: filepath.Join(runtime.config.Engine.StaticDir, "networks"),
+ DefaultNetwork: runtime.config.Network.DefaultNetwork,
+ DefaultSubnet: runtime.config.Network.DefaultSubnet,
+ IsMachine: runtime.config.Engine.MachineEnabled,
+ LockFile: filepath.Join(runtime.config.Network.NetworkConfigDir, "netavark.lock"),
+ })
+ if err != nil {
+ return errors.Wrapf(err, "could not create network interface")
+ }
+ default:
+ return errors.Errorf("unsupported network backend %q, check network_backend in containers.conf", runtime.config.Network.NetworkBackend)
}
runtime.network = netInterface
@@ -543,6 +573,8 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {
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)
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 2256ba57c..114bf9315 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -326,15 +326,6 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
}
}
- 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 {
@@ -389,7 +380,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
if ctr.restoreFromCheckpoint {
// Remove information about bind mount
// for new container from imported checkpoint
- g := generate.Generator{Config: ctr.config.Spec}
+ g := generate.NewFromSpec(ctr.config.Spec)
g.RemoveMount("/dev/shm")
ctr.config.ShmDir = ""
g.RemoveMount("/etc/resolv.conf")
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 7d7fef4d1..15050ef48 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -43,18 +43,6 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option
}
}
- if pod.config.Name == "" {
- name, err := r.generateName()
- if err != nil {
- return nil, err
- }
- pod.config.Name = name
- }
-
- if p.InfraContainerSpec != nil && p.InfraContainerSpec.Hostname == "" {
- p.InfraContainerSpec.Hostname = pod.config.Name
- }
-
// Allocate a lock for the pod
lock, err := r.lockManager.AllocateLock()
if err != nil {
@@ -131,9 +119,33 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option
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")
+ // 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 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
}
diff --git a/libpod/state_test.go b/libpod/state_test.go
index 4799d7b8d..5c3b0d7f7 100644
--- a/libpod/state_test.go
+++ b/libpod/state_test.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/lock"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -3705,3 +3706,42 @@ func TestGetContainerConfigNonExistentIDFails(t *testing.T) {
assert.Error(t, err)
})
}
+
+// Test that the state will convert the ports to the new format
+func TestConvertPortMapping(t *testing.T) {
+ runForAllStates(t, func(t *testing.T, state State, manager lock.Manager) {
+ testCtr, err := getTestCtr1(manager)
+ assert.NoError(t, err)
+
+ ports := testCtr.config.PortMappings
+
+ oldPorts := []types.OCICNIPortMapping{
+ {
+ HostPort: 80,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ HostIP: "192.168.3.3",
+ },
+ {
+ HostPort: 100,
+ ContainerPort: 110,
+ Protocol: "udp",
+ HostIP: "192.168.4.4",
+ },
+ }
+
+ testCtr.config.OldPortMappings = oldPorts
+ testCtr.config.PortMappings = nil
+
+ err = state.AddContainer(testCtr)
+ assert.NoError(t, err)
+
+ retrievedCtr, err := state.Container(testCtr.ID())
+ assert.NoError(t, err)
+
+ // set values to expected ones
+ testCtr.config.PortMappings = ports
+
+ testContainersEqual(t, retrievedCtr, testCtr, true)
+ })
+}
diff --git a/libpod/util.go b/libpod/util.go
index 8f8303ff2..5154a261e 100644
--- a/libpod/util.go
+++ b/libpod/util.go
@@ -295,19 +295,21 @@ func writeHijackHeader(r *http.Request, conn io.Writer) {
}
// Convert OCICNI port bindings into Inspect-formatted port bindings.
-func makeInspectPortBindings(bindings []types.OCICNIPortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort {
+func makeInspectPortBindings(bindings []types.PortMapping, expose map[uint16][]string) map[string][]define.InspectHostPort {
portBindings := make(map[string][]define.InspectHostPort)
for _, port := range bindings {
- key := fmt.Sprintf("%d/%s", port.ContainerPort, port.Protocol)
- hostPorts := portBindings[key]
- if hostPorts == nil {
- hostPorts = []define.InspectHostPort{}
+ protocols := strings.Split(port.Protocol, ",")
+ for _, protocol := range protocols {
+ for i := uint16(0); i < port.Range; i++ {
+ key := fmt.Sprintf("%d/%s", port.ContainerPort+i, protocol)
+ hostPorts := portBindings[key]
+ hostPorts = append(hostPorts, define.InspectHostPort{
+ HostIP: port.HostIP,
+ HostPort: fmt.Sprintf("%d", port.HostPort+i),
+ })
+ portBindings[key] = hostPorts
+ }
}
- hostPorts = append(hostPorts, define.InspectHostPort{
- HostIP: port.HostIP,
- HostPort: fmt.Sprintf("%d", port.HostPort),
- })
- portBindings[key] = hostPorts
}
// add exposed ports without host port information to match docker
for port, protocols := range expose {
diff --git a/nix/default-arm64.nix b/nix/default-arm64.nix
index bb958a193..fa076f27d 100644
--- a/nix/default-arm64.nix
+++ b/nix/default-arm64.nix
@@ -59,7 +59,8 @@ let
self = with pkgs; buildGoModule rec {
name = "podman";
- src = ./..;
+ src = builtins.filterSource
+ (path: type: !(type == "directory" && baseNameOf path == "bin")) ./..;
vendorSha256 = null;
doCheck = false;
enableParallelBuilding = true;
diff --git a/nix/default.nix b/nix/default.nix
index 1dc6f92b6..30ae21503 100644
--- a/nix/default.nix
+++ b/nix/default.nix
@@ -57,7 +57,8 @@ let
self = with pkgs; buildGoModule rec {
name = "podman";
- src = ./..;
+ src = builtins.filterSource
+ (path: type: !(type == "directory" && baseNameOf path == "bin")) ./..;
vendorSha256 = null;
doCheck = false;
enableParallelBuilding = true;
diff --git a/pause/pause.c b/pause/pause.c
new file mode 100644
index 000000000..1e363bd7a
--- /dev/null
+++ b/pause/pause.c
@@ -0,0 +1,69 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+Copyright 2021 The Podman Authors.
+
+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.
+*/
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#define STRINGIFY(x) #x
+#define VERSION_STRING(x) STRINGIFY(x)
+
+#ifndef VERSION
+#define VERSION HEAD
+#endif
+
+static void sigdown(int signo) {
+ psignal(signo, "Shutting down, got signal");
+ exit(0);
+}
+
+static void sigreap(int signo) {
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+}
+
+int main(int argc, char **argv) {
+ int i;
+ for (i = 1; i < argc; ++i) {
+ if (!strcasecmp(argv[i], "-v")) {
+ printf("pause.c %s\n", VERSION_STRING(VERSION));
+ return 0;
+ }
+ }
+
+ if (getpid() != 1)
+ /* Not an error because pause sees use outside of infra containers. */
+ fprintf(stderr, "Warning: pause should be the first process\n");
+
+ if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
+ return 1;
+ if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
+ return 2;
+ if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
+ .sa_flags = SA_NOCLDSTOP},
+ NULL) < 0)
+ return 3;
+
+ for (;;)
+ pause();
+ fprintf(stderr, "Error: infinite loop terminated\n");
+ return 42;
+}
diff --git a/pkg/api/handlers/compat/containers_create.go b/pkg/api/handlers/compat/containers_create.go
index 94d20a04a..1e175d664 100644
--- a/pkg/api/handlers/compat/containers_create.go
+++ b/pkg/api/handlers/compat/containers_create.go
@@ -86,6 +86,8 @@ func CreateContainer(w http.ResponseWriter, r *http.Request) {
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 --git a/pkg/api/handlers/compat/containers_top.go b/pkg/api/handlers/compat/containers_top.go
index b5debd37d..545320ad9 100644
--- a/pkg/api/handlers/compat/containers_top.go
+++ b/pkg/api/handlers/compat/containers_top.go
@@ -1,8 +1,11 @@
package compat
import (
+ "encoding/json"
+ "fmt"
"net/http"
"strings"
+ "time"
"github.com/containers/podman/v3/libpod"
"github.com/containers/podman/v3/pkg/api/handlers"
@@ -10,20 +13,24 @@ import (
api "github.com/containers/podman/v3/pkg/api/types"
"github.com/gorilla/schema"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
func TopContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
- defaultValue := "-ef"
+ psArgs := "-ef"
if utils.IsLibpodRequest(r) {
- defaultValue = ""
+ psArgs = ""
}
query := struct {
+ Delay int `schema:"delay"`
PsArgs string `schema:"ps_args"`
+ Stream bool `schema:"stream"`
}{
- PsArgs: defaultValue,
+ Delay: 5,
+ PsArgs: psArgs,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
@@ -31,6 +38,12 @@ func TopContainer(w http.ResponseWriter, r *http.Request) {
return
}
+ if query.Delay < 1 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ fmt.Errorf("\"delay\" parameter of value %d < 1", query.Delay))
+ return
+ }
+
name := utils.GetName(r)
c, err := runtime.LookupContainer(name)
if err != nil {
@@ -38,26 +51,56 @@ func TopContainer(w http.ResponseWriter, r *http.Request) {
return
}
- output, err := c.Top([]string{query.PsArgs})
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ // We are committed now - all errors logged but not reported to client, ship has sailed
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
}
- var body = handlers.ContainerTopOKBody{}
- if len(output) > 0 {
- body.Titles = strings.Split(output[0], "\t")
- for i := range body.Titles {
- body.Titles[i] = strings.TrimSpace(body.Titles[i])
- }
+ encoder := json.NewEncoder(w)
+
+loop: // break out of for/select infinite` loop
+ for {
+ select {
+ case <-r.Context().Done():
+ break loop
+ default:
+ output, err := c.Top([]string{query.PsArgs})
+ if err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+
+ if len(output) > 0 {
+ body := handlers.ContainerTopOKBody{}
+ body.Titles = strings.Split(output[0], "\t")
+ for i := range body.Titles {
+ body.Titles[i] = strings.TrimSpace(body.Titles[i])
+ }
+
+ for _, line := range output[1:] {
+ process := strings.Split(line, "\t")
+ for i := range process {
+ process[i] = strings.TrimSpace(process[i])
+ }
+ body.Processes = append(body.Processes, process)
+ }
+
+ if err := encoder.Encode(body); err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+ }
- for _, line := range output[1:] {
- process := strings.Split(line, "\t")
- for i := range process {
- process[i] = strings.TrimSpace(process[i])
+ if query.Stream {
+ time.Sleep(time.Duration(query.Delay) * time.Second)
+ } else {
+ break loop
}
- body.Processes = append(body.Processes, process)
}
}
- utils.WriteJSON(w, http.StatusOK, body)
}
diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go
index 606c52e41..6152f1c02 100644
--- a/pkg/api/handlers/compat/images_build.go
+++ b/pkg/api/handlers/compat/images_build.go
@@ -151,22 +151,19 @@ func BuildImage(w http.ResponseWriter, r *http.Request) {
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"}
}
}
diff --git a/pkg/api/handlers/libpod/containers.go b/pkg/api/handlers/libpod/containers.go
index 343c0d0b3..3aeebc334 100644
--- a/pkg/api/handlers/libpod/containers.go
+++ b/pkg/api/handlers/libpod/containers.go
@@ -214,6 +214,7 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) {
TCPEstablished bool `schema:"tcpEstablished"`
Export bool `schema:"export"`
IgnoreRootFS bool `schema:"ignoreRootFS"`
+ PrintStats bool `schema:"printStats"`
}{
// override any golang type defaults
}
@@ -248,11 +249,12 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) {
KeepRunning: query.LeaveRunning,
TCPEstablished: query.TCPEstablished,
IgnoreRootfs: query.IgnoreRootFS,
+ PrintStats: query.PrintStats,
}
if query.Export {
options.TargetFile = targetFile
}
- err = ctr.Checkpoint(r.Context(), options)
+ criuStatistics, runtimeCheckpointDuration, err := ctr.Checkpoint(r.Context(), options)
if err != nil {
utils.InternalServerError(w, err)
return
@@ -267,7 +269,15 @@ func Checkpoint(w http.ResponseWriter, r *http.Request) {
utils.WriteResponse(w, http.StatusOK, f)
return
}
- utils.WriteResponse(w, http.StatusOK, entities.CheckpointReport{Id: ctr.ID()})
+ utils.WriteResponse(
+ w,
+ http.StatusOK,
+ entities.CheckpointReport{
+ Id: ctr.ID(),
+ RuntimeDuration: runtimeCheckpointDuration,
+ CRIUStatistics: criuStatistics,
+ },
+ )
}
func Restore(w http.ResponseWriter, r *http.Request) {
@@ -284,6 +294,7 @@ func Restore(w http.ResponseWriter, r *http.Request) {
IgnoreVolumes bool `schema:"ignoreVolumes"`
IgnoreStaticIP bool `schema:"ignoreStaticIP"`
IgnoreStaticMAC bool `schema:"ignoreStaticMAC"`
+ PrintStats bool `schema:"printStats"`
}{
// override any golang type defaults
}
@@ -319,17 +330,26 @@ func Restore(w http.ResponseWriter, r *http.Request) {
IgnoreRootfs: query.IgnoreRootFS,
IgnoreStaticIP: query.IgnoreStaticIP,
IgnoreStaticMAC: query.IgnoreStaticMAC,
+ PrintStats: query.PrintStats,
}
if query.Import {
options.TargetFile = targetFile
options.Name = query.Name
}
- err = ctr.Restore(r.Context(), options)
+ criuStatistics, runtimeRestoreDuration, err := ctr.Restore(r.Context(), options)
if err != nil {
utils.InternalServerError(w, err)
return
}
- utils.WriteResponse(w, http.StatusOK, entities.RestoreReport{Id: ctr.ID()})
+ utils.WriteResponse(
+ w,
+ http.StatusOK,
+ entities.RestoreReport{
+ Id: ctr.ID(),
+ RuntimeDuration: runtimeRestoreDuration,
+ CRIUStatistics: criuStatistics,
+ },
+ )
}
func InitContainer(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
index 851e0f6c8..f943fc240 100644
--- a/pkg/api/handlers/libpod/play.go
+++ b/pkg/api/handlers/libpod/play.go
@@ -26,6 +26,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
Network string `schema:"network"`
TLSVerify bool `schema:"tlsVerify"`
LogDriver string `schema:"logDriver"`
+ LogOptions []string `schema:"logOptions"`
Start bool `schema:"start"`
StaticIPs []string `schema:"staticIPs"`
StaticMACs []string `schema:"staticMACs"`
@@ -106,6 +107,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
NoHosts: query.NoHosts,
Quiet: true,
LogDriver: query.LogDriver,
+ LogOptions: query.LogOptions,
StaticIPs: staticIPs,
StaticMACs: staticMACs,
}
diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go
index 77d026550..3d18406a5 100644
--- a/pkg/api/handlers/libpod/pods.go
+++ b/pkg/api/handlers/libpod/pods.go
@@ -1,15 +1,13 @@
package libpod
import (
- "context"
"encoding/json"
"fmt"
"net/http"
"strings"
+ "time"
- "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"
@@ -41,8 +39,10 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
return
}
if !psg.NoInfra {
- infraOptions := &entities.ContainerCreateOptions{ImageVolume: "bind", IsInfra: true, Net: &entities.NetOptions{}, Devices: psg.Devices} // options for pulling the image and FillOutSpec
- err = specgenutil.FillOutSpecGen(psg.InfraContainerSpec, infraOptions, []string{}) // necessary for default values in many cases (userns, idmappings)
+ infraOptions := entities.NewInfraContainerCreateOptions() // options for pulling the image and FillOutSpec
+ infraOptions.Net = &entities.NetOptions{}
+ infraOptions.Devices = psg.Devices
+ 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
@@ -67,20 +67,6 @@ func PodCreate(w http.ResponseWriter, r *http.Request) {
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
@@ -380,10 +366,17 @@ func PodTop(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
+ psArgs := "-ef"
+ if utils.IsLibpodRequest(r) {
+ psArgs = ""
+ }
query := struct {
+ Delay int `schema:"delay"`
PsArgs string `schema:"ps_args"`
+ Stream bool `schema:"stream"`
}{
- PsArgs: "",
+ Delay: 5,
+ PsArgs: psArgs,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
@@ -391,6 +384,12 @@ func PodTop(w http.ResponseWriter, r *http.Request) {
return
}
+ if query.Delay < 1 {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ fmt.Errorf("\"delay\" parameter of value %d < 1", query.Delay))
+ return
+ }
+
name := utils.GetName(r)
pod, err := runtime.LookupPod(name)
if err != nil {
@@ -398,24 +397,58 @@ func PodTop(w http.ResponseWriter, r *http.Request) {
return
}
- args := []string{}
- if query.PsArgs != "" {
- args = append(args, query.PsArgs)
- }
- output, err := pod.GetPodPidInformation(args)
- if err != nil {
- utils.InternalServerError(w, err)
- return
+ // We are committed now - all errors logged but not reported to client, ship has sailed
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", "application/json")
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
}
- var body = handlers.PodTopOKBody{}
- if len(output) > 0 {
- body.Titles = strings.Split(output[0], "\t")
- for _, line := range output[1:] {
- body.Processes = append(body.Processes, strings.Split(line, "\t"))
+ encoder := json.NewEncoder(w)
+
+loop: // break out of for/select infinite` loop
+ for {
+ select {
+ case <-r.Context().Done():
+ break loop
+ default:
+ output, err := pod.GetPodPidInformation([]string{query.PsArgs})
+ if err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+
+ if len(output) > 0 {
+ var body = handlers.PodTopOKBody{}
+ body.Titles = strings.Split(output[0], "\t")
+ for i := range body.Titles {
+ body.Titles[i] = strings.TrimSpace(body.Titles[i])
+ }
+
+ for _, line := range output[1:] {
+ process := strings.Split(line, "\t")
+ for i := range process {
+ process[i] = strings.TrimSpace(process[i])
+ }
+ body.Processes = append(body.Processes, process)
+ }
+
+ if err := encoder.Encode(body); err != nil {
+ logrus.Infof("Error from %s %q : %v", r.Method, r.URL, err)
+ break loop
+ }
+ if f, ok := w.(http.Flusher); ok {
+ f.Flush()
+ }
+ }
+
+ if query.Stream {
+ time.Sleep(time.Duration(query.Delay) * time.Second)
+ } else {
+ break loop
+ }
}
}
- utils.WriteJSON(w, http.StatusOK, body)
}
func PodKill(w http.ResponseWriter, r *http.Request) {
diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go
index 3ba39b860..ffdb30551 100644
--- a/pkg/api/handlers/libpod/volumes.go
+++ b/pkg/api/handlers/libpod/volumes.go
@@ -29,12 +29,13 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
}{
// override any golang type defaults
}
- input := entities.VolumeCreateOptions{}
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
}
+
+ input := entities.VolumeCreateOptions{}
// decode params from body
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Decode()"))
@@ -47,9 +48,19 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) {
if len(input.Driver) > 0 {
volumeOptions = append(volumeOptions, libpod.WithVolumeDriver(input.Driver))
}
- if len(input.Label) > 0 {
- volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label))
+
+ // Label provided for compatibility.
+ labels := make(map[string]string, len(input.Label)+len(input.Labels))
+ for k, v := range input.Label {
+ labels[k] = v
}
+ for k, v := range input.Labels {
+ labels[k] = v
+ }
+ if len(labels) > 0 {
+ volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(labels))
+ }
+
if len(input.Options) > 0 {
parsedOptions, err := parse.VolumeOptions(input.Options)
if err != nil {
diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go
index b90154e30..35120a1a5 100644
--- a/pkg/api/handlers/types.go
+++ b/pkg/api/handlers/types.go
@@ -42,9 +42,15 @@ type ContainersPruneReport struct {
}
type LibpodContainersPruneReport struct {
- ID string `json:"id"`
- SpaceReclaimed int64 `json:"space"`
- PruneError string `json:"error"`
+ ID string `json:"Id"`
+ SpaceReclaimed int64 `json:"Size"`
+ // Error which occurred during prune operation (if any).
+ // This field is optional and may be omitted if no error occurred.
+ //
+ // Extensions:
+ // x-omitempty: true
+ // x-nullable: true
+ PruneError string `json:"Err,omitempty"`
}
type Info struct {
diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go
index 29139a98e..96b7a957c 100644
--- a/pkg/api/handlers/utils/handler.go
+++ b/pkg/api/handlers/utils/handler.go
@@ -145,12 +145,12 @@ func MarshalErrorSliceJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) {
}
}
-func MarshalErrorJSONIsEmpty(_ unsafe.Pointer) bool {
- return false
+func MarshalErrorJSONIsEmpty(ptr unsafe.Pointer) bool {
+ return *((*error)(ptr)) == nil
}
-func MarshalErrorSliceJSONIsEmpty(_ unsafe.Pointer) bool {
- return false
+func MarshalErrorSliceJSONIsEmpty(ptr unsafe.Pointer) bool {
+ return len(*((*[]error)(ptr))) <= 0
}
// WriteJSON writes an interface value encoded as JSON to w
diff --git a/pkg/api/handlers/utils/handler_test.go b/pkg/api/handlers/utils/handler_test.go
index 18a1d2678..5957e7d74 100644
--- a/pkg/api/handlers/utils/handler_test.go
+++ b/pkg/api/handlers/utils/handler_test.go
@@ -138,3 +138,51 @@ func TestEqualVersion(t *testing.T) {
rr.Body.String(), expected)
}
}
+
+func TestErrorEncoderFuncOmit(t *testing.T) {
+ data, err := json.Marshal(struct {
+ Err error `json:"err,omitempty"`
+ Errs []error `json:"errs,omitempty"`
+ }{})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ dataAsMap := make(map[string]interface{})
+ err = json.Unmarshal(data, &dataAsMap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, ok := dataAsMap["err"]
+ if ok {
+ t.Errorf("the `err` field should have been omitted")
+ }
+ _, ok = dataAsMap["errs"]
+ if ok {
+ t.Errorf("the `errs` field should have been omitted")
+ }
+
+ dataAsMap = make(map[string]interface{})
+ data, err = json.Marshal(struct {
+ Err error `json:"err"`
+ Errs []error `json:"errs"`
+ }{})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = json.Unmarshal(data, &dataAsMap)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, ok = dataAsMap["err"]
+ if !ok {
+ t.Errorf("the `err` field shouldn't have been omitted")
+ }
+ _, ok = dataAsMap["errs"]
+ if !ok {
+ t.Errorf("the `errs` field shouldn't have been omitted")
+ }
+}
diff --git a/pkg/api/server/handler_rid.go b/pkg/api/server/handler_rid.go
index b624b99a6..7dcf436f7 100644
--- a/pkg/api/server/handler_rid.go
+++ b/pkg/api/server/handler_rid.go
@@ -2,6 +2,7 @@ package server
import (
"fmt"
+ "io/ioutil"
"net/http"
"github.com/containers/podman/v3/pkg/api/types"
@@ -15,7 +16,13 @@ import (
// and Apache style request logging
func referenceIDHandler() mux.MiddlewareFunc {
return func(h http.Handler) http.Handler {
- return handlers.CombinedLoggingHandler(logrus.StandardLogger().Out,
+ // 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 == "" {
diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go
index 8dcea1301..601e1251b 100644
--- a/pkg/api/server/register_containers.go
+++ b/pkg/api/server/register_containers.go
@@ -442,6 +442,7 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// - in: query
// name: ps_args
// type: string
+ // default: -ef
// description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
// produces:
// - application/json
@@ -1142,19 +1143,23 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// name: name
// type: string
// required: true
- // description: |
- // Name of container to query for processes
- // (As of version 1.xx)
+ // description: Name of container to query for processes (As of version 1.xx)
// - in: query
// name: stream
// type: boolean
- // default: true
- // description: Stream the output
+ // description: when true, repeatedly stream the latest output (As of version 4.0)
+ // - in: query
+ // name: delay
+ // type: integer
+ // description: if streaming, delay in seconds between updates. Must be >1. (As of version 4.0)
+ // default: 5
// - in: query
// name: ps_args
// type: string
// default: -ef
- // description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
+ // description: |
+ // arguments to pass to ps such as aux.
+ // Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
// produces:
// - application/json
// responses:
@@ -1436,6 +1441,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// name: ignoreRootFS
// type: boolean
// description: do not include root file-system changes when exporting
+ // - in: query
+ // name: printStats
+ // type: boolean
+ // description: add checkpoint statistics to the returned CheckpointReport
// produces:
// - application/json
// responses:
@@ -1490,6 +1499,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
// name: ignoreStaticMAC
// type: boolean
// description: ignore MAC address if set statically
+ // - in: query
+ // name: printStats
+ // type: boolean
+ // description: add restore statistics to the returned RestoreReport
// produces:
// - application/json
// responses:
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 95a8b4939..38ceea271 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -103,7 +103,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - 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)
@@ -837,7 +837,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - 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)
@@ -967,7 +967,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DocsImageDeleteResponse"
+ // $ref: "#/responses/DocsLibpodImagesRemoveResponse"
// 400:
// $ref: "#/responses/BadParamError"
// 404:
@@ -1069,7 +1069,7 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// - application/json
// responses:
// 200:
- // $ref: "#/responses/DocsImageDeleteResponse"
+ // $ref: "#/responses/DocsLibpodPruneResponse"
// 500:
// $ref: '#/responses/InternalError'
r.Handle(VersionedPath("/libpod/images/prune"), s.APIHandler(libpod.PruneImages)).Methods(http.MethodPost)
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index de3669a0a..16a7bbb4c 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -296,18 +296,23 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
// name: name
// type: string
// required: true
- // description: |
- // Name of pod to query for processes
+ // description: Name of pod to query for processes
// - in: query
// name: stream
// type: boolean
- // default: true
- // description: Stream the output
+ // description: when true, repeatedly stream the latest output (As of version 4.0)
+ // - in: query
+ // name: delay
+ // type: integer
+ // description: if streaming, delay in seconds between updates. Must be >1. (As of version 4.0)
+ // default: 5
// - in: query
// name: ps_args
// type: string
// default: -ef
- // description: arguments to pass to ps such as aux. Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
+ // description: |
+ // arguments to pass to ps such as aux.
+ // Requires ps(1) to be installed in the container if no ps(1) compatible AIX descriptors are used.
// responses:
// 200:
// $ref: "#/responses/DocsPodTopResponse"
diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go
index 0fd66652e..3f8f6f9c5 100644
--- a/pkg/api/server/swagger.go
+++ b/pkg/api/server/swagger.go
@@ -6,6 +6,7 @@ import (
"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,9 +135,16 @@ type swagPodAlreadyStopped struct {
}
}
-// Image summary
-// swagger:response DockerImageSummary
-type swagImageSummary struct {
+// Image summary for compat API
+// swagger:response DockerImageSummaryResponse
+type swagDockerImageSummaryResponse struct {
+ // in:body
+ Body []docker.ImageSummary
+}
+
+// Image summary for libpod API
+// swagger:response LibpodImageSummaryResponse
+type swagLibpodImageSummaryResponse struct {
// in:body
Body []entities.ImageSummary
}
diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go
index dc75dac5a..a2be44ab4 100644
--- a/pkg/bindings/connection.go
+++ b/pkg/bindings/connection.go
@@ -390,6 +390,11 @@ func (h *APIResponse) IsClientError() bool {
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 --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go
index c5f54c1af..47de89b33 100644
--- a/pkg/bindings/containers/attach.go
+++ b/pkg/bindings/containers/attach.go
@@ -214,7 +214,7 @@ func Attach(ctx context.Context, nameOrID string, stdin io.Reader, stdout io.Wri
// 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
@@ -531,7 +531,7 @@ func ExecStartAndAttach(ctx context.Context, sessionID string, options *ExecStar
// 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 --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go
index 67db94487..37ffdf0a5 100644
--- a/pkg/bindings/containers/logs.go
+++ b/pkg/bindings/containers/logs.go
@@ -39,7 +39,7 @@ func Logs(ctx context.Context, nameOrID string, options *LogOptions, stdoutChan,
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 --git a/pkg/bindings/errors.go b/pkg/bindings/errors.go
index 9c311d912..ec837b39c 100644
--- a/pkg/bindings/errors.go
+++ b/pkg/bindings/errors.go
@@ -12,17 +12,22 @@ var (
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")
@@ -33,14 +38,22 @@ func (h APIResponse) Process(unmarshalInto interface{}) error {
}
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 --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go
index fdfc4a6fa..011f7f9ca 100644
--- a/pkg/bindings/play/types.go
+++ b/pkg/bindings/play/types.go
@@ -37,6 +37,8 @@ type KubeOptions struct {
ConfigMaps *[]string
// LogDriver for the container. For example: journald
LogDriver *string
+ // LogOptions for the container. For example: journald
+ LogOptions *[]string
// Start - don't start the pod if false
Start *bool
}
diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go
index 1a6324302..344771e0c 100644
--- a/pkg/bindings/play/types_kube_options.go
+++ b/pkg/bindings/play/types_kube_options.go
@@ -228,6 +228,21 @@ func (o *KubeOptions) GetLogDriver() string {
return *o.LogDriver
}
+// WithLogOptions set field LogOptions to given value
+func (o *KubeOptions) WithLogOptions(value []string) *KubeOptions {
+ o.LogOptions = &value
+ return o
+}
+
+// GetLogOptions returns value of field LogOptions
+func (o *KubeOptions) GetLogOptions() []string {
+ if o.LogOptions == nil {
+ var z []string
+ return z
+ }
+ return *o.LogOptions
+}
+
// WithStart set field Start to given value
func (o *KubeOptions) WithStart(value bool) *KubeOptions {
o.Start = &value
diff --git a/pkg/bindings/pods/pods.go b/pkg/bindings/pods/pods.go
index a1a431a3b..3b5832373 100644
--- a/pkg/bindings/pods/pods.go
+++ b/pkg/bindings/pods/pods.go
@@ -9,6 +9,7 @@ import (
"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/errorhandling"
jsoniter "github.com/json-iterator/go"
)
@@ -97,7 +98,7 @@ func Kill(ctx context.Context, nameOrID string, options *KillOptions) (*entities
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Pause pauses all running containers in a given pod.
@@ -117,7 +118,7 @@ func Pause(ctx context.Context, nameOrID string, options *PauseOptions) (*entiti
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Prune by default removes all non-running pods in local storage.
@@ -184,7 +185,7 @@ func Restart(ctx context.Context, nameOrID string, options *RestartOptions) (*en
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Remove deletes a Pod from from local storage. The optional force parameter denotes
@@ -232,7 +233,8 @@ func Start(ctx context.Context, nameOrID string, options *StartOptions) (*entiti
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
@@ -260,7 +262,7 @@ func Stop(ctx context.Context, nameOrID string, options *StopOptions) (*entities
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
@@ -316,7 +318,7 @@ func Unpause(ctx context.Context, nameOrID string, options *UnpauseOptions) (*en
}
defer response.Body.Close()
- return &report, response.Process(&report)
+ return &report, response.ProcessWithError(&report, &errorhandling.PodConflictErrorModel{})
}
// Stats display resource-usage statistics of one or more pods.
diff --git a/pkg/bindings/test/common_test.go b/pkg/bindings/test/common_test.go
index 91ebe21fc..d996595bf 100644
--- a/pkg/bindings/test/common_test.go
+++ b/pkg/bindings/test/common_test.go
@@ -225,12 +225,23 @@ func (b *bindingTest) RunTopContainer(containerName *string, podName *string) (s
// 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 --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go
index b9ed67255..0f535bc31 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -259,6 +259,7 @@ var _ = Describe("Podman containers ", func() {
_, 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 @@ var _ = Describe("Podman containers ", func() {
_, 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)
@@ -366,7 +368,10 @@ var _ = Describe("Podman containers ", func() {
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 --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go
index 5331cf439..879d4d00d 100644
--- a/pkg/bindings/test/pods_test.go
+++ b/pkg/bindings/test/pods_test.go
@@ -1,6 +1,7 @@
package test_bindings
import (
+ "fmt"
"net/http"
"strings"
"time"
@@ -9,7 +10,9 @@ import (
"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"
@@ -208,6 +211,29 @@ var _ = Describe("Podman pods", func() {
}
})
+ 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)
diff --git a/pkg/cgroups/cgroups.go b/pkg/cgroups/cgroups.go
index f1ef538e4..d0c090012 100644
--- a/pkg/cgroups/cgroups.go
+++ b/pkg/cgroups/cgroups.go
@@ -461,10 +461,10 @@ func (c *CgroupControl) CreateSystemdUnit(path string) error {
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 --git a/pkg/checkpoint/checkpoint_restore.go b/pkg/checkpoint/checkpoint_restore.go
index f53e31f9b..da82c9745 100644
--- a/pkg/checkpoint/checkpoint_restore.go
+++ b/pkg/checkpoint/checkpoint_restore.go
@@ -6,6 +6,7 @@ import (
"os"
metadata "github.com/checkpoint-restore/checkpointctl/lib"
+ "github.com/checkpoint-restore/go-criu/v5/stats"
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v3/libpod"
@@ -39,6 +40,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
"volumes",
"ctr.log",
"artifacts",
+ stats.StatsDump,
metadata.RootFsDiffTar,
metadata.DeletedFilesFile,
metadata.NetworkStatusFile,
@@ -193,7 +195,7 @@ func CRImportCheckpoint(ctx context.Context, runtime *libpod.Runtime, restoreOpt
}
if len(restoreOptions.PublishPorts) > 0 {
- ports, _, _, err := generate.ParsePortMapping(restoreOptions.PublishPorts)
+ ports, err := generate.ParsePortMapping(restoreOptions.PublishPorts, nil)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go
index 58f231a2f..d018d373f 100644
--- a/pkg/domain/entities/container_ps.go
+++ b/pkg/domain/entities/container_ps.go
@@ -54,7 +54,7 @@ type ListContainer struct {
// boolean to be set
PodName string
// Port mappings
- Ports []types.OCICNIPortMapping
+ Ports []types.PortMapping
// Size of the container rootfs. Requires the size boolean to be true
Size *define.ContainerSize
// Time when container started
diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go
index deae85fe1..8b7cd62d9 100644
--- a/pkg/domain/entities/containers.go
+++ b/pkg/domain/entities/containers.go
@@ -190,11 +190,14 @@ type CheckpointOptions struct {
PreCheckPoint bool
WithPrevious bool
Compression archive.Compression
+ PrintStats bool
}
type CheckpointReport struct {
- Err error
- Id string //nolint
+ Err error `json:"-"`
+ Id string `json:"Id` //nolint
+ RuntimeDuration int64 `json:"runtime_checkpoint_duration"`
+ CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"`
}
type RestoreOptions struct {
@@ -211,11 +214,14 @@ type RestoreOptions struct {
ImportPrevious string
PublishPorts []nettypes.PortMapping
Pod string
+ PrintStats bool
}
type RestoreReport struct {
- Err error
- Id string //nolint
+ Err error `json:"-"`
+ Id string `json:"Id` //nolint
+ RuntimeDuration int64 `json:"runtime_restore_duration"`
+ CRIUStatistics *define.CRIUCheckpointRestoreStatistics `json:"criu_statistics"`
}
type ContainerCreateReport struct {
@@ -422,7 +428,7 @@ type ContainerPortOptions struct {
// the CLI to output ports
type ContainerPortReport struct {
Id string //nolint
- Ports []nettypes.OCICNIPortMapping
+ Ports []nettypes.PortMapping
}
// ContainerCpOptions describes input options for cp.
diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go
index b0f9ae408..d72f64b5e 100644
--- a/pkg/domain/entities/engine_image.go
+++ b/pkg/domain/entities/engine_image.go
@@ -27,6 +27,7 @@ type ImageEngine interface {
ShowTrust(ctx context.Context, args []string, options ShowTrustOptions) (*ShowTrustReport, error)
Shutdown(ctx context.Context)
Tag(ctx context.Context, nameOrID string, tags []string, options ImageTagOptions) error
+ Transfer(ctx context.Context, scpOpts ImageScpOptions) error
Tree(ctx context.Context, nameOrID string, options ImageTreeOptions) (*ImageTreeReport, error)
Unmount(ctx context.Context, images []string, options ImageUnmountOptions) ([]*ImageUnmountReport, error)
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index ac5e6f410..54f7b5d45 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -50,6 +50,7 @@ func (i *Image) Id() string { // nolint
return i.ID
}
+// swagger:model LibpodImageSummary
type ImageSummary struct {
ID string `json:"Id"`
ParentId string // nolint
@@ -328,6 +329,10 @@ type ImageScpOptions struct {
Save ImageSaveOptions
// Load options used for the second half of the scp operation
Load ImageLoadOptions
+ // Rootless determines whether we are loading locally from root storage to rootless storage
+ Rootless bool
+ // User is used in conjunction with Rootless to determine which user to use to obtain the uid
+ User string
}
// ImageTreeOptions provides options for ImageEngine.Tree()
@@ -368,6 +373,7 @@ type SignOptions struct {
Directory string
SignBy string
CertDir string
+ Authfile string
All bool
}
diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go
index 715d8acaf..ad35dfe25 100644
--- a/pkg/domain/entities/play.go
+++ b/pkg/domain/entities/play.go
@@ -46,6 +46,8 @@ type PlayKubeOptions struct {
ConfigMaps []string
// LogDriver for the container. For example: journald
LogDriver string
+ // LogOptions for the log driver for the container.
+ LogOptions []string
// Start - don't start the pod if false
Start types.OptionalBool
}
diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go
index 309677396..70d2be1e6 100644
--- a/pkg/domain/entities/pods.go
+++ b/pkg/domain/entities/pods.go
@@ -5,7 +5,9 @@ import (
"strings"
"time"
+ commonFlag "github.com/containers/common/pkg/flag"
"github.com/containers/podman/v3/libpod/define"
+ "github.com/containers/podman/v3/libpod/network/types"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/util"
"github.com/opencontainers/runtime-spec/specs-go"
@@ -235,13 +237,13 @@ type ContainerCreateOptions struct {
SignaturePolicy string
StopSignal string
StopTimeout uint
- StorageOpt []string
+ StorageOpts []string
SubUIDName string
SubGIDName string
Sysctl []string
Systemd string
Timeout uint
- TLSVerify bool
+ TLSVerify commonFlag.OptionalBool
TmpFS []string
TTY bool
Timezone string
@@ -264,6 +266,15 @@ type ContainerCreateOptions struct {
CgroupConf []string
}
+func NewInfraContainerCreateOptions() ContainerCreateOptions {
+ options := ContainerCreateOptions{
+ IsInfra: true,
+ ImageVolume: "bind",
+ MemorySwappiness: -1,
+ }
+ return options
+}
+
type PodCreateReport struct {
Id string //nolint
}
@@ -317,7 +328,8 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod
if p.Net != nil {
s.NetNS = p.Net.Network
s.StaticIP = p.Net.StaticIP
- s.StaticMAC = p.Net.StaticMAC
+ // type cast to types.HardwareAddr
+ s.StaticMAC = (*types.HardwareAddr)(p.Net.StaticMAC)
s.PortMappings = p.Net.PublishPorts
s.CNINetworks = p.Net.CNINetworks
s.NetworkOptions = p.Net.NetworkOptions
diff --git a/pkg/domain/entities/reports/prune.go b/pkg/domain/entities/reports/prune.go
index 5494ac3ae..219e35b67 100644
--- a/pkg/domain/entities/reports/prune.go
+++ b/pkg/domain/entities/reports/prune.go
@@ -1,9 +1,9 @@
package reports
type PruneReport struct {
- Id string //nolint
- Err error
- Size uint64
+ Id string `json:"Id"` //nolint
+ Err error `json:"Err,omitempty"`
+ Size uint64 `json:"Size"`
}
func PruneReportsIds(r []*PruneReport) []string {
diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go
index fe041dec8..49f0c2323 100644
--- a/pkg/domain/entities/system.go
+++ b/pkg/domain/entities/system.go
@@ -100,7 +100,7 @@ type SystemVersionReport struct {
// SystemUnshareOptions describes the options for the unshare command
type SystemUnshareOptions struct {
- RootlessCNI bool
+ RootlessNetNS bool
}
type ComponentVersion struct {
diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go
index 2ecfb4446..9b2a170e2 100644
--- a/pkg/domain/entities/volumes.go
+++ b/pkg/domain/entities/volumes.go
@@ -78,8 +78,10 @@ type VolumeCreateOptions struct {
Name string `schema:"name"`
// Volume driver to use
Driver string `schema:"driver"`
- // User-defined key/value metadata.
+ // User-defined key/value metadata. Provided for compatibility
Label map[string]string `schema:"label"`
+ // User-defined key/value metadata. Preferred field, will override Label
+ Labels map[string]string `schema:"labels"`
// Mapping of driver options and values.
Options map[string]string `schema:"opts"`
}
diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go
index c30129001..69c628669 100644
--- a/pkg/domain/infra/abi/containers.go
+++ b/pkg/domain/infra/abi/containers.go
@@ -515,6 +515,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
PreCheckPoint: options.PreCheckPoint,
WithPrevious: options.WithPrevious,
Compression: options.Compression,
+ PrintStats: options.PrintStats,
}
if options.All {
@@ -531,10 +532,12 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [
}
reports := make([]*entities.CheckpointReport, 0, len(cons))
for _, con := range cons {
- err = con.Checkpoint(ctx, checkOpts)
+ criuStatistics, runtimeCheckpointDuration, err := con.Checkpoint(ctx, checkOpts)
reports = append(reports, &entities.CheckpointReport{
- Err: err,
- Id: con.ID(),
+ Err: err,
+ Id: con.ID(),
+ RuntimeDuration: runtimeCheckpointDuration,
+ CRIUStatistics: criuStatistics,
})
}
return reports, nil
@@ -557,6 +560,7 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
IgnoreStaticMAC: options.IgnoreStaticMAC,
ImportPrevious: options.ImportPrevious,
Pod: options.Pod,
+ PrintStats: options.PrintStats,
}
filterFuncs := []libpod.ContainerFilter{
@@ -579,10 +583,12 @@ func (ic *ContainerEngine) ContainerRestore(ctx context.Context, namesOrIds []st
}
reports := make([]*entities.RestoreReport, 0, len(cons))
for _, con := range cons {
- err := con.Restore(ctx, restoreOptions)
+ criuStatistics, runtimeRestoreDuration, err := con.Restore(ctx, restoreOptions)
reports = append(reports, &entities.RestoreReport{
- Err: err,
- Id: con.ID(),
+ Err: err,
+ Id: con.ID(),
+ RuntimeDuration: runtimeRestoreDuration,
+ CRIUStatistics: criuStatistics,
})
}
return reports, nil
diff --git a/pkg/domain/infra/abi/generate.go b/pkg/domain/infra/abi/generate.go
index 081a2464b..a4d6bcf86 100644
--- a/pkg/domain/infra/abi/generate.go
+++ b/pkg/domain/infra/abi/generate.go
@@ -124,8 +124,7 @@ func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrIDs []string,
if err != nil {
return nil, err
}
-
- b, err := generateKubeYAML(po)
+ b, err := generateKubeYAML(libpod.ConvertV1PodToYAMLPod(po))
if err != nil {
return nil, err
}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 8878bf128..8b44b869a 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -6,9 +6,12 @@ import (
"io/ioutil"
"net/url"
"os"
+ "os/exec"
+ "os/user"
"path"
"path/filepath"
"strconv"
+ "strings"
"github.com/containers/common/libimage"
"github.com/containers/common/pkg/config"
@@ -18,6 +21,7 @@ import (
"github.com/containers/image/v5/signature"
"github.com/containers/image/v5/transports"
"github.com/containers/image/v5/transports/alltransports"
+ "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
domainUtils "github.com/containers/podman/v3/pkg/domain/utils"
@@ -57,7 +61,7 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
pruneOptions.Filters = append(pruneOptions.Filters, "containers=false")
}
- var pruneReports []*reports.PruneReport
+ pruneReports := make([]*reports.PruneReport, 0)
// Now prune all images until we converge.
numPreviouslyRemovedImages := 1
@@ -330,6 +334,67 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri
return pushError
}
+// Transfer moves images from root to rootless storage so the user specified in the scp call can access and use the image modified by root
+func (ir *ImageEngine) Transfer(ctx context.Context, scpOpts entities.ImageScpOptions) error {
+ if scpOpts.User == "" {
+ return errors.Wrapf(define.ErrInvalidArg, "you must define a user when transferring from root to rootless storage")
+ }
+ var u *user.User
+ scpOpts.User = strings.Split(scpOpts.User, ":")[0] // split in case provided with uid:gid
+ _, err := strconv.Atoi(scpOpts.User)
+ if err != nil {
+ u, err = user.Lookup(scpOpts.User)
+ if err != nil {
+ return err
+ }
+ } else {
+ u, err = user.LookupId(scpOpts.User)
+ if err != nil {
+ return err
+ }
+ }
+ uid, err := strconv.Atoi(u.Uid)
+ if err != nil {
+ return err
+ }
+ gid, err := strconv.Atoi(u.Gid)
+ if err != nil {
+ return err
+ }
+ err = os.Chown(scpOpts.Save.Output, uid, gid) // chown the output because was created by root so we need to give th euser read access
+ if err != nil {
+ return err
+ }
+
+ podman, err := os.Executable()
+ if err != nil {
+ return err
+ }
+ machinectl, err := exec.LookPath("machinectl")
+ if err != nil {
+ logrus.Warn("defaulting to su since machinectl is not available, su will fail if no user session is available")
+ cmd := exec.Command("su", "-l", u.Username, "--command", podman+" --log-level="+logrus.GetLevel().String()+" --cgroup-manager=cgroupfs load --input="+scpOpts.Save.Output) // load the new image to the rootless storage
+ cmd.Stderr = os.Stderr
+ cmd.Stdout = os.Stdout
+ logrus.Debug("Executing load command su")
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ } else {
+ cmd := exec.Command(machinectl, "shell", "-q", u.Username+"@.host", podman, "--log-level="+logrus.GetLevel().String(), "--cgroup-manager=cgroupfs", "load", "--input", scpOpts.Save.Output) // load the new image to the rootless storage
+ cmd.Stderr = os.Stderr
+ cmd.Stdout = os.Stdout
+ logrus.Debug("Executing load command machinectl")
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error {
// Allow tagging manifest list instead of resolving instances from manifest
lookupOptions := &libimage.LookupImageOptions{ManifestList: true}
@@ -576,6 +641,7 @@ func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entitie
}
sc := ir.Libpod.SystemContext()
sc.DockerCertPath = options.CertDir
+ sc.AuthFilePath = options.Authfile
for _, signimage := range names {
err = func() error {
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 751d6cc05..d2bb95f7c 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -269,17 +269,11 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
if podOpt.Infra {
- containerConfig := util.DefaultContainerConfig()
-
- pulledImages, err := pullImage(ic, writer, containerConfig.Engine.InfraImage, options, config.PullPolicyNewer)
- if err != nil {
- return nil, err
- }
- infraOptions := entities.ContainerCreateOptions{ImageVolume: "bind"}
-
- podSpec.PodSpecGen.InfraImage = pulledImages[0].Names()[0]
+ infraImage := util.DefaultContainerConfig().Engine.InfraImage
+ infraOptions := entities.NewInfraContainerCreateOptions()
+ podSpec.PodSpecGen.InfraImage = infraImage
podSpec.PodSpecGen.NoInfra = false
- podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(pulledImages[0].Names()[0], false)
+ podSpec.PodSpecGen.InfraContainerSpec = specgen.NewSpecGenerator(infraImage, false)
podSpec.PodSpecGen.InfraContainerSpec.NetworkOptions = p.NetworkOptions
err = specgenutil.FillOutSpecGen(podSpec.PodSpecGen.InfraContainerSpec, &infraOptions, []string{})
@@ -333,6 +327,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
NetNSIsHost: p.NetNS.IsHost(),
SecretsManager: secretsManager,
LogDriver: options.LogDriver,
+ LogOptions: options.LogOptions,
Labels: labels,
InitContainerType: define.AlwaysInitContainer,
}
@@ -371,6 +366,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
NetNSIsHost: p.NetNS.IsHost(),
SecretsManager: secretsManager,
LogDriver: options.LogDriver,
+ LogOptions: options.LogOptions,
Labels: labels,
}
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
@@ -756,21 +752,3 @@ func (ic *ContainerEngine) PlayKubeDown(ctx context.Context, path string, _ enti
}
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 --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go
index e326f26a8..7da7754f2 100644
--- a/pkg/domain/infra/abi/system.go
+++ b/pkg/domain/infra/abi/system.go
@@ -360,15 +360,15 @@ func (ic *ContainerEngine) Unshare(ctx context.Context, args []string, options e
return cmd.Run()
}
- if options.RootlessCNI {
- rootlesscni, err := ic.Libpod.GetRootlessCNINetNs(true)
+ if options.RootlessNetNS {
+ rootlessNetNS, err := ic.Libpod.GetRootlessNetNs(true)
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)
+ rootlessNetNS.Lock.Unlock()
+ defer rootlessNetNS.Cleanup(ic.Libpod)
+ return rootlessNetNS.Do(unshare)
}
return unshare()
}
diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go
index 7ec6135ee..cfb674b6d 100644
--- a/pkg/domain/infra/runtime_libpod.go
+++ b/pkg/domain/infra/runtime_libpod.go
@@ -200,6 +200,9 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo
if fs.Changed("network-cmd-path") {
options = append(options, libpod.WithNetworkCmdPath(cfg.Engine.NetworkCmdPath))
}
+ if fs.Changed("network-backend") {
+ options = append(options, libpod.WithNetworkBackend(cfg.Network.NetworkBackend))
+ }
if fs.Changed("events-backend") {
options = append(options, libpod.WithEventsLogger(cfg.Engine.EventsLogger))
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 3f78ba7bc..5b5a1912c 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -228,7 +228,7 @@ func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []st
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
}
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index b8af2de68..fde57972f 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
+ "github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/pkg/bindings/images"
"github.com/containers/podman/v3/pkg/domain/entities"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
@@ -122,6 +123,10 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, opts entities.
return &entities.ImagePullReport{Images: pulledImages}, nil
}
+func (ir *ImageEngine) Transfer(ctx context.Context, scpOpts entities.ImageScpOptions) error {
+ return errors.Wrapf(define.ErrNotImplemented, "cannot use the remote client to transfer images between root and rootless storage")
+}
+
func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, opt entities.ImageTagOptions) error {
options := new(images.TagOptions)
for _, newTag := range tags {
@@ -188,7 +193,7 @@ func (ir *ImageEngine) Inspect(ctx context.Context, namesOrIDs []string, opts en
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
}
diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go
index 79fba1943..069982d30 100644
--- a/pkg/domain/infra/tunnel/network.go
+++ b/pkg/domain/infra/tunnel/network.go
@@ -25,7 +25,7 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri
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 --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go
index 0b1c3d2ca..75952ce2c 100644
--- a/pkg/domain/infra/tunnel/play.go
+++ b/pkg/domain/infra/tunnel/play.go
@@ -13,6 +13,9 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entit
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot)
options.WithStaticIPs(opts.StaticIPs).WithStaticMACs(opts.StaticMACs)
+ if len(opts.LogOptions) > 0 {
+ options.WithLogOptions(opts.LogOptions)
+ }
options.WithNoHosts(opts.NoHosts)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
diff --git a/pkg/domain/infra/tunnel/secrets.go b/pkg/domain/infra/tunnel/secrets.go
index 6337c7fbe..e5fa200bd 100644
--- a/pkg/domain/infra/tunnel/secrets.go
+++ b/pkg/domain/infra/tunnel/secrets.go
@@ -28,7 +28,7 @@ func (ic *ContainerEngine) SecretInspect(ctx context.Context, nameOrIDs []string
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
}
@@ -67,7 +67,7 @@ func (ic *ContainerEngine) SecretRm(ctx context.Context, nameOrIDs []string, opt
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 --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go
index cfd1574c3..ccb363935 100644
--- a/pkg/domain/infra/tunnel/volumes.go
+++ b/pkg/domain/infra/tunnel/volumes.go
@@ -59,7 +59,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
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
}
diff --git a/pkg/errorhandling/errorhandling.go b/pkg/errorhandling/errorhandling.go
index 44a0c3efd..04110b62a 100644
--- a/pkg/errorhandling/errorhandling.go
+++ b/pkg/errorhandling/errorhandling.go
@@ -83,6 +83,12 @@ func Contains(err error, sub error) bool {
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
@@ -106,3 +112,11 @@ func (e ErrorModel) Cause() error {
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 --git a/pkg/machine/config.go b/pkg/machine/config.go
index 3ff5c7fe7..55d5dd7b4 100644
--- a/pkg/machine/config.go
+++ b/pkg/machine/config.go
@@ -57,6 +57,7 @@ type ListResponse struct {
CreatedAt time.Time
LastUp time.Time
Running bool
+ Stream string
VMType string
CPUs uint64
Memory uint64
diff --git a/pkg/machine/ignition.go b/pkg/machine/ignition.go
index e211f5ea6..42d729458 100644
--- a/pkg/machine/ignition.go
+++ b/pkg/machine/ignition.go
@@ -304,6 +304,24 @@ machine_enabled=true
},
})
+ 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
}
diff --git a/pkg/machine/qemu/config.go b/pkg/machine/qemu/config.go
index 9f5f45b58..c04773450 100644
--- a/pkg/machine/qemu/config.go
+++ b/pkg/machine/qemu/config.go
@@ -13,6 +13,8 @@ type MachineVM struct {
IdentityPath string
// IgnitionFilePath is the fq path to the .ign file
IgnitionFilePath string
+ // ImageStream is the update stream for the image
+ ImageStream string
// ImagePath is the fq path to
ImagePath string
// Memory in megabytes assigned to the vm
diff --git a/pkg/machine/qemu/machine.go b/pkg/machine/qemu/machine.go
index 727b3cda4..a7174aac3 100644
--- a/pkg/machine/qemu/machine.go
+++ b/pkg/machine/qemu/machine.go
@@ -143,6 +143,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
switch opts.ImagePath {
case "testing", "next", "stable", "":
// Get image as usual
+ v.ImageStream = opts.ImagePath
dd, err := machine.NewFcosDownloader(vmtype, v.Name, opts.ImagePath)
if err != nil {
return err
@@ -154,6 +155,7 @@ func (v *MachineVM) Init(opts machine.InitOptions) error {
default:
// The user has provided an alternate image which can be a file path
// or URL.
+ v.ImageStream = "custom"
g, err := machine.NewGenericDownloader(vmtype, v.Name, opts.ImagePath)
if err != nil {
return err
@@ -396,7 +398,6 @@ func (v *MachineVM) Stop(name string, _ machine.StopOptions) error {
return err
}
- fmt.Printf("Successfully stopped machine: %s", name)
return nil
}
@@ -595,6 +596,7 @@ func GetVMInfos() ([]*machine.ListResponse, error) {
listEntry := new(machine.ListResponse)
listEntry.Name = vm.Name
+ listEntry.Stream = vm.ImageStream
listEntry.VMType = "qemu"
listEntry.CPUs = vm.CPUs
listEntry.Memory = vm.Memory
diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go
index 7f9228666..3e81d5c14 100644
--- a/pkg/rootless/rootless_linux.go
+++ b/pkg/rootless/rootless_linux.go
@@ -325,7 +325,7 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (_ boo
uidsMapped = err == nil
}
if !uidsMapped {
- logrus.Warnf("Using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding sub*ids")
+ logrus.Warnf("Using rootless single mapping into the namespace. This might break some images. Check /etc/subuid and /etc/subgid for adding sub*ids if not using a network user")
setgroups := fmt.Sprintf("/proc/%d/setgroups", pid)
err = ioutil.WriteFile(setgroups, []byte("deny\n"), 0666)
if err != nil {
diff --git a/pkg/rootlessport/rootlessport_linux.go b/pkg/rootlessport/rootlessport_linux.go
index 7b9e5bbfa..7e6075789 100644
--- a/pkg/rootlessport/rootlessport_linux.go
+++ b/pkg/rootlessport/rootlessport_linux.go
@@ -23,7 +23,7 @@ const (
// 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 []types.OCICNIPortMapping
+ Mappings []types.PortMapping
NetNSPath string
ExitFD int
ReadyFD int
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index 92c0f22d9..f3dc28b01 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -366,6 +366,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.Entrypoint != nil {
options = append(options, libpod.WithEntrypoint(s.Entrypoint))
}
+ if len(s.ContainerStorageConfig.StorageOpts) > 0 {
+ options = append(options, libpod.WithStorageOpts(s.StorageOpts))
+ }
// If the user did not specify a workdir on the CLI, let's extract it
// from the image.
if s.WorkDir == "" && imageData != nil {
@@ -375,6 +378,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.WorkDir == "" {
s.WorkDir = "/"
}
+ if s.CreateWorkingDir {
+ options = append(options, libpod.WithCreateWorkingDir())
+ }
if s.StopSignal != nil {
options = append(options, libpod.WithStopSignal(*s.StopSignal))
}
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index 6eebc6376..c502a6e62 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -19,6 +19,7 @@ import (
"github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/podman/v3/pkg/util"
+ "github.com/docker/go-units"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
v1 "k8s.io/api/core/v1"
@@ -116,6 +117,8 @@ type CtrSpecGenOptions struct {
SecretsManager *secrets.SecretsManager
// LogDriver which should be used for the container
LogDriver string
+ // LogOptions log options which should be used for the container
+ LogOptions []string
// Labels define key-value pairs of metadata
Labels map[string]string
//
@@ -144,6 +147,27 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
Driver: opts.LogDriver,
}
+ for _, o := range opts.LogOptions {
+ split := strings.SplitN(o, "=", 2)
+ if len(split) < 2 {
+ return nil, 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 nil, err
+ }
+ s.LogConfiguration.Size = logSize
+ default:
+ s.LogConfiguration.Options[split[0]] = split[1]
+ }
+ }
+
s.InitContainerType = opts.InitContainerType
setupSecurityContext(s, opts.Container)
diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go
index a4027eae7..bfd81739a 100644
--- a/pkg/specgen/generate/pod_create.go
+++ b/pkg/specgen/generate/pod_create.go
@@ -2,22 +2,117 @@ package generate
import (
"context"
+ "fmt"
+ "io/ioutil"
"net"
+ "os"
+ buildahDefine "github.com/containers/buildah/define"
"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/domain/entities"
- "github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/specgen"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+func buildPauseImage(rt *libpod.Runtime, rtConfig *config.Config) (string, error) {
+ version, err := define.GetVersion()
+ if err != nil {
+ return "", err
+ }
+ imageName := fmt.Sprintf("localhost/podman-pause:%s-%d", version.Version, version.Built)
+
+ // First check if the image has already been built.
+ if _, _, err := rt.LibimageRuntime().LookupImage(imageName, nil); err == nil {
+ return imageName, nil
+ }
+
+ // NOTE: Having the pause binary in its own directory keeps the door
+ // open for replacing the image building with using an overlay root FS.
+ // The latter turned out to be complex and error prone (see #11956) but
+ // we may be able to come up with a proper solution at a later point in
+ // time.
+ pausePath, err := rtConfig.FindHelperBinary("pause/pause", false)
+ if err != nil {
+ return "", fmt.Errorf("finding pause binary: %w", err)
+ }
+
+ buildContent := fmt.Sprintf(`FROM scratch
+COPY %s /pause
+ENTRYPOINT ["/pause"]`, pausePath)
+
+ tmpF, err := ioutil.TempFile("", "pause.containerfile")
+ if err != nil {
+ return "", err
+ }
+ if _, err := tmpF.WriteString(buildContent); err != nil {
+ return "", err
+ }
+ if err := tmpF.Close(); err != nil {
+ return "", err
+ }
+ defer os.Remove(tmpF.Name())
+
+ buildOptions := buildahDefine.BuildOptions{
+ CommonBuildOpts: &buildahDefine.CommonBuildOptions{},
+ Output: imageName,
+ Quiet: true,
+ IIDFile: "/dev/null", // prevents Buildah from writing the ID on stdout
+ }
+ if _, _, err := rt.Build(context.Background(), buildOptions, tmpF.Name()); err != nil {
+ return "", err
+ }
+
+ return imageName, nil
+}
+
+func pullOrBuildInfraImage(p *entities.PodSpec, rt *libpod.Runtime) error {
+ if p.PodSpecGen.NoInfra {
+ return nil
+ }
+
+ rtConfig, err := rt.GetConfigNoCopy()
+ if err != nil {
+ return err
+ }
+
+ // NOTE: we need pull down the infra image if it was explicitly set by
+ // the user (or containers.conf) to the non-default one.
+ imageName := p.PodSpecGen.InfraImage
+ if imageName == "" {
+ imageName = rtConfig.Engine.InfraImage
+ }
+
+ if imageName != config.DefaultInfraImage {
+ _, err := rt.LibimageRuntime().Pull(context.Background(), imageName, config.PullPolicyMissing, nil)
+ if err != nil {
+ return err
+ }
+ } else {
+ name, err := buildPauseImage(rt, rtConfig)
+ if err != nil {
+ return fmt.Errorf("building local pause image: %w", err)
+ }
+ imageName = name
+ }
+
+ p.PodSpecGen.InfraImage = imageName
+ p.PodSpecGen.InfraContainerSpec.RawImageName = imageName
+
+ return nil
+}
+
func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
if err := p.PodSpecGen.Validate(); err != nil {
return nil, err
}
+
+ if err := pullOrBuildInfraImage(p, rt); err != nil {
+ return nil, err
+ }
+
if !p.PodSpecGen.NoInfra && p.PodSpecGen.InfraContainerSpec != nil {
var err error
p.PodSpecGen.InfraContainerSpec, err = MapSpec(&p.PodSpecGen)
@@ -35,7 +130,6 @@ func MakePod(p *entities.PodSpec, rt *libpod.Runtime) (*libpod.Pod, error) {
return nil, err
}
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"
}
@@ -109,11 +203,11 @@ func createPodOptions(p *specgen.PodSpecGenerator, rt *libpod.Runtime, infraSpec
// 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)
+ ports, err := ParsePortMapping(p.PortMappings, nil)
if err != nil {
return nil, err
}
- p.InfraContainerSpec.PortMappings = libpod.WithInfraContainerPorts(ports, p.InfraContainerSpec)
+ p.InfraContainerSpec.PortMappings = ports
}
switch p.NetNS.NSMode {
case specgen.Default, "":
@@ -121,15 +215,6 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
logrus.Debugf("No networking because the infra container is missing")
break
}
- if rootless.IsRootless() {
- logrus.Debugf("Pod will use slirp4netns")
- if p.InfraContainerSpec.NetNS.NSMode != "host" {
- p.InfraContainerSpec.NetworkOptions = p.NetworkOptions
- p.InfraContainerSpec.NetNS.NSMode = specgen.NamespaceMode("slirp4netns")
- }
- } else {
- logrus.Debugf("Pod using bridge network mode")
- }
case specgen.Bridge:
p.InfraContainerSpec.NetNS.NSMode = specgen.Bridge
logrus.Debugf("Pod using bridge network mode")
@@ -163,7 +248,6 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode)
}
- libpod.WithPodCgroups()
if len(p.InfraCommand) > 0 {
p.InfraContainerSpec.Entrypoint = p.InfraCommand
}
diff --git a/pkg/specgen/generate/ports.go b/pkg/specgen/generate/ports.go
index 992b4a8e9..53a5e5697 100644
--- a/pkg/specgen/generate/ports.go
+++ b/pkg/specgen/generate/ports.go
@@ -2,7 +2,9 @@ package generate
import (
"context"
+ "fmt"
"net"
+ "sort"
"strconv"
"strings"
@@ -11,6 +13,7 @@ import (
"github.com/containers/podman/v3/utils"
"github.com/containers/podman/v3/pkg/specgen"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -21,252 +24,323 @@ const (
protoSCTP = "sctp"
)
-// Parse port maps to OCICNI port mappings.
-// Returns a set of OCICNI port mappings, and maps of utilized container and
+// joinTwoPortsToRangePortIfPossible will expect two ports the previous port one must have a lower or equal hostPort than the current port.
+func joinTwoPortsToRangePortIfPossible(ports *[]types.PortMapping, allHostPorts, allContainerPorts, currentHostPorts *[65536]bool,
+ previousPort *types.PortMapping, port types.PortMapping) (*types.PortMapping, error) {
+ // no previous port just return the current one
+ if previousPort == nil {
+ return &port, nil
+ }
+ if previousPort.HostPort+previousPort.Range >= port.HostPort {
+ // check if the port range matches the host and container ports
+ portDiff := port.HostPort - previousPort.HostPort
+ if portDiff == port.ContainerPort-previousPort.ContainerPort {
+ // calc the new range use the old range and add the difference between the ports
+ newRange := port.Range + portDiff
+ // if the newRange is greater than the old range use it
+ // this is important otherwise we would could lower the range
+ if newRange > previousPort.Range {
+ previousPort.Range = newRange
+ }
+ return previousPort, nil
+ }
+ // if both host port ranges overlap and the container port range did not match
+ // we have to error because we cannot assign the same host port to more than one container port
+ if previousPort.HostPort+previousPort.Range-1 > port.HostPort {
+ return nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", port.HostPort, port.Protocol)
+ }
+ }
+ // we could not join the ports so we append the old one to the list
+ // and return the current port as previous port
+ addPortToUsedPorts(ports, allHostPorts, allContainerPorts, currentHostPorts, previousPort)
+ return &port, nil
+}
+
+// joinTwoContainerPortsToRangePortIfPossible will expect two ports with both no host port set,
+// the previous port one must have a lower or equal containerPort than the current port.
+func joinTwoContainerPortsToRangePortIfPossible(ports *[]types.PortMapping, allHostPorts, allContainerPorts, currentHostPorts *[65536]bool,
+ previousPort *types.PortMapping, port types.PortMapping) (*types.PortMapping, error) {
+ // no previous port just return the current one
+ if previousPort == nil {
+ return &port, nil
+ }
+ if previousPort.ContainerPort+previousPort.Range > port.ContainerPort {
+ // calc the new range use the old range and add the difference between the ports
+ newRange := port.ContainerPort - previousPort.ContainerPort + port.Range
+ // if the newRange is greater than the old range use it
+ // this is important otherwise we would could lower the range
+ if newRange > previousPort.Range {
+ previousPort.Range = newRange
+ }
+ return previousPort, nil
+ }
+ // we could not join the ports so we append the old one to the list
+ // and return the current port as previous port
+ newPort, err := getRandomHostPort(currentHostPorts, *previousPort)
+ if err != nil {
+ return nil, err
+ }
+ addPortToUsedPorts(ports, allHostPorts, allContainerPorts, currentHostPorts, &newPort)
+ return &port, nil
+}
+
+func addPortToUsedPorts(ports *[]types.PortMapping, allHostPorts, allContainerPorts, currentHostPorts *[65536]bool, port *types.PortMapping) {
+ for i := uint16(0); i < port.Range; i++ {
+ h := port.HostPort + i
+ allHostPorts[h] = true
+ currentHostPorts[h] = true
+ c := port.ContainerPort + i
+ allContainerPorts[c] = true
+ }
+ *ports = append(*ports, *port)
+}
+
+// getRandomHostPort get a random host port mapping for the given port
+// the caller has to supply a array with he already used ports
+func getRandomHostPort(hostPorts *[65536]bool, port types.PortMapping) (types.PortMapping, error) {
+outer:
+ for i := 0; i < 15; i++ {
+ ranPort, err := utils.GetRandomPort()
+ if err != nil {
+ return port, err
+ }
+
+ // if port range is exceeds max port we cannot use it
+ if ranPort+int(port.Range) > 65535 {
+ continue
+ }
+
+ // check if there is a port in the range which is used
+ for j := 0; j < int(port.Range); j++ {
+ // port already used
+ if hostPorts[ranPort+j] {
+ continue outer
+ }
+ }
+
+ port.HostPort = uint16(ranPort)
+ return port, nil
+ }
+
+ // add range to error message if needed
+ rangePort := ""
+ if port.Range > 1 {
+ rangePort = fmt.Sprintf("with range %d ", port.Range)
+ }
+
+ return port, errors.Errorf("failed to find an open port to expose container port %d %son the host", port.ContainerPort, rangePort)
+}
+
+// Parse port maps to port mappings.
+// Returns a set of port mappings, and maps of utilized container and
// host ports.
-func ParsePortMapping(portMappings []types.PortMapping) ([]types.OCICNIPortMapping, 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 {
- mapping types.OCICNIPortMapping
- startOfRange bool
- isInRange bool
+func ParsePortMapping(portMappings []types.PortMapping, exposePorts map[uint16][]string) ([]types.PortMapping, error) {
+ if len(portMappings) == 0 && len(exposePorts) == 0 {
+ return nil, nil
}
- tempMappings := []tempMapping{}
-
- // To validate, we need two maps: one for host ports, one for container
- // ports.
- // Each is a map of protocol to map of IP address to map of port to
- // port (for hostPortValidate, it's host port to container port;
- // for containerPortValidate, container port to host port.
- // These will ensure no collisions.
- hostPortValidate := make(map[string]map[string]map[uint16]uint16)
- containerPortValidate := make(map[string]map[string]map[uint16]uint16)
-
- // Initialize the first level of maps (we can't really guess keys for
- // the rest).
- for _, proto := range []string{protoTCP, protoUDP, protoSCTP} {
- hostPortValidate[proto] = make(map[string]map[uint16]uint16)
- containerPortValidate[proto] = make(map[string]map[uint16]uint16)
+
+ // tempMapping stores the ports without ip and protocol
+ type tempMapping struct {
+ hostPort uint16
+ containerPort uint16
+ rangePort uint16
}
- postAssignHostPort := false
+ // portMap is a temporary structure to sort all ports
+ // the map is hostIp -> protocol -> array of mappings
+ portMap := make(map[string]map[string][]tempMapping)
+
+ // allUsedContainerPorts stores all used ports for each protocol
+ // the key is the protocol and the array is 65536 elements long for each port.
+ allUsedContainerPortsMap := make(map[string][65536]bool)
+ allUsedHostPortsMap := make(map[string][65536]bool)
- // Iterate through all port mappings, generating OCICNI PortMapping
- // structs and validating there is no overlap.
+ // First, we need to validate the ports passed in the specgen
for _, port := range portMappings {
// First, check proto
protocols, err := checkProtocol(port.Protocol, true)
if err != nil {
- return nil, nil, nil, err
- }
-
- // Validate host IP
- hostIP := port.HostIP
- if hostIP == "" {
- hostIP = "0.0.0.0"
+ return nil, err
}
- if ip := net.ParseIP(hostIP); ip == nil {
- return nil, nil, nil, errors.Errorf("invalid IP address %s in port mapping", port.HostIP)
+ if port.HostIP != "" {
+ if ip := net.ParseIP(port.HostIP); ip == nil {
+ return nil, errors.Errorf("invalid IP address %q in port mapping", port.HostIP)
+ }
}
// Validate port numbers and range.
- len := port.Range
- if len == 0 {
- len = 1
+ portRange := port.Range
+ if portRange == 0 {
+ portRange = 1
}
containerPort := port.ContainerPort
if containerPort == 0 {
- return nil, nil, nil, errors.Errorf("container port number must be non-0")
+ return nil, errors.Errorf("container port number must be non-0")
}
hostPort := port.HostPort
- if uint32(len-1)+uint32(containerPort) > 65535 {
- return nil, nil, nil, errors.Errorf("container port range exceeds maximum allowable port number")
+ if uint32(portRange-1)+uint32(containerPort) > 65535 {
+ return nil, errors.Errorf("container port range exceeds maximum allowable port number")
}
- if uint32(len-1)+uint32(hostPort) > 65536 {
- return nil, nil, nil, errors.Errorf("host port range exceeds maximum allowable port number")
+ if uint32(portRange-1)+uint32(hostPort) > 65535 {
+ return nil, errors.Errorf("host port range exceeds maximum allowable port number")
}
- // Iterate through ports, populating maps to check for conflicts
- // and generating CNI port mappings.
- for _, p := range protocols {
- hostIPMap := hostPortValidate[p]
- ctrIPMap := containerPortValidate[p]
-
- hostPortMap, ok := hostIPMap[hostIP]
- if !ok {
- hostPortMap = make(map[uint16]uint16)
- hostIPMap[hostIP] = hostPortMap
+ hostProtoMap, ok := portMap[port.HostIP]
+ if !ok {
+ hostProtoMap = make(map[string][]tempMapping)
+ for _, proto := range []string{protoTCP, protoUDP, protoSCTP} {
+ hostProtoMap[proto] = make([]tempMapping, 0)
}
- ctrPortMap, ok := ctrIPMap[hostIP]
- if !ok {
- ctrPortMap = make(map[uint16]uint16)
- ctrIPMap[hostIP] = ctrPortMap
- }
-
- // Iterate through all port numbers in the requested
- // range.
- var index uint16
- for index = 0; index < len; index++ {
- cPort := containerPort + index
- hPort := hostPort
- // Only increment host port if it's not 0.
- if hostPort != 0 {
- hPort += index
- }
-
- if cPort == 0 {
- return nil, nil, nil, errors.Errorf("container port cannot be 0")
- }
+ portMap[port.HostIP] = hostProtoMap
+ }
- // Host port is allowed to be 0. If it is, we
- // select a random port on the host.
- // This will happen *after* all other ports are
- // placed, to ensure we don't accidentally
- // select a port that a later mapping wanted.
- if hPort == 0 {
- // If we already have a host port
- // assigned to their container port -
- // just use that.
- if ctrPortMap[cPort] != 0 {
- hPort = ctrPortMap[cPort]
- } else {
- postAssignHostPort = true
- }
- } else {
- testHPort := hostPortMap[hPort]
- if testHPort != 0 && testHPort != cPort {
- return nil, nil, nil, errors.Errorf("conflicting port mappings for host port %d (protocol %s)", hPort, p)
- }
- hostPortMap[hPort] = cPort
-
- // Mapping a container port to multiple
- // host ports is allowed.
- // We only store the latest of these in
- // the container port map - we don't
- // need to know all of them, just one.
- testCPort := ctrPortMap[cPort]
- ctrPortMap[cPort] = hPort
-
- // If we have an exact duplicate, just continue
- if testCPort == hPort && testHPort == cPort {
- continue
- }
- }
+ p := tempMapping{
+ hostPort: port.HostPort,
+ containerPort: port.ContainerPort,
+ rangePort: portRange,
+ }
- // We appear to be clear. Make an OCICNI port
- // struct.
- // Don't use hostIP - we want to preserve the
- // empty string hostIP by default for compat.
- cniPort := types.OCICNIPortMapping{
- HostPort: int32(hPort),
- ContainerPort: int32(cPort),
- Protocol: p,
- HostIP: port.HostIP,
- }
- tempMappings = append(
- tempMappings,
- tempMapping{
- mapping: cniPort,
- startOfRange: port.Range > 1 && index == 0,
- isInRange: port.Range > 1,
- },
- )
- }
+ for _, proto := range protocols {
+ hostProtoMap[proto] = append(hostProtoMap[proto], p)
}
}
- // Handle any 0 host ports now by setting random container ports.
- if postAssignHostPort {
- remadeMappings := make([]types.OCICNIPortMapping, 0, len(tempMappings))
-
- var (
- candidate int
- err error
- )
-
- // Iterate over all
- for _, tmp := range tempMappings {
- p := tmp.mapping
+ // we do no longer need the original port mappings
+ // set it to 0 length so we can resuse it to populate
+ // the slice again while keeping the underlying capacity
+ portMappings = portMappings[:0]
- if p.HostPort != 0 {
- remadeMappings = append(remadeMappings, p)
+ for hostIP, protoMap := range portMap {
+ for protocol, ports := range protoMap {
+ ports := ports
+ if len(ports) == 0 {
continue
}
-
- hostIPMap := hostPortValidate[p.Protocol]
- ctrIPMap := containerPortValidate[p.Protocol]
-
- hostPortMap, ok := hostIPMap[p.HostIP]
- if !ok {
- hostPortMap = make(map[uint16]uint16)
- hostIPMap[p.HostIP] = hostPortMap
+ // 1. sort the ports by host port
+ // use a small hack to make sure ports with host port 0 are sorted last
+ sort.Slice(ports, func(i, j int) bool {
+ if ports[i].hostPort == ports[j].hostPort {
+ return ports[i].containerPort < ports[j].containerPort
+ }
+ if ports[i].hostPort == 0 {
+ return false
+ }
+ if ports[j].hostPort == 0 {
+ return true
+ }
+ return ports[i].hostPort < ports[j].hostPort
+ })
+
+ allUsedContainerPorts := allUsedContainerPortsMap[protocol]
+ allUsedHostPorts := allUsedHostPortsMap[protocol]
+ var usedHostPorts [65536]bool
+
+ var previousPort *types.PortMapping
+ var i int
+ for i = 0; i < len(ports); i++ {
+ if ports[i].hostPort == 0 {
+ // because the ports are sorted and host port 0 is last
+ // we can break when we hit 0
+ // we will fit them in afterwards
+ break
+ }
+ p := types.PortMapping{
+ HostIP: hostIP,
+ Protocol: protocol,
+ HostPort: ports[i].hostPort,
+ ContainerPort: ports[i].containerPort,
+ Range: ports[i].rangePort,
+ }
+ var err error
+ previousPort, err = joinTwoPortsToRangePortIfPossible(&portMappings, &allUsedHostPorts,
+ &allUsedContainerPorts, &usedHostPorts, previousPort, p)
+ if err != nil {
+ return nil, err
+ }
}
- ctrPortMap, ok := ctrIPMap[p.HostIP]
- if !ok {
- ctrPortMap = make(map[uint16]uint16)
- ctrIPMap[p.HostIP] = ctrPortMap
+ if previousPort != nil {
+ addPortToUsedPorts(&portMappings, &allUsedHostPorts,
+ &allUsedContainerPorts, &usedHostPorts, previousPort)
}
- // See if container port has been used elsewhere
- if ctrPortMap[uint16(p.ContainerPort)] != 0 {
- // Duplicate definition. Let's not bother
- // including it.
- continue
+ // now take care of the hostPort = 0 ports
+ previousPort = nil
+ for i < len(ports) {
+ p := types.PortMapping{
+ HostIP: hostIP,
+ Protocol: protocol,
+ ContainerPort: ports[i].containerPort,
+ Range: ports[i].rangePort,
+ }
+ var err error
+ previousPort, err = joinTwoContainerPortsToRangePortIfPossible(&portMappings, &allUsedHostPorts,
+ &allUsedContainerPorts, &usedHostPorts, previousPort, p)
+ if err != nil {
+ return nil, err
+ }
+ i++
+ }
+ if previousPort != nil {
+ newPort, err := getRandomHostPort(&usedHostPorts, *previousPort)
+ if err != nil {
+ return nil, err
+ }
+ addPortToUsedPorts(&portMappings, &allUsedHostPorts,
+ &allUsedContainerPorts, &usedHostPorts, &newPort)
}
- // Max retries to ensure we don't loop forever.
- for i := 0; i < 15; i++ {
- // Only get a random candidate for single entries or the start
- // of a range. Otherwise we just increment the candidate.
- if !tmp.isInRange || tmp.startOfRange {
- candidate, err = utils.GetRandomPort()
+ allUsedContainerPortsMap[protocol] = allUsedContainerPorts
+ allUsedHostPortsMap[protocol] = allUsedHostPorts
+ }
+ }
+
+ if len(exposePorts) > 0 {
+ logrus.Debugf("Adding exposed ports")
+
+ for port, protocols := range exposePorts {
+ newProtocols := make([]string, 0, len(protocols))
+ for _, protocol := range protocols {
+ if !allUsedContainerPortsMap[protocol][port] {
+ p := types.PortMapping{
+ ContainerPort: port,
+ Protocol: protocol,
+ Range: 1,
+ }
+ allPorts := allUsedContainerPortsMap[protocol]
+ p, err := getRandomHostPort(&allPorts, p)
if err != nil {
- return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort)
+ return nil, err
}
+ portMappings = append(portMappings, p)
} else {
- candidate++
- }
-
- if hostPortMap[uint16(candidate)] == 0 {
- logrus.Debugf("Successfully assigned container port %d to host port %d (IP %s Protocol %s)", p.ContainerPort, candidate, p.HostIP, p.Protocol)
- hostPortMap[uint16(candidate)] = uint16(p.ContainerPort)
- ctrPortMap[uint16(p.ContainerPort)] = uint16(candidate)
- p.HostPort = int32(candidate)
- break
+ newProtocols = append(newProtocols, protocol)
}
}
- if p.HostPort == 0 {
- return nil, nil, nil, errors.Errorf("could not find open host port to map container port %d to", p.ContainerPort)
+ // make sure to delete the key from the map if there are no protocols left
+ if len(newProtocols) == 0 {
+ delete(exposePorts, port)
+ } else {
+ exposePorts[port] = newProtocols
}
- remadeMappings = append(remadeMappings, p)
}
- return remadeMappings, containerPortValidate, hostPortValidate, nil
}
+ return portMappings, nil
+}
- finalMappings := []types.OCICNIPortMapping{}
- for _, m := range tempMappings {
- finalMappings = append(finalMappings, m.mapping)
+func appendProtocolsNoDuplicates(slice []string, protocols []string) []string {
+ for _, proto := range protocols {
+ if util.StringInSlice(proto, slice) {
+ continue
+ }
+ slice = append(slice, proto)
}
-
- return finalMappings, containerPortValidate, hostPortValidate, nil
+ return slice
}
// Make final port mappings for the container
-func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]types.OCICNIPortMapping, map[uint16][]string, error) {
- finalMappings, containerPortValidate, hostPortValidate, err := ParsePortMapping(s.PortMappings)
- if err != nil {
- return nil, nil, err
- }
-
- // 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")
-
+func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData *libimage.ImageData) ([]types.PortMapping, map[uint16][]string, error) {
expose := make(map[uint16]string)
+ var err error
if imageData != nil {
expose, err = GenExposedPorts(imageData.Config.ExposedPorts)
if err != nil {
@@ -274,103 +348,30 @@ func createPortMappings(ctx context.Context, s *specgen.SpecGenerator, imageData
}
}
- // 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.
- toExpose := make(map[uint16][]string)
- for port, proto := range expose {
- // Validate protocol first
- protocols, err := checkProtocol(proto, false)
- if err != nil {
- return nil, nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port)
- }
-
- if port == 0 {
- 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
- // mappings.
- for _, p := range protocols {
- ctrPortMap, ok := containerPortValidate[p]["0.0.0.0"]
- if !ok {
- ctrPortMap = make(map[uint16]uint16)
- containerPortValidate[p]["0.0.0.0"] = ctrPortMap
+ toExpose := make(map[uint16][]string, len(s.Expose)+len(expose))
+ for _, expose := range []map[uint16]string{expose, s.Expose} {
+ for port, proto := range expose {
+ if port == 0 {
+ return nil, nil, errors.Errorf("cannot expose 0 as it is not a valid port number")
}
-
- if portNum := ctrPortMap[port]; portNum == 0 {
- // We want to expose this port for this protocol
- exposeProto, ok := toExpose[port]
- if !ok {
- exposeProto = []string{}
- }
- exposeProto = append(exposeProto, p)
- toExpose[port] = exposeProto
+ protocols, err := checkProtocol(proto, false)
+ if err != nil {
+ return nil, nil, errors.Wrapf(err, "error validating protocols for exposed port %d", port)
}
+ toExpose[port] = appendProtocolsNoDuplicates(toExpose[port], protocols)
}
}
- // If not publishing exposed ports return mappings and exposed ports.
+ publishPorts := toExpose
if !s.PublishExposedPorts {
- return finalMappings, toExpose, nil
+ publishPorts = 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 {
- for _, p := range protocols {
- // Find an open port on the host.
- // I see a faint possibility that this will infinite
- // loop trying to find a valid open port, so I've
- // included a max-tries counter.
- hostPort := 0
- tries := 15
- for hostPort == 0 && tries > 0 {
- // We can't select a specific protocol, which is
- // unfortunate for the UDP case.
- candidate, err := utils.GetRandomPort()
- if err != nil {
- return nil, nil, err
- }
-
- // Check if the host port is already bound
- hostPortMap, ok := hostPortValidate[p]["0.0.0.0"]
- if !ok {
- hostPortMap = make(map[uint16]uint16)
- hostPortValidate[p]["0.0.0.0"] = hostPortMap
- }
-
- if checkPort := hostPortMap[uint16(candidate)]; checkPort != 0 {
- // Host port is already allocated, try again
- tries--
- continue
- }
-
- hostPortMap[uint16(candidate)] = port
- hostPort = candidate
- logrus.Debugf("Mapping exposed port %d/%s to host port %d", port, p, hostPort)
-
- // Make a CNI port mapping
- cniPort := types.OCICNIPortMapping{
- HostPort: int32(candidate),
- ContainerPort: int32(port),
- Protocol: p,
- HostIP: "",
- }
- finalMappings = append(finalMappings, cniPort)
- }
- if tries == 0 && hostPort == 0 {
- // We failed to find an open port.
- return nil, nil, errors.Errorf("failed to find an open port to expose container port %d on the host", port)
- }
- }
+ finalMappings, err := ParsePortMapping(s.PortMappings, publishPorts)
+ if err != nil {
+ return nil, nil, err
}
-
- return finalMappings, nil, nil
+ return finalMappings, toExpose, nil
}
// Check a string to ensure it is a comma-separated set of valid protocols
@@ -409,7 +410,7 @@ func checkProtocol(protocol string, allowSCTP bool) ([]string, error) {
}
func GenExposedPorts(exposedPorts map[string]struct{}) (map[uint16]string, error) {
- expose := make(map[uint16]string)
+ expose := make(map[uint16]string, len(exposedPorts))
for imgExpose := range exposedPorts {
// Expose format is portNumber[/protocol]
splitExpose := strings.SplitN(imgExpose, "/", 2)
@@ -420,12 +421,20 @@ func GenExposedPorts(exposedPorts map[string]struct{}) (map[uint16]string, error
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"
+
+ // No need to validate protocol, we'll do it later.
+ newProto := "tcp"
+ if len(splitExpose) == 2 {
+ newProto = splitExpose[1]
+ }
+
+ proto := expose[uint16(num)]
+ if len(proto) > 1 {
+ proto = proto + "," + newProto
} else {
- expose[uint16(num)] = splitExpose[1]
+ proto = newProto
}
+ expose[uint16(num)] = proto
}
return expose, nil
}
diff --git a/pkg/specgen/generate/ports_bench_test.go b/pkg/specgen/generate/ports_bench_test.go
new file mode 100644
index 000000000..06f02acda
--- /dev/null
+++ b/pkg/specgen/generate/ports_bench_test.go
@@ -0,0 +1,197 @@
+package generate
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+)
+
+func benchmarkParsePortMapping(b *testing.B, ports []types.PortMapping) {
+ for n := 0; n < b.N; n++ {
+ ParsePortMapping(ports, nil)
+ }
+}
+
+func BenchmarkParsePortMappingNoPorts(b *testing.B) {
+ benchmarkParsePortMapping(b, nil)
+}
+
+func BenchmarkParsePortMapping1(b *testing.B) {
+ benchmarkParsePortMapping(b, []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ })
+}
+
+func BenchmarkParsePortMapping100(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 100)
+ for i := uint16(8080); i < 8180; i++ {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMapping1k(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 1000)
+ for i := uint16(8080); i < 9080; i++ {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMapping10k(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 30000)
+ for i := uint16(8080); i < 18080; i++ {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMapping1m(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 1000000)
+ for j := 0; j < 20; j++ {
+ for i := uint16(1); i <= 50000; i++ {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ HostIP: fmt.Sprintf("192.168.1.%d", j),
+ })
+ }
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMappingReverse100(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 100)
+ for i := uint16(8180); i > 8080; i-- {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMappingReverse1k(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 1000)
+ for i := uint16(9080); i > 8080; i-- {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMappingReverse10k(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 30000)
+ for i := uint16(18080); i > 8080; i-- {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMappingReverse1m(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 1000000)
+ for j := 0; j < 20; j++ {
+ for i := uint16(50000); i > 0; i-- {
+ ports = append(ports, types.PortMapping{
+ HostPort: i,
+ ContainerPort: i,
+ Protocol: "tcp",
+ HostIP: fmt.Sprintf("192.168.1.%d", j),
+ })
+ }
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
+
+func BenchmarkParsePortMappingRange1(b *testing.B) {
+ benchmarkParsePortMapping(b, []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ })
+}
+
+func BenchmarkParsePortMappingRange100(b *testing.B) {
+ benchmarkParsePortMapping(b, []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 100,
+ },
+ })
+}
+
+func BenchmarkParsePortMappingRange1k(b *testing.B) {
+ benchmarkParsePortMapping(b, []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1000,
+ },
+ })
+}
+
+func BenchmarkParsePortMappingRange10k(b *testing.B) {
+ benchmarkParsePortMapping(b, []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10000,
+ },
+ })
+}
+
+func BenchmarkParsePortMappingRange1m(b *testing.B) {
+ ports := make([]types.PortMapping, 0, 1000000)
+ for j := 0; j < 20; j++ {
+ ports = append(ports, types.PortMapping{
+ HostPort: 1,
+ ContainerPort: 1,
+ Protocol: "tcp",
+ Range: 50000,
+ HostIP: fmt.Sprintf("192.168.1.%d", j),
+ })
+ }
+ b.ResetTimer()
+ benchmarkParsePortMapping(b, ports)
+}
diff --git a/pkg/specgen/generate/ports_test.go b/pkg/specgen/generate/ports_test.go
new file mode 100644
index 000000000..20d5d0166
--- /dev/null
+++ b/pkg/specgen/generate/ports_test.go
@@ -0,0 +1,989 @@
+package generate
+
+import (
+ "testing"
+
+ "github.com/containers/podman/v3/libpod/network/types"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestParsePortMappingWithHostPort(t *testing.T) {
+ tests := []struct {
+ name string
+ arg []types.PortMapping
+ arg2 map[uint16][]string
+ want []types.PortMapping
+ }{
+ {
+ name: "no ports",
+ arg: nil,
+ want: nil,
+ },
+ {
+ name: "one tcp port",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one tcp port no proto",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one udp port",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "udp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "udp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one sctp port",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "sctp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "sctp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one port two protocols",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp,udp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "udp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one port three protocols",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp,udp,sctp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "udp",
+ Range: 1,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "sctp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one port with range 1",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one port with range 5",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 5,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 5,
+ },
+ },
+ },
+ {
+ name: "two ports joined",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ },
+ },
+ {
+ name: "two ports joined with range",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ },
+ },
+ {
+ name: "two ports with no overlapping range",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ {
+ HostPort: 9090,
+ ContainerPort: 9090,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 9090,
+ ContainerPort: 9090,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ },
+ },
+ {
+ name: "four ports with two overlapping ranges",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ {
+ HostPort: 8085,
+ ContainerPort: 85,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ {
+ HostPort: 100,
+ ContainerPort: 5,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 101,
+ ContainerPort: 6,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 15,
+ },
+ {
+ HostPort: 100,
+ ContainerPort: 5,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ },
+ },
+ {
+ name: "two overlapping ranges",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ {
+ HostPort: 8085,
+ ContainerPort: 85,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ },
+ },
+ {
+ name: "four overlapping ranges",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ {
+ HostPort: 8085,
+ ContainerPort: 85,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ {
+ HostPort: 8090,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ Range: 7,
+ },
+ {
+ HostPort: 8095,
+ ContainerPort: 95,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 17,
+ },
+ },
+ },
+ {
+ name: "one port range overlaps 5 ports",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Range: 20,
+ },
+ {
+ HostPort: 8085,
+ ContainerPort: 85,
+ Range: 2,
+ },
+ {
+ HostPort: 8090,
+ ContainerPort: 90,
+ },
+ {
+ HostPort: 8095,
+ ContainerPort: 95,
+ },
+ {
+ HostPort: 8096,
+ ContainerPort: 96,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 20,
+ },
+ },
+ },
+ {
+ name: "different host ip same port",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ HostIP: "192.168.1.1",
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ HostIP: "192.168.2.1",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ HostIP: "192.168.1.1",
+ Range: 1,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ HostIP: "192.168.2.1",
+ Range: 1,
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ParsePortMapping(tt.arg, tt.arg2)
+ assert.NoError(t, err, "error is not nil")
+ // use ElementsMatch instead of Equal because the order is not consistent
+ assert.ElementsMatch(t, tt.want, got, "got unexpected port mapping")
+ })
+ }
+}
+
+func TestParsePortMappingWithoutHostPort(t *testing.T) {
+ tests := []struct {
+ name string
+ arg []types.PortMapping
+ arg2 map[uint16][]string
+ want []types.PortMapping
+ }{
+ {
+ name: "one tcp port",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "one port with two protocols",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp,udp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "udp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "same port twice",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "neighbor ports are not joined",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "overlapping range ports are joined",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 2,
+ },
+ },
+ },
+ {
+ name: "four overlapping range ports are joined",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 3,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 81,
+ Protocol: "tcp",
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 82,
+ Protocol: "tcp",
+ Range: 10,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ Range: 5,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 15,
+ },
+ },
+ },
+ {
+ name: "expose one tcp port",
+ arg2: map[uint16][]string{
+ 8080: {"tcp"},
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "expose already defined port",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ },
+ },
+ arg2: map[uint16][]string{
+ 8080: {"tcp"},
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ },
+ {
+ name: "expose different proto",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ },
+ },
+ arg2: map[uint16][]string{
+ 8080: {"udp"},
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 8080,
+ Protocol: "udp",
+ Range: 1,
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ParsePortMapping(tt.arg, tt.arg2)
+ assert.NoError(t, err, "error is not nil")
+
+ // because we always get random host ports when it is set to 0 we cannot check that exactly
+ // check if it is not 0 and set to to 0 afterwards
+ for i := range got {
+ assert.Greater(t, got[i].HostPort, uint16(0), "host port is zero")
+ got[i].HostPort = 0
+ }
+
+ // use ElementsMatch instead of Equal because the order is not consistent
+ assert.ElementsMatch(t, tt.want, got, "got unexpected port mapping")
+ })
+ }
+}
+
+func TestParsePortMappingMixedHostPort(t *testing.T) {
+ tests := []struct {
+ name string
+ arg []types.PortMapping
+ want []types.PortMapping
+ resetHostPorts []int
+ }{
+ {
+ name: "two ports one without a hostport set",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ resetHostPorts: []int{1},
+ },
+ {
+ name: "two ports one without a hostport set, inverted order",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ resetHostPorts: []int{1},
+ },
+ {
+ name: "three ports without host ports, one with a hostport set, , inverted order",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 85,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 90,
+ },
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 85,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ resetHostPorts: []int{1, 2, 3},
+ },
+ {
+ name: "three ports without host ports, one with a hostport set",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 90,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 85,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ },
+ },
+ want: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 8080,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 85,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 90,
+ Protocol: "tcp",
+ Range: 1,
+ },
+ },
+ resetHostPorts: []int{1, 2, 3},
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := ParsePortMapping(tt.arg, nil)
+ assert.NoError(t, err, "error is not nil")
+
+ // because we always get random host ports when it is set to 0 we cannot check that exactly
+ // use resetHostPorts to know which port element is 0
+ for _, num := range tt.resetHostPorts {
+ assert.Greater(t, got[num].HostPort, uint16(0), "host port is zero")
+ got[num].HostPort = 0
+ }
+
+ assert.Equal(t, tt.want, got, "got unexpected port mapping")
+ })
+ }
+}
+
+func TestParsePortMappingError(t *testing.T) {
+ tests := []struct {
+ name string
+ arg []types.PortMapping
+ err string
+ }{
+ {
+ name: "container port is 0",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 0,
+ Protocol: "tcp",
+ },
+ },
+ err: "container port number must be non-0",
+ },
+ {
+ name: "container port range exceeds max",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 65000,
+ Protocol: "tcp",
+ Range: 10000,
+ },
+ },
+ err: "container port range exceeds maximum allowable port number",
+ },
+ {
+ name: "host port range exceeds max",
+ arg: []types.PortMapping{
+ {
+ HostPort: 60000,
+ ContainerPort: 1,
+ Protocol: "tcp",
+ Range: 10000,
+ },
+ },
+ err: "host port range exceeds maximum allowable port number",
+ },
+ {
+ name: "invalid protocol",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "1",
+ },
+ },
+ err: "unrecognized protocol \"1\" in port mapping",
+ },
+ {
+ name: "invalid protocol 2",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Protocol: "udp,u",
+ },
+ },
+ err: "unrecognized protocol \"u\" in port mapping",
+ },
+ {
+ name: "invalid ip address",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ HostIP: "blah",
+ },
+ },
+ err: "invalid IP address \"blah\" in port mapping",
+ },
+ {
+ name: "invalid overalpping range",
+ arg: []types.PortMapping{
+ {
+ HostPort: 8080,
+ ContainerPort: 80,
+ Range: 5,
+ },
+ {
+ HostPort: 8081,
+ ContainerPort: 60,
+ },
+ },
+ err: "conflicting port mappings for host port 8081 (protocol tcp)",
+ },
+ {
+ name: "big port range with host port zero does not fit",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 1,
+ Range: 65535,
+ },
+ },
+ err: "failed to find an open port to expose container port 1 with range 65535 on the host",
+ },
+ {
+ name: "big port range with host port zero does not fit",
+ arg: []types.PortMapping{
+ {
+ HostPort: 0,
+ ContainerPort: 80,
+ Range: 1,
+ },
+ {
+ HostPort: 0,
+ ContainerPort: 1000,
+ Range: 64535,
+ },
+ },
+ err: "failed to find an open port to expose container port 1000 with range 64535 on the host",
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := ParsePortMapping(tt.arg, nil)
+ assert.EqualError(t, err, tt.err, "error does not match")
+ })
+ }
+}
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index 3fde1a1b4..30248a886 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -214,9 +214,6 @@ func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGe
}
for volume := range inspect.Config.Volumes {
logrus.Debugf("Image has volume at %q", volume)
- if err = parse.ValidateVolumeCtrDir(volume); err != nil {
- return nil, nil, err
- }
cleanDest := filepath.Clean(volume)
switch mode {
case "", "anonymous":
diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go
index 7713ea26c..948fb990c 100644
--- a/pkg/specgen/podspecgen.go
+++ b/pkg/specgen/podspecgen.go
@@ -99,7 +99,8 @@ type PodNetworkConfig struct {
// Only available if NetNS is set to Bridge (the default for root).
// As such, conflicts with NoInfra=true by proxy.
// Optional.
- StaticMAC *net.HardwareAddr `json:"static_mac,omitempty"`
+ // swagger:strfmt string
+ StaticMAC *types.HardwareAddr `json:"static_mac,omitempty"`
// PortMappings is a set of ports to map into the infra container.
// As, by default, containers share their network with the infra
// container, this will forward the ports to the entire pod.
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 79185db04..d777287d7 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -272,6 +272,13 @@ type ContainerStorageConfig struct {
// 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"`
+ // StorageOpts is the container's storage options
+ // Optional.
+ StorageOpts map[string]string `json:"storage_opts,omitempty"`
// RootfsPropagation is the rootfs propagation mode for the container.
// If not set, the default of rslave will be used.
// Optional.
@@ -398,7 +405,8 @@ type ContainerNetworkConfig struct {
// StaticMAC is a static MAC address to set in the container.
// Only available if NetNS is set to bridge.
// Optional.
- StaticMAC *net.HardwareAddr `json:"static_mac,omitempty"`
+ // swagger:strfmt string
+ StaticMAC *nettypes.HardwareAddr `json:"static_mac,omitempty"`
// PortBindings is a set of ports to map into the container.
// Only available if NetNS is set to bridge or slirp.
// Optional.
diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go
index 8007e5d8e..04d3add32 100644
--- a/pkg/specgenutil/specgen.go
+++ b/pkg/specgenutil/specgen.go
@@ -11,6 +11,7 @@ import (
"github.com/containers/image/v5/manifest"
"github.com/containers/podman/v3/cmd/podman/parse"
"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"
envLib "github.com/containers/podman/v3/pkg/env"
@@ -171,7 +172,7 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *entities.ContainerCreateOption
memory.Kernel = &mk
hasLimits = true
}
- if c.MemorySwappiness > 0 {
+ if c.MemorySwappiness >= 0 {
swappiness := uint64(c.MemorySwappiness)
memory.Swappiness = &swappiness
hasLimits = true
@@ -394,6 +395,17 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
}
s.Annotations = annotations
+ if len(c.StorageOpts) > 0 {
+ opts := make(map[string]string, len(c.StorageOpts))
+ for _, opt := range c.StorageOpts {
+ split := strings.SplitN(opt, "=", 2)
+ if len(split) != 2 {
+ return errors.Errorf("storage-opt must be formatted KEY=VALUE")
+ }
+ opts[split[0]] = split[1]
+ }
+ s.StorageOpts = opts
+ }
s.WorkDir = c.Workdir
if c.Entrypoint != nil {
entrypoint := []string{}
@@ -446,7 +458,8 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.DNSSearch = c.Net.DNSSearch
s.DNSOptions = c.Net.DNSOptions
s.StaticIP = c.Net.StaticIP
- s.StaticMAC = c.Net.StaticMAC
+ // type cast to types.HardwareAddr
+ s.StaticMAC = (*types.HardwareAddr)(c.Net.StaticMAC)
s.NetworkOptions = c.Net.NetworkOptions
s.UseImageHosts = c.Net.NoHosts
}
diff --git a/pkg/specgenutil/volumes.go b/pkg/specgenutil/volumes.go
index 3ce96164f..184bfadf8 100644
--- a/pkg/specgenutil/volumes.go
+++ b/pkg/specgenutil/volumes.go
@@ -360,7 +360,7 @@ func getBindMount(args []string) (spec.Mount, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -460,7 +460,7 @@ func getTmpfsMount(args []string) (spec.Mount, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -483,6 +483,8 @@ func getDevptsMount(args []string) (spec.Mount, error) {
for _, val := range args {
kv := strings.SplitN(val, "=", 2)
switch kv[0] {
+ case "uid", "gid", "mode", "ptxmode", "newinstance", "max":
+ newMount.Options = append(newMount.Options, val)
case "target", "dst", "destination":
if len(kv) == 1 {
return newMount, errors.Wrapf(optionArgError, kv[0])
@@ -493,7 +495,7 @@ func getDevptsMount(args []string) (spec.Mount, error) {
newMount.Destination = filepath.Clean(kv[1])
setDest = true
default:
- return newMount, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return newMount, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -573,7 +575,7 @@ func getNamedVolume(args []string) (*specgen.NamedVolume, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return nil, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
@@ -624,7 +626,7 @@ func getImageVolume(args []string) (*specgen.ImageVolume, error) {
// Since Docker ignores this option so shall we.
continue
default:
- return nil, errors.Wrapf(util.ErrBadMntOption, kv[0])
+ return nil, errors.Wrapf(util.ErrBadMntOption, "%s", kv[0])
}
}
diff --git a/pkg/systemd/dbus.go b/pkg/systemd/dbus.go
index c49f537b6..04aaa117a 100644
--- a/pkg/systemd/dbus.go
+++ b/pkg/systemd/dbus.go
@@ -84,7 +84,7 @@ func IsSystemdSessionValid(uid int) bool {
return true
}
-// GetDbusConnection returns an user connection to D-BUS
+// GetDbusConnection returns a user connection to D-BUS
func GetLogindConnection(uid int) (*godbus.Conn, error) {
return dbusAuthConnectionLogind(uid)
}
diff --git a/test/apiv2/10-images.at b/test/apiv2/10-images.at
index d3fde9f9d..b7bcaf81d 100644
--- a/test/apiv2/10-images.at
+++ b/test/apiv2/10-images.at
@@ -218,6 +218,9 @@ if ! grep -q '400 Bad Request' "${TMPD}/headers.txt"; then
BUILD_TEST_ERROR="1"
fi
+t POST libpod/images/prune 200
+t POST libpod/images/prune 200 length=0 []
+
cleanBuildTest
if [[ "${BUILD_TEST_ERROR}" ]]; then
exit 1
diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at
index 985b26411..f45e85f61 100644
--- a/test/apiv2/40-pods.at
+++ b/test/apiv2/40-pods.at
@@ -110,11 +110,11 @@ t GET libpod/pods/fakename/top 404 \
.cause="no such pod"
t GET libpod/pods/foo/top 200 \
- .Processes[0][-1]="/pause " \
+ .Processes[0][-1]="/pause" \
.Titles[-1]="COMMAND"
t GET libpod/pods/foo/top?ps_args=args,pid 200 \
- .Processes[0][0]="/pause " \
+ .Processes[0][0]="/pause" \
.Processes[0][1]="1" \
.Titles[0]="COMMAND" \
.Titles[1]="PID" \
diff --git a/test/apiv2/python/rest_api/test_v2_0_0_container.py b/test/apiv2/python/rest_api/test_v2_0_0_container.py
index 853e9da88..101044bbb 100644
--- a/test/apiv2/python/rest_api/test_v2_0_0_container.py
+++ b/test/apiv2/python/rest_api/test_v2_0_0_container.py
@@ -1,8 +1,11 @@
+import multiprocessing
+import queue
import random
+import threading
import unittest
-import json
import requests
+import time
from dateutil.parser import parse
from .fixtures import APITestCase
@@ -16,7 +19,10 @@ class ContainerTestCase(APITestCase):
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")
+ 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)
@@ -33,18 +39,18 @@ class ContainerTestCase(APITestCase):
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
- }
+ 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)
@@ -67,7 +73,7 @@ class ContainerTestCase(APITestCase):
self.assertEqual(r.status_code, 200, r.text)
self.assertId(r.content)
out = r.json()
- hc = out["Config"]["Healthcheck"]["Test"]
+ hc = out["Config"]["Healthcheck"]["Test"]
self.assertListEqual(["CMD", "pidof", "top"], hc)
r = requests.post(self.podman_url + f"/v1.40/containers/{container_id}/start")
@@ -84,7 +90,9 @@ class ContainerTestCase(APITestCase):
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")))
+ 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)
@@ -136,9 +144,15 @@ class ContainerTestCase(APITestCase):
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")
+ 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")
+ 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):
@@ -257,6 +271,63 @@ class ContainerTestCase(APITestCase):
r = requests.delete(self.podman_url + f"/v1.40/containers/{container_id}")
self.assertEqual(r.status_code, 204, r.text)
+ def test_top_no_stream(self):
+ uri = self.uri(self.resolve_container("/containers/{}/top"))
+ q = queue.Queue()
+
+ def _impl(fifo):
+ fifo.put(requests.get(uri, params={"stream": False}, timeout=2))
+
+ top = threading.Thread(target=_impl, args=(q,))
+ top.start()
+ time.sleep(2)
+ self.assertFalse(top.is_alive(), f"GET {uri} failed to return in 2s")
+
+ qr = q.get(False)
+ self.assertEqual(qr.status_code, 200, qr.text)
+
+ qr.close()
+ top.join()
+
+ def test_top_stream(self):
+ uri = self.uri(self.resolve_container("/containers/{}/top"))
+ q = queue.Queue()
+
+ stop_thread = False
+
+ def _impl(fifo, stop):
+ try:
+ with requests.get(uri, params={"stream": True, "delay": 1}, stream=True) as r:
+ r.raise_for_status()
+ fifo.put(r)
+ for buf in r.iter_lines(chunk_size=None):
+ if stop():
+ break
+ fifo.put(buf)
+ except Exception:
+ pass
+
+ top = threading.Thread(target=_impl, args=(q, (lambda: stop_thread)))
+ top.start()
+ time.sleep(4)
+ self.assertTrue(top.is_alive(), f"GET {uri} exited too soon")
+ stop_thread = True
+
+ for _ in range(10):
+ try:
+ qr = q.get_nowait()
+ if qr is not None:
+ self.assertEqual(qr.status_code, 200)
+ qr.close()
+ break
+ except queue.Empty:
+ pass
+ finally:
+ time.sleep(1)
+ else:
+ self.fail("Server failed to respond in 10s")
+ top.join()
+
if __name__ == "__main__":
unittest.main()
diff --git a/test/apiv2/python/rest_api/test_v2_0_0_volume.py b/test/apiv2/python/rest_api/test_v2_0_0_volume.py
index f5231e17c..2156318b0 100644
--- a/test/apiv2/python/rest_api/test_v2_0_0_volume.py
+++ b/test/apiv2/python/rest_api/test_v2_0_0_volume.py
@@ -7,7 +7,7 @@ from .fixtures import APITestCase
class VolumeTestCase(APITestCase):
- def test_volume(self):
+ def test_volume_crud(self):
name = f"Volume_{random.getrandbits(160):x}"
ls = requests.get(self.podman_url + "/v1.40/volumes")
@@ -70,6 +70,71 @@ class VolumeTestCase(APITestCase):
self.assertIn(name, payload["VolumesDeleted"])
self.assertGreater(payload["SpaceReclaimed"], 0)
+ def test_volume_label(self):
+ name = f"Volume_{random.getrandbits(160):x}"
+ expected = {
+ "Production": "False",
+ "Database": "Foxbase",
+ }
+
+ create = requests.post(
+ self.podman_url + "/v4.0.0/libpod/volumes/create",
+ json={"name": name, "label": expected},
+ )
+ self.assertEqual(create.status_code, 201, create.text)
+
+ inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json")
+ self.assertEqual(inspect.status_code, 200, inspect.text)
+
+ volume = inspect.json()
+ self.assertIn("Labels", volume)
+ self.assertNotIn("Label", volume)
+ self.assertDictEqual(expected, volume["Labels"])
+
+ def test_volume_labels(self):
+ name = f"Volume_{random.getrandbits(160):x}"
+ expected = {
+ "Production": "False",
+ "Database": "Foxbase",
+ }
+
+ create = requests.post(
+ self.podman_url + "/v4.0.0/libpod/volumes/create",
+ json={"name": name, "labels": expected},
+ )
+ self.assertEqual(create.status_code, 201, create.text)
+
+ inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json")
+ self.assertEqual(inspect.status_code, 200, inspect.text)
+
+ volume = inspect.json()
+ self.assertIn("Labels", volume)
+ self.assertDictEqual(expected, volume["Labels"])
+
+ def test_volume_label_override(self):
+ name = f"Volume_{random.getrandbits(160):x}"
+ create = requests.post(
+ self.podman_url + "/v4.0.0/libpod/volumes/create",
+ json={
+ "Name": name,
+ "Label": {
+ "Database": "dbase",
+ },
+ "Labels": {
+ "Database": "sqlserver",
+ },
+ },
+ )
+ self.assertEqual(create.status_code, 201, create.text)
+
+ inspect = requests.get(self.podman_url + f"/v4.0.0/libpod/volumes/{name}/json")
+ self.assertEqual(inspect.status_code, 200, inspect.text)
+
+ volume = inspect.json()
+ self.assertIn("Labels", volume)
+ self.assertNotIn("Label", volume)
+ self.assertDictEqual({"Database": "sqlserver"}, volume["Labels"])
+
if __name__ == "__main__":
unittest.main()
diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go
index 770a7c7bd..6b294802d 100644
--- a/test/e2e/checkpoint_test.go
+++ b/test/e2e/checkpoint_test.go
@@ -1,15 +1,19 @@
package integration
import (
+ "encoding/json"
"fmt"
"net"
"os"
"os/exec"
+ "path/filepath"
"strings"
"time"
+ "github.com/checkpoint-restore/go-criu/v5/stats"
"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/test/utils"
"github.com/containers/podman/v3/utils"
. "github.com/onsi/ginkgo"
@@ -1156,4 +1160,183 @@ var _ = Describe("Podman checkpoint", func() {
os.Remove(fileName)
})
}
+
+ It("podman checkpoint container with export (migration) and --ipc host", func() {
+ localRunString := getRunString([]string{"--rm", "--ipc", "host", 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.gz"
+
+ 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))
+
+ 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"))
+
+ result = podmanTest.Podman([]string{"rm", "-t", "0", "-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)
+ })
+
+ It("podman checkpoint container with export and statistics", 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.gz"
+
+ 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))
+
+ // Extract checkpoint archive
+ destinationDirectory, err := CreateTempDirInTempDir()
+ Expect(err).ShouldNot(HaveOccurred())
+
+ tarsession := SystemExec(
+ "tar",
+ []string{
+ "xf",
+ fileName,
+ "-C",
+ destinationDirectory,
+ },
+ )
+ Expect(tarsession).Should(Exit(0))
+
+ _, err = os.Stat(filepath.Join(destinationDirectory, stats.StatsDump))
+ Expect(err).ShouldNot(HaveOccurred())
+
+ Expect(os.RemoveAll(destinationDirectory)).To(BeNil())
+
+ // Remove exported checkpoint
+ os.Remove(fileName)
+ })
+
+ It("podman checkpoint and restore containers with --print-stats", func() {
+ session1 := podmanTest.Podman(getRunString([]string{redis}))
+ session1.WaitWithDefaultTimeout()
+ Expect(session1).Should(Exit(0))
+
+ session2 := podmanTest.Podman(getRunString([]string{redis, "top"}))
+ session2.WaitWithDefaultTimeout()
+ Expect(session2).Should(Exit(0))
+
+ result := podmanTest.Podman([]string{
+ "container",
+ "checkpoint",
+ "-a",
+ "--print-stats",
+ })
+ result.WaitWithDefaultTimeout()
+
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+
+ type checkpointStatistics struct {
+ PodmanDuration int64 `json:"podman_checkpoint_duration"`
+ ContainerStatistics []*entities.CheckpointReport `json:"container_statistics"`
+ }
+
+ cS := new(checkpointStatistics)
+ err := json.Unmarshal([]byte(result.OutputToString()), cS)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ Expect(len(cS.ContainerStatistics)).To(Equal(2))
+ Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[0].RuntimeDuration))
+ Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[1].RuntimeDuration))
+ Expect(cS.ContainerStatistics[0].RuntimeDuration).To(
+ BeNumerically(">", cS.ContainerStatistics[0].CRIUStatistics.FrozenTime),
+ )
+ Expect(cS.ContainerStatistics[1].RuntimeDuration).To(
+ BeNumerically(">", cS.ContainerStatistics[1].CRIUStatistics.FrozenTime),
+ )
+
+ ps := podmanTest.Podman([]string{
+ "ps",
+ "-q",
+ "--no-trunc",
+ })
+ ps.WaitWithDefaultTimeout()
+ 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",
+ "--print-stats",
+ })
+ result.WaitWithDefaultTimeout()
+
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(2))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring("Up"))
+ Expect(podmanTest.GetContainerStatus()).To(Not(ContainSubstring("Exited")))
+
+ type restoreStatistics struct {
+ PodmanDuration int64 `json:"podman_restore_duration"`
+ ContainerStatistics []*entities.RestoreReport `json:"container_statistics"`
+ }
+
+ rS := new(restoreStatistics)
+ err = json.Unmarshal([]byte(result.OutputToString()), rS)
+ Expect(err).ShouldNot(HaveOccurred())
+
+ Expect(len(cS.ContainerStatistics)).To(Equal(2))
+ Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[0].RuntimeDuration))
+ Expect(cS.PodmanDuration).To(BeNumerically(">", cS.ContainerStatistics[1].RuntimeDuration))
+ Expect(cS.ContainerStatistics[0].RuntimeDuration).To(
+ BeNumerically(">", cS.ContainerStatistics[0].CRIUStatistics.RestoreTime),
+ )
+ Expect(cS.ContainerStatistics[1].RuntimeDuration).To(
+ BeNumerically(">", cS.ContainerStatistics[1].CRIUStatistics.RestoreTime),
+ )
+
+ result = podmanTest.Podman([]string{
+ "rm",
+ "-t",
+ "0",
+ "-fa",
+ })
+ result.WaitWithDefaultTimeout()
+ Expect(result).Should(Exit(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
})
diff --git a/test/e2e/common_test.go b/test/e2e/common_test.go
index 7228682f3..200faae2d 100644
--- a/test/e2e/common_test.go
+++ b/test/e2e/common_test.go
@@ -208,9 +208,7 @@ var _ = SynchronizedAfterSuite(func() {},
// PodmanTestCreate creates a PodmanTestIntegration instance for the tests
func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
- var (
- podmanRemoteBinary string
- )
+ var podmanRemoteBinary string
host := GetHostDistributionInfo()
cwd, _ := os.Getwd()
@@ -220,12 +218,11 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
podmanBinary = os.Getenv("PODMAN_BINARY")
}
- if remote {
- podmanRemoteBinary = filepath.Join(cwd, "../../bin/podman-remote")
- if os.Getenv("PODMAN_REMOTE_BINARY") != "" {
- podmanRemoteBinary = os.Getenv("PODMAN_REMOTE_BINARY")
- }
+ podmanRemoteBinary = filepath.Join(cwd, "../../bin/podman-remote")
+ if os.Getenv("PODMAN_REMOTE_BINARY") != "" {
+ podmanRemoteBinary = os.Getenv("PODMAN_REMOTE_BINARY")
}
+
conmonBinary := filepath.Join("/usr/libexec/podman/conmon")
altConmonBinary := "/usr/bin/conmon"
if _, err := os.Stat(conmonBinary); os.IsNotExist(err) {
@@ -271,12 +268,13 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
p := &PodmanTestIntegration{
PodmanTest: PodmanTest{
- PodmanBinary: podmanBinary,
- ArtifactPath: ARTIFACT_DIR,
- TempDir: tempDir,
- RemoteTest: remote,
- ImageCacheFS: storageFs,
- ImageCacheDir: ImageCacheDir,
+ PodmanBinary: podmanBinary,
+ RemotePodmanBinary: podmanRemoteBinary,
+ ArtifactPath: ARTIFACT_DIR,
+ TempDir: tempDir,
+ RemoteTest: remote,
+ ImageCacheFS: storageFs,
+ ImageCacheDir: ImageCacheDir,
},
ConmonBinary: conmonBinary,
CrioRoot: filepath.Join(tempDir, "crio"),
@@ -289,8 +287,8 @@ func PodmanTestCreateUtil(tempDir string, remote bool) *PodmanTestIntegration {
CgroupManager: cgroupManager,
Host: host,
}
+
if remote {
- p.PodmanTest.RemotePodmanBinary = podmanRemoteBinary
uuid := stringid.GenerateNonCryptoID()
if !rootless.IsRootless() {
p.RemoteSocket = fmt.Sprintf("unix:/run/podman/podman-%s.sock", uuid)
@@ -632,6 +630,19 @@ func SkipIfNotRootless(reason string) {
}
}
+func SkipIfSystemdNotRunning(reason string) {
+ checkReason(reason)
+
+ cmd := exec.Command("systemctl", "list-units")
+ err := cmd.Run()
+ if err != nil {
+ if _, ok := err.(*exec.Error); ok {
+ ginkgo.Skip("[notSystemd]: not running " + reason)
+ }
+ Expect(err).ToNot(HaveOccurred())
+ }
+}
+
func SkipIfNotSystemd(manager, reason string) {
checkReason(reason)
if manager != "systemd" {
@@ -683,9 +694,44 @@ func SkipIfContainerized(reason string) {
}
}
+func SkipIfRemote(reason string) {
+ checkReason(reason)
+ if !IsRemote() {
+ return
+ }
+ ginkgo.Skip("[remote]: " + reason)
+}
+
+// SkipIfInContainer skips a test if the test is run inside a container
+func SkipIfInContainer(reason string) {
+ checkReason(reason)
+ if os.Getenv("TEST_ENVIRON") == "container" {
+ Skip("[container]: " + reason)
+ }
+}
+
+// SkipIfNotActive skips a test if the given systemd unit is not active
+func SkipIfNotActive(unit string, reason string) {
+ checkReason(reason)
+
+ var buffer bytes.Buffer
+ cmd := exec.Command("systemctl", "is-active", unit)
+ cmd.Stdout = &buffer
+ err := cmd.Start()
+ Expect(err).ToNot(HaveOccurred())
+
+ err = cmd.Wait()
+ Expect(err).ToNot(HaveOccurred())
+
+ Expect(err).ToNot(HaveOccurred())
+ if strings.TrimSpace(buffer.String()) != "active" {
+ Skip(fmt.Sprintf("[systemd]: unit %s is not active: %s", unit, reason))
+ }
+}
+
// 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}
}
diff --git a/test/e2e/config/containers-netns.conf b/test/e2e/config/containers-netns.conf
new file mode 100644
index 000000000..3f796f25d
--- /dev/null
+++ b/test/e2e/config/containers-netns.conf
@@ -0,0 +1,3 @@
+[containers]
+
+netns = "host"
diff --git a/test/e2e/config/containers-netns2.conf b/test/e2e/config/containers-netns2.conf
new file mode 100644
index 000000000..1ffd100f5
--- /dev/null
+++ b/test/e2e/config/containers-netns2.conf
@@ -0,0 +1,3 @@
+[containers]
+
+netns = "bridge"
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index 69941494b..cd382eba9 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -67,6 +67,10 @@ var _ = Describe("Podman generate kube", func() {
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 {
@@ -103,6 +107,7 @@ var _ = Describe("Podman generate kube", func() {
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() {
@@ -1079,7 +1084,7 @@ USER test1`
top1.WaitWithDefaultTimeout()
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).Should(Exit(0))
@@ -1090,6 +1095,8 @@ USER test1`
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 --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go
index b2666c789..c9a6f926f 100644
--- a/test/e2e/healthcheck_run_test.go
+++ b/test/e2e/healthcheck_run_test.go
@@ -52,6 +52,18 @@ var _ = Describe("Podman healthcheck run", func() {
Expect(hc).Should(Exit(125))
})
+ It("podman healthcheck from image's config (not container config)", func() {
+ // Regression test for #12226: a health check may be defined in
+ // the container or the container-config of an image.
+ session := podmanTest.Podman([]string{"create", "--name", "hc", "quay.io/libpod/healthcheck:config-only", "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ hc := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.Config.Healthcheck}}", "hc"})
+ hc.WaitWithDefaultTimeout()
+ Expect(hc).Should(Exit(0))
+ Expect(hc.OutputToString()).To(Equal("{[CMD-SHELL curl -f http://localhost/ || exit 1] 0s 5m0s 3s 0}"))
+ })
+
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()
diff --git a/test/e2e/image_scp_test.go b/test/e2e/image_scp_test.go
index 9fd8d7e27..3e7e8da48 100644
--- a/test/e2e/image_scp_test.go
+++ b/test/e2e/image_scp_test.go
@@ -22,12 +22,14 @@ var _ = Describe("podman image scp", func() {
)
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)
@@ -38,6 +40,7 @@ var _ = Describe("podman image scp", func() {
AfterEach(func() {
podmanTest.Cleanup()
+
os.Remove(os.Getenv("CONTAINERS_CONF"))
if ConfPath.IsSet {
os.Setenv("CONTAINERS_CONF", ConfPath.Value)
@@ -58,6 +61,29 @@ var _ = Describe("podman image scp", func() {
Expect(scp).To(Exit(0))
})
+ It("podman image scp root to rootless transfer", func() {
+ SkipIfNotRootless("this is a rootless only test, transfering from root to rootless using PodmanAsUser")
+ if IsRemote() {
+ Skip("this test is only for non-remote")
+ }
+ env := os.Environ()
+ img := podmanTest.PodmanAsUser([]string{"image", "pull", ALPINE}, 0, 0, "", env) // pull image to root
+ img.WaitWithDefaultTimeout()
+ Expect(img).To(Exit(0))
+ scp := podmanTest.PodmanAsUser([]string{"image", "scp", "root@localhost::" + ALPINE, "1000:1000@localhost::"}, 0, 0, "", env) //transfer from root to rootless (us)
+ scp.WaitWithDefaultTimeout()
+ Expect(scp).To(Exit(0))
+
+ list := podmanTest.Podman([]string{"image", "list"}) // our image should now contain alpine loaded in from root
+ list.WaitWithDefaultTimeout()
+ Expect(list).To(Exit(0))
+ Expect(list.LineInOutputStartsWith("quay.io/libpod/alpine")).To(BeTrue())
+
+ scp = podmanTest.PodmanAsUser([]string{"image", "scp", "root@localhost::" + ALPINE}, 0, 0, "", env) //transfer from root to rootless (us)
+ scp.WaitWithDefaultTimeout()
+ Expect(scp).To(Exit(0))
+ })
+
It("podman image scp bogus image", func() {
if IsRemote() {
Skip("this test is only for non-remote")
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index 3115c246f..1fa29daa1 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -16,20 +16,12 @@ import (
"time"
"github.com/containers/podman/v3/pkg/rootless"
- "github.com/onsi/ginkgo"
)
func IsRemote() bool {
return true
}
-func SkipIfRemote(reason string) {
- if len(reason) < 5 {
- panic("SkipIfRemote must specify a reason to skip")
- }
- ginkgo.Skip("[remote]: " + reason)
-}
-
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
var remoteArgs = []string{"--remote", "--url", p.RemoteSocket}
@@ -38,11 +30,25 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration
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 --git a/test/e2e/libpod_suite_test.go b/test/e2e/libpod_suite_test.go
index cc03ccc96..001a869b1 100644
--- a/test/e2e/libpod_suite_test.go
+++ b/test/e2e/libpod_suite_test.go
@@ -8,24 +8,33 @@ import (
"os"
"path/filepath"
"strings"
+
+ "github.com/containers/podman/v3/pkg/rootless"
)
func IsRemote() bool {
return false
}
-func SkipIfRemote(string) {
-}
-
// Podman is the exec call to podman on the filesystem
func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration {
podmanSession := p.PodmanBase(args, false, false)
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 --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 314e09b9a..3beabec4b 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -13,6 +13,19 @@ import (
. "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
@@ -38,8 +51,18 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -53,6 +76,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -65,6 +90,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -77,6 +104,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -89,6 +118,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -101,6 +132,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -113,6 +146,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -125,6 +160,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -137,6 +174,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -149,6 +188,7 @@ var _ = Describe("Podman logs", func() {
})
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()
@@ -165,13 +205,17 @@ var _ = Describe("Podman logs", func() {
})
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).Should(Exit(0))
@@ -192,6 +236,8 @@ var _ = Describe("Podman logs", func() {
})
It("podman logs on a created container should result in 0 exit code: "+log, func() {
+ skipIfJournaldInContainer()
+
session := podmanTest.Podman([]string{"create", "--log-driver", log, "-t", "--name", "log", ALPINE})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
@@ -202,6 +248,8 @@ var _ = Describe("Podman logs", func() {
})
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"})
@@ -210,6 +258,14 @@ var _ = Describe("Podman logs", func() {
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"))
@@ -233,6 +289,8 @@ var _ = Describe("Podman logs", func() {
})
It("follow output stopped container: "+log, func() {
+ skipIfJournaldInContainer()
+
containerName := "logs-f"
logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-d", ALPINE, "true"})
@@ -241,10 +299,17 @@ var _ = Describe("Podman logs", func() {
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))
@@ -266,6 +331,8 @@ var _ = Describe("Podman logs", func() {
})
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))
@@ -284,6 +351,8 @@ var _ = Describe("Podman logs", func() {
})
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 --git a/test/e2e/network_connect_disconnect_test.go b/test/e2e/network_connect_disconnect_test.go
index 6cddf9285..2205a1263 100644
--- a/test/e2e/network_connect_disconnect_test.go
+++ b/test/e2e/network_connect_disconnect_test.go
@@ -87,6 +87,7 @@ var _ = Describe("Podman network connect and disconnect", func() {
dis := podmanTest.Podman([]string{"network", "disconnect", netName, "test"})
dis.WaitWithDefaultTimeout()
Expect(dis).Should(Exit(0))
+ Expect(dis.ErrorToString()).Should(Equal(""))
inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"})
inspect.WaitWithDefaultTimeout()
@@ -183,6 +184,7 @@ var _ = Describe("Podman network connect and disconnect", func() {
connect := podmanTest.Podman([]string{"network", "connect", newNetName, "test"})
connect.WaitWithDefaultTimeout()
Expect(connect).Should(Exit(0))
+ Expect(connect.ErrorToString()).Should(Equal(""))
inspect := podmanTest.Podman([]string{"container", "inspect", "test", "--format", "{{len .NetworkSettings.Networks}}"})
inspect.WaitWithDefaultTimeout()
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 8aeba9d75..64b46756f 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -11,7 +11,6 @@ import (
"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"
@@ -1120,24 +1119,6 @@ var _ = Describe("Podman play kube", func() {
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 --no-host", func() {
err := writeYaml(checkInfraImagePodYaml, kubeYaml)
Expect(err).To(BeNil())
@@ -2421,14 +2402,19 @@ MemoryReservation: {{ .HostConfig.MemoryReservation }}`})
err := generateKubeYaml("pod", pod, kubeYaml)
Expect(err).To(BeNil())
- kube := podmanTest.Podman([]string{"play", "kube", "--log-driver", "journald", kubeYaml})
+ kube := podmanTest.Podman([]string{"play", "kube", "--log-opt=max-size=10k", "--log-driver", "journald", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(Exit(0))
- inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod), "--format", "'{{ .HostConfig.LogConfig.Type }}'"})
+ cid := getCtrNameInPod(pod)
+ inspect := podmanTest.Podman([]string{"inspect", cid, "--format", "'{{ .HostConfig.LogConfig.Type }}'"})
inspect.WaitWithDefaultTimeout()
Expect(inspect).Should(Exit(0))
Expect(inspect.OutputToString()).To(ContainSubstring("journald"))
+ inspect = podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect).To(Exit(0))
+ Expect(inspect.OutputToString()).To(Equal("10kB"))
})
It("podman play kube test only creating the containers", func() {
@@ -2800,6 +2786,34 @@ invalid kube kind
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"))
+ })
+
It("podman play kube replace", func() {
pod := getPod()
err := generateKubeYaml("pod", pod, kubeYaml)
diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go
index 34e879ed4..12aeffd1b 100644
--- a/test/e2e/pod_create_test.go
+++ b/test/e2e/pod_create_test.go
@@ -957,4 +957,22 @@ ENTRYPOINT ["sleep","99999"]
Expect(ctr3.OutputToString()).To(ContainSubstring("hello"))
})
+ 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 --git a/test/e2e/push_test.go b/test/e2e/push_test.go
index b7e8309fb..7b35acd35 100644
--- a/test/e2e/push_test.go
+++ b/test/e2e/push_test.go
@@ -146,7 +146,7 @@ var _ = Describe("Podman push", func() {
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())
@@ -163,7 +163,7 @@ var _ = Describe("Podman push", func() {
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())
}
diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go
index e2f2937ba..04952bb03 100644
--- a/test/e2e/run_memory_test.go
+++ b/test/e2e/run_memory_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"os"
"strconv"
@@ -67,13 +68,17 @@ var _ = Describe("Podman run memory", func() {
Expect(session.OutputToString()).To(Equal("41943040"))
})
- It("podman run memory-swappiness test", func() {
- 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).Should(Exit(0))
- Expect(session.OutputToString()).To(Equal("15"))
- })
+ for _, limit := range []string{"0", "15", "100"} {
+ limit := limit // Keep this value in a proper scope
+ testName := fmt.Sprintf("podman run memory-swappiness test(%s)", limit)
+ It(testName, func() {
+ SkipIfCgroupV2("memory-swappiness not supported on cgroupV2")
+ session := podmanTest.Podman([]string{"run", fmt.Sprintf("--memory-swappiness=%s", limit), ALPINE, "cat", "/sys/fs/cgroup/memory/memory.swappiness"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(Equal(limit))
+ })
+ }
It("podman run kernel-memory test", func() {
if podmanTest.Host.Distribution == "ubuntu" {
diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go
index ca242a17c..c64cfd2d5 100644
--- a/test/e2e/run_networking_test.go
+++ b/test/e2e/run_networking_test.go
@@ -357,6 +357,26 @@ var _ = Describe("Podman run networking", func() {
Expect(ncBusy).To(ExitWithError())
})
+ 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).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)
@@ -474,6 +494,23 @@ var _ = Describe("Podman run networking", func() {
Expect(containerConfig[0].NetworkSettings.Ports["80/tcp"][0].HostPort).ToNot(Equal(80))
})
+ It("podman run forward sctp protocol", func() {
+ SkipIfRootless("sctp protocol only works as root")
+ session := podmanTest.Podman([]string{"--log-level=info", "run", "--name=test", "-p", "80/sctp", "-p", "81/sctp", ALPINE})
+ session.Wait(90)
+ Expect(session).Should(Exit(0))
+ // we can only check logrus on local podman
+ if !IsRemote() {
+ // check that the info message for sctp protocol is only displayed once
+ Expect(strings.Count(session.ErrorToString(), "Port reservation for SCTP is not supported")).To(Equal(1), "`Port reservation for SCTP is not supported` is not displayed exactly one time in the logrus logs")
+ }
+ results := podmanTest.Podman([]string{"inspect", "test"})
+ results.Wait(30)
+ Expect(results).Should(Exit(0))
+ Expect(results.OutputToString()).To(ContainSubstring(`"80/sctp":`))
+ Expect(results.OutputToString()).To(ContainSubstring(`"81/sctp":`))
+ })
+
It("podman run hostname test", func() {
session := podmanTest.Podman([]string{"run", "--rm", ALPINE, "printenv", "HOSTNAME"})
session.WaitWithDefaultTimeout()
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 8502879ff..ed2d8938d 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -188,6 +188,12 @@ var _ = Describe("Podman run", func() {
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() {
@@ -1192,6 +1198,14 @@ USER mail`, BB)
Expect(session.OutputToString()).To(ContainSubstring("devpts"))
})
+ It("podman run --mount type=devpts,target=/dev/pts with uid, gid and mode", func() {
+ // runc doesn't seem to honor uid= so avoid testing it
+ session := podmanTest.Podman([]string{"run", "-t", "--mount", "type=devpts,target=/dev/pts,uid=1000,gid=1001,mode=123", fedoraMinimal, "stat", "-c%g-%a", "/dev/pts/0"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.OutputToString()).To(ContainSubstring("1001-123"))
+ })
+
It("podman run --pod automatically", func() {
session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:foobar", ALPINE, "nc", "-l", "-p", "8686"})
session.WaitWithDefaultTimeout()
@@ -1367,13 +1381,13 @@ USER mail`, BB)
}
}
- container := podmanTest.Podman([]string{"run", "--rm", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"})
+ 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.Podman([]string{"run", "--rm", "--pod", "new:split-test-pod", "--cgroups=split", ALPINE, "cat", "/proc/self/cgroup"})
+ 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())
diff --git a/test/e2e/system_connection_test.go b/test/e2e/system_connection_test.go
index 842ae8df6..76b442ce8 100644
--- a/test/e2e/system_connection_test.go
+++ b/test/e2e/system_connection_test.go
@@ -3,7 +3,11 @@ package integration
import (
"fmt"
"io/ioutil"
+ "net/url"
"os"
+ "os/exec"
+ "os/user"
+ "path/filepath"
"github.com/containers/common/pkg/config"
. "github.com/containers/podman/v3/test/utils"
@@ -19,22 +23,16 @@ var _ = Describe("podman system connection", func() {
IsSet bool
}{}
- var (
- podmanTest *PodmanTestIntegration
- )
+ var podmanTest *PodmanTestIntegration
BeforeEach(func() {
ConfPath.Value, ConfPath.IsSet = os.LookupEnv("CONTAINERS_CONF")
conf, err := ioutil.TempFile("", "containersconf")
- if err != nil {
- panic(err)
- }
+ Expect(err).ToNot(HaveOccurred())
os.Setenv("CONTAINERS_CONF", conf.Name())
tempdir, err := CreateTempDirInTempDir()
- if err != nil {
- panic(err)
- }
+ Expect(err).ToNot(HaveOccurred())
podmanTest = PodmanTestCreate(tempdir)
podmanTest.Setup()
})
@@ -49,196 +47,241 @@ var _ = Describe("podman system connection", func() {
}
f := CurrentGinkgoTestDescription()
- timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
- GinkgoWriter.Write([]byte(timedResult))
+ GinkgoWriter.Write(
+ []byte(
+ fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())))
})
- It("add ssh://", func() {
- cmd := []string{"system", "connection", "add",
- "--default",
- "--identity", "~/.ssh/id_rsa",
- "QA",
- "ssh://root@server.fubar.com:2222/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"))
- Expect(cfg.Engine.ServiceDestinations["QA"]).To(Equal(
- config.Destination{
- URI: "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
- Identity: "~/.ssh/id_rsa",
- },
- ))
-
- cmd = []string{"system", "connection", "rename",
- "QA",
- "QE",
- }
- session = podmanTest.Podman(cmd)
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
-
- cfg, err = config.ReadCustomConfig()
- Expect(err).ShouldNot(HaveOccurred())
- Expect(cfg.Engine.ActiveService).To(Equal("QE"))
- Expect(cfg.Engine.ServiceDestinations["QE"]).To(Equal(
- config.Destination{
- URI: "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
- Identity: "~/.ssh/id_rsa",
- },
- ))
- })
+ Context("without running API service", func() {
+ It("add ssh://", func() {
+ cmd := []string{"system", "connection", "add",
+ "--default",
+ "--identity", "~/.ssh/id_rsa",
+ "QA",
+ "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
+ }
+ session := podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.Out.Contents()).Should(BeEmpty())
- 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: "",
- },
- ))
- })
+ cfg, err := config.ReadCustomConfig()
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(cfg).To(HaveActiveService("QA"))
+ Expect(cfg).Should(VerifyService(
+ "QA",
+ "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
+ "~/.ssh/id_rsa",
+ ))
- 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: "",
- },
- ))
- })
+ cmd = []string{"system", "connection", "rename",
+ "QA",
+ "QE",
+ }
+ session = podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
- It("remove", func() {
- cmd := []string{"system", "connection", "add",
- "--default",
- "--identity", "~/.ssh/id_rsa",
- "QA",
- "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
- }
- session := podmanTest.Podman(cmd)
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
+ Expect(config.ReadCustomConfig()).To(HaveActiveService("QE"))
+ })
- for i := 0; i < 2; i++ {
- cmd = []string{"system", "connection", "remove", "QA"}
+ 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.Contents()).Should(BeEmpty())
+
+ Expect(config.ReadCustomConfig()).Should(VerifyService(
+ "QA-UDS",
+ "unix:///run/podman/podman.sock",
+ "",
+ ))
+
+ 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(""))
+ Expect(session.Out.Contents()).Should(BeEmpty())
- cfg, err := config.ReadCustomConfig()
- Expect(err).ShouldNot(HaveOccurred())
- Expect(cfg.Engine.ActiveService).To(BeEmpty())
- Expect(cfg.Engine.ServiceDestinations).To(BeEmpty())
- }
- })
+ Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA-UDS"))
+ Expect(config.ReadCustomConfig()).Should(VerifyService(
+ "QA-UDS1",
+ "unix:///run/user/podman/podman.sock",
+ "",
+ ))
+ })
- It("default", func() {
- for _, name := range []string{"devl", "qe"} {
+ 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.Contents()).Should(BeEmpty())
+
+ Expect(config.ReadCustomConfig()).Should(VerifyService(
+ "QA-TCP",
+ "tcp://localhost:8888",
+ "",
+ ))
+ })
+
+ It("remove", func() {
+ session := podmanTest.Podman([]string{"system", "connection", "add",
+ "--default",
+ "--identity", "~/.ssh/id_rsa",
+ "QA",
+ "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
+ })
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ // two passes to test that removing non-existent connection is not an error
+ for i := 0; i < 2; i++ {
+ session = podmanTest.Podman([]string{"system", "connection", "remove", "QA"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.Out.Contents()).Should(BeEmpty())
+
+ cfg, err := config.ReadCustomConfig()
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(cfg.Engine.ActiveService).To(BeEmpty())
+ Expect(cfg.Engine.ServiceDestinations).To(BeEmpty())
+ }
+ })
+
+ It("remove --all", func() {
+ session := podmanTest.Podman([]string{"system", "connection", "add",
"--default",
"--identity", "~/.ssh/id_rsa",
- name,
+ "QA",
"ssh://root@server.fubar.com:2222/run/podman/podman.sock",
+ })
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+
+ session = podmanTest.Podman([]string{"system", "connection", "remove", "--all"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.Out.Contents()).Should(BeEmpty())
+ Expect(session.Err.Contents()).Should(BeEmpty())
+
+ session = podmanTest.Podman([]string{"system", "connection", "list"})
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ })
+
+ It("default", func() {
+ for _, name := range []string{"devl", "qe"} {
+ cmd := []string{"system", "connection", "add",
+ "--default",
+ "--identity", "~/.ssh/id_rsa",
+ name,
+ "ssh://root@server.fubar.com:2222/run/podman/podman.sock",
+ }
+ session := podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
}
+
+ cmd := []string{"system", "connection", "default", "devl"}
session := podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
- }
+ Expect(session.Out.Contents()).Should(BeEmpty())
- cmd := []string{"system", "connection", "default", "devl"}
- 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("devl"))
-
- cmd = []string{"system", "connection", "list"}
- session = podmanTest.Podman(cmd)
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- Expect(session.Out).Should(Say("Name *URI *Identity *Default"))
-
- 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"))
- })
+ Expect(config.ReadCustomConfig()).Should(HaveActiveService("devl"))
- It("failed default", func() {
- cmd := []string{"system", "connection", "default", "devl"}
- session := podmanTest.Podman(cmd)
- session.WaitWithDefaultTimeout()
- Expect(session).ShouldNot(Exit(0))
- Expect(session.Err).Should(Say("destination is not defined"))
- })
+ cmd = []string{"system", "connection", "list"}
+ session = podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(session.Out).Should(Say("Name *URI *Identity *Default"))
+
+ 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() {
+ cmd := []string{"system", "connection", "default", "devl"}
+ session := podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).ShouldNot(Exit(0))
+ Expect(session.Err).Should(Say("destination is not defined"))
+ })
+
+ It("failed rename", func() {
+ cmd := []string{"system", "connection", "rename", "devl", "QE"}
+ session := podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).ShouldNot(Exit(0))
+ Expect(session.Err).Should(Say("destination is not defined"))
+ })
- It("failed rename", func() {
- cmd := []string{"system", "connection", "rename", "devl", "QE"}
- session := podmanTest.Podman(cmd)
- session.WaitWithDefaultTimeout()
- Expect(session).ShouldNot(Exit(0))
- Expect(session.Err).Should(Say("destination is not defined"))
+ It("empty list", func() {
+ cmd := []string{"system", "connection", "list"}
+ session := podmanTest.Podman(cmd)
+ session.WaitWithDefaultTimeout()
+ Expect(session).Should(Exit(0))
+ Expect(len(session.OutputToStringArray())).Should(Equal(1))
+ Expect(session.Err.Contents()).Should(BeEmpty())
+ })
})
- It("empty list", func() {
- cmd := []string{"system", "connection", "list"}
- session := podmanTest.Podman(cmd)
- session.WaitWithDefaultTimeout()
- Expect(session).Should(Exit(0))
- Expect(session.Out).Should(Say(""))
- Expect(session.Err).Should(Say(""))
+ Context("sshd and API services required", func() {
+ BeforeEach(func() {
+ // These tests are unique in as much as they require podman, podman-remote, systemd and sshd.
+ // podman-remote commands will be executed by ginkgo directly.
+ SkipIfContainerized("sshd is not available when running in a container")
+ SkipIfRemote("connection heuristic requires both podman and podman-remote binaries")
+ SkipIfNotRootless("FIXME: setup ssh keys when root")
+ SkipIfSystemdNotRunning("cannot test connection heuristic if systemd is not running")
+ SkipIfNotActive("sshd", "cannot test connection heuristic if sshd is not running")
+ })
+
+ It("add ssh:// socket path using connection heuristic", func() {
+ u, err := user.Current()
+ Expect(err).ShouldNot(HaveOccurred())
+
+ cmd := exec.Command(podmanTest.RemotePodmanBinary,
+ "system", "connection", "add",
+ "--default",
+ "--identity", filepath.Join(u.HomeDir, ".ssh", "id_ed25519"),
+ "QA",
+ fmt.Sprintf("ssh://%s@localhost", u.Username))
+
+ session, err := Start(cmd, GinkgoWriter, GinkgoWriter)
+ Expect(err).ToNot(HaveOccurred(), fmt.Sprintf("%q failed to execute", podmanTest.RemotePodmanBinary))
+ Eventually(session, DefaultWaitTimeout).Should(Exit(0))
+ Expect(session.Out.Contents()).Should(BeEmpty())
+ Expect(session.Err.Contents()).Should(BeEmpty())
+
+ uri := url.URL{
+ Scheme: "ssh",
+ User: url.User(u.Username),
+ Host: "localhost:22",
+ Path: fmt.Sprintf("/run/user/%s/podman/podman.sock", u.Uid),
+ }
+
+ Expect(config.ReadCustomConfig()).Should(HaveActiveService("QA"))
+ Expect(config.ReadCustomConfig()).Should(VerifyService(
+ "QA",
+ uri.String(),
+ filepath.Join(u.HomeDir, ".ssh", "id_ed25519"),
+ ))
+ })
})
})
diff --git a/test/e2e/system_reset_test.go b/test/e2e/system_reset_test.go
index 102526a46..93ab166cd 100644
--- a/test/e2e/system_reset_test.go
+++ b/test/e2e/system_reset_test.go
@@ -60,6 +60,8 @@ var _ = Describe("podman system reset", func() {
session.WaitWithDefaultTimeout()
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
podmanTest.StartRemoteService()
diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go
index a1b25b723..98def3d8f 100644
--- a/test/e2e/systemd_test.go
+++ b/test/e2e/systemd_test.go
@@ -4,7 +4,6 @@ import (
"io/ioutil"
"os"
"strings"
- "time"
. "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo"
@@ -82,27 +81,13 @@ WantedBy=multi-user.target
run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", ubi_init, "/sbin/init"})
run.WaitWithDefaultTimeout()
Expect(run).Should(Exit(0))
- ctrID := run.OutputToString()
logs := podmanTest.Podman([]string{"logs", ctrName})
logs.WaitWithDefaultTimeout()
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).Should(Exit(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"})
diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go
index 7f97f280a..b591e1c02 100644
--- a/test/e2e/trust_test.go
+++ b/test/e2e/trust_test.go
@@ -14,7 +14,8 @@ import (
var _ = Describe("Podman trust", func() {
var (
- tempdir string
+ tempdir string
+
err error
podmanTest *PodmanTestIntegration
)
@@ -38,21 +39,17 @@ var _ = Describe("Podman trust", func() {
})
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).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() {
@@ -76,24 +73,52 @@ var _ = Describe("Podman trust", func() {
})
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).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).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 --git a/test/e2e/unshare_test.go b/test/e2e/unshare_test.go
index 79ce68e89..cf1b8db53 100644
--- a/test/e2e/unshare_test.go
+++ b/test/e2e/unshare_test.go
@@ -51,7 +51,7 @@ var _ = Describe("Podman unshare", func() {
})
It("podman unshare --rootles-cni", func() {
- session := podmanTest.Podman([]string{"unshare", "--rootless-cni", "ip", "addr"})
+ session := podmanTest.Podman([]string{"unshare", "--rootless-netns", "ip", "addr"})
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(0))
Expect(session.OutputToString()).To(ContainSubstring("tap0"))
diff --git a/test/e2e/version_test.go b/test/e2e/version_test.go
index 75986e671..9398248b8 100644
--- a/test/e2e/version_test.go
+++ b/test/e2e/version_test.go
@@ -31,7 +31,6 @@ var _ = Describe("Podman version", func() {
f := CurrentGinkgoTestDescription()
processTestResult(f)
podmanTest.SeedImages()
-
})
It("podman version", func() {
@@ -96,4 +95,13 @@ var _ = Describe("Podman version", func() {
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 --git a/test/python/docker/compat/test_containers.py b/test/python/docker/compat/test_containers.py
index 1ad1e7f15..e6f7d992d 100644
--- a/test/python/docker/compat/test_containers.py
+++ b/test/python/docker/compat/test_containers.py
@@ -251,3 +251,17 @@ class TestContainers(unittest.TestCase):
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")
diff --git a/test/registries.conf b/test/registries.conf
index 0559c9e52..8e4671760 100644
--- a/test/registries.conf
+++ b/test/registries.conf
@@ -15,3 +15,9 @@ location="mirror.gcr.io"
[[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 --git a/test/system/001-basic.bats b/test/system/001-basic.bats
index 78b8ecdfd..03f07d602 100644
--- a/test/system/001-basic.bats
+++ b/test/system/001-basic.bats
@@ -120,9 +120,7 @@ function setup() {
fi
run_podman 125 --remote
- is "$output" "Error: missing command 'podman COMMAND'
-Try 'podman --help' for more information." \
- "podman --remote show usage message without running endpoint"
+ is "$output" ".*Usage:" "podman --remote show usage message without running endpoint"
}
# This is for development only; it's intended to make sure our timeout
diff --git a/test/system/011-image.bats b/test/system/011-image.bats
new file mode 100644
index 000000000..5150e875e
--- /dev/null
+++ b/test/system/011-image.bats
@@ -0,0 +1,54 @@
+#!/usr/bin/env bats
+
+load helpers
+
+function setup() {
+ skip_if_remote "--sign-by does not work with podman-remote"
+
+ basic_setup
+
+ export _GNUPGHOME_TMP=$PODMAN_TMPDIR/.gnupg
+ mkdir --mode=0700 $_GNUPGHOME_TMP $PODMAN_TMPDIR/signatures
+
+ cat >$PODMAN_TMPDIR/keydetails <<EOF
+ %echo Generating a basic OpenPGP key
+ Key-Type: RSA
+ Key-Length: 2048
+ Subkey-Type: RSA
+ Subkey-Length: 2048
+ Name-Real: Foo
+ Name-Comment: Foo
+ Name-Email: foo@bar.com
+ Expire-Date: 0
+ %no-ask-passphrase
+ %no-protection
+ # Do a commit here, so that we can later print "done" :-)
+ %commit
+ %echo done
+EOF
+ GNUPGHOME=$_GNUPGHOME_TMP gpg --verbose --batch --gen-key $PODMAN_TMPDIR/keydetails
+}
+
+function check_signature() {
+ local sigfile=$1
+ ls -laR $PODMAN_TMPDIR/signatures
+ run_podman inspect --format '{{.Digest}}' $PODMAN_TEST_IMAGE_FQN
+ local repodigest=${output/:/=}
+
+ local dir="$PODMAN_TMPDIR/signatures/libpod/${PODMAN_TEST_IMAGE_NAME}@${repodigest}"
+ test -d $dir || die "Missing signature directory $dir"
+ test -e "$dir/$sigfile" || die "Missing signature file '$sigfile'"
+
+ # Confirm good signature
+ run env GNUPGHOME=$_GNUPGHOME_TMP gpg --verify "$dir/$sigfile"
+ is "$output" ".*Good signature from .Foo.*<foo@bar.com>" \
+ "gpg --verify $sigfile"
+}
+
+
+@test "podman image - sign with no sigfile" {
+ GNUPGHOME=$_GNUPGHOME_TMP run_podman image sign --sign-by foo@bar.com --directory $PODMAN_TMPDIR/signatures "docker://$PODMAN_TEST_IMAGE_FQN"
+ check_signature "signature-1"
+}
+
+# vim: filetype=sh
diff --git a/test/system/015-help.bats b/test/system/015-help.bats
index b0795b524..a87081687 100644
--- a/test/system/015-help.bats
+++ b/test/system/015-help.bats
@@ -149,12 +149,12 @@ function check_help() {
count=$(expr $count + 1)
done
- # Any command that takes subcommands, must throw error if called
+ # Any command that takes subcommands, prints its help and errors if called
# without one.
dprint "podman $@"
run_podman '?' "$@"
is "$status" 125 "'podman $*' without any subcommand - exit status"
- is "$output" "Error: missing command .*$@ COMMAND" \
+ is "$output" ".*Usage:.*Error: missing command '.*$@ COMMAND'" \
"'podman $*' without any subcommand - expected error message"
# Assume that 'NoSuchCommand' is not a command
diff --git a/test/system/035-logs.bats b/test/system/035-logs.bats
index 44b66676e..7fb3e62e4 100644
--- a/test/system/035-logs.bats
+++ b/test/system/035-logs.bats
@@ -30,6 +30,17 @@ load helpers
run_podman rm $cid
}
+function _additional_events_backend() {
+ local driver=$1
+ # Since PR#10431, 'logs -f' with journald driver is only supported with journald events backend.
+ if [[ $driver = "journald" ]]; then
+ run_podman info --format '{{.Host.EventLogger}}' >/dev/null
+ if [[ $output != "journald" ]]; then
+ echo "--events-backend journald"
+ fi
+ fi
+}
+
function _log_test_multi() {
local driver=$1
@@ -42,10 +53,12 @@ function _log_test_multi() {
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}"))
}
@@ -57,7 +70,7 @@ function _log_test_multi() {
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
@@ -187,15 +200,20 @@ function _log_test_follow() {
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 run --log-driver=$driver --name $cname $IMAGE sh -c "echo $contentA; echo $contentB; echo $contentC"
- run_podman logs -f $cname
+ 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 rm -t 0 -f $cname
+ run_podman ${events_backend} rm -t 0 -f $cname
}
@test "podman logs - --follow k8s-file" {
diff --git a/test/system/070-build.bats b/test/system/070-build.bats
index d3dc14d81..3c47b1f5b 100644
--- a/test/system/070-build.bats
+++ b/test/system/070-build.bats
@@ -39,6 +39,8 @@ EOF
cat >$dockerfile <<EOF
FROM $IMAGE
RUN echo $rand_content > /$rand_filename
+VOLUME /a/b/c
+VOLUME ['/etc/foo', '/etc/bar']
EOF
run_podman buildx build --load -t build_test --format=docker $tmpdir
@@ -47,6 +49,33 @@ EOF
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"
+
+ # Now confirm that each volume got a unique device ID
+ run_podman run --rm build_test stat -c '%D' / /a /a/b /a/b/c /\[ /\[/etc /\[/etc/foo, /etc /etc/bar\]
+ # First, the non-volumes should all be the same...
+ is "${lines[0]}" "${lines[1]}" "devnum( / ) = devnum( /a )"
+ is "${lines[0]}" "${lines[2]}" "devnum( / ) = devnum( /a/b )"
+ is "${lines[0]}" "${lines[4]}" "devnum( / ) = devnum( /[ )"
+ is "${lines[0]}" "${lines[5]}" "devnum( / ) = devnum( /[etc )"
+ is "${lines[0]}" "${lines[7]}" "devnum( / ) = devnum( /etc )"
+ is "${lines[6]}" "${lines[8]}" "devnum( /[etc/foo, ) = devnum( /etc/bar] )"
+ # ...then, each volume should be different
+ if [[ "${lines[0]}" = "${lines[3]}" ]]; then
+ die "devnum( / ) (${lines[0]}) = devnum( volume0 ) (${lines[3]}) -- they should differ"
+ fi
+ if [[ "${lines[0]}" = "${lines[6]}" ]]; then
+ die "devnum( / ) (${lines[0]}) = devnum( volume1 ) (${lines[6]}) -- they should differ"
+ fi
+ # FIXME: is this expected? I thought /a/b/c and /[etc/foo, would differ
+ is "${lines[3]}" "${lines[6]}" "devnum( volume0 ) = devnum( volume1 )"
+
run_podman rmi -f build_test
}
diff --git a/test/system/120-load.bats b/test/system/120-load.bats
index e9959271f..a5508b2f4 100644
--- a/test/system/120-load.bats
+++ b/test/system/120-load.bats
@@ -126,6 +126,26 @@ verify_iid_and_name() {
verify_iid_and_name $img_name
}
+@test "podman load - from URL" {
+ get_iid_and_name
+ run_podman save $img_name -o $archive
+ run_podman rmi $iid
+
+ HOST_PORT=$(random_free_port)
+ SERVER=http://127.0.0.1:$HOST_PORT
+
+ # Bind-mount the archive to a container running httpd
+ run_podman run -d --name myweb -p "$HOST_PORT:80" \
+ -v $archive:/var/www/image.tar:Z \
+ -w /var/www \
+ $IMAGE /bin/busybox-extras httpd -f -p 80
+
+ run_podman load -i $SERVER/image.tar
+ verify_iid_and_name $img_name
+
+ run_podman rm -f -t0 myweb
+}
+
@test "podman load - redirect corrupt payload" {
run_podman 125 load <<< "Danger, Will Robinson!! This is a corrupt tarball!"
is "$output" \
diff --git a/test/system/130-kill.bats b/test/system/130-kill.bats
index d85f0a6a9..1ff3a7b61 100644
--- a/test/system/130-kill.bats
+++ b/test/system/130-kill.bats
@@ -6,9 +6,22 @@
load helpers
@test "podman kill - test signal handling in containers" {
+
+ # Prepare for 'logs -f'
+ run_podman info --format '{{.Host.LogDriver}}'
+ log_driver=$output
+ run_podman info --format '{{.Host.EventLogger}}'
+ event_logger=$output
+ opt_log_driver=
+ if [ $log_driver = "journald" ] && [ $event_logger != "journald" ]; then
+ # Since PR#10431, 'logs -f' with journald driver is only supported with journald events backend.
+ # Set '--log driver' temporally because remote doesn't support '--events-backend'.
+ opt_log_driver="--log-driver k8s-file"
+ fi
+
# Start a container that will handle all signals by emitting 'got: N'
local -a signals=(1 2 3 4 5 6 8 10 12 13 14 15 16 20 21 22 23 24 25 26 64)
- run_podman run -d $IMAGE sh -c \
+ run_podman run -d ${opt_log_driver} $IMAGE sh -c \
"for i in ${signals[*]}; do trap \"echo got: \$i\" \$i; done;
echo READY;
while ! test -e /stop; do sleep 0.05; done;
diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats
index 86f3610ab..60cfbc9dd 100644
--- a/test/system/200-pod.bats
+++ b/test/system/200-pod.bats
@@ -61,6 +61,29 @@ function teardown() {
}
+@test "podman pod create - custom infra image" {
+ image="i.do/not/exist:image"
+
+ tmpdir=$PODMAN_TMPDIR/pod-test
+ run mkdir -p $tmpdir
+ containersconf=$tmpdir/containers.conf
+ cat >$containersconf <<EOF
+[engine]
+infra_image="$image"
+EOF
+
+ run_podman 125 pod create --infra-image $image
+ is "$output" ".*initializing source docker://$image:.*"
+
+ CONTAINERS_CONF=$containersconf run_podman 125 pod create
+ is "$output" ".*initializing source docker://$image:.*"
+}
+
+function rm_podman_pause_image() {
+ run_podman version --format "{{.Server.Version}}-{{.Server.Built}}"
+ run_podman rmi -f "localhost/podman-pause:$output"
+}
+
@test "podman pod - communicating between pods" {
podname=pod$(random_string)
run_podman 1 pod exists $podname
@@ -100,19 +123,14 @@ function teardown() {
# Clean up. First the nc -l container...
run_podman rm $cid1
- # ...then, from pause container, find the image ID of the pause image...
- run_podman pod inspect --format '{{(index .Containers 0).ID}}' $podname
- pause_cid="$output"
- run_podman container inspect --format '{{.Image}}' $pause_cid
- pause_iid="$output"
-
# ...then rm the pod, then rmi the pause image so we don't leave strays.
run_podman pod rm $podname
- run_podman rmi $pause_iid
# Pod no longer exists
run_podman 1 pod exists $podid
run_podman 1 pod exists $podname
+
+ rm_podman_pause_image
}
@test "podman pod - communicating via /dev/shm " {
@@ -133,6 +151,10 @@ function teardown() {
# Pod no longer exists
run_podman 1 pod exists $podid
run_podman 1 pod exists $podname
+
+ # Pause image hasn't been pulled
+ run_podman 1 image exists k8s.gcr.io/pause:3.5
+ rm_podman_pause_image
}
# Random byte
@@ -303,16 +325,25 @@ EOF
run_podman rm $cid
run_podman pod rm -t 0 -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"
+ local pod_name="$(random_string 10 | tr A-Z a-z)"
+
+ # Note that the internal pause image is built even when --infra-image is
+ # set to the K8s one.
+ run_podman pod create --name $pod_name --infra-name "$infra_name" --infra-image "k8s.gcr.io/pause:3.5"
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
+ run_podman pod rm -f $pod_name
+ run_podman images -a
+
+ # Pause image hasn't been pulled
+ run_podman 1 image exists k8s.gcr.io/pause:3.5
+ rm_podman_pause_image
}
# vim: filetype=sh
diff --git a/test/system/272-system-connection.bats b/test/system/272-system-connection.bats
index 5a90d9398..4e9ac4dd6 100644
--- a/test/system/272-system-connection.bats
+++ b/test/system/272-system-connection.bats
@@ -34,10 +34,7 @@ function teardown() {
| xargs -l1 --no-run-if-empty umount
# Remove all system connections
- run_podman system connection ls --format json
- while read name; do
- run_podman system connection rm "$name"
- done < <(jq -r '.[].Name' <<<"$output")
+ run_podman system connection rm --all
basic_teardown
}
@@ -53,7 +50,8 @@ function _run_podman_remote() {
# Very basic test, does not actually connect at any time
@test "podman system connection - basic add / ls / remove" {
run_podman system connection ls
- is "$output" "" "system connection ls: no connections"
+ is "$output" "Name URI Identity Default" \
+ "system connection ls: no connections"
c1="c1_$(random_string 15)"
c2="c2_$(random_string 15)"
diff --git a/test/system/500-networking.bats b/test/system/500-networking.bats
index cb73cf24d..21350ed36 100644
--- a/test/system/500-networking.bats
+++ b/test/system/500-networking.bats
@@ -78,6 +78,9 @@ load helpers
if [[ -z $cidr ]]; then
# regex to match that we are in 10.X subnet
match="10\..*"
+ # force bridge networking also for rootless
+ # this ensures that rootless + bridge + userns + ports works
+ network_arg="--network bridge"
else
# Issue #9828 make sure a custom slir4netns cidr also works
network_arg="--network slirp4netns:cidr=$cidr"
@@ -167,6 +170,13 @@ load helpers
$IMAGE nc -l -n -v -p $myport
cid="$output"
+ # FIXME: debugging for #11871
+ run_podman exec $cid cat /etc/resolv.conf
+ if is_rootless; 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
@@ -420,6 +430,7 @@ load helpers
is "$output" "[${cid:0:12}]" "short container id in network aliases"
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
@@ -428,6 +439,7 @@ load helpers
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
@@ -444,8 +456,23 @@ load helpers
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"
+
+ # FIXME FIXME FIXME: #11825: bodhi tests are failing, remote+rootless only,
+ # with "dnsmasq: failed to create inotify". This error has never occurred
+ # in CI, and Ed has been unable to reproduce it on 1minutetip. This next
+ # line is a suggestion from Paul Holzinger for trying to shed light on
+ # the system context before the failure. This output will be invisible
+ # if the test passes.
+ for foo in /proc/\*/fd/*; do readlink -f $foo; done |grep '^/proc/.*inotify' |cut -d/ -f3 | xargs -I '{}' -- ps --no-headers -o '%p %U %a' -p '{}' |uniq -c |sort -n
+
# connect a second network
run_podman network connect $netname2 $cid
+ is "$output" "" "Output should be empty (no errors)"
# check network2 alias for container short id
run_podman inspect $cid --format "{{(index .NetworkSettings.Networks \"$netname2\").Aliases}}"
diff --git a/test/system/700-play.bats b/test/system/700-play.bats
index 8cf279ada..c3e5e9354 100644
--- a/test/system/700-play.bats
+++ b/test/system/700-play.bats
@@ -76,6 +76,12 @@ RELABEL="system_u:object_r:container_file_t:s0"
is "$output" "${RELABEL} $TESTDIR" "selinux relabel should have happened"
fi
+ # Make sure that the K8s pause image isn't pulled but the local podman-pause is built.
+ run_podman images
+ run_podman 1 image exists k8s.gcr.io/pause
+ run_podman version --format "{{.Server.Version}}-{{.Server.Built}}"
+ run_podman image exists localhost/podman-pause:$output
+
run_podman stop -a -t 0
run_podman pod rm -t 0 -f test_pod
}
diff --git a/test/upgrade/test-upgrade.bats b/test/upgrade/test-upgrade.bats
index 5cb302a85..c0d601586 100644
--- a/test/upgrade/test-upgrade.bats
+++ b/test/upgrade/test-upgrade.bats
@@ -97,8 +97,10 @@ podman \$opts run --name myfailedcontainer --label mylabel=$LABEL_FAILED \
podman \$opts run -d --name myrunningcontainer --label mylabel=$LABEL_RUNNING \
--network bridge \
-p $HOST_PORT:80 \
+ -p 127.0.0.1:8080-8082:8080-8082 \
-v $pmroot/var/www:/var/www \
-w /var/www \
+ --mac-address aa:bb:cc:dd:ee:ff \
$IMAGE /bin/busybox-extras httpd -f -p 80
podman \$opts pod create --name mypod
@@ -121,8 +123,15 @@ EOF
# Clean up vestiges of previous run
$PODMAN rm -f podman_parent || true
- # Not entirely a NOP! This is just so we get /run/crun created on a CI VM
- $PODMAN run --rm $OLD_PODMAN true
+
+ local netname=testnet-$(random_string 10)
+ $PODMAN network create $netname
+
+ # Not entirely a NOP! This is just so we get the /run/... mount points created on a CI VM
+ # --mac-address is needed to create /run/cni, --network is needed to create /run/containers for dnsname
+ $PODMAN run --rm --mac-address 78:28:a6:8d:24:8a --network $netname $OLD_PODMAN true
+ $PODMAN network rm -f $netname
+
#
# Use new-podman to run the above script under old-podman.
@@ -134,7 +143,8 @@ EOF
#
# mount /etc/containers/storage.conf to use the same storage settings as on the host
# mount /dev/shm because the container locks are stored there
- # mount /var/lib/cni and /etc/cni/net.d for cni networking
+ # mount /var/lib/cni, /run/cni and /etc/cni/net.d for cni networking
+ # mount /run/containers for the dnsname plugin
#
$PODMAN run -d --name podman_parent --pid=host \
--privileged \
@@ -145,6 +155,8 @@ EOF
-v /dev/fuse:/dev/fuse \
-v /run/crun:/run/crun \
-v /run/netns:/run/netns:rshared \
+ -v /run/containers:/run/containers \
+ -v /run/cni:/run/cni \
-v /var/lib/cni:/var/lib/cni \
-v /etc/cni/net.d:/etc/cni/net.d \
-v /dev/shm:/dev/shm \
@@ -185,7 +197,7 @@ EOF
is "${lines[1]}" "mycreatedcontainer--Created----$LABEL_CREATED" "created"
is "${lines[2]}" "mydonecontainer--Exited (0).*----<no value>" "done"
is "${lines[3]}" "myfailedcontainer--Exited (17) .*----$LABEL_FAILED" "fail"
- is "${lines[4]}" "myrunningcontainer--Up .*--0.0.0.0:$HOST_PORT->80/tcp--$LABEL_RUNNING" "running"
+ is "${lines[4]}" "myrunningcontainer--Up .*--0\.0\.0\.0:$HOST_PORT->80\/tcp, 127\.0\.0\.1\:8080-8082->8080-8082\/tcp--$LABEL_RUNNING" "running"
# For debugging: dump containers and IDs
if [[ -n "$PODMAN_UPGRADE_TEST_DEBUG" ]]; then
diff --git a/test/utils/matchers.go b/test/utils/matchers.go
index 07c1232e7..17ff3ea75 100644
--- a/test/utils/matchers.go
+++ b/test/utils/matchers.go
@@ -2,57 +2,164 @@ package utils
import (
"fmt"
+ "net/url"
+ "github.com/containers/common/pkg/config"
+ . "github.com/onsi/gomega"
"github.com/onsi/gomega/format"
"github.com/onsi/gomega/gexec"
+ "github.com/onsi/gomega/matchers"
+ "github.com/onsi/gomega/types"
)
+// HaveActiveService verifies the given service is the active service
+func HaveActiveService(name interface{}) OmegaMatcher {
+ return WithTransform(
+ func(cfg *config.Config) string {
+ return cfg.Engine.ActiveService
+ },
+ Equal(name))
+}
+
+type ServiceMatcher struct {
+ types.GomegaMatcher
+ Name interface{}
+ URI interface{}
+ Identity interface{}
+ failureMessage string
+ negatedFailureMessage string
+}
+
+func VerifyService(name, uri, identity interface{}) OmegaMatcher {
+ return &ServiceMatcher{
+ Name: name,
+ URI: uri,
+ Identity: identity,
+ }
+}
+
+func (matcher *ServiceMatcher) Match(actual interface{}) (success bool, err error) {
+ cfg, ok := actual.(*config.Config)
+ if !ok {
+ return false, fmt.Errorf("ServiceMatcher matcher expects a config.Config")
+ }
+
+ if _, err = url.Parse(matcher.URI.(string)); err != nil {
+ return false, err
+ }
+
+ success, err = HaveKey(matcher.Name).Match(cfg.Engine.ServiceDestinations)
+ if !success || err != nil {
+ matcher.failureMessage = HaveKey(matcher.Name).FailureMessage(cfg.Engine.ServiceDestinations)
+ matcher.negatedFailureMessage = HaveKey(matcher.Name).NegatedFailureMessage(cfg.Engine.ServiceDestinations)
+ return
+ }
+
+ sd := cfg.Engine.ServiceDestinations[matcher.Name.(string)]
+ success, err = Equal(matcher.URI).Match(sd.URI)
+ if !success || err != nil {
+ matcher.failureMessage = Equal(matcher.URI).FailureMessage(sd.URI)
+ matcher.negatedFailureMessage = Equal(matcher.URI).NegatedFailureMessage(sd.URI)
+ return
+ }
+
+ success, err = Equal(matcher.Identity).Match(sd.Identity)
+ if !success || err != nil {
+ matcher.failureMessage = Equal(matcher.Identity).FailureMessage(sd.Identity)
+ matcher.negatedFailureMessage = Equal(matcher.Identity).NegatedFailureMessage(sd.Identity)
+ return
+ }
+
+ return true, nil
+}
+
+func (matcher *ServiceMatcher) FailureMessage(_ interface{}) string {
+ return matcher.failureMessage
+}
+
+func (matcher *ServiceMatcher) NegatedFailureMessage(_ interface{}) string {
+ return matcher.negatedFailureMessage
+}
+
+type URLMatcher struct {
+ matchers.EqualMatcher
+}
+
+// VerifyURL matches when actual is a valid URL and matches expected
+func VerifyURL(uri interface{}) OmegaMatcher {
+ return &URLMatcher{matchers.EqualMatcher{Expected: uri}}
+}
+
+func (matcher *URLMatcher) Match(actual interface{}) (bool, error) {
+ e, ok := matcher.Expected.(string)
+ if !ok {
+ return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", matcher.Expected)
+ }
+ e_uri, err := url.Parse(e)
+ if err != nil {
+ return false, err
+ }
+
+ a, ok := actual.(string)
+ if !ok {
+ return false, fmt.Errorf("VerifyURL requires string inputs %T is not supported", actual)
+ }
+ a_uri, err := url.Parse(a)
+ if err != nil {
+ return false, err
+ }
+
+ return (&matchers.EqualMatcher{Expected: e_uri}).Match(a_uri)
+}
+
+type ExitMatcher struct {
+ types.GomegaMatcher
+ Expected int
+ Actual int
+}
+
// ExitWithError matches when assertion is > argument. Default 0
-// Modeled after the gomega Exit() matcher
-func ExitWithError(optionalExitCode ...int) *exitMatcher {
+// Modeled after the gomega Exit() matcher and also operates on sessions.
+func ExitWithError(optionalExitCode ...int) *ExitMatcher {
exitCode := 0
if len(optionalExitCode) > 0 {
exitCode = optionalExitCode[0]
}
- return &exitMatcher{exitCode: exitCode}
+ return &ExitMatcher{Expected: exitCode}
}
-type exitMatcher struct {
- exitCode int
- actualExitCode int
-}
-
-func (m *exitMatcher) Match(actual interface{}) (success bool, err error) {
+// Match follows gexec.Matcher interface
+func (matcher *ExitMatcher) Match(actual interface{}) (success bool, err error) {
exiter, ok := actual.(gexec.Exiter)
if !ok {
return false, fmt.Errorf("ExitWithError must be passed a gexec.Exiter (Missing method ExitCode() int) Got:\n#{format.Object(actual, 1)}")
}
- m.actualExitCode = exiter.ExitCode()
- if m.actualExitCode == -1 {
+ matcher.Actual = exiter.ExitCode()
+ if matcher.Actual == -1 {
return false, nil
}
- return m.actualExitCode > m.exitCode, nil
+ return matcher.Actual > matcher.Expected, nil
}
-func (m *exitMatcher) FailureMessage(actual interface{}) (message string) {
- if m.actualExitCode == -1 {
+func (matcher *ExitMatcher) FailureMessage(_ interface{}) (message string) {
+ if matcher.Actual == -1 {
return "Expected process to exit. It did not."
}
- return format.Message(m.actualExitCode, "to be greater than exit code:", m.exitCode)
+ return format.Message(matcher.Actual, "to be greater than exit code: ", matcher.Expected)
}
-func (m *exitMatcher) NegatedFailureMessage(actual interface{}) (message string) {
- if m.actualExitCode == -1 {
+func (matcher *ExitMatcher) NegatedFailureMessage(_ interface{}) (message string) {
+ switch {
+ case matcher.Actual == -1:
return "you really shouldn't be able to see this!"
- } else {
- if m.exitCode == -1 {
- return "Expected process not to exit. It did."
- }
- return format.Message(m.actualExitCode, "is less than or equal to exit code:", m.exitCode)
+ case matcher.Expected == -1:
+ return "Expected process not to exit. It did."
}
+ return format.Message(matcher.Actual, "is less than or equal to exit code: ", matcher.Expected)
}
-func (m *exitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
+
+func (matcher *ExitMatcher) MatchMayChangeInTheFuture(actual interface{}) bool {
session, ok := actual.(*gexec.Session)
if ok {
return session.ExitCode() == -1
diff --git a/test/utils/podmantest_test.go b/test/utils/podmantest_test.go
index 9bd1c4a66..1bb9ecb6b 100644
--- a/test/utils/podmantest_test.go
+++ b/test/utils/podmantest_test.go
@@ -23,7 +23,7 @@ var _ = Describe("PodmanTest test", func() {
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 --git a/test/utils/utils.go b/test/utils/utils.go
index bfefc58ec..8d1edb23a 100644
--- a/test/utils/utils.go
+++ b/test/utils/utils.go
@@ -66,27 +66,31 @@ func (p *PodmanTest) MakeOptions(args []string, noEvents, noCache bool) []string
// 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 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string
// 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
@@ -393,7 +397,7 @@ func tagOutputToMap(imagesOutput []string) map[string]map[string]bool {
return m
}
-// GetHostDistributionInfo returns a struct with its distribution name and version
+// GetHostDistributionInfo returns a struct with its distribution Name and version
func GetHostDistributionInfo() HostOS {
f, err := os.Open(OSReleasePath)
defer f.Close()
@@ -487,13 +491,3 @@ func RandomString(n int) string {
}
return string(b)
}
-
-//SkipIfInContainer skips a test if the test is run inside a container
-func SkipIfInContainer(reason string) {
- if len(reason) < 5 {
- panic("SkipIfInContainer must specify a reason to skip")
- }
- if os.Getenv("TEST_ENVIRON") == "container" {
- Skip("[container]: " + reason)
- }
-}
diff --git a/troubleshooting.md b/troubleshooting.md
index b335eaaa8..44028fc42 100644
--- a/troubleshooting.md
+++ b/troubleshooting.md
@@ -51,9 +51,9 @@ In cases where the container image runs as a specific, non-root user, though, th
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 ':'
diff --git a/utils/utils.go b/utils/utils.go
index b08630d2f..109ae088b 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -14,6 +14,7 @@ import (
"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"
)
@@ -177,13 +178,26 @@ func RunsOnSystemd() bool {
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)
}
- return RunUnderSystemdScope(int(pid), slice, scope)
+ 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
@@ -191,8 +205,8 @@ func moveProcessToScope(pidPath, slice, scope string) error {
func MovePauseProcessToScope(pausePidPath string) {
err := moveProcessToScope(pausePidPath, "user.slice", "podman-pause.scope")
if err != nil {
- unified, err := cgroups.IsCgroup2UnifiedMode()
- 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 {
diff --git a/vendor/github.com/Microsoft/hcsshim/.gitignore b/vendor/github.com/Microsoft/hcsshim/.gitignore
index aec9bd4bb..54ed6f06c 100644
--- a/vendor/github.com/Microsoft/hcsshim/.gitignore
+++ b/vendor/github.com/Microsoft/hcsshim/.gitignore
@@ -1,3 +1,38 @@
+# Binaries for programs and plugins
*.exe
-.idea
-.vscode
+*.dll
+*.so
+*.dylib
+
+# Ignore vscode setting files
+.vscode/
+
+# Test binary, build with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
+.glide/
+
+# Ignore gcs bin directory
+service/bin/
+service/pkg/
+
+*.img
+*.vhd
+*.tar.gz
+
+# Make stuff
+.rootfs-done
+bin/*
+rootfs/*
+*.o
+/build/
+
+deps/*
+out/*
+
+.idea/
+.vscode/ \ No newline at end of file
diff --git a/vendor/github.com/Microsoft/hcsshim/.golangci.yml b/vendor/github.com/Microsoft/hcsshim/.golangci.yml
new file mode 100644
index 000000000..16b25be55
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/.golangci.yml
@@ -0,0 +1,96 @@
+linters:
+ enable:
+ - stylecheck
+
+linters-settings:
+ stylecheck:
+ # https://staticcheck.io/docs/checks
+ checks: ["all"]
+
+
+issues:
+ # This repo has a LOT of generated schema files, operating system bindings, and other things that ST1003 from stylecheck won't like
+ # (screaming case Windows api constants for example). There's also some structs that we *could* change the initialisms to be Go
+ # friendly (Id -> ID) but they're exported and it would be a breaking change. This makes it so that most new code, code that isn't
+ # supposed to be a pretty faithful mapping to an OS call/constants, or non-generated code still checks if we're following idioms,
+ # while ignoring the things that are just noise or would be more of a hassle than it'd be worth to change.
+ exclude-rules:
+ - path: layer.go
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: hcsshim.go
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\hcs\\schema2\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\wclayer\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: hcn\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\hcs\\schema1\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\hns\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: ext4\\internal\\compactext4\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: ext4\\internal\\format\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\guestrequest\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\guest\\prot\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\windevice\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\winapi\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\vmcompute\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\regstate\\
+ linters:
+ - stylecheck
+ Text: "ST1003:"
+
+ - path: internal\\hcserror\\
+ linters:
+ - stylecheck
+ Text: "ST1003:" \ No newline at end of file
diff --git a/vendor/github.com/Microsoft/hcsshim/Makefile b/vendor/github.com/Microsoft/hcsshim/Makefile
new file mode 100644
index 000000000..a8f5516cd
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/Makefile
@@ -0,0 +1,87 @@
+BASE:=base.tar.gz
+
+GO:=go
+GO_FLAGS:=-ldflags "-s -w" # strip Go binaries
+CGO_ENABLED:=0
+GOMODVENDOR:=
+
+CFLAGS:=-O2 -Wall
+LDFLAGS:=-static -s # strip C binaries
+
+GO_FLAGS_EXTRA:=
+ifeq "$(GOMODVENDOR)" "1"
+GO_FLAGS_EXTRA += -mod=vendor
+endif
+GO_BUILD:=CGO_ENABLED=$(CGO_ENABLED) $(GO) build $(GO_FLAGS) $(GO_FLAGS_EXTRA)
+
+SRCROOT=$(dir $(abspath $(firstword $(MAKEFILE_LIST))))
+
+# The link aliases for gcstools
+GCS_TOOLS=\
+ generichook
+
+.PHONY: all always rootfs test
+
+all: out/initrd.img out/rootfs.tar.gz
+
+clean:
+ find -name '*.o' -print0 | xargs -0 -r rm
+ rm -rf bin deps rootfs out
+
+test:
+ cd $(SRCROOT) && go test -v ./internal/guest/...
+
+out/delta.tar.gz: bin/init bin/vsockexec bin/cmd/gcs bin/cmd/gcstools Makefile
+ @mkdir -p out
+ rm -rf rootfs
+ mkdir -p rootfs/bin/
+ cp bin/init rootfs/
+ cp bin/vsockexec rootfs/bin/
+ cp bin/cmd/gcs rootfs/bin/
+ cp bin/cmd/gcstools rootfs/bin/
+ for tool in $(GCS_TOOLS); do ln -s gcstools rootfs/bin/$$tool; done
+ git -C $(SRCROOT) rev-parse HEAD > rootfs/gcs.commit && \
+ git -C $(SRCROOT) rev-parse --abbrev-ref HEAD > rootfs/gcs.branch
+ tar -zcf $@ -C rootfs .
+ rm -rf rootfs
+
+out/rootfs.tar.gz: out/initrd.img
+ rm -rf rootfs-conv
+ mkdir rootfs-conv
+ gunzip -c out/initrd.img | (cd rootfs-conv && cpio -imd)
+ tar -zcf $@ -C rootfs-conv .
+ rm -rf rootfs-conv
+
+out/initrd.img: $(BASE) out/delta.tar.gz $(SRCROOT)/hack/catcpio.sh
+ $(SRCROOT)/hack/catcpio.sh "$(BASE)" out/delta.tar.gz > out/initrd.img.uncompressed
+ gzip -c out/initrd.img.uncompressed > $@
+ rm out/initrd.img.uncompressed
+
+-include deps/cmd/gcs.gomake
+-include deps/cmd/gcstools.gomake
+
+# Implicit rule for includes that define Go targets.
+%.gomake: $(SRCROOT)/Makefile
+ @mkdir -p $(dir $@)
+ @/bin/echo $(@:deps/%.gomake=bin/%): $(SRCROOT)/hack/gomakedeps.sh > $@.new
+ @/bin/echo -e '\t@mkdir -p $$(dir $$@) $(dir $@)' >> $@.new
+ @/bin/echo -e '\t$$(GO_BUILD) -o $$@.new $$(SRCROOT)/$$(@:bin/%=%)' >> $@.new
+ @/bin/echo -e '\tGO="$(GO)" $$(SRCROOT)/hack/gomakedeps.sh $$@ $$(SRCROOT)/$$(@:bin/%=%) $$(GO_FLAGS) $$(GO_FLAGS_EXTRA) > $(@:%.gomake=%.godeps).new' >> $@.new
+ @/bin/echo -e '\tmv $(@:%.gomake=%.godeps).new $(@:%.gomake=%.godeps)' >> $@.new
+ @/bin/echo -e '\tmv $$@.new $$@' >> $@.new
+ @/bin/echo -e '-include $(@:%.gomake=%.godeps)' >> $@.new
+ mv $@.new $@
+
+VPATH=$(SRCROOT)
+
+bin/vsockexec: vsockexec/vsockexec.o vsockexec/vsock.o
+ @mkdir -p bin
+ $(CC) $(LDFLAGS) -o $@ $^
+
+bin/init: init/init.o vsockexec/vsock.o
+ @mkdir -p bin
+ $(CC) $(LDFLAGS) -o $@ $^
+
+%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
diff --git a/vendor/github.com/Microsoft/hcsshim/README.md b/vendor/github.com/Microsoft/hcsshim/README.md
index 95c300365..b8ca926a9 100644
--- a/vendor/github.com/Microsoft/hcsshim/README.md
+++ b/vendor/github.com/Microsoft/hcsshim/README.md
@@ -2,13 +2,67 @@
[![Build status](https://github.com/microsoft/hcsshim/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/microsoft/hcsshim/actions?query=branch%3Amaster)
-This package contains the Golang interface for using the Windows [Host Compute Service](https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS).
+This package contains the Golang interface for using the Windows [Host Compute Service](https://techcommunity.microsoft.com/t5/containers/introducing-the-host-compute-service-hcs/ba-p/382332) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS), as well as code for the [guest agent](./internal/guest/README.md) (commonly referred to as the GCS or Guest Compute Service in the codebase) used to support running Linux Hyper-V containers.
-It is primarily used in the [Moby Project](https://github.com/moby/moby), but it can be freely used by other projects as well.
+It is primarily used in the [Moby](https://github.com/moby/moby) and [Containerd](https://github.com/containerd/containerd) projects, but it can be freely used by other projects as well.
+
+## Building
+
+While this repository can be used as a library of sorts to call the HCS apis, there are a couple binaries built out of the repository as well. The main ones being the Linux guest agent, and an implementation of the [runtime v2 containerd shim api](https://github.com/containerd/containerd/blob/master/runtime/v2/README.md).
+### Linux Hyper-V Container Guest Agent
+
+To build the Linux guest agent itself all that's needed is to set your GOOS to "Linux" and build out of ./cmd/gcs.
+```powershell
+C:\> $env:GOOS="linux"
+C:\> go build .\cmd\gcs\
+```
+
+or on a Linux machine
+```sh
+> go build ./cmd/gcs
+```
+
+If you want it to be packaged inside of a rootfs to boot with alongside all of the other tools then you'll need to provide a rootfs that it can be packaged inside of. An easy way is to export the rootfs of a container.
+
+```sh
+docker pull busybox
+docker run --name base_image_container busybox
+docker export base_image_container | gzip > base.tar.gz
+BASE=./base.tar.gz
+make all
+```
+
+If the build is successful, in the `./out` folder you should see:
+```sh
+> ls ./out/
+delta.tar.gz initrd.img rootfs.tar.gz
+```
+
+### Containerd Shim
+For info on the Runtime V2 API: https://github.com/containerd/containerd/blob/master/runtime/v2/README.md.
+
+Contrary to the typical Linux architecture of shim -> runc, the runhcs shim is used both to launch and manage the lifetime of containers.
+
+```powershell
+C:\> $env:GOOS="windows"
+C:\> go build .\cmd\containerd-shim-runhcs-v1
+```
+
+Then place the binary in the same directory that Containerd is located at in your environment. A default Containerd configuration file can be generated by running:
+```powershell
+.\containerd.exe config default | Out-File "C:\Program Files\containerd\config.toml" -Encoding ascii
+```
+
+This config file will already have the shim set as the default runtime for cri interactions.
+
+To trial using the shim out with ctr.exe:
+```powershell
+C:\> ctr.exe run --runtime io.containerd.runhcs.v1 --rm mcr.microsoft.com/windows/nanoserver:2004 windows-test cmd /c "echo Hello World!"
+```
## Contributing
-This project welcomes contributions and suggestions. Most contributions require you to agree to a
+This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
@@ -16,7 +70,27 @@ When you submit a pull request, a CLA-bot will automatically determine whether y
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
-We also ask that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to certify they either authored the work themselves or otherwise have permission to use it in this project.
+We also require that contributors [sign their commits](https://git-scm.com/docs/git-commit) using `git commit -s` or `git commit --signoff` to
+certify they either authored the work themselves or otherwise have permission to use it in this project. Please see https://developercertificate.org/ for
+more info, as well as to make sure that you can attest to the rules listed. Our CI uses the [DCO Github app](https://github.com/apps/dco) to ensure
+that all commits in a given PR are signed-off.
+
+### Test Directory (Important to note)
+
+This project has tried to trim some dependencies from the root Go modules file that would be cumbersome to get transitively included if this
+project is being vendored/used as a library. Some of these dependencies were only being used for tests, so the /test directory in this project also has
+its own go.mod file where these are now included to get around this issue. Our tests rely on the code in this project to run, so the test Go modules file
+has a relative path replace directive to pull in the latest hcsshim code that the tests actually touch from this project
+(which is the repo itself on your disk).
+
+```
+replace (
+ github.com/Microsoft/hcsshim => ../
+)
+```
+
+Because of this, for most code changes you may need to run `go mod vendor` + `go mod tidy` in the /test directory in this repository, as the
+CI in this project will check if the files are out of date and will fail if this is true.
## Code of Conduct
diff --git a/vendor/github.com/Microsoft/hcsshim/go.mod b/vendor/github.com/Microsoft/hcsshim/go.mod
index 8360b0a50..4d26c7c36 100644
--- a/vendor/github.com/Microsoft/hcsshim/go.mod
+++ b/vendor/github.com/Microsoft/hcsshim/go.mod
@@ -3,25 +3,33 @@ module github.com/Microsoft/hcsshim
go 1.13
require (
+ github.com/BurntSushi/toml v0.3.1
github.com/Microsoft/go-winio v0.4.17
github.com/containerd/cgroups v1.0.1
github.com/containerd/console v1.0.2
- github.com/containerd/containerd v1.4.9
- github.com/containerd/continuity v0.1.0 // indirect
- github.com/containerd/fifo v1.0.0 // indirect
+ github.com/containerd/containerd v1.5.7
github.com/containerd/go-runc v1.0.0
github.com/containerd/ttrpc v1.0.2
github.com/containerd/typeurl v1.0.2
github.com/gogo/protobuf v1.3.2
- github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d
+ github.com/golang/mock v1.6.0
+ github.com/google/go-cmp v0.5.6
+ github.com/google/go-containerregistry v0.5.1
+ github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3
+ github.com/mattn/go-shellwords v1.0.6
+ github.com/opencontainers/runc v1.0.2
+ github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/pkg/errors v0.9.1
- github.com/sirupsen/logrus v1.7.0
+ github.com/sirupsen/logrus v1.8.1
github.com/urfave/cli v1.22.2
+ github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
+ github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
+ go.etcd.io/bbolt v1.3.6
go.opencensus.io v0.22.3
- golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
- golang.org/x/sys v0.0.0-20210324051608-47abb6519492
- google.golang.org/grpc v1.33.2
- gotest.tools/v3 v3.0.3 // indirect
+ golang.org/x/net v0.0.0-20210825183410-e898025ed96a // indirect
+ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
+ golang.org/x/sys v0.0.0-20210510120138-977fb7262007
+ google.golang.org/grpc v1.40.0
)
replace (
diff --git a/vendor/github.com/Microsoft/hcsshim/go.sum b/vendor/github.com/Microsoft/hcsshim/go.sum
index 8ad3a8b8f..572931b3b 100644
--- a/vendor/github.com/Microsoft/hcsshim/go.sum
+++ b/vendor/github.com/Microsoft/hcsshim/go.sum
@@ -1,241 +1,988 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+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.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+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/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/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/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=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+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/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+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=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+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/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=
+github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.4.17 h1:iT12IBVClFevaf8PuVyi3UmZOVh4OqnaLxDTW2O6j3w=
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
+github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
+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.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
+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=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
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/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+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/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
+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.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
+github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
+github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
+github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
+github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
+github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.2 h1:Pi6D+aZXM+oUw1czuKgH5IJ+y0jhYcwBJfx5/Ghn9dE=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
-github.com/containerd/containerd v1.4.9 h1:JIw9mjVw4LsGmnA/Bqg9j9e+XB7soOJufrKUpA6n2Ns=
-github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+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.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/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
+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=
+github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
+github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
+github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/continuity v0.1.0 h1:UFRRY5JemiAhPZrr/uE0n8fMTLcZsUvySPr1+D7pgr8=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/fifo v1.0.0 h1:6PirWBr9/L7GDamKr+XM0IeUFXu5mf3M/BPpH9gaLBU=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
+github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
+github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
+github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
+github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
+github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
+github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
+github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
+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.4.1 h1:5e7heayhB7CcgdTkqfZqrNaNv15gABwr3Q2jBTbLlt4=
+github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
+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=
+github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.0.2 h1:2/O3oTZN36q2xRolk0a2WWGgh7/Vf/liElg5hFYLX9U=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
+github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY=
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
+github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
+github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
+github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
+github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
+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/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
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/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8=
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.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=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+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/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=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
+github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017 h1:2HQmlpI3yI9deH18Q6xiSOIjXD4sLI55Y/gfpa8/558=
+github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
+github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.7.1+incompatible 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.20190924003213-a8608b5b67c7 h1:Cvj7S8I4Xpx78KAl6TwTmMHuHlZ/0SM60NUneGJQ7IE=
+github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/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/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
+github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/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/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=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
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/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/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+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=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+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/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=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
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/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.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
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=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+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 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+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=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
+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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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-containerregistry v0.5.1 h1:/+mFTs4AlwsJ/mJe8NDtKb7BxLtbZFpcn8vDsneEkwQ=
+github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+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/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+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.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
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/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/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/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
+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/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+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/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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/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/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.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
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/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 h1:jUp75lepDg0phMUJBCmvaeFDldD2N3S1lBuPwUTszio=
+github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+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/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-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-shellwords v1.0.6 h1:9Jok5pILi5S1MnDirGVTufYGtksUs/V2BWUP3ZkeUUI=
+github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
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/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
+github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
+github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+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/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
+github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
+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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
+github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+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.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+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=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+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=
+github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/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-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
+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=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d h1:pNa8metDkwZjb9g4T8s+krQ+HRgZAkqnXml+wNir/+s=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+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.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+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=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1 h1:Lo6mRUjdS99f3zxYOUalftWHUoOGaDRqFk1+j0Q57/I=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
+github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+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=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
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/cast v1.3.0/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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+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=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+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/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
+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=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+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/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+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 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+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=
+github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
+github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
+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/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
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.27/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/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.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.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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+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-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/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
+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=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+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/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=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+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.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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+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=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
+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-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-20201021035429-f5854403a974/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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a/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/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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=
+golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-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=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-20200501052902-10377860bb8e/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-20200817155316-9781c653f443/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-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+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 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+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=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+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-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/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-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.1/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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+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.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+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/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63 h1:YzfoEYWbODU5Fbt37+h7X16BWQbad7Q4S6gclTKFXM8=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+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=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+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=
+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=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+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=
+gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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=
+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/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/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=
+k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
+k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
+k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
+k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
+k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
+k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
+k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
+k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
+k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
+k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
+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=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+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.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+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/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go
index bcfeb34d5..70884aad7 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/attachment.go
@@ -27,4 +27,10 @@ type Attachment struct {
CaptureIoAttributionContext bool `json:"CaptureIoAttributionContext,omitempty"`
ReadOnly bool `json:"ReadOnly,omitempty"`
+
+ SupportCompressedVolumes bool `json:"SupportCompressedVolumes,omitempty"`
+
+ AlwaysAllowSparseFiles bool `json:"AlwaysAllowSparseFiles,omitempty"`
+
+ ExtensibleVirtualDiskType string `json:"ExtensibleVirtualDiskType,omitempty"`
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go
index 4fb231076..39a54432c 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/container.go
@@ -31,4 +31,6 @@ type Container struct {
RegistryChanges *RegistryChanges `json:"RegistryChanges,omitempty"`
AssignedDevices []Device `json:"AssignedDevices,omitempty"`
+
+ AdditionalDeviceNamespace *ContainerDefinitionDevice `json:"AdditionalDeviceNamespace,omitempty"`
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go
index f1a28cd38..0be0475d4 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/cpu_group_config.go
@@ -14,5 +14,5 @@ type CpuGroupConfig struct {
Affinity *CpuGroupAffinity `json:"Affinity,omitempty"`
GroupProperties []CpuGroupProperty `json:"GroupProperties,omitempty"`
// Hypervisor CPU group IDs exposed to clients
- HypervisorGroupId int32 `json:"HypervisorGroupId,omitempty"`
+ HypervisorGroupId uint64 `json:"HypervisorGroupId,omitempty"`
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go
index 107caddad..31c4538af 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/device.go
@@ -12,9 +12,9 @@ package hcsschema
type DeviceType string
const (
- ClassGUID DeviceType = "ClassGuid"
- DeviceInstance DeviceType = "DeviceInstance"
- GPUMirror DeviceType = "GpuMirror"
+ ClassGUID DeviceType = "ClassGuid"
+ DeviceInstanceID DeviceType = "DeviceInstance"
+ GPUMirror DeviceType = "GpuMirror"
)
type Device struct {
@@ -22,6 +22,6 @@ type Device struct {
Type DeviceType `json:"Type,omitempty"`
// The interface class guid of the device interfaces to assign to the container. Only used when Type is ClassGuid.
InterfaceClassGuid string `json:"InterfaceClassGuid,omitempty"`
- // The location path of the device to assign to the container. Only used when Type is DeviceInstance.
+ // The location path of the device to assign to the container. Only used when Type is DeviceInstanceID.
LocationPath string `json:"LocationPath,omitempty"`
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go
new file mode 100644
index 000000000..8dbe40b3b
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_container_definition_device.go
@@ -0,0 +1,14 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type ContainerDefinitionDevice struct {
+ DeviceExtension []DeviceExtension `json:"device_extension,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go
new file mode 100644
index 000000000..8fe89f927
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_category.go
@@ -0,0 +1,15 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type DeviceCategory struct {
+ Name string `json:"name,omitempty"`
+ InterfaceClass []InterfaceClass `json:"interface_class,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go
new file mode 100644
index 000000000..a62568d89
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_extension.go
@@ -0,0 +1,15 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type DeviceExtension struct {
+ DeviceCategory *DeviceCategory `json:"device_category,omitempty"`
+ Namespace *DeviceExtensionNamespace `json:"namespace,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go
new file mode 100644
index 000000000..a7410febd
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_instance.go
@@ -0,0 +1,17 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type DeviceInstance struct {
+ Id string `json:"id,omitempty"`
+ LocationPath string `json:"location_path,omitempty"`
+ PortName string `json:"port_name,omitempty"`
+ InterfaceClass []InterfaceClass `json:"interface_class,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go
new file mode 100644
index 000000000..355364064
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_device_namespace.go
@@ -0,0 +1,16 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type DeviceNamespace struct {
+ RequiresDriverstore bool `json:"requires_driverstore,omitempty"`
+ DeviceCategory []DeviceCategory `json:"device_category,omitempty"`
+ DeviceInstance []DeviceInstance `json:"device_instance,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go
new file mode 100644
index 000000000..7be98b541
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_interface_class.go
@@ -0,0 +1,16 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type InterfaceClass struct {
+ Type_ string `json:"type,omitempty"`
+ Identifier string `json:"identifier,omitempty"`
+ Recurse bool `json:"recurse,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go
new file mode 100644
index 000000000..3ab9cf1ec
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_namespace.go
@@ -0,0 +1,15 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type DeviceExtensionNamespace struct {
+ Ob *ObjectNamespace `json:"ob,omitempty"`
+ Device *DeviceNamespace `json:"device,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go
new file mode 100644
index 000000000..d2f51b3b5
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_directory.go
@@ -0,0 +1,18 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type ObjectDirectory struct {
+ Name string `json:"name,omitempty"`
+ Clonesd string `json:"clonesd,omitempty"`
+ Shadow string `json:"shadow,omitempty"`
+ Symlink []ObjectSymlink `json:"symlink,omitempty"`
+ Objdir []ObjectDirectory `json:"objdir,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go
new file mode 100644
index 000000000..47dfb55bf
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_namespace.go
@@ -0,0 +1,16 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type ObjectNamespace struct {
+ Shadow string `json:"shadow,omitempty"`
+ Symlink []ObjectSymlink `json:"symlink,omitempty"`
+ Objdir []ObjectDirectory `json:"objdir,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go
new file mode 100644
index 000000000..8867ebe5f
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/model_object_symlink.go
@@ -0,0 +1,18 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type ObjectSymlink struct {
+ Name string `json:"name,omitempty"`
+ Path string `json:"path,omitempty"`
+ Scope string `json:"scope,omitempty"`
+ Pathtoclone string `json:"pathtoclone,omitempty"`
+ AccessMask int32 `json:"access_mask,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go
new file mode 100644
index 000000000..9ef322f61
--- /dev/null
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/schema2/virtual_p_mem_mapping.go
@@ -0,0 +1,15 @@
+/*
+ * HCS API
+ *
+ * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen)
+ *
+ * API version: 2.4
+ * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
+ */
+
+package hcsschema
+
+type VirtualPMemMapping struct {
+ HostPath string `json:"HostPath,omitempty"`
+ ImageFormat string `json:"ImageFormat,omitempty"`
+}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go
index 262714b4d..7cf954c7b 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go
@@ -20,6 +20,7 @@ type HNSEndpoint struct {
IPv6Address net.IP `json:",omitempty"`
DNSSuffix string `json:",omitempty"`
DNSServerList string `json:",omitempty"`
+ DNSDomain string `json:",omitempty"`
GatewayAddress string `json:",omitempty"`
GatewayAddressV6 string `json:",omitempty"`
EnableInternalDNS bool `json:",omitempty"`
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go
index 6765aaead..591a2631e 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go
@@ -22,9 +22,9 @@ const (
type NatPolicy struct {
Type PolicyType `json:"Type"`
- Protocol string
- InternalPort uint16
- ExternalPort uint16
+ Protocol string `json:",omitempty"`
+ InternalPort uint16 `json:",omitempty"`
+ ExternalPort uint16 `json:",omitempty"`
}
type QosPolicy struct {
@@ -88,20 +88,20 @@ const (
type ACLPolicy struct {
Type PolicyType `json:"Type"`
Id string `json:"Id,omitempty"`
- Protocol uint16
- Protocols string `json:"Protocols,omitempty"`
- InternalPort uint16
+ Protocol uint16 `json:",omitempty"`
+ Protocols string `json:"Protocols,omitempty"`
+ InternalPort uint16 `json:",omitempty"`
Action ActionType
Direction DirectionType
- LocalAddresses string
- RemoteAddresses string
- LocalPorts string `json:"LocalPorts,omitempty"`
- LocalPort uint16
- RemotePorts string `json:"RemotePorts,omitempty"`
- RemotePort uint16
- RuleType RuleType `json:"RuleType,omitempty"`
- Priority uint16
- ServiceName string
+ LocalAddresses string `json:",omitempty"`
+ RemoteAddresses string `json:",omitempty"`
+ LocalPorts string `json:"LocalPorts,omitempty"`
+ LocalPort uint16 `json:",omitempty"`
+ RemotePorts string `json:"RemotePorts,omitempty"`
+ RemotePort uint16 `json:",omitempty"`
+ RuleType RuleType `json:"RuleType,omitempty"`
+ Priority uint16 `json:",omitempty"`
+ ServiceName string `json:",omitempty"`
}
type Policy struct {
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go
index ff81ac2c1..5debe974d 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go
@@ -21,7 +21,7 @@ func ActivateLayer(ctx context.Context, path string) (err error) {
err = activateLayer(&stdDriverInfo, path)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go
index ffee31ab1..480aee872 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go
@@ -21,7 +21,7 @@ func CreateLayer(ctx context.Context, path, parent string) (err error) {
err = createLayer(&stdDriverInfo, path, parent)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go
index 5a3809ae2..131aa94f1 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go
@@ -28,7 +28,7 @@ func CreateScratchLayer(ctx context.Context, path string, parentLayerPaths []str
err = createSandboxLayer(&stdDriverInfo, path, 0, layers)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go
index 787054e79..424467ac3 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go
@@ -19,7 +19,7 @@ func DestroyLayer(ctx context.Context, path string) (err error) {
err = destroyLayer(&stdDriverInfo, path)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go
index 22f7605be..035c9041e 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go
@@ -25,7 +25,7 @@ func ExpandScratchSize(ctx context.Context, path string, size uint64) (err error
err = expandSandboxSize(&stdDriverInfo, path, size)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
// Manually expand the volume now in order to work around bugs in 19H1 and
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go
index 09f0de1a4..97b27eb7d 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go
@@ -35,7 +35,7 @@ func ExportLayer(ctx context.Context, path string, exportFolderPath string, pare
err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go
index 4d22d0ecf..8d213f587 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go
@@ -27,7 +27,7 @@ func GetLayerMountPath(ctx context.Context, path string) (_ string, err error) {
log.G(ctx).Debug("Calling proc (1)")
err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil)
if err != nil {
- return "", hcserror.New(err, title+" - failed", "(first call)")
+ return "", hcserror.New(err, title, "(first call)")
}
// Allocate a mount path of the returned length.
@@ -41,7 +41,7 @@ func GetLayerMountPath(ctx context.Context, path string) (_ string, err error) {
log.G(ctx).Debug("Calling proc (2)")
err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0])
if err != nil {
- return "", hcserror.New(err, title+" - failed", "(second call)")
+ return "", hcserror.New(err, title, "(second call)")
}
mountPath := syscall.UTF16ToString(mountPathp[0:])
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go
index bcc8fbd42..ae1fff840 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go
@@ -21,7 +21,7 @@ func GetSharedBaseImages(ctx context.Context) (_ string, err error) {
var buffer *uint16
err = getBaseImages(&buffer)
if err != nil {
- return "", hcserror.New(err, title+" - failed", "")
+ return "", hcserror.New(err, title, "")
}
imageData := interop.ConvertAndFreeCoTaskMemString(buffer)
span.AddAttributes(trace.StringAttribute("imageData", imageData))
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go
index 3eaca2780..4b282fef9 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go
@@ -20,7 +20,7 @@ func GrantVmAccess(ctx context.Context, vmid string, filepath string) (err error
err = grantVmAccess(vmid, filepath)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go
index b3c150d66..687550f0b 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go
@@ -36,7 +36,7 @@ func ImportLayer(ctx context.Context, path string, importFolderPath string, pare
err = importLayer(&stdDriverInfo, path, importFolderPath, layers)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go
index c6999973c..01e672339 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go
@@ -21,7 +21,7 @@ func LayerExists(ctx context.Context, path string) (_ bool, err error) {
var exists uint32
err = layerExists(&stdDriverInfo, path, &exists)
if err != nil {
- return false, hcserror.New(err, title+" - failed", "")
+ return false, hcserror.New(err, title, "")
}
span.AddAttributes(trace.BoolAttribute("layer-exists", exists != 0))
return exists != 0, nil
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go
index 83ba72cfa..b7f3064f2 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go
@@ -76,7 +76,7 @@ func readTombstones(path string) (map[string]([]string), error) {
defer tf.Close()
s := bufio.NewScanner(tf)
if !s.Scan() || s.Text() != "\xef\xbb\xbfVersion 1.0" {
- return nil, errors.New("Invalid tombstones file")
+ return nil, errors.New("invalid tombstones file")
}
ts := make(map[string]([]string))
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go
index bcf39c6b8..09950297c 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go
@@ -17,12 +17,12 @@ func NameToGuid(ctx context.Context, name string) (_ guid.GUID, err error) {
ctx, span := trace.StartSpan(ctx, title) //nolint:ineffassign,staticcheck
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
- span.AddAttributes(trace.StringAttribute("name", name))
+ span.AddAttributes(trace.StringAttribute("objectName", name))
var id guid.GUID
err = nameToGuid(name, &id)
if err != nil {
- return guid.GUID{}, hcserror.New(err, title+" - failed", "")
+ return guid.GUID{}, hcserror.New(err, title, "")
}
span.AddAttributes(trace.StringAttribute("guid", id.String()))
return id, nil
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go
index 55f7730d0..90129faef 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go
@@ -38,7 +38,7 @@ func PrepareLayer(ctx context.Context, path string, parentLayerPaths []string) (
defer prepareLayerLock.Unlock()
err = prepareLayer(&stdDriverInfo, path, layers)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go
index 79fb98678..71b130c52 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go
@@ -19,7 +19,7 @@ func UnprepareLayer(ctx context.Context, path string) (err error) {
err = unprepareLayer(&stdDriverInfo, path)
if err != nil {
- return hcserror.New(err, title+" - failed", "")
+ return hcserror.New(err, title, "")
}
return nil
}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go
index 83f704064..53f62948c 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/memory.go
@@ -1,27 +1,4 @@
package winapi
-// VOID RtlMoveMemory(
-// _Out_ VOID UNALIGNED *Destination,
-// _In_ const VOID UNALIGNED *Source,
-// _In_ SIZE_T Length
-// );
-//sys RtlMoveMemory(destination *byte, source *byte, length uintptr) (err error) = kernel32.RtlMoveMemory
-
//sys LocalAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc
//sys LocalFree(ptr uintptr) = kernel32.LocalFree
-
-// BOOL QueryWorkingSet(
-// HANDLE hProcess,
-// PVOID pv,
-// DWORD cb
-// );
-//sys QueryWorkingSet(handle windows.Handle, pv uintptr, cb uint32) (err error) = psapi.QueryWorkingSet
-
-type PSAPI_WORKING_SET_INFORMATION struct {
- NumberOfEntries uintptr
- WorkingSetInfo [1]PSAPI_WORKING_SET_BLOCK
-}
-
-type PSAPI_WORKING_SET_BLOCK struct {
- Flags uintptr
-}
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go
index db59567d0..859b753c2 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/utils.go
@@ -20,36 +20,41 @@ func Uint16BufferToSlice(buffer *uint16, bufferLength int) (result []uint16) {
return
}
+// UnicodeString corresponds to UNICODE_STRING win32 struct defined here
+// https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string
type UnicodeString struct {
Length uint16
MaximumLength uint16
Buffer *uint16
}
+// NTSTRSAFE_UNICODE_STRING_MAX_CCH is a constant defined in ntstrsafe.h. This value
+// denotes the maximum number of wide chars a path can have.
+const NTSTRSAFE_UNICODE_STRING_MAX_CCH = 32767
+
//String converts a UnicodeString to a golang string
func (uni UnicodeString) String() string {
// UnicodeString is not guaranteed to be null terminated, therefore
// use the UnicodeString's Length field
- return syscall.UTF16ToString(Uint16BufferToSlice(uni.Buffer, int(uni.Length/2)))
+ return windows.UTF16ToString(Uint16BufferToSlice(uni.Buffer, int(uni.Length/2)))
}
// NewUnicodeString allocates a new UnicodeString and copies `s` into
// the buffer of the new UnicodeString.
func NewUnicodeString(s string) (*UnicodeString, error) {
- // Get length of original `s` to use in the UnicodeString since the `buf`
- // created later will have an additional trailing null character
- length := len(s)
- if length > 32767 {
- return nil, syscall.ENAMETOOLONG
- }
-
buf, err := windows.UTF16FromString(s)
if err != nil {
return nil, err
}
+
+ if len(buf) > NTSTRSAFE_UNICODE_STRING_MAX_CCH {
+ return nil, syscall.ENAMETOOLONG
+ }
+
uni := &UnicodeString{
- Length: uint16(length * 2),
- MaximumLength: uint16(length * 2),
+ // The length is in bytes and should not include the trailing null character.
+ Length: uint16((len(buf) - 1) * 2),
+ MaximumLength: uint16((len(buf) - 1) * 2),
Buffer: &buf[0],
}
return uni, nil
diff --git a/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go
index 2941b0f98..59ddee274 100644
--- a/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go
+++ b/vendor/github.com/Microsoft/hcsshim/internal/winapi/zsyscall_windows.go
@@ -41,7 +41,6 @@ var (
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
- modpsapi = windows.NewLazySystemDLL("psapi.dll")
modcfgmgr32 = windows.NewLazySystemDLL("cfgmgr32.dll")
procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation")
@@ -57,10 +56,8 @@ var (
procNtOpenJobObject = modntdll.NewProc("NtOpenJobObject")
procNtCreateJobObject = modntdll.NewProc("NtCreateJobObject")
procLogonUserW = modadvapi32.NewProc("LogonUserW")
- procRtlMoveMemory = modkernel32.NewProc("RtlMoveMemory")
procLocalAlloc = modkernel32.NewProc("LocalAlloc")
procLocalFree = modkernel32.NewProc("LocalFree")
- procQueryWorkingSet = modpsapi.NewProc("QueryWorkingSet")
procGetProcessImageFileNameW = modkernel32.NewProc("GetProcessImageFileNameW")
procGetActiveProcessorCount = modkernel32.NewProc("GetActiveProcessorCount")
procCM_Get_Device_ID_List_SizeA = modcfgmgr32.NewProc("CM_Get_Device_ID_List_SizeA")
@@ -219,18 +216,6 @@ func LogonUser(username *uint16, domain *uint16, password *uint16, logonType uin
return
}
-func RtlMoveMemory(destination *byte, source *byte, length uintptr) (err error) {
- r1, _, e1 := syscall.Syscall(procRtlMoveMemory.Addr(), 3, uintptr(unsafe.Pointer(destination)), uintptr(unsafe.Pointer(source)), uintptr(length))
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
func LocalAlloc(flags uint32, size int) (ptr uintptr) {
r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0)
ptr = uintptr(r0)
@@ -242,18 +227,6 @@ func LocalFree(ptr uintptr) {
return
}
-func QueryWorkingSet(handle windows.Handle, pv uintptr, cb uint32) (err error) {
- r1, _, e1 := syscall.Syscall(procQueryWorkingSet.Addr(), 3, uintptr(handle), uintptr(pv), uintptr(cb))
- if r1 == 0 {
- if e1 != 0 {
- err = errnoErr(e1)
- } else {
- err = syscall.EINVAL
- }
- }
- return
-}
-
func GetProcessImageFileName(hProcess windows.Handle, imageFileName *uint16, nSize uint32) (size uint32, err error) {
r0, _, e1 := syscall.Syscall(procGetProcessImageFileNameW.Addr(), 3, uintptr(hProcess), uintptr(unsafe.Pointer(imageFileName)), uintptr(nSize))
size = uint32(r0)
diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go
index e9267b955..49fb740cd 100644
--- a/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go
+++ b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go
@@ -35,4 +35,7 @@ const (
// V20H2 corresponds to Windows Server 20H2 (semi-annual channel).
V20H2 = 19042
+
+ // V21H1 corresponds to Windows Server 21H1 (semi-annual channel).
+ V21H1 = 19043
)
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore b/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore
index 6c7385fa2..1b87ff10e 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/.gitignore
@@ -1,5 +1,6 @@
test/test
+test/test.coverage
test/piggie/piggie
-test/phaul
+test/phaul/phaul
+test/phaul/phaul.coverage
image
-stats/stats.proto
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile b/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile
index a5c5f5542..558e61453 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/Makefile
@@ -1,5 +1,7 @@
+SHELL = /bin/bash
GO ?= go
CC ?= gcc
+COVERAGE_PATH ?= $(shell pwd)/.coverage
all: build test phaul-test
@@ -9,13 +11,15 @@ lint:
build:
$(GO) build -v ./...
-TEST_BINARIES := test/test test/piggie/piggie test/phaul/phaul
+TEST_PAYLOAD := test/piggie/piggie
+TEST_BINARIES := test/test $(TEST_PAYLOAD) test/phaul/phaul
+COVERAGE_BINARIES := test/test.coverage test/phaul/phaul.coverage
test-bin: $(TEST_BINARIES)
test/piggie/piggie: test/piggie/piggie.c
$(CC) $^ -o $@
-test/test: test/*.go
+test/test: test/main.go
$(GO) build -v -o $@ $^
test: $(TEST_BINARIES)
@@ -27,7 +31,7 @@ test: $(TEST_BINARIES)
}
rm -rf image
-test/phaul/phaul: test/phaul/*.go
+test/phaul/phaul: test/phaul/main.go
$(GO) build -v -o $@ $^
phaul-test: $(TEST_BINARIES)
@@ -37,10 +41,39 @@ phaul-test: $(TEST_BINARIES)
pkill -9 piggie; \
}
+test/test.coverage: test/*.go
+ $(GO) test \
+ -covermode=count \
+ -coverpkg=./... \
+ -mod=vendor \
+ -tags coverage \
+ -buildmode=pie -c -o $@ $^
+
+test/phaul/phaul.coverage: test/phaul/*.go
+ $(GO) test \
+ -covermode=count \
+ -coverpkg=./... \
+ -mod=vendor \
+ -tags coverage \
+ -buildmode=pie -c -o $@ $^
+
+coverage: $(COVERAGE_BINARIES) $(TEST_PAYLOAD)
+ mkdir -p $(COVERAGE_PATH)
+ mkdir -p image
+ PID=$$(test/piggie/piggie) && { \
+ test/test.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE dump $$PID image && \
+ test/test.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE restore image; \
+ pkill -9 piggie; \
+ }
+ rm -rf image
+ PID=$$(test/piggie/piggie) && { \
+ test/phaul/phaul.coverage -test.coverprofile=coverprofile.integration.$$RANDOM -test.outputdir=${COVERAGE_PATH} COVERAGE $$PID; \
+ pkill -9 piggie; \
+ }
+
clean:
- @rm -f $(TEST_BINARIES)
- @rm -rf image
- @rm -f rpc/rpc.proto stats/stats.proto
+ @rm -f $(TEST_BINARIES) $(COVERAGE_BINARIES) codecov
+ @rm -rf image $(COVERAGE_PATH)
rpc/rpc.proto:
curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/rpc.proto -o $@
@@ -49,14 +82,19 @@ stats/stats.proto:
curl -sSL https://raw.githubusercontent.com/checkpoint-restore/criu/master/images/stats.proto -o $@
rpc/rpc.pb.go: rpc/rpc.proto
- protoc --go_out=. --go_opt=Mrpc/rpc.proto=rpc/ $^
+ protoc --go_out=. --go_opt=M$^=rpc/ $^
stats/stats.pb.go: stats/stats.proto
- protoc --go_out=. $^
+ protoc --go_out=. --go_opt=M$^=stats/ $^
vendor:
GO111MODULE=on $(GO) mod tidy
GO111MODULE=on $(GO) mod vendor
GO111MODULE=on $(GO) mod verify
-.PHONY: build test phaul-test test-bin clean lint vendor
+codecov:
+ curl -Os https://uploader.codecov.io/latest/linux/codecov
+ chmod +x codecov
+ ./codecov -f '.coverage/*'
+
+.PHONY: build test phaul-test test-bin clean lint vendor coverage codecov
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/README.md b/vendor/github.com/checkpoint-restore/go-criu/v5/README.md
index 390da3e98..a7483321b 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/README.md
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/README.md
@@ -16,7 +16,7 @@ The following example would print the version of CRIU:
import (
"log"
- "github.com/checkpoint/restore/go-criu/v5"
+ "github.com/checkpoint-restore/go-criu/v5"
)
func main() {
@@ -50,6 +50,7 @@ The following table shows the relation between go-criu and criu versions:
| Major version | Latest release | CRIU version |
| -------------- | -------------- | ------------ |
+| v5             | 5.2.0         | 3.16         |
| v5             | 5.0.0         | 3.15         |
| v4             | 4.1.0         | 3.14         |
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/go.mod b/vendor/github.com/checkpoint-restore/go-criu/v5/go.mod
index 69595701f..cf4fea9f0 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/go.mod
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/go.mod
@@ -3,7 +3,6 @@ module github.com/checkpoint-restore/go-criu/v5
go 1.13
require (
- github.com/golang/protobuf v1.5.2
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
- google.golang.org/protobuf v1.26.0
+ google.golang.org/protobuf v1.27.1
)
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/go.sum b/vendor/github.com/checkpoint-restore/go-criu/v5/go.sum
index 7e17df214..789fdcb11 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/go.sum
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/go.sum
@@ -1,6 +1,4 @@
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-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/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
@@ -8,5 +6,5 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go b/vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go
new file mode 100644
index 000000000..24cc3989a
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/magic/types.go
@@ -0,0 +1,12 @@
+package magic
+
+const (
+ ImgCommonMagic = 0x54564319 /* Sarov (a.k.a. Arzamas-16) */
+ ImgServiceMagic = 0x55105940 /* Zlatoust */
+ StatsMagic = 0x57093306 /* Ostashkov */
+
+ PrimaryMagicOffset = 0x0
+ SecondaryMagicOffset = 0x4
+ SizeOffset = 0x8
+ PayloadOffset = 0xC
+)
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go b/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go
index 9f22f1539..15e33fea5 100644
--- a/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/rpc/rpc.pb.go
@@ -2,8 +2,8 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.26.0
-// protoc v3.14.0
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
// source: rpc/rpc.proto
package rpc
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go
new file mode 100644
index 000000000..ff011fc2c
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.pb.go
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: MIT
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// protoc-gen-go v1.27.1
+// protoc v3.12.4
+// source: stats/stats.proto
+
+package stats
+
+import (
+ protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+ protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+ reflect "reflect"
+ sync "sync"
+)
+
+const (
+ // Verify that this generated code is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+ // Verify that runtime/protoimpl is sufficiently up-to-date.
+ _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This one contains statistics about dump/restore process
+type DumpStatsEntry struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ FreezingTime *uint32 `protobuf:"varint,1,req,name=freezing_time,json=freezingTime" json:"freezing_time,omitempty"`
+ FrozenTime *uint32 `protobuf:"varint,2,req,name=frozen_time,json=frozenTime" json:"frozen_time,omitempty"`
+ MemdumpTime *uint32 `protobuf:"varint,3,req,name=memdump_time,json=memdumpTime" json:"memdump_time,omitempty"`
+ MemwriteTime *uint32 `protobuf:"varint,4,req,name=memwrite_time,json=memwriteTime" json:"memwrite_time,omitempty"`
+ PagesScanned *uint64 `protobuf:"varint,5,req,name=pages_scanned,json=pagesScanned" json:"pages_scanned,omitempty"`
+ PagesSkippedParent *uint64 `protobuf:"varint,6,req,name=pages_skipped_parent,json=pagesSkippedParent" json:"pages_skipped_parent,omitempty"`
+ PagesWritten *uint64 `protobuf:"varint,7,req,name=pages_written,json=pagesWritten" json:"pages_written,omitempty"`
+ IrmapResolve *uint32 `protobuf:"varint,8,opt,name=irmap_resolve,json=irmapResolve" json:"irmap_resolve,omitempty"`
+ PagesLazy *uint64 `protobuf:"varint,9,req,name=pages_lazy,json=pagesLazy" json:"pages_lazy,omitempty"`
+ PagePipes *uint64 `protobuf:"varint,10,opt,name=page_pipes,json=pagePipes" json:"page_pipes,omitempty"`
+ PagePipeBufs *uint64 `protobuf:"varint,11,opt,name=page_pipe_bufs,json=pagePipeBufs" json:"page_pipe_bufs,omitempty"`
+ ShpagesScanned *uint64 `protobuf:"varint,12,opt,name=shpages_scanned,json=shpagesScanned" json:"shpages_scanned,omitempty"`
+ ShpagesSkippedParent *uint64 `protobuf:"varint,13,opt,name=shpages_skipped_parent,json=shpagesSkippedParent" json:"shpages_skipped_parent,omitempty"`
+ ShpagesWritten *uint64 `protobuf:"varint,14,opt,name=shpages_written,json=shpagesWritten" json:"shpages_written,omitempty"`
+}
+
+func (x *DumpStatsEntry) Reset() {
+ *x = DumpStatsEntry{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_stats_stats_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *DumpStatsEntry) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DumpStatsEntry) ProtoMessage() {}
+
+func (x *DumpStatsEntry) ProtoReflect() protoreflect.Message {
+ mi := &file_stats_stats_proto_msgTypes[0]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DumpStatsEntry.ProtoReflect.Descriptor instead.
+func (*DumpStatsEntry) Descriptor() ([]byte, []int) {
+ return file_stats_stats_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *DumpStatsEntry) GetFreezingTime() uint32 {
+ if x != nil && x.FreezingTime != nil {
+ return *x.FreezingTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetFrozenTime() uint32 {
+ if x != nil && x.FrozenTime != nil {
+ return *x.FrozenTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetMemdumpTime() uint32 {
+ if x != nil && x.MemdumpTime != nil {
+ return *x.MemdumpTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetMemwriteTime() uint32 {
+ if x != nil && x.MemwriteTime != nil {
+ return *x.MemwriteTime
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesScanned() uint64 {
+ if x != nil && x.PagesScanned != nil {
+ return *x.PagesScanned
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesSkippedParent() uint64 {
+ if x != nil && x.PagesSkippedParent != nil {
+ return *x.PagesSkippedParent
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesWritten() uint64 {
+ if x != nil && x.PagesWritten != nil {
+ return *x.PagesWritten
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetIrmapResolve() uint32 {
+ if x != nil && x.IrmapResolve != nil {
+ return *x.IrmapResolve
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagesLazy() uint64 {
+ if x != nil && x.PagesLazy != nil {
+ return *x.PagesLazy
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagePipes() uint64 {
+ if x != nil && x.PagePipes != nil {
+ return *x.PagePipes
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetPagePipeBufs() uint64 {
+ if x != nil && x.PagePipeBufs != nil {
+ return *x.PagePipeBufs
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetShpagesScanned() uint64 {
+ if x != nil && x.ShpagesScanned != nil {
+ return *x.ShpagesScanned
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetShpagesSkippedParent() uint64 {
+ if x != nil && x.ShpagesSkippedParent != nil {
+ return *x.ShpagesSkippedParent
+ }
+ return 0
+}
+
+func (x *DumpStatsEntry) GetShpagesWritten() uint64 {
+ if x != nil && x.ShpagesWritten != nil {
+ return *x.ShpagesWritten
+ }
+ return 0
+}
+
+type RestoreStatsEntry struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ PagesCompared *uint64 `protobuf:"varint,1,req,name=pages_compared,json=pagesCompared" json:"pages_compared,omitempty"`
+ PagesSkippedCow *uint64 `protobuf:"varint,2,req,name=pages_skipped_cow,json=pagesSkippedCow" json:"pages_skipped_cow,omitempty"`
+ ForkingTime *uint32 `protobuf:"varint,3,req,name=forking_time,json=forkingTime" json:"forking_time,omitempty"`
+ RestoreTime *uint32 `protobuf:"varint,4,req,name=restore_time,json=restoreTime" json:"restore_time,omitempty"`
+ PagesRestored *uint64 `protobuf:"varint,5,opt,name=pages_restored,json=pagesRestored" json:"pages_restored,omitempty"`
+}
+
+func (x *RestoreStatsEntry) Reset() {
+ *x = RestoreStatsEntry{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_stats_stats_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *RestoreStatsEntry) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RestoreStatsEntry) ProtoMessage() {}
+
+func (x *RestoreStatsEntry) ProtoReflect() protoreflect.Message {
+ mi := &file_stats_stats_proto_msgTypes[1]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use RestoreStatsEntry.ProtoReflect.Descriptor instead.
+func (*RestoreStatsEntry) Descriptor() ([]byte, []int) {
+ return file_stats_stats_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *RestoreStatsEntry) GetPagesCompared() uint64 {
+ if x != nil && x.PagesCompared != nil {
+ return *x.PagesCompared
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetPagesSkippedCow() uint64 {
+ if x != nil && x.PagesSkippedCow != nil {
+ return *x.PagesSkippedCow
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetForkingTime() uint32 {
+ if x != nil && x.ForkingTime != nil {
+ return *x.ForkingTime
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetRestoreTime() uint32 {
+ if x != nil && x.RestoreTime != nil {
+ return *x.RestoreTime
+ }
+ return 0
+}
+
+func (x *RestoreStatsEntry) GetPagesRestored() uint64 {
+ if x != nil && x.PagesRestored != nil {
+ return *x.PagesRestored
+ }
+ return 0
+}
+
+type StatsEntry struct {
+ state protoimpl.MessageState
+ sizeCache protoimpl.SizeCache
+ unknownFields protoimpl.UnknownFields
+
+ Dump *DumpStatsEntry `protobuf:"bytes,1,opt,name=dump" json:"dump,omitempty"`
+ Restore *RestoreStatsEntry `protobuf:"bytes,2,opt,name=restore" json:"restore,omitempty"`
+}
+
+func (x *StatsEntry) Reset() {
+ *x = StatsEntry{}
+ if protoimpl.UnsafeEnabled {
+ mi := &file_stats_stats_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+ }
+}
+
+func (x *StatsEntry) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StatsEntry) ProtoMessage() {}
+
+func (x *StatsEntry) ProtoReflect() protoreflect.Message {
+ mi := &file_stats_stats_proto_msgTypes[2]
+ if protoimpl.UnsafeEnabled && x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use StatsEntry.ProtoReflect.Descriptor instead.
+func (*StatsEntry) Descriptor() ([]byte, []int) {
+ return file_stats_stats_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *StatsEntry) GetDump() *DumpStatsEntry {
+ if x != nil {
+ return x.Dump
+ }
+ return nil
+}
+
+func (x *StatsEntry) GetRestore() *RestoreStatsEntry {
+ if x != nil {
+ return x.Restore
+ }
+ return nil
+}
+
+var File_stats_stats_proto protoreflect.FileDescriptor
+
+var file_stats_stats_proto_rawDesc = []byte{
+ 0x0a, 0x11, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x73, 0x2e, 0x70, 0x72,
+ 0x6f, 0x74, 0x6f, 0x22, 0xad, 0x04, 0x0a, 0x10, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x73, 0x74, 0x61,
+ 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x72, 0x65, 0x65,
+ 0x7a, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x02, 0x28, 0x0d, 0x52,
+ 0x0c, 0x66, 0x72, 0x65, 0x65, 0x7a, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1f, 0x0a,
+ 0x0b, 0x66, 0x72, 0x6f, 0x7a, 0x65, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x02,
+ 0x28, 0x0d, 0x52, 0x0a, 0x66, 0x72, 0x6f, 0x7a, 0x65, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21,
+ 0x0a, 0x0c, 0x6d, 0x65, 0x6d, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03,
+ 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x6d, 0x65, 0x6d, 0x64, 0x75, 0x6d, 0x70, 0x54, 0x69, 0x6d,
+ 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6d, 0x65, 0x6d, 0x77, 0x72, 0x69, 0x74, 0x65, 0x5f, 0x74, 0x69,
+ 0x6d, 0x65, 0x18, 0x04, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0c, 0x6d, 0x65, 0x6d, 0x77, 0x72, 0x69,
+ 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f,
+ 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x05, 0x20, 0x02, 0x28, 0x04, 0x52, 0x0c, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x53, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72,
+ 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x02, 0x28, 0x04, 0x52, 0x12, 0x70, 0x61, 0x67, 0x65, 0x73,
+ 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a,
+ 0x0d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x07,
+ 0x20, 0x02, 0x28, 0x04, 0x52, 0x0c, 0x70, 0x61, 0x67, 0x65, 0x73, 0x57, 0x72, 0x69, 0x74, 0x74,
+ 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x72, 0x6d, 0x61, 0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f,
+ 0x6c, 0x76, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x69, 0x72, 0x6d, 0x61, 0x70,
+ 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x73,
+ 0x5f, 0x6c, 0x61, 0x7a, 0x79, 0x18, 0x09, 0x20, 0x02, 0x28, 0x04, 0x52, 0x09, 0x70, 0x61, 0x67,
+ 0x65, 0x73, 0x4c, 0x61, 0x7a, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x70,
+ 0x69, 0x70, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x70, 0x61, 0x67, 0x65,
+ 0x50, 0x69, 0x70, 0x65, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x69,
+ 0x70, 0x65, 0x5f, 0x62, 0x75, 0x66, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x70,
+ 0x61, 0x67, 0x65, 0x50, 0x69, 0x70, 0x65, 0x42, 0x75, 0x66, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x73,
+ 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x64, 0x18, 0x0c,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x53, 0x63, 0x61,
+ 0x6e, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x16, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f,
+ 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x0d,
+ 0x20, 0x01, 0x28, 0x04, 0x52, 0x14, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x53, 0x6b, 0x69,
+ 0x70, 0x70, 0x65, 0x64, 0x50, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x68,
+ 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x77, 0x72, 0x69, 0x74, 0x74, 0x65, 0x6e, 0x18, 0x0e, 0x20,
+ 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x68, 0x70, 0x61, 0x67, 0x65, 0x73, 0x57, 0x72, 0x69, 0x74,
+ 0x74, 0x65, 0x6e, 0x22, 0xd5, 0x01, 0x0a, 0x13, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f,
+ 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20,
+ 0x02, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61, 0x67, 0x65, 0x73, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x72,
+ 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x73, 0x6b, 0x69, 0x70,
+ 0x70, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x77, 0x18, 0x02, 0x20, 0x02, 0x28, 0x04, 0x52, 0x0f, 0x70,
+ 0x61, 0x67, 0x65, 0x73, 0x53, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x43, 0x6f, 0x77, 0x12, 0x21,
+ 0x0a, 0x0c, 0x66, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x03,
+ 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x54, 0x69, 0x6d,
+ 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x69, 0x6d,
+ 0x65, 0x18, 0x04, 0x20, 0x02, 0x28, 0x0d, 0x52, 0x0b, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65,
+ 0x54, 0x69, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, 0x72, 0x65,
+ 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x70, 0x61,
+ 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x22, 0x64, 0x0a, 0x0b, 0x73,
+ 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x04, 0x64, 0x75,
+ 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x64, 0x75, 0x6d, 0x70, 0x5f,
+ 0x73, 0x74, 0x61, 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x75, 0x6d,
+ 0x70, 0x12, 0x2e, 0x0a, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x73, 0x74, 0x61,
+ 0x74, 0x73, 0x5f, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x72, 0x65, 0x73, 0x74, 0x6f, 0x72,
+ 0x65,
+}
+
+var (
+ file_stats_stats_proto_rawDescOnce sync.Once
+ file_stats_stats_proto_rawDescData = file_stats_stats_proto_rawDesc
+)
+
+func file_stats_stats_proto_rawDescGZIP() []byte {
+ file_stats_stats_proto_rawDescOnce.Do(func() {
+ file_stats_stats_proto_rawDescData = protoimpl.X.CompressGZIP(file_stats_stats_proto_rawDescData)
+ })
+ return file_stats_stats_proto_rawDescData
+}
+
+var file_stats_stats_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_stats_stats_proto_goTypes = []interface{}{
+ (*DumpStatsEntry)(nil), // 0: dump_stats_entry
+ (*RestoreStatsEntry)(nil), // 1: restore_stats_entry
+ (*StatsEntry)(nil), // 2: stats_entry
+}
+var file_stats_stats_proto_depIdxs = []int32{
+ 0, // 0: stats_entry.dump:type_name -> dump_stats_entry
+ 1, // 1: stats_entry.restore:type_name -> restore_stats_entry
+ 2, // [2:2] is the sub-list for method output_type
+ 2, // [2:2] is the sub-list for method input_type
+ 2, // [2:2] is the sub-list for extension type_name
+ 2, // [2:2] is the sub-list for extension extendee
+ 0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_stats_stats_proto_init() }
+func file_stats_stats_proto_init() {
+ if File_stats_stats_proto != nil {
+ return
+ }
+ if !protoimpl.UnsafeEnabled {
+ file_stats_stats_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*DumpStatsEntry); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_stats_stats_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*RestoreStatsEntry); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ file_stats_stats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+ switch v := v.(*StatsEntry); i {
+ case 0:
+ return &v.state
+ case 1:
+ return &v.sizeCache
+ case 2:
+ return &v.unknownFields
+ default:
+ return nil
+ }
+ }
+ }
+ type x struct{}
+ out := protoimpl.TypeBuilder{
+ File: protoimpl.DescBuilder{
+ GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+ RawDescriptor: file_stats_stats_proto_rawDesc,
+ NumEnums: 0,
+ NumMessages: 3,
+ NumExtensions: 0,
+ NumServices: 0,
+ },
+ GoTypes: file_stats_stats_proto_goTypes,
+ DependencyIndexes: file_stats_stats_proto_depIdxs,
+ MessageInfos: file_stats_stats_proto_msgTypes,
+ }.Build()
+ File_stats_stats_proto = out.File
+ file_stats_stats_proto_rawDesc = nil
+ file_stats_stats_proto_goTypes = nil
+ file_stats_stats_proto_depIdxs = nil
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.proto b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.proto
new file mode 100644
index 000000000..64e46181d
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/stats.proto
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: MIT
+
+syntax = "proto2";
+
+// This one contains statistics about dump/restore process
+message dump_stats_entry {
+ required uint32 freezing_time = 1;
+ required uint32 frozen_time = 2;
+ required uint32 memdump_time = 3;
+ required uint32 memwrite_time = 4;
+
+ required uint64 pages_scanned = 5;
+ required uint64 pages_skipped_parent = 6;
+ required uint64 pages_written = 7;
+
+ optional uint32 irmap_resolve = 8;
+
+ required uint64 pages_lazy = 9;
+ optional uint64 page_pipes = 10;
+ optional uint64 page_pipe_bufs = 11;
+
+ optional uint64 shpages_scanned = 12;
+ optional uint64 shpages_skipped_parent = 13;
+ optional uint64 shpages_written = 14;
+}
+
+message restore_stats_entry {
+ required uint64 pages_compared = 1;
+ required uint64 pages_skipped_cow = 2;
+
+ required uint32 forking_time = 3;
+ required uint32 restore_time = 4;
+
+ optional uint64 pages_restored = 5;
+}
+
+message stats_entry {
+ optional dump_stats_entry dump = 1;
+ optional restore_stats_entry restore = 2;
+}
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go
new file mode 100644
index 000000000..9044ad976
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/types.go
@@ -0,0 +1,6 @@
+package stats
+
+const (
+ StatsDump = "stats-dump"
+ StatsRestore = "stats-restore"
+)
diff --git a/vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go
new file mode 100644
index 000000000..368b2039e
--- /dev/null
+++ b/vendor/github.com/checkpoint-restore/go-criu/v5/stats/utils.go
@@ -0,0 +1,54 @@
+package stats
+
+import (
+ "encoding/binary"
+ "errors"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ "github.com/checkpoint-restore/go-criu/v5/magic"
+ "google.golang.org/protobuf/proto"
+)
+
+func readStatisticsFile(imgDir *os.File, fileName string) (*StatsEntry, error) {
+ buf, err := ioutil.ReadFile(filepath.Join(imgDir.Name(), fileName))
+ if err != nil {
+ return nil, err
+ }
+
+ if binary.LittleEndian.Uint32(buf[magic.PrimaryMagicOffset:magic.SecondaryMagicOffset]) != magic.ImgServiceMagic {
+ return nil, errors.New("Primary magic not found")
+ }
+
+ if binary.LittleEndian.Uint32(buf[magic.SecondaryMagicOffset:magic.SizeOffset]) != magic.StatsMagic {
+ return nil, errors.New("Secondary magic not found")
+ }
+
+ payloadSize := binary.LittleEndian.Uint32(buf[magic.SizeOffset:magic.PayloadOffset])
+
+ st := &StatsEntry{}
+ if err := proto.Unmarshal(buf[magic.PayloadOffset:magic.PayloadOffset+payloadSize], st); err != nil {
+ return nil, err
+ }
+
+ return st, nil
+}
+
+func CriuGetDumpStats(imgDir *os.File) (*DumpStatsEntry, error) {
+ st, err := readStatisticsFile(imgDir, StatsDump)
+ if err != nil {
+ return nil, err
+ }
+
+ return st.GetDump(), nil
+}
+
+func CriuGetRestoreStats(imgDir *os.File) (*RestoreStatsEntry, error) {
+ st, err := readStatisticsFile(imgDir, StatsRestore)
+ if err != nil {
+ return nil, err
+ }
+
+ return st.GetRestore(), nil
+}
diff --git a/vendor/github.com/containers/common/libimage/download.go b/vendor/github.com/containers/common/libimage/download.go
deleted file mode 100644
index 54edf1b9a..000000000
--- a/vendor/github.com/containers/common/libimage/download.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package libimage
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "os"
-
- "github.com/pkg/errors"
-)
-
-// tmpdir returns a path to a temporary directory.
-func tmpdir() string {
- tmpdir := os.Getenv("TMPDIR")
- if tmpdir == "" {
- tmpdir = "/var/tmp"
- }
-
- return tmpdir
-}
-
-// downloadFromURL downloads an image in the format "https:/example.com/myimage.tar"
-// and temporarily saves in it $TMPDIR/importxyz, which is deleted after the image is imported
-func (r *Runtime) downloadFromURL(source string) (string, error) {
- fmt.Printf("Downloading from %q\n", source)
-
- outFile, err := ioutil.TempFile(r.systemContext.BigFilesTemporaryDir, "import")
- if err != nil {
- return "", errors.Wrap(err, "error creating file")
- }
- defer outFile.Close()
-
- response, err := http.Get(source) // nolint:noctx
- if err != nil {
- return "", errors.Wrapf(err, "error downloading %q", source)
- }
- defer response.Body.Close()
-
- _, err = io.Copy(outFile, response.Body)
- if err != nil {
- return "", errors.Wrapf(err, "error saving %s to %s", source, outFile.Name())
- }
-
- return outFile.Name(), nil
-}
diff --git a/vendor/github.com/containers/common/libimage/filters.go b/vendor/github.com/containers/common/libimage/filters.go
index 833f940cc..521af5d06 100644
--- a/vendor/github.com/containers/common/libimage/filters.go
+++ b/vendor/github.com/containers/common/libimage/filters.go
@@ -50,6 +50,18 @@ func filterImages(images []*Image, filters []filterFunc) ([]*Image, error) {
func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOptions) ([]filterFunc, error) {
logrus.Tracef("Parsing image filters %s", options.Filters)
+ var tree *layerTree
+ getTree := func() (*layerTree, error) {
+ if tree == nil {
+ t, err := r.layerTree()
+ if err != nil {
+ return nil, err
+ }
+ tree = t
+ }
+ return tree, nil
+ }
+
filterFuncs := []filterFunc{}
for _, filter := range options.Filters {
var key, value string
@@ -93,7 +105,11 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
if err != nil {
return nil, errors.Wrapf(err, "non-boolean value %q for dangling filter", value)
}
- filterFuncs = append(filterFuncs, filterDangling(ctx, dangling))
+ t, err := getTree()
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, filterDangling(ctx, dangling, t))
case "id":
filterFuncs = append(filterFuncs, filterID(value))
@@ -103,7 +119,11 @@ func (r *Runtime) compileImageFilters(ctx context.Context, options *ListImagesOp
if err != nil {
return nil, errors.Wrapf(err, "non-boolean value %q for intermediate filter", value)
}
- filterFuncs = append(filterFuncs, filterIntermediate(ctx, intermediate))
+ t, err := getTree()
+ if err != nil {
+ return nil, err
+ }
+ filterFuncs = append(filterFuncs, filterIntermediate(ctx, intermediate, t))
case "label":
filterFuncs = append(filterFuncs, filterLabel(ctx, value))
@@ -221,9 +241,9 @@ func filterContainers(value string, fn IsExternalContainerFunc) filterFunc {
}
// filterDangling creates a dangling filter for matching the specified value.
-func filterDangling(ctx context.Context, value bool) filterFunc {
+func filterDangling(ctx context.Context, value bool, tree *layerTree) filterFunc {
return func(img *Image) (bool, error) {
- isDangling, err := img.IsDangling(ctx)
+ isDangling, err := img.isDangling(ctx, tree)
if err != nil {
return false, err
}
@@ -241,9 +261,9 @@ func filterID(value string) filterFunc {
// filterIntermediate creates an intermediate filter for images. An image is
// considered to be an intermediate image if it is dangling (i.e., no tags) and
// has no children (i.e., no other image depends on it).
-func filterIntermediate(ctx context.Context, value bool) filterFunc {
+func filterIntermediate(ctx context.Context, value bool, tree *layerTree) filterFunc {
return func(img *Image) (bool, error) {
- isIntermediate, err := img.IsIntermediate(ctx)
+ isIntermediate, err := img.isIntermediate(ctx, tree)
if err != nil {
return false, err
}
diff --git a/vendor/github.com/containers/common/libimage/image.go b/vendor/github.com/containers/common/libimage/image.go
index 00a2d620e..bf3310da2 100644
--- a/vendor/github.com/containers/common/libimage/image.go
+++ b/vendor/github.com/containers/common/libimage/image.go
@@ -128,10 +128,16 @@ func (i *Image) IsReadOnly() bool {
// IsDangling returns true if the image is dangling, that is an untagged image
// without children.
func (i *Image) IsDangling(ctx context.Context) (bool, error) {
+ return i.isDangling(ctx, nil)
+}
+
+// isDangling returns true if the image is dangling, that is an untagged image
+// without children. If tree is nil, it will created for this invocation only.
+func (i *Image) isDangling(ctx context.Context, tree *layerTree) (bool, error) {
if len(i.Names()) > 0 {
return false, nil
}
- children, err := i.getChildren(ctx, false)
+ children, err := i.getChildren(ctx, false, tree)
if err != nil {
return false, err
}
@@ -141,10 +147,17 @@ func (i *Image) IsDangling(ctx context.Context) (bool, error) {
// IsIntermediate returns true if the image is an intermediate image, that is
// an untagged image with children.
func (i *Image) IsIntermediate(ctx context.Context) (bool, error) {
+ return i.isIntermediate(ctx, nil)
+}
+
+// isIntermediate returns true if the image is an intermediate image, that is
+// an untagged image with children. If tree is nil, it will created for this
+// invocation only.
+func (i *Image) isIntermediate(ctx context.Context, tree *layerTree) (bool, error) {
if len(i.Names()) > 0 {
return false, nil
}
- children, err := i.getChildren(ctx, false)
+ children, err := i.getChildren(ctx, false, tree)
if err != nil {
return false, err
}
@@ -189,7 +202,7 @@ func (i *Image) Parent(ctx context.Context) (*Image, error) {
// HasChildren returns indicates if the image has children.
func (i *Image) HasChildren(ctx context.Context) (bool, error) {
- children, err := i.getChildren(ctx, false)
+ children, err := i.getChildren(ctx, false, nil)
if err != nil {
return false, err
}
@@ -198,7 +211,7 @@ func (i *Image) HasChildren(ctx context.Context) (bool, error) {
// Children returns the image's children.
func (i *Image) Children(ctx context.Context) ([]*Image, error) {
- children, err := i.getChildren(ctx, true)
+ children, err := i.getChildren(ctx, true, nil)
if err != nil {
return nil, err
}
@@ -206,13 +219,16 @@ func (i *Image) Children(ctx context.Context) ([]*Image, error) {
}
// getChildren returns a list of imageIDs that depend on the image. If all is
-// false, only the first child image is returned.
-func (i *Image) getChildren(ctx context.Context, all bool) ([]*Image, error) {
- tree, err := i.runtime.layerTree()
- if err != nil {
- return nil, err
+// false, only the first child image is returned. If tree is nil, it will be
+// created for this invocation only.
+func (i *Image) getChildren(ctx context.Context, all bool, tree *layerTree) ([]*Image, error) {
+ if tree == nil {
+ t, err := i.runtime.layerTree()
+ if err != nil {
+ return nil, err
+ }
+ tree = t
}
-
return tree.children(ctx, i, all)
}
diff --git a/vendor/github.com/containers/common/libimage/import.go b/vendor/github.com/containers/common/libimage/import.go
index bcfb4e129..67ab654b2 100644
--- a/vendor/github.com/containers/common/libimage/import.go
+++ b/vendor/github.com/containers/common/libimage/import.go
@@ -2,9 +2,11 @@ package libimage
import (
"context"
+ "fmt"
"net/url"
"os"
+ "github.com/containers/common/pkg/download"
storageTransport "github.com/containers/image/v5/storage"
tarballTransport "github.com/containers/image/v5/tarball"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
@@ -61,7 +63,8 @@ func (r *Runtime) Import(ctx context.Context, path string, options *ImportOption
u, err := url.ParseRequestURI(path)
if err == nil && u.Scheme != "" {
// If source is a URL, download the file.
- file, err := r.downloadFromURL(path)
+ fmt.Printf("Downloading from %q\n", path)
+ file, err := download.FromURL(r.systemContext.BigFilesTemporaryDir, path)
if err != nil {
return "", err
}
diff --git a/vendor/github.com/containers/common/libimage/inspect.go b/vendor/github.com/containers/common/libimage/inspect.go
index 007cbdd89..d44ebf46e 100644
--- a/vendor/github.com/containers/common/libimage/inspect.go
+++ b/vendor/github.com/containers/common/libimage/inspect.go
@@ -187,7 +187,12 @@ func (i *Image) Inspect(ctx context.Context, options *InspectOptions) (*ImageDat
return nil, err
}
data.Comment = dockerManifest.Comment
+ // NOTE: Health checks may be listed in the container config or
+ // the config.
data.HealthCheck = dockerManifest.ContainerConfig.Healthcheck
+ if data.HealthCheck == nil {
+ data.HealthCheck = dockerManifest.Config.Healthcheck
+ }
}
if data.Annotations == nil {
diff --git a/vendor/github.com/containers/common/libimage/runtime.go b/vendor/github.com/containers/common/libimage/runtime.go
index 7f25df200..d1b6e6cfb 100644
--- a/vendor/github.com/containers/common/libimage/runtime.go
+++ b/vendor/github.com/containers/common/libimage/runtime.go
@@ -21,6 +21,16 @@ import (
// Faster than the standard library, see https://github.com/json-iterator/go.
var json = jsoniter.ConfigCompatibleWithStandardLibrary
+// tmpdir returns a path to a temporary directory.
+func tmpdir() string {
+ tmpdir := os.Getenv("TMPDIR")
+ if tmpdir == "" {
+ tmpdir = "/var/tmp"
+ }
+
+ return tmpdir
+}
+
// RuntimeOptions allow for creating a customized Runtime.
type RuntimeOptions struct {
// The base system context of the runtime which will be used throughout
diff --git a/vendor/github.com/containers/common/libimage/search.go b/vendor/github.com/containers/common/libimage/search.go
index 7e20e4331..ece81531a 100644
--- a/vendor/github.com/containers/common/libimage/search.go
+++ b/vendor/github.com/containers/common/libimage/search.go
@@ -244,7 +244,7 @@ func (r *Runtime) searchImageInRegistry(ctx context.Context, term, registry stri
name = index + "/library/" + results[i].Name
}
params := SearchResult{
- Index: index,
+ Index: registry,
Name: name,
Description: description,
Official: official,
diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go
index d5be77edd..2eda0290a 100644
--- a/vendor/github.com/containers/common/pkg/config/config.go
+++ b/vendor/github.com/containers/common/pkg/config/config.go
@@ -461,6 +461,10 @@ type SetOptions struct {
// NetworkConfig represents the "network" TOML config table
type NetworkConfig struct {
+ // NetworkBackend determines what backend should be used for Podman's
+ // networking.
+ NetworkBackend string `toml:"network_backend,omitempty"`
+
// CNIPluginDirs is where CNI plugin binaries are stored.
CNIPluginDirs []string `toml:"cni_plugin_dirs,omitempty"`
@@ -574,7 +578,7 @@ func readConfigFromFile(path string, config *Config) error {
}
keys := meta.Undecoded()
if len(keys) > 0 {
- logrus.Warningf("Failed to decode the keys %q from %q.", keys, path)
+ logrus.Debugf("Failed to decode the keys %q from %q.", keys, path)
}
return nil
@@ -1151,10 +1155,11 @@ func (c *Config) FindHelperBinary(name string, searchPATH bool) (string, error)
if searchPATH {
return exec.LookPath(name)
}
+ configHint := "To resolve this error, set the helper_binaries_dir key in the `[engine]` section of containers.conf to the directory containing your helper binaries."
if len(c.Engine.HelperBinariesDir) == 0 {
- return "", errors.Errorf("could not find %q because there are no helper binary directories configured", name)
+ return "", errors.Errorf("could not find %q because there are no helper binary directories configured. %s", name, configHint)
}
- return "", errors.Errorf("could not find %q in one of %v", name, c.Engine.HelperBinariesDir)
+ return "", errors.Errorf("could not find %q in one of %v. %s", name, c.Engine.HelperBinariesDir, configHint)
}
// ImageCopyTmpDir default directory to store tempory image files during copy
diff --git a/vendor/github.com/containers/common/pkg/config/containers.conf b/vendor/github.com/containers/common/pkg/config/containers.conf
index 1d3c003e3..0c3ea8054 100644
--- a/vendor/github.com/containers/common/pkg/config/containers.conf
+++ b/vendor/github.com/containers/common/pkg/config/containers.conf
@@ -260,6 +260,10 @@ default_sysctls = [
[network]
+# Network backend to use. Default "CNI".
+#
+#network_backend = "cni"
+
# Path to directory where CNI plugin binaries are located.
#
#cni_plugin_dirs = [
diff --git a/vendor/github.com/containers/common/pkg/config/default.go b/vendor/github.com/containers/common/pkg/config/default.go
index e72e1b3e4..515c46e8b 100644
--- a/vendor/github.com/containers/common/pkg/config/default.go
+++ b/vendor/github.com/containers/common/pkg/config/default.go
@@ -201,6 +201,7 @@ func DefaultConfig() (*Config, error) {
UserNSSize: DefaultUserNSSize,
},
Network: NetworkConfig{
+ NetworkBackend: "cni",
DefaultNetwork: "podman",
DefaultSubnet: DefaultSubnet,
NetworkConfigDir: cniConfig,
diff --git a/vendor/github.com/containers/common/pkg/download/download.go b/vendor/github.com/containers/common/pkg/download/download.go
new file mode 100644
index 000000000..abf4c8773
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/download/download.go
@@ -0,0 +1,31 @@
+package download
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+)
+
+// FromURL downloads the specified source to a file in tmpdir (OS defaults if
+// empty).
+func FromURL(tmpdir, source string) (string, error) {
+ tmp, err := ioutil.TempFile(tmpdir, "")
+ if err != nil {
+ return "", fmt.Errorf("creating temporary download file: %w", err)
+ }
+ defer tmp.Close()
+
+ response, err := http.Get(source) // nolint:noctx
+ if err != nil {
+ return "", fmt.Errorf("downloading %s: %w", source, err)
+ }
+ defer response.Body.Close()
+
+ _, err = io.Copy(tmp, response.Body)
+ if err != nil {
+ return "", fmt.Errorf("copying %s to %s: %w", source, tmp.Name(), err)
+ }
+
+ return tmp.Name(), nil
+}
diff --git a/vendor/github.com/containers/common/pkg/flag/flag.go b/vendor/github.com/containers/common/pkg/flag/flag.go
new file mode 100644
index 000000000..52eb50da0
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/flag/flag.go
@@ -0,0 +1,174 @@
+package flag
+
+import (
+ "strconv"
+
+ "github.com/spf13/pflag"
+)
+
+// OptionalBool is a boolean with a separate presence flag and value.
+type OptionalBool struct {
+ present bool
+ value bool
+}
+
+// Present returns the bool's presence flag.
+func (ob *OptionalBool) Present() bool {
+ return ob.present
+}
+
+// Present returns the bool's value. Should only be used if Present() is true.
+func (ob *OptionalBool) Value() bool {
+ return ob.value
+}
+
+// optionalBool is a cli.Generic == flag.Value implementation equivalent to
+// the one underlying flag.Bool, except that it records whether the flag has been set.
+// This is distinct from optionalBool to (pretend to) force callers to use
+// optionalBoolFlag
+type optionalBoolValue OptionalBool
+
+// OptionalBoolFlag creates new flag for an optional in the specified flag with
+// the specified name and usage.
+func OptionalBoolFlag(fs *pflag.FlagSet, p *OptionalBool, name, usage string) *pflag.Flag {
+ flag := fs.VarPF(internalNewOptionalBoolValue(p), name, "", usage)
+ flag.NoOptDefVal = "true"
+ flag.DefValue = "false"
+ return flag
+}
+
+// WARNING: Do not directly use this method to define optionalBool flag.
+// Caller should use optionalBoolFlag
+func internalNewOptionalBoolValue(p *OptionalBool) pflag.Value {
+ p.present = false
+ return (*optionalBoolValue)(p)
+}
+
+// Set parses the string to a bool and sets it.
+func (ob *optionalBoolValue) Set(s string) error {
+ v, err := strconv.ParseBool(s)
+ if err != nil {
+ return err
+ }
+ ob.value = v
+ ob.present = true
+ return nil
+}
+
+// String returns the string representation of the string.
+func (ob *optionalBoolValue) String() string {
+ if !ob.present {
+ return "" // This is, sadly, not round-trip safe: --flag is interpreted as --flag=true
+ }
+ return strconv.FormatBool(ob.value)
+}
+
+// Type returns the type.
+func (ob *optionalBoolValue) Type() string {
+ return "bool"
+}
+
+// IsBoolFlag indicates that it's a bool flag.
+func (ob *optionalBoolValue) IsBoolFlag() bool {
+ return true
+}
+
+// OptionalString is a string with a separate presence flag.
+type OptionalString struct {
+ present bool
+ value string
+}
+
+// Present returns the strings's presence flag.
+func (os *OptionalString) Present() bool {
+ return os.present
+}
+
+// Present returns the string's value. Should only be used if Present() is true.
+func (os *OptionalString) Value() string {
+ return os.value
+}
+
+// optionalString is a cli.Generic == flag.Value implementation equivalent to
+// the one underlying flag.String, except that it records whether the flag has been set.
+// This is distinct from optionalString to (pretend to) force callers to use
+// newoptionalString
+type optionalStringValue OptionalString
+
+// NewOptionalStringValue returns a pflag.Value fo the string.
+func NewOptionalStringValue(p *OptionalString) pflag.Value {
+ p.present = false
+ return (*optionalStringValue)(p)
+}
+
+// Set sets the string.
+func (ob *optionalStringValue) Set(s string) error {
+ ob.value = s
+ ob.present = true
+ return nil
+}
+
+// String returns the string if present.
+func (ob *optionalStringValue) String() string {
+ if !ob.present {
+ return "" // This is, sadly, not round-trip safe: --flag= is interpreted as {present:true, value:""}
+ }
+ return ob.value
+}
+
+// Type returns the string type.
+func (ob *optionalStringValue) Type() string {
+ return "string"
+}
+
+// OptionalInt is a int with a separate presence flag.
+type OptionalInt struct {
+ present bool
+ value int
+}
+
+// Present returns the int's presence flag.
+func (oi *OptionalInt) Present() bool {
+ return oi.present
+}
+
+// Present returns the int's value. Should only be used if Present() is true.
+func (oi *OptionalInt) Value() int {
+ return oi.value
+}
+
+// optionalInt is a cli.Generic == flag.Value implementation equivalent to
+// the one underlying flag.Int, except that it records whether the flag has been set.
+// This is distinct from optionalInt to (pretend to) force callers to use
+// newoptionalIntValue
+type optionalIntValue OptionalInt
+
+// NewOptionalIntValue returns the pflag.Value of the int.
+func NewOptionalIntValue(p *OptionalInt) pflag.Value {
+ p.present = false
+ return (*optionalIntValue)(p)
+}
+
+// Set parses the string to an int and sets it.
+func (ob *optionalIntValue) Set(s string) error {
+ v, err := strconv.ParseInt(s, 0, strconv.IntSize)
+ if err != nil {
+ return err
+ }
+ ob.value = int(v)
+ ob.present = true
+ return nil
+}
+
+// String returns the string representation of the int.
+func (ob *optionalIntValue) String() string {
+ if !ob.present {
+ return "" // If the value is not present, just return an empty string, any other value wouldn't make sense.
+ }
+ return strconv.Itoa(int(ob.value))
+}
+
+// Type returns the int's type.
+func (ob *optionalIntValue) Type() string {
+ return "int"
+}
diff --git a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go
index cf333744c..d196384f0 100644
--- a/vendor/github.com/containers/common/pkg/seccomp/default_linux.go
+++ b/vendor/github.com/containers/common/pkg/seccomp/default_linux.go
@@ -80,6 +80,7 @@ func DefaultProfile() *Seccomp {
"vmsplice",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
},
@@ -574,6 +575,7 @@ func DefaultProfile() *Seccomp {
"open_by_handle_at",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -609,6 +611,7 @@ func DefaultProfile() *Seccomp {
"setns",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -630,6 +633,7 @@ func DefaultProfile() *Seccomp {
"chroot",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -657,6 +661,7 @@ func DefaultProfile() *Seccomp {
"query_module",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -678,6 +683,7 @@ func DefaultProfile() *Seccomp {
"acct",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -707,6 +713,7 @@ func DefaultProfile() *Seccomp {
"ptrace",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -730,6 +737,7 @@ func DefaultProfile() *Seccomp {
"ioperm",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -757,6 +765,7 @@ func DefaultProfile() *Seccomp {
"clock_settime64",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -778,6 +787,7 @@ func DefaultProfile() *Seccomp {
"vhangup",
},
Action: ActErrno,
+ Errno: "EPERM",
ErrnoRet: &eperm,
Args: []*Arg{},
Excludes: Filter{
@@ -789,6 +799,7 @@ func DefaultProfile() *Seccomp {
"socket",
},
Action: ActErrno,
+ Errno: "EINVAL",
ErrnoRet: &einval,
Args: []*Arg{
{
@@ -867,6 +878,7 @@ func DefaultProfile() *Seccomp {
return &Seccomp{
DefaultAction: ActErrno,
+ DefaultErrno: "ENOSYS",
DefaultErrnoRet: &enosys,
ArchMap: arches(),
Syscalls: syscalls,
diff --git a/vendor/github.com/containers/common/pkg/seccomp/errno_list.go b/vendor/github.com/containers/common/pkg/seccomp/errno_list.go
new file mode 100644
index 000000000..55b92ecc8
--- /dev/null
+++ b/vendor/github.com/containers/common/pkg/seccomp/errno_list.go
@@ -0,0 +1,91 @@
+package seccomp
+
+import (
+ "golang.org/x/sys/unix"
+)
+
+// Error table
+var errnoArch = map[string]uint{
+ "EPERM": uint(unix.EPERM),
+ "ENOENT": uint(unix.ENOENT),
+ "ESRCH": uint(unix.ESRCH),
+ "EIO": uint(unix.EIO),
+ "ENXIO": uint(unix.ENXIO),
+ "E2BIG": uint(unix.E2BIG),
+ "ENOEXEC": uint(unix.ENOEXEC),
+ "EBADF": uint(unix.EBADF),
+ "ECHILD": uint(unix.ECHILD),
+ "EDEADLK": uint(unix.EDEADLK),
+ "ENOMEM": uint(unix.ENOMEM),
+ "EACCES": uint(unix.EACCES),
+ "EFAULT": uint(unix.EFAULT),
+ "ENOTBLK": uint(unix.ENOTBLK),
+ "EBUSY": uint(unix.EBUSY),
+ "EEXIST": uint(unix.EEXIST),
+ "EXDEV": uint(unix.EXDEV),
+ "ENODEV": uint(unix.ENODEV),
+ "ENOTDIR": uint(unix.ENOTDIR),
+ "EISDIR": uint(unix.EISDIR),
+ "EINVAL": uint(unix.EINVAL),
+ "ENFILE": uint(unix.ENFILE),
+ "EMFILE": uint(unix.EMFILE),
+ "ENOTTY": uint(unix.ENOTTY),
+ "ETXTBSY": uint(unix.ETXTBSY),
+ "EFBIG": uint(unix.EFBIG),
+ "ENOSPC": uint(unix.ENOSPC),
+ "ESPIPE": uint(unix.ESPIPE),
+ "EROFS": uint(unix.EROFS),
+ "EMLINK": uint(unix.EMLINK),
+ "EPIPE": uint(unix.EPIPE),
+ "EDOM": uint(unix.EDOM),
+ "ERANGE": uint(unix.ERANGE),
+ "EAGAIN": uint(unix.EAGAIN),
+ "EINPROGRESS": uint(unix.EINPROGRESS),
+ "EALREADY": uint(unix.EALREADY),
+ "ENOTSOCK": uint(unix.ENOTSOCK),
+ "EDESTADDRREQ": uint(unix.EDESTADDRREQ),
+ "EMSGSIZE": uint(unix.EMSGSIZE),
+ "EPROTOTYPE": uint(unix.EPROTOTYPE),
+ "ENOPROTOOPT": uint(unix.ENOPROTOOPT),
+ "EPROTONOSUPPORT": uint(unix.EPROTONOSUPPORT),
+ "ESOCKTNOSUPPORT": uint(unix.ESOCKTNOSUPPORT),
+ "EOPNOTSUPP": uint(unix.EOPNOTSUPP),
+ "EPFNOSUPPORT": uint(unix.EPFNOSUPPORT),
+ "EAFNOSUPPORT": uint(unix.EAFNOSUPPORT),
+ "EADDRINUSE": uint(unix.EADDRINUSE),
+ "EADDRNOTAVAIL": uint(unix.EADDRNOTAVAIL),
+ "ENETDOWN": uint(unix.ENETDOWN),
+ "ENETUNREACH": uint(unix.ENETUNREACH),
+ "ENETRESET": uint(unix.ENETRESET),
+ "ECONNABORTED": uint(unix.ECONNABORTED),
+ "ECONNRESET": uint(unix.ECONNRESET),
+ "ENOBUFS": uint(unix.ENOBUFS),
+ "EISCONN": uint(unix.EISCONN),
+ "ENOTCONN": uint(unix.ENOTCONN),
+ "ESHUTDOWN": uint(unix.ESHUTDOWN),
+ "ETOOMANYREFS": uint(unix.ETOOMANYREFS),
+ "ETIMEDOUT": uint(unix.ETIMEDOUT),
+ "ECONNREFUSED": uint(unix.ECONNREFUSED),
+ "ELOOP": uint(unix.ELOOP),
+ "ENAMETOOLONG": uint(unix.ENAMETOOLONG),
+ "EHOSTDOWN": uint(unix.EHOSTDOWN),
+ "EHOSTUNREACH": uint(unix.EHOSTUNREACH),
+ "ENOTEMPTY": uint(unix.ENOTEMPTY),
+ "EUSERS": uint(unix.EUSERS),
+ "EDQUOT": uint(unix.EDQUOT),
+ "ESTALE": uint(unix.ESTALE),
+ "EREMOTE": uint(unix.EREMOTE),
+ "ENOLCK": uint(unix.ENOLCK),
+ "ENOSYS": uint(unix.ENOSYS),
+ "EILSEQ": uint(unix.EILSEQ),
+ "ENOMEDIUM": uint(unix.ENOMEDIUM),
+ "EMEDIUMTYPE": uint(unix.EMEDIUMTYPE),
+ "EOVERFLOW": uint(unix.EOVERFLOW),
+ "ECANCELED": uint(unix.ECANCELED),
+ "EIDRM": uint(unix.EIDRM),
+ "ENOMSG": uint(unix.ENOMSG),
+ "ENOTSUP": uint(unix.ENOTSUP),
+ "EBADMSG": uint(unix.EBADMSG),
+ "ENOTRECOVERABLE": uint(unix.ENOTRECOVERABLE),
+ "EOWNERDEAD": uint(unix.EOWNERDEAD),
+}
diff --git a/vendor/github.com/containers/common/pkg/seccomp/seccomp.json b/vendor/github.com/containers/common/pkg/seccomp/seccomp.json
index c009134e3..9314eb3cc 100644
--- a/vendor/github.com/containers/common/pkg/seccomp/seccomp.json
+++ b/vendor/github.com/containers/common/pkg/seccomp/seccomp.json
@@ -1,6 +1,7 @@
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 38,
+ "defaultErrno": "ENOSYS",
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
@@ -87,7 +88,8 @@
"comment": "",
"includes": {},
"excludes": {},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -650,7 +652,8 @@
"CAP_DAC_READ_SEARCH"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -693,7 +696,8 @@
"CAP_SYS_ADMIN"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -722,7 +726,8 @@
"CAP_SYS_CHROOT"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -757,7 +762,8 @@
"CAP_SYS_MODULE"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -786,7 +792,8 @@
"CAP_SYS_PACCT"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -823,7 +830,8 @@
"CAP_SYS_PTRACE"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -854,7 +862,8 @@
"CAP_SYS_RAWIO"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -889,7 +898,8 @@
"CAP_SYS_TIME"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -918,7 +928,8 @@
"CAP_SYS_TTY_CONFIG"
]
},
- "errnoRet": 1
+ "errnoRet": 1,
+ "errno": "EPERM"
},
{
"names": [
@@ -946,7 +957,8 @@
"CAP_AUDIT_WRITE"
]
},
- "errnoRet": 22
+ "errnoRet": 22,
+ "errno": "EINVAL"
},
{
"names": [
diff --git a/vendor/github.com/containers/common/pkg/seccomp/seccomp_linux.go b/vendor/github.com/containers/common/pkg/seccomp/seccomp_linux.go
index af36b9990..0c022ac7a 100644
--- a/vendor/github.com/containers/common/pkg/seccomp/seccomp_linux.go
+++ b/vendor/github.com/containers/common/pkg/seccomp/seccomp_linux.go
@@ -10,6 +10,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "strconv"
"github.com/opencontainers/runtime-spec/specs-go"
libseccomp "github.com/seccomp/libseccomp-golang"
@@ -66,6 +67,37 @@ func inSlice(slice []string, s string) bool {
return false
}
+func getArchitectures(config *Seccomp, newConfig *specs.LinuxSeccomp) error {
+ if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
+ return errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
+ }
+
+ // if config.Architectures == 0 then libseccomp will figure out the architecture to use
+ if len(config.Architectures) != 0 {
+ for _, a := range config.Architectures {
+ newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
+ }
+ }
+ return nil
+}
+
+func getErrno(errno string, def *uint) (*uint, error) {
+ if errno == "" {
+ return def, nil
+ }
+ v, err := strconv.ParseUint(errno, 10, 32)
+ if err == nil {
+ v2 := uint(v)
+ return &v2, nil
+ }
+
+ v2, found := errnoArch[errno]
+ if !found {
+ return nil, fmt.Errorf("unknown errno %s", errno)
+ }
+ return &v2, nil
+}
+
func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error) {
if config == nil {
return nil, nil
@@ -84,15 +116,8 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
arch = native.String()
}
- if len(config.Architectures) != 0 && len(config.ArchMap) != 0 {
- return nil, errors.New("'architectures' and 'archMap' were specified in the seccomp profile, use either 'architectures' or 'archMap'")
- }
-
- // if config.Architectures == 0 then libseccomp will figure out the architecture to use
- if len(config.Architectures) != 0 {
- for _, a := range config.Architectures {
- newConfig.Architectures = append(newConfig.Architectures, specs.Arch(a))
- }
+ if err := getArchitectures(config, newConfig); err != nil {
+ return nil, err
}
if len(config.ArchMap) != 0 {
@@ -111,7 +136,11 @@ func setupSeccomp(config *Seccomp, rs *specs.Spec) (*specs.LinuxSeccomp, error)
}
newConfig.DefaultAction = specs.LinuxSeccompAction(config.DefaultAction)
- newConfig.DefaultErrnoRet = config.DefaultErrnoRet
+
+ newConfig.DefaultErrnoRet, err = getErrno(config.DefaultErrno, config.DefaultErrnoRet)
+ if err != nil {
+ return nil, err
+ }
Loop:
// Loop through all syscall blocks and convert them to libcontainer format after filtering them
@@ -145,12 +174,17 @@ Loop:
return nil, errors.New("'name' and 'names' were specified in the seccomp profile, use either 'name' or 'names'")
}
+ errno, err := getErrno(call.Errno, call.ErrnoRet)
+ if err != nil {
+ return nil, err
+ }
+
if call.Name != "" {
- newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, call.ErrnoRet))
+ newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall([]string{call.Name}, call.Action, call.Args, errno))
}
if len(call.Names) > 0 {
- newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, call.ErrnoRet))
+ newConfig.Syscalls = append(newConfig.Syscalls, createSpecsSyscall(call.Names, call.Action, call.Args, errno))
}
}
diff --git a/vendor/github.com/containers/common/pkg/seccomp/types.go b/vendor/github.com/containers/common/pkg/seccomp/types.go
index 07751f729..a8a9e9d4f 100644
--- a/vendor/github.com/containers/common/pkg/seccomp/types.go
+++ b/vendor/github.com/containers/common/pkg/seccomp/types.go
@@ -6,8 +6,12 @@ package seccomp
// Seccomp represents the config for a seccomp profile for syscall restriction.
type Seccomp struct {
- DefaultAction Action `json:"defaultAction"`
+ DefaultAction Action `json:"defaultAction"`
+
+ // DefaultErrnoRet is obsolete, please use DefaultErrno
DefaultErrnoRet *uint `json:"defaultErrnoRet,omitempty"`
+ DefaultErrno string `json:"defaultErrno,omitempty"`
+
// Architectures is kept to maintain backward compatibility with the old
// seccomp profile.
Architectures []Arch `json:"architectures,omitempty"`
@@ -107,5 +111,7 @@ type Syscall struct {
Comment string `json:"comment"`
Includes Filter `json:"includes"`
Excludes Filter `json:"excludes"`
- ErrnoRet *uint `json:"errnoRet,omitempty"`
+ // ErrnoRet is obsolete, please use Errno
+ ErrnoRet *uint `json:"errnoRet,omitempty"`
+ Errno string `json:"errno,omitempty"`
}
diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION
index bf50e910e..97dd8ea50 100644
--- a/vendor/github.com/containers/storage/VERSION
+++ b/vendor/github.com/containers/storage/VERSION
@@ -1 +1 @@
-1.37.0
+1.37.0+dev
diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod
index 6cd809745..2d68cc2ef 100644
--- a/vendor/github.com/containers/storage/go.mod
+++ b/vendor/github.com/containers/storage/go.mod
@@ -5,12 +5,10 @@ module github.com/containers/storage
require (
github.com/BurntSushi/toml v0.4.1
github.com/Microsoft/go-winio v0.5.0
- github.com/Microsoft/hcsshim v0.8.22
+ github.com/Microsoft/hcsshim v0.9.0
github.com/containerd/stargz-snapshotter/estargz v0.9.0
github.com/docker/go-units v0.4.0
- github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect
github.com/google/go-intervals v0.0.2
- github.com/google/uuid v1.2.0 // indirect
github.com/hashicorp/go-multierror v1.1.1
github.com/json-iterator/go v1.1.12
github.com/klauspost/compress v1.13.6
@@ -18,11 +16,10 @@ require (
github.com/mattn/go-shellwords v1.0.12
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible
github.com/moby/sys/mountinfo v0.4.1
- github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/runc v1.0.2
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
- github.com/opencontainers/selinux v1.8.5
+ github.com/opencontainers/selinux v1.9.1
github.com/pkg/errors v0.9.1
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
@@ -30,8 +27,7 @@ require (
github.com/tchap/go-patricia v2.3.0+incompatible
github.com/ulikunitz/xz v0.5.10
github.com/vbatts/tar-split v0.11.2
- golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
+ golang.org/x/net v0.0.0-20210825183410-e898025ed96a
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55
- gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gotest.tools v2.2.0+incompatible
)
diff --git a/vendor/github.com/containers/storage/go.sum b/vendor/github.com/containers/storage/go.sum
index 1b602d484..ebb3a5a52 100644
--- a/vendor/github.com/containers/storage/go.sum
+++ b/vendor/github.com/containers/storage/go.sum
@@ -1,85 +1,325 @@
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
+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.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
+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/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/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/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=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+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/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
+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=
+github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
+github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
+github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
+github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
+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/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=
+github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
+github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-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 v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
+github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
+github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
+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.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4=
+github.com/Microsoft/hcsshim v0.9.0 h1:BBgYMxl5YZDZVIijz02AlDINpYZOzQqRNCl9CZM13vk=
+github.com/Microsoft/hcsshim v0.9.0/go.mod h1:VBJWdC71NSWPlEo7lwde1aL21748J8B6Sdgno7NqEGE=
+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=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+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/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
+github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
+github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
+github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
+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/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
+github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+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/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
+github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
+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.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-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
+github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
+github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
+github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
+github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
+github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
+github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
+github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
+github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
+github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
+github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.0.1 h1:iJnMvco9XGvKUvNQkv88bE4uJXxRQH18efbKo9w5vHQ=
github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
+github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
+github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
-github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
+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.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/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
+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=
+github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
+github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
+github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
+github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
+github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
+github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
+github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
+github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
+github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
+github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
+github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
+github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
+github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
+github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
+github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
+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.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM=
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=
+github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
+github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
+github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
+github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
+github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
+github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
+github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
+github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
+github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
+github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
+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/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
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/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
+github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
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.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
+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/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=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
+github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
+github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
+github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
+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=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/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/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=
+github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
+github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
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.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
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/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/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+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=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
+github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
+github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
+github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
+github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
+github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
+github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
+github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
+github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
+github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
+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/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+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=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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/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.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
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=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
@@ -87,242 +327,639 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+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/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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.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.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-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0=
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/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+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/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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+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.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
+github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
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/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/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/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
+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/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+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.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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.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/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.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
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/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+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/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-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
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/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
+github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk=
github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+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/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM=
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/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/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/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
+github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+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.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+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=
+github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
+github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
+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=
+github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/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/runc v0.0.0-20190115041553-12f6a991201f/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-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
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=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
+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.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
-github.com/opencontainers/selinux v1.8.5 h1:OkT6bMHOQ1JQQO4ihjQ49sj0+wciDcjziSVTRn8VeTA=
-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/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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
+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=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+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=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
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/cast v1.3.0/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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+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=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+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/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
+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=
+github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
+github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs=
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
+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/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=
+github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+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/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/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/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
+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/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
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.27/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/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
+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.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.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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+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=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+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-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/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
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=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
+golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+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/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=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+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.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=
+golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+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=
+golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
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-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-20201021035429-f5854403a974/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-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a h1:bRuuGXV8wwSdGTB+CtJf+FjgO1APK1CoO39T4BN/XBw=
+golang.org/x/net v0.0.0-20210825183410-e898025ed96a/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/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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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=
+golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-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=
+golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+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-20200501052902-10377860bb8e/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-20200817155316-9781c653f443/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-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210124154548-22da62e12c0c/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55 h1:rw6UNGRMfarCepjI8qOepea/SXwIBVfTKjztZ5gBbq4=
golang.org/x/sys v0.0.0-20210820121016-41cdb8703e55/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+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=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+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-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
+golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/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-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.1/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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+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.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
+google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
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/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=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+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-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
+google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+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=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
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.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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
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=
@@ -331,22 +968,87 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+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/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=
+gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
+gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
+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=
+gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
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=
+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/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/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=
+k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
+k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
+k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
+k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
+k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
+k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
+k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
+k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
+k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
+k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
+k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
+k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
+k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
+k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
+k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
+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=
+rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
+rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
+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.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
+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/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/vendor/github.com/containers/storage/storage.conf b/vendor/github.com/containers/storage/storage.conf
index 722750c0c..bf1534adc 100644
--- a/vendor/github.com/containers/storage/storage.conf
+++ b/vendor/github.com/containers/storage/storage.conf
@@ -11,8 +11,14 @@ driver = "overlay"
runroot = "/run/containers/storage"
# Primary Read/Write location of container storage
+# When changing the graphroot location on an SELINUX system, you must
+# ensure the labeling matches the default locations labels with the
+# following commands:
+# semanage fcontext -a -e /var/lib/containers/storage /NEWSTORAGEPATH
+# restorecon -R -v /NEWSTORAGEPATH
graphroot = "/var/lib/containers/storage"
+
# Storage path for rootless users
#
# rootless_storage_path = "$HOME/.local/share/containers/storage"
diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go
index 169c7d151..4d28eb140 100644
--- a/vendor/github.com/containers/storage/store.go
+++ b/vendor/github.com/containers/storage/store.go
@@ -575,10 +575,11 @@ type ContainerOptions struct {
// container's layer will inherit settings from the image's top layer
// or, if it is not being created based on an image, the Store object.
types.IDMappingOptions
- LabelOpts []string
- Flags map[string]interface{}
- MountOpts []string
- Volatile bool
+ LabelOpts []string
+ Flags map[string]interface{}
+ MountOpts []string
+ Volatile bool
+ StorageOpt map[string]string
}
type store struct {
@@ -1384,7 +1385,7 @@ func (s *store) CreateContainer(id string, names []string, image, layer, metadat
options.Flags["MountLabel"] = mountLabel
}
- clayer, err := rlstore.Create(layer, imageTopLayer, nil, options.Flags["MountLabel"].(string), nil, layerOptions, true)
+ clayer, err := rlstore.Create(layer, imageTopLayer, nil, options.Flags["MountLabel"].(string), options.StorageOpt, layerOptions, true)
if err != nil {
return nil, err
}
@@ -2830,10 +2831,33 @@ func (s *store) Diff(from, to string, options *DiffOptions) (io.ReadCloser, erro
if err != nil {
return nil, err
}
+
+ // NaiveDiff could cause mounts to happen without a lock, so be safe
+ // and treat the .Diff operation as a Mount.
+ s.graphLock.Lock()
+ defer s.graphLock.Unlock()
+
+ modified, err := s.graphLock.Modified()
+ if err != nil {
+ return nil, err
+ }
+
+ // We need to make sure the home mount is present when the Mount is done.
+ if modified {
+ s.graphDriver = nil
+ s.layerStore = nil
+ s.graphDriver, err = s.getGraphDriver()
+ if err != nil {
+ return nil, err
+ }
+ s.lastLoaded = time.Now()
+ }
+
for _, s := range append([]ROLayerStore{lstore}, lstores...) {
store := s
store.RLock()
if err := store.ReloadIfChanged(); err != nil {
+ store.Unlock()
return nil, err
}
if store.Exists(to) {
diff --git a/vendor/github.com/containers/storage/types/options.go b/vendor/github.com/containers/storage/types/options.go
index f9bf7e6b6..8a3858d9f 100644
--- a/vendor/github.com/containers/storage/types/options.go
+++ b/vendor/github.com/containers/storage/types/options.go
@@ -29,8 +29,9 @@ type tomlConfig struct {
// defaultConfigFile path to the system wide storage.conf file
var (
- defaultConfigFile = "/etc/containers/storage.conf"
- defaultConfigFileSet = false
+ defaultConfigFile = "/usr/share/containers/storage.conf"
+ defaultOverrideConfigFile = "/etc/containers/storage.conf"
+ defaultConfigFileSet = false
// DefaultStoreOptions is a reasonable default set of options.
defaultStoreOptions StoreOptions
)
@@ -40,7 +41,14 @@ func init() {
defaultStoreOptions.GraphRoot = "/var/lib/containers/storage"
defaultStoreOptions.GraphDriverName = ""
- ReloadConfigurationFileIfNeeded(defaultConfigFile, &defaultStoreOptions)
+ if _, err := os.Stat(defaultOverrideConfigFile); err == nil {
+ ReloadConfigurationFileIfNeeded(defaultOverrideConfigFile, &defaultStoreOptions)
+ } else {
+ if !os.IsNotExist(err) {
+ logrus.Warningf("Attempting to use %s, %v", defaultConfigFile, err)
+ }
+ ReloadConfigurationFileIfNeeded(defaultConfigFile, &defaultStoreOptions)
+ }
}
// defaultStoreOptionsIsolated is an internal implementation detail of DefaultStoreOptions to allow testing.
diff --git a/vendor/github.com/godbus/dbus/v5/auth.go b/vendor/github.com/godbus/dbus/v5/auth.go
index eb0b2f434..a59b4c0eb 100644
--- a/vendor/github.com/godbus/dbus/v5/auth.go
+++ b/vendor/github.com/godbus/dbus/v5/auth.go
@@ -53,7 +53,7 @@ type Auth interface {
// bus. Auth must not be called on shared connections.
func (conn *Conn) Auth(methods []Auth) error {
if methods == nil {
- uid := strconv.Itoa(os.Getuid())
+ uid := strconv.Itoa(os.Geteuid())
methods = []Auth{AuthExternal(uid), AuthCookieSha1(uid, getHomeDir())}
}
in := bufio.NewReader(conn.transport)
diff --git a/vendor/github.com/godbus/dbus/v5/conn.go b/vendor/github.com/godbus/dbus/v5/conn.go
index cb8966a74..76fc5cde3 100644
--- a/vendor/github.com/godbus/dbus/v5/conn.go
+++ b/vendor/github.com/godbus/dbus/v5/conn.go
@@ -73,7 +73,7 @@ func SessionBus() (conn *Conn, err error) {
return
}
-func getSessionBusAddress() (string, error) {
+func getSessionBusAddress(autolaunch bool) (string, error) {
if address := os.Getenv("DBUS_SESSION_BUS_ADDRESS"); address != "" && address != "autolaunch:" {
return address, nil
@@ -81,12 +81,26 @@ func getSessionBusAddress() (string, error) {
os.Setenv("DBUS_SESSION_BUS_ADDRESS", address)
return address, nil
}
+ if !autolaunch {
+ return "", errors.New("dbus: couldn't determine address of session bus")
+ }
return getSessionBusPlatformAddress()
}
// SessionBusPrivate returns a new private connection to the session bus.
func SessionBusPrivate(opts ...ConnOption) (*Conn, error) {
- address, err := getSessionBusAddress()
+ address, err := getSessionBusAddress(true)
+ if err != nil {
+ return nil, err
+ }
+
+ return Dial(address, opts...)
+}
+
+// SessionBusPrivate returns a new private connection to the session bus. If
+// the session bus is not already open, do not attempt to launch it.
+func SessionBusPrivateNoAutoStartup(opts ...ConnOption) (*Conn, error) {
+ address, err := getSessionBusAddress(false)
if err != nil {
return nil, err
}
@@ -121,7 +135,7 @@ func SystemBus() (conn *Conn, err error) {
// ConnectSessionBus connects to the session bus.
func ConnectSessionBus(opts ...ConnOption) (*Conn, error) {
- address, err := getSessionBusAddress()
+ address, err := getSessionBusAddress(true)
if err != nil {
return nil, err
}
@@ -180,7 +194,7 @@ func Dial(address string, opts ...ConnOption) (*Conn, error) {
//
// Deprecated: use Dial with options instead.
func DialHandler(address string, handler Handler, signalHandler SignalHandler) (*Conn, error) {
- return Dial(address, WithSignalHandler(signalHandler))
+ return Dial(address, WithHandler(handler), WithSignalHandler(signalHandler))
}
// ConnOption is a connection option.
diff --git a/vendor/github.com/godbus/dbus/v5/message.go b/vendor/github.com/godbus/dbus/v5/message.go
index dd86aff4f..16693eb30 100644
--- a/vendor/github.com/godbus/dbus/v5/message.go
+++ b/vendor/github.com/godbus/dbus/v5/message.go
@@ -279,8 +279,8 @@ func (msg *Message) EncodeToWithFDs(out io.Writer, order binary.ByteOrder) (fds
// be either binary.LittleEndian or binary.BigEndian. If the message is not
// valid or an error occurs when writing, an error is returned.
func (msg *Message) EncodeTo(out io.Writer, order binary.ByteOrder) (err error) {
- _, err = msg.EncodeToWithFDs(out, order);
- return err;
+ _, err = msg.EncodeToWithFDs(out, order)
+ return err
}
// IsValid checks whether msg is a valid message and returns an
diff --git a/vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go b/vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
new file mode 100644
index 000000000..af7bafdf9
--- /dev/null
+++ b/vendor/github.com/godbus/dbus/v5/transport_unixcred_netbsd.go
@@ -0,0 +1,14 @@
+package dbus
+
+import "io"
+
+func (t *unixTransport) SendNullByte() error {
+ n, _, err := t.UnixConn.WriteMsgUnix([]byte{0}, nil, nil)
+ if err != nil {
+ return err
+ }
+ if n != 1 {
+ return io.ErrShortWrite
+ }
+ return nil
+}
diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
index 6d3268902..70b175362 100644
--- a/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
+++ b/vendor/github.com/opencontainers/runtime-tools/generate/generate.go
@@ -29,6 +29,9 @@ var (
type Generator struct {
Config *rspec.Spec
HostSpecific bool
+ // This is used to keep a cache of the ENVs added to improve
+ // performance when adding a huge number of ENV variables
+ envMap map[string]int
}
// ExportOptions have toggles for exporting only certain parts of the specification
@@ -179,7 +182,7 @@ func New(os string) (generator Generator, err error) {
Destination: "/dev",
Type: "tmpfs",
Source: "tmpfs",
- Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
+ Options: []string{"nosuid", "noexec", "strictatime", "mode=755", "size=65536k"},
},
{
Destination: "/dev/pts",
@@ -236,7 +239,12 @@ func New(os string) (generator Generator, err error) {
}
}
- return Generator{Config: &config}, nil
+ envCache := map[string]int{}
+ if config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
+ return Generator{Config: &config, envMap: envCache}, nil
}
// NewFromSpec creates a configuration Generator from a given
@@ -246,8 +254,14 @@ func New(os string) (generator Generator, err error) {
//
// generator := Generator{Config: config}
func NewFromSpec(config *rspec.Spec) Generator {
+ envCache := map[string]int{}
+ if config != nil && config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
return Generator{
Config: config,
+ envMap: envCache,
}
}
@@ -273,11 +287,27 @@ func NewFromTemplate(r io.Reader) (Generator, error) {
if err := json.NewDecoder(r).Decode(&config); err != nil {
return Generator{}, err
}
+
+ envCache := map[string]int{}
+ if config.Process != nil {
+ envCache = createEnvCacheMap(config.Process.Env)
+ }
+
return Generator{
Config: &config,
+ envMap: envCache,
}, nil
}
+// createEnvCacheMap creates a hash map with the ENV variables given by the config
+func createEnvCacheMap(env []string) map[string]int {
+ envMap := make(map[string]int, len(env))
+ for i, val := range env {
+ envMap[val] = i
+ }
+ return envMap
+}
+
// SetSpec sets the configuration in the Generator g.
//
// Deprecated: Replace with:
@@ -414,6 +444,13 @@ func (g *Generator) SetProcessUsername(username string) {
g.Config.Process.User.Username = username
}
+// SetProcessUmask sets g.Config.Process.User.Umask.
+func (g *Generator) SetProcessUmask(umask uint32) {
+ g.initConfigProcess()
+ u := umask
+ g.Config.Process.User.Umask = &u
+}
+
// SetProcessGID sets g.Config.Process.User.GID.
func (g *Generator) SetProcessGID(gid uint32) {
g.initConfigProcess()
@@ -456,21 +493,44 @@ func (g *Generator) ClearProcessEnv() {
return
}
g.Config.Process.Env = []string{}
+ // Clear out the env cache map as well
+ g.envMap = map[string]int{}
}
// AddProcessEnv adds name=value into g.Config.Process.Env, or replaces an
// existing entry with the given name.
func (g *Generator) AddProcessEnv(name, value string) {
+ if name == "" {
+ return
+ }
+
g.initConfigProcess()
+ g.addEnv(fmt.Sprintf("%s=%s", name, value), name)
+}
- env := fmt.Sprintf("%s=%s", name, value)
- for idx := range g.Config.Process.Env {
- if strings.HasPrefix(g.Config.Process.Env[idx], name+"=") {
- g.Config.Process.Env[idx] = env
- return
- }
+// AddMultipleProcessEnv adds multiple name=value into g.Config.Process.Env, or replaces
+// existing entries with the given name.
+func (g *Generator) AddMultipleProcessEnv(envs []string) {
+ g.initConfigProcess()
+
+ for _, val := range envs {
+ split := strings.SplitN(val, "=", 2)
+ g.addEnv(val, split[0])
+ }
+}
+
+// addEnv looks through adds ENV to the Process and checks envMap for
+// any duplicates
+// This is called by both AddMultipleProcessEnv and AddProcessEnv
+func (g *Generator) addEnv(env, key string) {
+ if idx, ok := g.envMap[key]; ok {
+ // The ENV exists in the cache, so change its value in g.Config.Process.Env
+ g.Config.Process.Env[idx] = env
+ } else {
+ // else the env doesn't exist, so add it and add it's index to g.envMap
+ g.Config.Process.Env = append(g.Config.Process.Env, env)
+ g.envMap[key] = len(g.Config.Process.Env) - 1
}
- g.Config.Process.Env = append(g.Config.Process.Env, env)
}
// AddProcessRlimits adds rlimit into g.Config.Process.Rlimits.
@@ -1442,9 +1502,6 @@ func (g *Generator) AddDevice(device rspec.LinuxDevice) {
g.Config.Linux.Devices[i] = device
return
}
- if dev.Type == device.Type && dev.Major == device.Major && dev.Minor == device.Minor {
- fmt.Fprintln(os.Stderr, "WARNING: The same type, major and minor should not be used for multiple devices.")
- }
}
g.Config.Linux.Devices = append(g.Config.Linux.Devices, device)
diff --git a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
index 5fee5a3b2..8a8dc3970 100644
--- a/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
+++ b/vendor/github.com/opencontainers/runtime-tools/generate/seccomp/seccomp_default.go
@@ -566,6 +566,20 @@ func DefaultProfile(rs *specs.Spec) *rspec.LinuxSeccomp {
},
}...)
/* Flags parameter of the clone syscall is the 2nd on s390 */
+ syscalls = append(syscalls, []rspec.LinuxSyscall{
+ {
+ Names: []string{"clone"},
+ Action: rspec.ActAllow,
+ Args: []rspec.LinuxSeccompArg{
+ {
+ Index: 1,
+ Value: 2080505856,
+ ValueTwo: 0,
+ Op: rspec.OpMaskedEqual,
+ },
+ },
+ },
+ }...)
}
return &rspec.LinuxSeccomp{
diff --git a/vendor/github.com/opencontainers/runtime-tools/validate/validate.go b/vendor/github.com/opencontainers/runtime-tools/validate/validate.go
index e2e820979..9c3710529 100644
--- a/vendor/github.com/opencontainers/runtime-tools/validate/validate.go
+++ b/vendor/github.com/opencontainers/runtime-tools/validate/validate.go
@@ -144,7 +144,7 @@ func JSONSchemaURL(version string) (url string, err error) {
func (v *Validator) CheckJSONSchema() (errs error) {
logrus.Debugf("check JSON schema")
- url, err := JSONSchemaURL(v.spec.Version)
+ url, err := JSONSchemaURL(strings.TrimSuffix(v.spec.Version, "-dev"))
if err != nil {
errs = multierror.Append(errs, err)
return errs
diff --git a/vendor/golang.org/x/net/http2/README b/vendor/golang.org/x/net/http2/README
deleted file mode 100644
index 360d5aa37..000000000
--- a/vendor/golang.org/x/net/http2/README
+++ /dev/null
@@ -1,20 +0,0 @@
-This is a work-in-progress HTTP/2 implementation for Go.
-
-It will eventually live in the Go standard library and won't require
-any changes to your code to use. It will just be automatic.
-
-Status:
-
-* The server support is pretty good. A few things are missing
- but are being worked on.
-* The client work has just started but shares a lot of code
- is coming along much quicker.
-
-Docs are at https://godoc.org/golang.org/x/net/http2
-
-Demo test server at https://http2.golang.org/
-
-Help & bug reports welcome!
-
-Contributing: https://golang.org/doc/contribute.html
-Bugs: https://golang.org/issue/new?title=x/net/http2:+
diff --git a/vendor/golang.org/x/net/http2/ascii.go b/vendor/golang.org/x/net/http2/ascii.go
index 0c58d727c..17caa2058 100644
--- a/vendor/golang.org/x/net/http2/ascii.go
+++ b/vendor/golang.org/x/net/http2/ascii.go
@@ -6,6 +6,10 @@ package http2
import "strings"
+// The HTTP protocols are defined in terms of ASCII, not Unicode. This file
+// contains helper functions which may use Unicode-aware functions which would
+// otherwise be unsafe and could introduce vulnerabilities if used improperly.
+
// asciiEqualFold is strings.EqualFold, ASCII only. It reports whether s and t
// are equal, ASCII-case-insensitively.
func asciiEqualFold(s, t string) bool {
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index 09bc70533..19e449cfa 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -259,16 +259,12 @@ func ConfigureServer(s *http.Server, conf *Server) error {
s.TLSConfig.PreferServerCipherSuites = true
- haveNPN := false
- for _, p := range s.TLSConfig.NextProtos {
- if p == NextProtoTLS {
- haveNPN = true
- break
- }
- }
- if !haveNPN {
+ if !strSliceContains(s.TLSConfig.NextProtos, NextProtoTLS) {
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
}
+ if !strSliceContains(s.TLSConfig.NextProtos, "http/1.1") {
+ s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "http/1.1")
+ }
if s.TLSNextProto == nil {
s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
@@ -820,7 +816,7 @@ func (sc *serverConn) serve() {
})
sc.unackedSettings++
- // Each connection starts with intialWindowSize inflow tokens.
+ // Each connection starts with initialWindowSize inflow tokens.
// If a higher value is configured, we add more tokens.
if diff := sc.srv.initialConnRecvWindowSize() - initialWindowSize; diff > 0 {
sc.sendWindowUpdate(nil, int(diff))
@@ -860,6 +856,15 @@ func (sc *serverConn) serve() {
case res := <-sc.wroteFrameCh:
sc.wroteFrame(res)
case res := <-sc.readFrameCh:
+ // Process any written frames before reading new frames from the client since a
+ // written frame could have triggered a new stream to be started.
+ if sc.writingFrameAsync {
+ select {
+ case wroteRes := <-sc.wroteFrameCh:
+ sc.wroteFrame(wroteRes)
+ default:
+ }
+ }
if !sc.processFrameFromReader(res) {
return
}
diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go
index 7bd4b9c19..b261beb1d 100644
--- a/vendor/golang.org/x/net/http2/transport.go
+++ b/vendor/golang.org/x/net/http2/transport.go
@@ -264,9 +264,8 @@ type ClientConn struct {
peerMaxHeaderListSize uint64
initialWindowSize uint32
- hbuf bytes.Buffer // HPACK encoder writes into this
- henc *hpack.Encoder
- freeBuf [][]byte
+ hbuf bytes.Buffer // HPACK encoder writes into this
+ henc *hpack.Encoder
wmu sync.Mutex // held while writing; acquire AFTER mu if holding both
werr error // first write error that has occurred
@@ -386,8 +385,13 @@ func (cs *clientStream) abortRequestBodyWrite(err error) {
}
cc := cs.cc
cc.mu.Lock()
- cs.stopReqBody = err
- cc.cond.Broadcast()
+ if cs.stopReqBody == nil {
+ cs.stopReqBody = err
+ if cs.req.Body != nil {
+ cs.req.Body.Close()
+ }
+ cc.cond.Broadcast()
+ }
cc.mu.Unlock()
}
@@ -913,46 +917,6 @@ func (cc *ClientConn) closeForLostPing() error {
return cc.closeForError(err)
}
-const maxAllocFrameSize = 512 << 10
-
-// frameBuffer returns a scratch buffer suitable for writing DATA frames.
-// They're capped at the min of the peer's max frame size or 512KB
-// (kinda arbitrarily), but definitely capped so we don't allocate 4GB
-// bufers.
-func (cc *ClientConn) frameScratchBuffer() []byte {
- cc.mu.Lock()
- size := cc.maxFrameSize
- if size > maxAllocFrameSize {
- size = maxAllocFrameSize
- }
- for i, buf := range cc.freeBuf {
- if len(buf) >= int(size) {
- cc.freeBuf[i] = nil
- cc.mu.Unlock()
- return buf[:size]
- }
- }
- cc.mu.Unlock()
- return make([]byte, size)
-}
-
-func (cc *ClientConn) putFrameScratchBuffer(buf []byte) {
- cc.mu.Lock()
- defer cc.mu.Unlock()
- const maxBufs = 4 // arbitrary; 4 concurrent requests per conn? investigate.
- if len(cc.freeBuf) < maxBufs {
- cc.freeBuf = append(cc.freeBuf, buf)
- return
- }
- for i, old := range cc.freeBuf {
- if old == nil {
- cc.freeBuf[i] = buf
- return
- }
- }
- // forget about it.
-}
-
// errRequestCanceled is a copy of net/http's errRequestCanceled because it's not
// exported. At least they'll be DeepEqual for h1-vs-h2 comparisons tests.
var errRequestCanceled = errors.New("net/http: request canceled")
@@ -1151,40 +1115,28 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
return res, false, nil
}
+ handleError := func(err error) (*http.Response, bool, error) {
+ if !hasBody || bodyWritten {
+ cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
+ } else {
+ bodyWriter.cancel()
+ cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
+ <-bodyWriter.resc
+ }
+ cc.forgetStreamID(cs.ID)
+ return nil, cs.getStartedWrite(), err
+ }
+
for {
select {
case re := <-readLoopResCh:
return handleReadLoopResponse(re)
case <-respHeaderTimer:
- if !hasBody || bodyWritten {
- cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
- } else {
- bodyWriter.cancel()
- cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
- <-bodyWriter.resc
- }
- cc.forgetStreamID(cs.ID)
- return nil, cs.getStartedWrite(), errTimeout
+ return handleError(errTimeout)
case <-ctx.Done():
- if !hasBody || bodyWritten {
- cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
- } else {
- bodyWriter.cancel()
- cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
- <-bodyWriter.resc
- }
- cc.forgetStreamID(cs.ID)
- return nil, cs.getStartedWrite(), ctx.Err()
+ return handleError(ctx.Err())
case <-req.Cancel:
- if !hasBody || bodyWritten {
- cc.writeStreamReset(cs.ID, ErrCodeCancel, nil)
- } else {
- bodyWriter.cancel()
- cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
- <-bodyWriter.resc
- }
- cc.forgetStreamID(cs.ID)
- return nil, cs.getStartedWrite(), errRequestCanceled
+ return handleError(errRequestCanceled)
case <-cs.peerReset:
// processResetStream already removed the
// stream from the streams map; no need for
@@ -1295,11 +1247,35 @@ var (
errReqBodyTooLong = errors.New("http2: request body larger than specified content length")
)
+// frameScratchBufferLen returns the length of a buffer to use for
+// outgoing request bodies to read/write to/from.
+//
+// It returns max(1, min(peer's advertised max frame size,
+// Request.ContentLength+1, 512KB)).
+func (cs *clientStream) frameScratchBufferLen(maxFrameSize int) int {
+ const max = 512 << 10
+ n := int64(maxFrameSize)
+ if n > max {
+ n = max
+ }
+ if cl := actualContentLength(cs.req); cl != -1 && cl+1 < n {
+ // Add an extra byte past the declared content-length to
+ // give the caller's Request.Body io.Reader a chance to
+ // give us more bytes than they declared, so we can catch it
+ // early.
+ n = cl + 1
+ }
+ if n < 1 {
+ return 1
+ }
+ return int(n) // doesn't truncate; max is 512K
+}
+
+var bufPool sync.Pool // of *[]byte
+
func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (err error) {
cc := cs.cc
sentEnd := false // whether we sent the final DATA frame w/ END_STREAM
- buf := cc.frameScratchBuffer()
- defer cc.putFrameScratchBuffer(buf)
defer func() {
traceWroteRequest(cs.trace, err)
@@ -1307,7 +1283,13 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
// Request.Body is closed by the Transport,
// and in multiple cases: server replies <=299 and >299
// while still writing request body
- cerr := bodyCloser.Close()
+ var cerr error
+ cc.mu.Lock()
+ if cs.stopReqBody == nil {
+ cs.stopReqBody = errStopReqBodyWrite
+ cerr = bodyCloser.Close()
+ }
+ cc.mu.Unlock()
if err == nil {
err = cerr
}
@@ -1318,9 +1300,24 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
remainLen := actualContentLength(req)
hasContentLen := remainLen != -1
+ cc.mu.Lock()
+ maxFrameSize := int(cc.maxFrameSize)
+ cc.mu.Unlock()
+
+ // Scratch buffer for reading into & writing from.
+ scratchLen := cs.frameScratchBufferLen(maxFrameSize)
+ var buf []byte
+ if bp, ok := bufPool.Get().(*[]byte); ok && len(*bp) >= scratchLen {
+ defer bufPool.Put(bp)
+ buf = *bp
+ } else {
+ buf = make([]byte, scratchLen)
+ defer bufPool.Put(&buf)
+ }
+
var sawEOF bool
for !sawEOF {
- n, err := body.Read(buf[:len(buf)-1])
+ n, err := body.Read(buf[:len(buf)])
if hasContentLen {
remainLen -= int64(n)
if remainLen == 0 && err == nil {
@@ -1331,8 +1328,9 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
// to send the END_STREAM bit early, double-check that we're actually
// at EOF. Subsequent reads should return (0, EOF) at this point.
// If either value is different, we return an error in one of two ways below.
+ var scratch [1]byte
var n1 int
- n1, err = body.Read(buf[n:])
+ n1, err = body.Read(scratch[:])
remainLen -= int64(n1)
}
if remainLen < 0 {
@@ -1402,10 +1400,6 @@ func (cs *clientStream) writeRequestBody(body io.Reader, bodyCloser io.Closer) (
}
}
- cc.mu.Lock()
- maxFrameSize := int(cc.maxFrameSize)
- cc.mu.Unlock()
-
cc.wmu.Lock()
defer cc.wmu.Unlock()
diff --git a/vendor/google.golang.org/grpc/.travis.yml b/vendor/google.golang.org/grpc/.travis.yml
deleted file mode 100644
index 5847d94e5..000000000
--- a/vendor/google.golang.org/grpc/.travis.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-language: go
-
-matrix:
- include:
- - go: 1.14.x
- env: VET=1 GO111MODULE=on
- - go: 1.14.x
- env: RACE=1 GO111MODULE=on
- - go: 1.14.x
- env: RUN386=1
- - go: 1.14.x
- env: GRPC_GO_RETRY=on
- - go: 1.14.x
- env: TESTEXTRAS=1
- - go: 1.13.x
- env: GO111MODULE=on
- - go: 1.12.x
- env: GO111MODULE=on
- - go: 1.11.x # Keep until interop tests no longer require Go1.11
- env: GO111MODULE=on
-
-go_import_path: google.golang.org/grpc
-
-before_install:
- - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi
- - if [[ -n "${RUN386}" ]]; then export GOARCH=386; fi
- - if [[ "${TRAVIS_EVENT_TYPE}" = "cron" && -z "${RUN386}" ]]; then RACE=1; fi
- - if [[ "${TRAVIS_EVENT_TYPE}" != "cron" ]]; then export VET_SKIP_PROTO=1; fi
-
-install:
- - try3() { eval "$*" || eval "$*" || eval "$*"; }
- - try3 'if [[ "${GO111MODULE}" = "on" ]]; then go mod download; else make testdeps; fi'
- - if [[ -n "${GAE}" ]]; then source ./install_gae.sh; make testappenginedeps; fi
- - if [[ -n "${VET}" ]]; then ./vet.sh -install; fi
-
-script:
- - set -e
- - if [[ -n "${TESTEXTRAS}" ]]; then examples/examples_test.sh; security/advancedtls/examples/examples_test.sh; interop/interop_test.sh; make testsubmodule; exit 0; fi
- - if [[ -n "${VET}" ]]; then ./vet.sh; fi
- - if [[ -n "${GAE}" ]]; then make testappengine; exit 0; fi
- - if [[ -n "${RACE}" ]]; then make testrace; exit 0; fi
- - make test
diff --git a/vendor/google.golang.org/grpc/README.md b/vendor/google.golang.org/grpc/README.md
index 3949a683f..0e6ae69a5 100644
--- a/vendor/google.golang.org/grpc/README.md
+++ b/vendor/google.golang.org/grpc/README.md
@@ -136,6 +136,6 @@ errors.
[Go module]: https://github.com/golang/go/wiki/Modules
[gRPC]: https://grpc.io
[Go gRPC docs]: https://grpc.io/docs/languages/go
-[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5652536396611584&widget=490377658&container=1286539696
+[Performance benchmark]: https://performance-dot-grpc-testing.appspot.com/explore?dashboard=5180705743044608
[quick start]: https://grpc.io/docs/languages/go/quickstart
[go-releases]: https://golang.org/doc/devel/release.html
diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go
index 4cc7f9159..dd8397963 100644
--- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go
+++ b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go
@@ -43,7 +43,8 @@ type ccBalancerWrapper struct {
cc *ClientConn
balancerMu sync.Mutex // synchronizes calls to the balancer
balancer balancer.Balancer
- scBuffer *buffer.Unbounded
+ updateCh *buffer.Unbounded
+ closed *grpcsync.Event
done *grpcsync.Event
mu sync.Mutex
@@ -53,7 +54,8 @@ type ccBalancerWrapper struct {
func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper {
ccb := &ccBalancerWrapper{
cc: cc,
- scBuffer: buffer.NewUnbounded(),
+ updateCh: buffer.NewUnbounded(),
+ closed: grpcsync.NewEvent(),
done: grpcsync.NewEvent(),
subConns: make(map[*acBalancerWrapper]struct{}),
}
@@ -67,35 +69,53 @@ func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.Bui
func (ccb *ccBalancerWrapper) watcher() {
for {
select {
- case t := <-ccb.scBuffer.Get():
- ccb.scBuffer.Load()
- if ccb.done.HasFired() {
+ case t := <-ccb.updateCh.Get():
+ ccb.updateCh.Load()
+ if ccb.closed.HasFired() {
break
}
- ccb.balancerMu.Lock()
- su := t.(*scStateUpdate)
- ccb.balancer.UpdateSubConnState(su.sc, balancer.SubConnState{ConnectivityState: su.state, ConnectionError: su.err})
- ccb.balancerMu.Unlock()
- case <-ccb.done.Done():
+ switch u := t.(type) {
+ case *scStateUpdate:
+ ccb.balancerMu.Lock()
+ ccb.balancer.UpdateSubConnState(u.sc, balancer.SubConnState{ConnectivityState: u.state, ConnectionError: u.err})
+ ccb.balancerMu.Unlock()
+ case *acBalancerWrapper:
+ ccb.mu.Lock()
+ if ccb.subConns != nil {
+ delete(ccb.subConns, u)
+ ccb.cc.removeAddrConn(u.getAddrConn(), errConnDrain)
+ }
+ ccb.mu.Unlock()
+ default:
+ logger.Errorf("ccBalancerWrapper.watcher: unknown update %+v, type %T", t, t)
+ }
+ case <-ccb.closed.Done():
}
- if ccb.done.HasFired() {
+ if ccb.closed.HasFired() {
+ ccb.balancerMu.Lock()
ccb.balancer.Close()
+ ccb.balancerMu.Unlock()
ccb.mu.Lock()
scs := ccb.subConns
ccb.subConns = nil
ccb.mu.Unlock()
+ ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
+ ccb.done.Fire()
+ // Fire done before removing the addr conns. We can safely unblock
+ // ccb.close and allow the removeAddrConns to happen
+ // asynchronously.
for acbw := range scs {
ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
}
- ccb.UpdateState(balancer.State{ConnectivityState: connectivity.Connecting, Picker: nil})
return
}
}
}
func (ccb *ccBalancerWrapper) close() {
- ccb.done.Fire()
+ ccb.closed.Fire()
+ <-ccb.done.Done()
}
func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State, err error) {
@@ -109,7 +129,7 @@ func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s co
if sc == nil {
return
}
- ccb.scBuffer.Put(&scStateUpdate{
+ ccb.updateCh.Put(&scStateUpdate{
sc: sc,
state: s,
err: err,
@@ -150,17 +170,10 @@ func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer
}
func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) {
- acbw, ok := sc.(*acBalancerWrapper)
- if !ok {
- return
- }
- ccb.mu.Lock()
- defer ccb.mu.Unlock()
- if ccb.subConns == nil {
- return
- }
- delete(ccb.subConns, acbw)
- ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain)
+ // The RemoveSubConn() is handled in the run() goroutine, to avoid deadlock
+ // during switchBalancer() if the old balancer calls RemoveSubConn() in its
+ // Close().
+ ccb.updateCh.Put(sc)
}
func (ccb *ccBalancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver.Address) {
diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go
index 24109264f..b2bccfed1 100644
--- a/vendor/google.golang.org/grpc/clientconn.go
+++ b/vendor/google.golang.org/grpc/clientconn.go
@@ -711,7 +711,12 @@ func (cc *ClientConn) switchBalancer(name string) {
return
}
if cc.balancerWrapper != nil {
+ // Don't hold cc.mu while closing the balancers. The balancers may call
+ // methods that require cc.mu (e.g. cc.NewSubConn()). Holding the mutex
+ // would cause a deadlock in that case.
+ cc.mu.Unlock()
cc.balancerWrapper.close()
+ cc.mu.Lock()
}
builder := balancer.Get(name)
@@ -1046,12 +1051,12 @@ func (cc *ClientConn) Close() error {
cc.blockingpicker.close()
- if rWrapper != nil {
- rWrapper.close()
- }
if bWrapper != nil {
bWrapper.close()
}
+ if rWrapper != nil {
+ rWrapper.close()
+ }
for ac := range conns {
ac.tearDown(ErrClientConnClosing)
@@ -1424,26 +1429,14 @@ func (ac *addrConn) resetConnectBackoff() {
ac.mu.Unlock()
}
-// getReadyTransport returns the transport if ac's state is READY.
-// Otherwise it returns nil, false.
-// If ac's state is IDLE, it will trigger ac to connect.
-func (ac *addrConn) getReadyTransport() (transport.ClientTransport, bool) {
+// getReadyTransport returns the transport if ac's state is READY or nil if not.
+func (ac *addrConn) getReadyTransport() transport.ClientTransport {
ac.mu.Lock()
- if ac.state == connectivity.Ready && ac.transport != nil {
- t := ac.transport
- ac.mu.Unlock()
- return t, true
- }
- var idle bool
- if ac.state == connectivity.Idle {
- idle = true
- }
- ac.mu.Unlock()
- // Trigger idle ac to connect.
- if idle {
- ac.connect()
+ defer ac.mu.Unlock()
+ if ac.state == connectivity.Ready {
+ return ac.transport
}
- return nil, false
+ return nil
}
// tearDown starts to tear down the addrConn.
diff --git a/vendor/google.golang.org/grpc/go.mod b/vendor/google.golang.org/grpc/go.mod
index b177cfa66..2f2cf1eb7 100644
--- a/vendor/google.golang.org/grpc/go.mod
+++ b/vendor/google.golang.org/grpc/go.mod
@@ -3,15 +3,16 @@ module google.golang.org/grpc
go 1.11
require (
+ github.com/cespare/xxhash v1.1.0
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403
- github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d
+ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
- github.com/golang/protobuf v1.4.2
+ github.com/golang/protobuf v1.4.3
github.com/google/go-cmp v0.5.0
github.com/google/uuid v1.1.2
- golang.org/x/net v0.0.0-20190311183353-d8887717615a
- golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
- golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a
+ golang.org/x/net v0.0.0-20200822124328-c89045814202
+ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
+ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
google.golang.org/protobuf v1.25.0
)
diff --git a/vendor/google.golang.org/grpc/go.sum b/vendor/google.golang.org/grpc/go.sum
index 24d2976ab..372b4ea3d 100644
--- a/vendor/google.golang.org/grpc/go.sum
+++ b/vendor/google.golang.org/grpc/go.sum
@@ -1,32 +1,46 @@
-cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
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-20201120205902-5459f2c99403 h1:cqQfy1jclcSy/FwLjemeg3SR1yaINm74aQyupQ0Bl8M=
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 h1:OZmjad4L3H8ncOIR8rnb5MREYqG8ixi5+WbeUsquF0c=
+github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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.9-0.20210217033140-668b12f5399d h1:QyzYnTnPE15SQyUeqU6qLbWxMkwyAyu+vGksa0b7j00=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+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.20210512163311-63b5d3c536b0 h1:dulLQAYQFYtG5MTplgNGHWuV2D+OBD+Z8lmDBmbLg+s=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
+github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
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=
@@ -35,49 +49,66 @@ github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
+golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
+golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
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=
@@ -90,7 +121,8 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/vendor/google.golang.org/grpc/internal/binarylog/sink.go b/vendor/google.golang.org/grpc/internal/binarylog/sink.go
index 7d7a3056b..c2fdd58b3 100644
--- a/vendor/google.golang.org/grpc/internal/binarylog/sink.go
+++ b/vendor/google.golang.org/grpc/internal/binarylog/sink.go
@@ -69,7 +69,8 @@ type writerSink struct {
func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
b, err := proto.Marshal(e)
if err != nil {
- grpclogLogger.Infof("binary logging: failed to marshal proto message: %v", err)
+ grpclogLogger.Errorf("binary logging: failed to marshal proto message: %v", err)
+ return err
}
hdr := make([]byte, 4)
binary.BigEndian.PutUint32(hdr, uint32(len(b)))
@@ -85,24 +86,27 @@ func (ws *writerSink) Write(e *pb.GrpcLogEntry) error {
func (ws *writerSink) Close() error { return nil }
type bufferedSink struct {
- mu sync.Mutex
- closer io.Closer
- out Sink // out is built on buf.
- buf *bufio.Writer // buf is kept for flush.
-
- writeStartOnce sync.Once
- writeTicker *time.Ticker
+ mu sync.Mutex
+ closer io.Closer
+ out Sink // out is built on buf.
+ buf *bufio.Writer // buf is kept for flush.
+ flusherStarted bool
+
+ writeTicker *time.Ticker
+ done chan struct{}
}
func (fs *bufferedSink) Write(e *pb.GrpcLogEntry) error {
- // Start the write loop when Write is called.
- fs.writeStartOnce.Do(fs.startFlushGoroutine)
fs.mu.Lock()
+ defer fs.mu.Unlock()
+ if !fs.flusherStarted {
+ // Start the write loop when Write is called.
+ fs.startFlushGoroutine()
+ fs.flusherStarted = true
+ }
if err := fs.out.Write(e); err != nil {
- fs.mu.Unlock()
return err
}
- fs.mu.Unlock()
return nil
}
@@ -113,7 +117,12 @@ const (
func (fs *bufferedSink) startFlushGoroutine() {
fs.writeTicker = time.NewTicker(bufFlushDuration)
go func() {
- for range fs.writeTicker.C {
+ for {
+ select {
+ case <-fs.done:
+ return
+ case <-fs.writeTicker.C:
+ }
fs.mu.Lock()
if err := fs.buf.Flush(); err != nil {
grpclogLogger.Warningf("failed to flush to Sink: %v", err)
@@ -124,10 +133,12 @@ func (fs *bufferedSink) startFlushGoroutine() {
}
func (fs *bufferedSink) Close() error {
+ fs.mu.Lock()
+ defer fs.mu.Unlock()
if fs.writeTicker != nil {
fs.writeTicker.Stop()
}
- fs.mu.Lock()
+ close(fs.done)
if err := fs.buf.Flush(); err != nil {
grpclogLogger.Warningf("failed to flush to Sink: %v", err)
}
@@ -137,7 +148,6 @@ func (fs *bufferedSink) Close() error {
if err := fs.out.Close(); err != nil {
grpclogLogger.Warningf("failed to close the Sink: %v", err)
}
- fs.mu.Unlock()
return nil
}
@@ -155,5 +165,6 @@ func NewBufferedSink(o io.WriteCloser) Sink {
closer: o,
out: newWriterSink(bufW),
buf: bufW,
+ done: make(chan struct{}),
}
}
diff --git a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go
index 200b115ca..740f83c2b 100644
--- a/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go
+++ b/vendor/google.golang.org/grpc/internal/grpcrand/grpcrand.go
@@ -31,26 +31,37 @@ var (
mu sync.Mutex
)
+// Int implements rand.Int on the grpcrand global source.
+func Int() int {
+ mu.Lock()
+ defer mu.Unlock()
+ return r.Int()
+}
+
// Int63n implements rand.Int63n on the grpcrand global source.
func Int63n(n int64) int64 {
mu.Lock()
- res := r.Int63n(n)
- mu.Unlock()
- return res
+ defer mu.Unlock()
+ return r.Int63n(n)
}
// Intn implements rand.Intn on the grpcrand global source.
func Intn(n int) int {
mu.Lock()
- res := r.Intn(n)
- mu.Unlock()
- return res
+ defer mu.Unlock()
+ return r.Intn(n)
}
// Float64 implements rand.Float64 on the grpcrand global source.
func Float64() float64 {
mu.Lock()
- res := r.Float64()
- mu.Unlock()
- return res
+ defer mu.Unlock()
+ return r.Float64()
+}
+
+// Uint64 implements rand.Uint64 on the grpcrand global source.
+func Uint64() uint64 {
+ mu.Lock()
+ defer mu.Unlock()
+ return r.Uint64()
}
diff --git a/vendor/google.golang.org/grpc/internal/status/status.go b/vendor/google.golang.org/grpc/internal/status/status.go
index 710223b8d..e5c6513ed 100644
--- a/vendor/google.golang.org/grpc/internal/status/status.go
+++ b/vendor/google.golang.org/grpc/internal/status/status.go
@@ -97,7 +97,7 @@ func (s *Status) Err() error {
if s.Code() == codes.OK {
return nil
}
- return &Error{e: s.Proto()}
+ return &Error{s: s}
}
// WithDetails returns a new status with the provided details messages appended to the status.
@@ -136,19 +136,23 @@ func (s *Status) Details() []interface{} {
return details
}
+func (s *Status) String() string {
+ return fmt.Sprintf("rpc error: code = %s desc = %s", s.Code(), s.Message())
+}
+
// Error wraps a pointer of a status proto. It implements error and Status,
// and a nil *Error should never be returned by this package.
type Error struct {
- e *spb.Status
+ s *Status
}
func (e *Error) Error() string {
- return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(e.e.GetCode()), e.e.GetMessage())
+ return e.s.String()
}
// GRPCStatus returns the Status represented by se.
func (e *Error) GRPCStatus() *Status {
- return FromProto(e.e)
+ return e.s
}
// Is implements future error.Is functionality.
@@ -158,5 +162,5 @@ func (e *Error) Is(target error) bool {
if !ok {
return false
}
- return proto.Equal(e.e, tse.e)
+ return proto.Equal(e.s.s, tse.s.s)
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
index f63a01376..45532f8ae 100644
--- a/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
+++ b/vendor/google.golang.org/grpc/internal/transport/controlbuf.go
@@ -296,7 +296,7 @@ type controlBuffer struct {
// closed and nilled when transportResponseFrames drops below the
// threshold. Both fields are protected by mu.
transportResponseFrames int
- trfChan atomic.Value // *chan struct{}
+ trfChan atomic.Value // chan struct{}
}
func newControlBuffer(done <-chan struct{}) *controlBuffer {
@@ -310,10 +310,10 @@ func newControlBuffer(done <-chan struct{}) *controlBuffer {
// throttle blocks if there are too many incomingSettings/cleanupStreams in the
// controlbuf.
func (c *controlBuffer) throttle() {
- ch, _ := c.trfChan.Load().(*chan struct{})
+ ch, _ := c.trfChan.Load().(chan struct{})
if ch != nil {
select {
- case <-*ch:
+ case <-ch:
case <-c.done:
}
}
@@ -347,8 +347,7 @@ func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it cbItem) (b
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
// We are adding the frame that puts us over the threshold; create
// a throttling channel.
- ch := make(chan struct{})
- c.trfChan.Store(&ch)
+ c.trfChan.Store(make(chan struct{}))
}
}
c.mu.Unlock()
@@ -389,9 +388,9 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
if c.transportResponseFrames == maxQueuedTransportResponseFrames {
// We are removing the frame that put us over the
// threshold; close and clear the throttling channel.
- ch := c.trfChan.Load().(*chan struct{})
- close(*ch)
- c.trfChan.Store((*chan struct{})(nil))
+ ch := c.trfChan.Load().(chan struct{})
+ close(ch)
+ c.trfChan.Store((chan struct{})(nil))
}
c.transportResponseFrames--
}
@@ -407,7 +406,6 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
select {
case <-c.ch:
case <-c.done:
- c.finish()
return nil, ErrConnClosing
}
}
@@ -432,6 +430,14 @@ func (c *controlBuffer) finish() {
hdr.onOrphaned(ErrConnClosing)
}
}
+ // In case throttle() is currently in flight, it needs to be unblocked.
+ // Otherwise, the transport may not close, since the transport is closed by
+ // the reader encountering the connection error.
+ ch, _ := c.trfChan.Load().(chan struct{})
+ if ch != nil {
+ close(ch)
+ }
+ c.trfChan.Store((chan struct{})(nil))
c.mu.Unlock()
}
diff --git a/vendor/google.golang.org/grpc/internal/transport/handler_server.go b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
index 05d3871e6..1c3459c2b 100644
--- a/vendor/google.golang.org/grpc/internal/transport/handler_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/handler_server.go
@@ -141,9 +141,8 @@ type serverHandlerTransport struct {
stats stats.Handler
}
-func (ht *serverHandlerTransport) Close() error {
+func (ht *serverHandlerTransport) Close() {
ht.closeOnce.Do(ht.closeCloseChanOnce)
- return nil
}
func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
index 48c5e52ed..0cd6da1e7 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
@@ -24,6 +24,7 @@ import (
"io"
"math"
"net"
+ "net/http"
"strconv"
"strings"
"sync"
@@ -241,7 +242,15 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
// and passed to the credential handshaker. This makes it possible for
// address specific arbitrary data to reach the credential handshaker.
connectCtx = icredentials.NewClientHandshakeInfoContext(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes})
- conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, conn)
+ rawConn := conn
+ // Pull the deadline from the connectCtx, which will be used for
+ // timeouts in the authentication protocol handshake. Can ignore the
+ // boolean as the deadline will return the zero value, which will make
+ // the conn not timeout on I/O operations.
+ deadline, _ := connectCtx.Deadline()
+ rawConn.SetDeadline(deadline)
+ conn, authInfo, err = transportCreds.ClientHandshake(connectCtx, addr.ServerName, rawConn)
+ rawConn.SetDeadline(time.Time{})
if err != nil {
return nil, connectionErrorf(isTemporary(err), err, "transport: authentication handshake failed: %v", err)
}
@@ -399,11 +408,10 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
}
}
- // If it's a connection error, let reader goroutine handle it
- // since there might be data in the buffers.
- if _, ok := err.(net.Error); !ok {
- t.conn.Close()
- }
+ // Do not close the transport. Let reader goroutine handle it since
+ // there might be data in the buffers.
+ t.conn.Close()
+ t.controlBuf.finish()
close(t.writerDone)
}()
return t, nil
@@ -608,26 +616,39 @@ func (t *http2Client) getCallAuthData(ctx context.Context, audience string, call
return callAuthData, nil
}
-// PerformedIOError wraps an error to indicate IO may have been performed
-// before the error occurred.
-type PerformedIOError struct {
+// NewStreamError wraps an error and reports additional information.
+type NewStreamError struct {
Err error
+
+ DoNotRetry bool
+ PerformedIO bool
}
-// Error implements error.
-func (p PerformedIOError) Error() string {
- return p.Err.Error()
+func (e NewStreamError) Error() string {
+ return e.Err.Error()
}
// NewStream creates a stream and registers it into the transport as "active"
-// streams.
+// streams. All non-nil errors returned will be *NewStreamError.
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) {
+ defer func() {
+ if err != nil {
+ nse, ok := err.(*NewStreamError)
+ if !ok {
+ nse = &NewStreamError{Err: err}
+ }
+ if len(t.perRPCCreds) > 0 || callHdr.Creds != nil {
+ // We may have performed I/O in the per-RPC creds callback, so do not
+ // allow transparent retry.
+ nse.PerformedIO = true
+ }
+ err = nse
+ }
+ }()
ctx = peer.NewContext(ctx, t.getPeer())
headerFields, err := t.createHeaderFields(ctx, callHdr)
if err != nil {
- // We may have performed I/O in the per-RPC creds callback, so do not
- // allow transparent retry.
- return nil, PerformedIOError{err}
+ return nil, err
}
s := t.newStream(ctx, callHdr)
cleanup := func(err error) {
@@ -733,7 +754,7 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Strea
break
}
if hdrListSizeErr != nil {
- return nil, hdrListSizeErr
+ return nil, &NewStreamError{Err: hdrListSizeErr, DoNotRetry: true}
}
firstTry = false
select {
@@ -878,12 +899,18 @@ func (t *http2Client) Close(err error) {
// Append info about previous goaways if there were any, since this may be important
// for understanding the root cause for this connection to be closed.
_, goAwayDebugMessage := t.GetGoAwayReason()
+
+ var st *status.Status
if len(goAwayDebugMessage) > 0 {
- err = fmt.Errorf("closing transport due to: %v, received prior goaway: %v", err, goAwayDebugMessage)
+ st = status.Newf(codes.Unavailable, "closing transport due to: %v, received prior goaway: %v", err, goAwayDebugMessage)
+ err = st.Err()
+ } else {
+ st = status.New(codes.Unavailable, err.Error())
}
+
// Notify all active streams.
for _, s := range streams {
- t.closeStream(s, err, false, http2.ErrCodeNo, status.New(codes.Unavailable, err.Error()), nil, false)
+ t.closeStream(s, err, false, http2.ErrCodeNo, st, nil, false)
}
if t.statsHandler != nil {
connEnd := &stats.ConnEnd{
@@ -1221,7 +1248,11 @@ func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) {
t.goAwayReason = GoAwayTooManyPings
}
}
- t.goAwayDebugMessage = fmt.Sprintf("code: %s, debug data: %v", f.ErrCode, string(f.DebugData()))
+ if len(f.DebugData()) == 0 {
+ t.goAwayDebugMessage = fmt.Sprintf("code: %s", f.ErrCode)
+ } else {
+ t.goAwayDebugMessage = fmt.Sprintf("code: %s, debug data: %q", f.ErrCode, string(f.DebugData()))
+ }
}
func (t *http2Client) GetGoAwayReason() (GoAwayReason, string) {
@@ -1254,11 +1285,124 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
return
}
- state := &decodeState{}
- // Initialize isGRPC value to be !initialHeader, since if a gRPC Response-Headers has already been received, then it means that the peer is speaking gRPC and we are in gRPC mode.
- state.data.isGRPC = !initialHeader
- if h2code, err := state.decodeHeader(frame); err != nil {
- t.closeStream(s, err, true, h2code, status.Convert(err), nil, endStream)
+ // frame.Truncated is set to true when framer detects that the current header
+ // list size hits MaxHeaderListSize limit.
+ if frame.Truncated {
+ se := status.New(codes.Internal, "peer header list size exceeded limit")
+ t.closeStream(s, se.Err(), true, http2.ErrCodeFrameSize, se, nil, endStream)
+ return
+ }
+
+ var (
+ // If a gRPC Response-Headers has already been received, then it means
+ // that the peer is speaking gRPC and we are in gRPC mode.
+ isGRPC = !initialHeader
+ mdata = make(map[string][]string)
+ contentTypeErr = "malformed header: missing HTTP content-type"
+ grpcMessage string
+ statusGen *status.Status
+ recvCompress string
+ httpStatusCode *int
+ httpStatusErr string
+ rawStatusCode = codes.Unknown
+ // headerError is set if an error is encountered while parsing the headers
+ headerError string
+ )
+
+ if initialHeader {
+ httpStatusErr = "malformed header: missing HTTP status"
+ }
+
+ for _, hf := range frame.Fields {
+ switch hf.Name {
+ case "content-type":
+ if _, validContentType := grpcutil.ContentSubtype(hf.Value); !validContentType {
+ contentTypeErr = fmt.Sprintf("transport: received unexpected content-type %q", hf.Value)
+ break
+ }
+ contentTypeErr = ""
+ mdata[hf.Name] = append(mdata[hf.Name], hf.Value)
+ isGRPC = true
+ case "grpc-encoding":
+ recvCompress = hf.Value
+ case "grpc-status":
+ code, err := strconv.ParseInt(hf.Value, 10, 32)
+ if err != nil {
+ se := status.New(codes.Internal, fmt.Sprintf("transport: malformed grpc-status: %v", err))
+ t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
+ return
+ }
+ rawStatusCode = codes.Code(uint32(code))
+ case "grpc-message":
+ grpcMessage = decodeGrpcMessage(hf.Value)
+ case "grpc-status-details-bin":
+ var err error
+ statusGen, err = decodeGRPCStatusDetails(hf.Value)
+ if err != nil {
+ headerError = fmt.Sprintf("transport: malformed grpc-status-details-bin: %v", err)
+ }
+ case ":status":
+ if hf.Value == "200" {
+ httpStatusErr = ""
+ statusCode := 200
+ httpStatusCode = &statusCode
+ break
+ }
+
+ c, err := strconv.ParseInt(hf.Value, 10, 32)
+ if err != nil {
+ se := status.New(codes.Internal, fmt.Sprintf("transport: malformed http-status: %v", err))
+ t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
+ return
+ }
+ statusCode := int(c)
+ httpStatusCode = &statusCode
+
+ httpStatusErr = fmt.Sprintf(
+ "unexpected HTTP status code received from server: %d (%s)",
+ statusCode,
+ http.StatusText(statusCode),
+ )
+ default:
+ if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
+ break
+ }
+ v, err := decodeMetadataHeader(hf.Name, hf.Value)
+ if err != nil {
+ headerError = fmt.Sprintf("transport: malformed %s: %v", hf.Name, err)
+ logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
+ break
+ }
+ mdata[hf.Name] = append(mdata[hf.Name], v)
+ }
+ }
+
+ if !isGRPC || httpStatusErr != "" {
+ var code = codes.Internal // when header does not include HTTP status, return INTERNAL
+
+ if httpStatusCode != nil {
+ var ok bool
+ code, ok = HTTPStatusConvTab[*httpStatusCode]
+ if !ok {
+ code = codes.Unknown
+ }
+ }
+ var errs []string
+ if httpStatusErr != "" {
+ errs = append(errs, httpStatusErr)
+ }
+ if contentTypeErr != "" {
+ errs = append(errs, contentTypeErr)
+ }
+ // Verify the HTTP response is a 200.
+ se := status.New(code, strings.Join(errs, "; "))
+ t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
+ return
+ }
+
+ if headerError != "" {
+ se := status.New(codes.Internal, headerError)
+ t.closeStream(s, se.Err(), true, http2.ErrCodeProtocol, se, nil, endStream)
return
}
@@ -1293,9 +1437,9 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
// These values can be set without any synchronization because
// stream goroutine will read it only after seeing a closed
// headerChan which we'll close after setting this.
- s.recvCompress = state.data.encoding
- if len(state.data.mdata) > 0 {
- s.header = state.data.mdata
+ s.recvCompress = recvCompress
+ if len(mdata) > 0 {
+ s.header = mdata
}
} else {
// HEADERS frame block carries a Trailers-Only.
@@ -1308,9 +1452,13 @@ func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) {
return
}
+ if statusGen == nil {
+ statusGen = status.New(rawStatusCode, grpcMessage)
+ }
+
// if client received END_STREAM from server while stream was still active, send RST_STREAM
rst := s.getState() == streamActive
- t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, state.status(), state.data.mdata, true)
+ t.closeStream(s, io.EOF, rst, http2.ErrCodeNo, statusGen, mdata, true)
}
// reader runs as a separate goroutine in charge of reading data from network
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
index 11be5599c..e3799d50a 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
@@ -102,11 +102,11 @@ type http2Server struct {
mu sync.Mutex // guard the following
- // drainChan is initialized when drain(...) is called the first time.
+ // drainChan is initialized when Drain() is called the first time.
// After which the server writes out the first GoAway(with ID 2^31-1) frame.
// Then an independent goroutine will be launched to later send the second GoAway.
// During this time we don't want to write another first GoAway(with ID 2^31 -1) frame.
- // Thus call to drain(...) will be a no-op if drainChan is already initialized since draining is
+ // Thus call to Drain() will be a no-op if drainChan is already initialized since draining is
// already underway.
drainChan chan struct{}
state transportState
@@ -125,9 +125,14 @@ type http2Server struct {
connectionID uint64
}
-// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is
-// returned if something goes wrong.
-func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
+// NewServerTransport creates a http2 transport with conn and configuration
+// options from config.
+//
+// It returns a non-nil transport and a nil error on success. On failure, it
+// returns a non-nil transport and a nil-error. For a special case where the
+// underlying conn gets closed before the client preface could be read, it
+// returns a nil transport and a nil error.
+func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) {
writeBufSize := config.WriteBufferSize
readBufSize := config.ReadBufferSize
maxHeaderListSize := defaultServerMaxHeaderListSize
@@ -266,6 +271,13 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
// Check the validity of client preface.
preface := make([]byte, len(clientPreface))
if _, err := io.ReadFull(t.conn, preface); err != nil {
+ // In deployments where a gRPC server runs behind a cloud load balancer
+ // which performs regular TCP level health checks, the connection is
+ // closed immediately by the latter. Skipping the error here will help
+ // reduce log clutter.
+ if err == io.EOF {
+ return nil, nil
+ }
return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to receive the preface from client: %v", err)
}
if !bytes.Equal(preface, clientPreface) {
@@ -295,6 +307,7 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
}
}
t.conn.Close()
+ t.controlBuf.finish()
close(t.writerDone)
}()
go t.keepalive()
@@ -304,37 +317,92 @@ func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err
// operateHeader takes action on the decoded headers.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (fatal bool) {
streamID := frame.Header().StreamID
- state := &decodeState{
- serverSide: true,
- }
- if h2code, err := state.decodeHeader(frame); err != nil {
- if _, ok := status.FromError(err); ok {
- t.controlBuf.put(&cleanupStream{
- streamID: streamID,
- rst: true,
- rstCode: h2code,
- onWrite: func() {},
- })
- }
+
+ // frame.Truncated is set to true when framer detects that the current header
+ // list size hits MaxHeaderListSize limit.
+ if frame.Truncated {
+ t.controlBuf.put(&cleanupStream{
+ streamID: streamID,
+ rst: true,
+ rstCode: http2.ErrCodeFrameSize,
+ onWrite: func() {},
+ })
return false
}
buf := newRecvBuffer()
s := &Stream{
- id: streamID,
- st: t,
- buf: buf,
- fc: &inFlow{limit: uint32(t.initialWindowSize)},
- recvCompress: state.data.encoding,
- method: state.data.method,
- contentSubtype: state.data.contentSubtype,
+ id: streamID,
+ st: t,
+ buf: buf,
+ fc: &inFlow{limit: uint32(t.initialWindowSize)},
+ }
+
+ var (
+ // If a gRPC Response-Headers has already been received, then it means
+ // that the peer is speaking gRPC and we are in gRPC mode.
+ isGRPC = false
+ mdata = make(map[string][]string)
+ httpMethod string
+ // headerError is set if an error is encountered while parsing the headers
+ headerError bool
+
+ timeoutSet bool
+ timeout time.Duration
+ )
+
+ for _, hf := range frame.Fields {
+ switch hf.Name {
+ case "content-type":
+ contentSubtype, validContentType := grpcutil.ContentSubtype(hf.Value)
+ if !validContentType {
+ break
+ }
+ mdata[hf.Name] = append(mdata[hf.Name], hf.Value)
+ s.contentSubtype = contentSubtype
+ isGRPC = true
+ case "grpc-encoding":
+ s.recvCompress = hf.Value
+ case ":method":
+ httpMethod = hf.Value
+ case ":path":
+ s.method = hf.Value
+ case "grpc-timeout":
+ timeoutSet = true
+ var err error
+ if timeout, err = decodeTimeout(hf.Value); err != nil {
+ headerError = true
+ }
+ default:
+ if isReservedHeader(hf.Name) && !isWhitelistedHeader(hf.Name) {
+ break
+ }
+ v, err := decodeMetadataHeader(hf.Name, hf.Value)
+ if err != nil {
+ headerError = true
+ logger.Warningf("Failed to decode metadata header (%q, %q): %v", hf.Name, hf.Value, err)
+ break
+ }
+ mdata[hf.Name] = append(mdata[hf.Name], v)
+ }
}
+
+ if !isGRPC || headerError {
+ t.controlBuf.put(&cleanupStream{
+ streamID: streamID,
+ rst: true,
+ rstCode: http2.ErrCodeProtocol,
+ onWrite: func() {},
+ })
+ return false
+ }
+
if frame.StreamEnded() {
// s is just created by the caller. No lock needed.
s.state = streamReadDone
}
- if state.data.timeoutSet {
- s.ctx, s.cancel = context.WithTimeout(t.ctx, state.data.timeout)
+ if timeoutSet {
+ s.ctx, s.cancel = context.WithTimeout(t.ctx, timeout)
} else {
s.ctx, s.cancel = context.WithCancel(t.ctx)
}
@@ -347,14 +415,14 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
}
s.ctx = peer.NewContext(s.ctx, pr)
// Attach the received metadata to the context.
- if len(state.data.mdata) > 0 {
- s.ctx = metadata.NewIncomingContext(s.ctx, state.data.mdata)
- }
- if state.data.statsTags != nil {
- s.ctx = stats.SetIncomingTags(s.ctx, state.data.statsTags)
- }
- if state.data.statsTrace != nil {
- s.ctx = stats.SetIncomingTrace(s.ctx, state.data.statsTrace)
+ if len(mdata) > 0 {
+ s.ctx = metadata.NewIncomingContext(s.ctx, mdata)
+ if statsTags := mdata["grpc-tags-bin"]; len(statsTags) > 0 {
+ s.ctx = stats.SetIncomingTags(s.ctx, []byte(statsTags[len(statsTags)-1]))
+ }
+ if statsTrace := mdata["grpc-trace-bin"]; len(statsTrace) > 0 {
+ s.ctx = stats.SetIncomingTrace(s.ctx, []byte(statsTrace[len(statsTrace)-1]))
+ }
}
t.mu.Lock()
if t.state != reachable {
@@ -383,10 +451,10 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
return true
}
t.maxStreamID = streamID
- if state.data.httpMethod != http.MethodPost {
+ if httpMethod != http.MethodPost {
t.mu.Unlock()
if logger.V(logLevel) {
- logger.Warningf("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", state.data.httpMethod)
+ logger.Infof("transport: http2Server.operateHeaders parsed a :method field: %v which should be POST", httpMethod)
}
t.controlBuf.put(&cleanupStream{
streamID: streamID,
@@ -399,7 +467,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
}
if t.inTapHandle != nil {
var err error
- if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: state.data.method}); err != nil {
+ if s.ctx, err = t.inTapHandle(s.ctx, &tap.Info{FullMethodName: s.method}); err != nil {
t.mu.Unlock()
if logger.V(logLevel) {
logger.Infof("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err)
@@ -437,7 +505,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
LocalAddr: t.localAddr,
Compression: s.recvCompress,
WireLength: int(frame.Header().Length),
- Header: metadata.MD(state.data.mdata).Copy(),
+ Header: metadata.MD(mdata).Copy(),
}
t.stats.HandleRPC(s.ctx, inHeader)
}
@@ -1004,12 +1072,12 @@ func (t *http2Server) keepalive() {
if val <= 0 {
// The connection has been idle for a duration of keepalive.MaxConnectionIdle or more.
// Gracefully close the connection.
- t.drain(http2.ErrCodeNo, []byte{})
+ t.Drain()
return
}
idleTimer.Reset(val)
case <-ageTimer.C:
- t.drain(http2.ErrCodeNo, []byte{})
+ t.Drain()
ageTimer.Reset(t.kp.MaxConnectionAgeGrace)
select {
case <-ageTimer.C:
@@ -1063,11 +1131,11 @@ func (t *http2Server) keepalive() {
// Close starts shutting down the http2Server transport.
// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
// could cause some resource issue. Revisit this later.
-func (t *http2Server) Close() error {
+func (t *http2Server) Close() {
t.mu.Lock()
if t.state == closing {
t.mu.Unlock()
- return errors.New("transport: Close() was already called")
+ return
}
t.state = closing
streams := t.activeStreams
@@ -1075,7 +1143,9 @@ func (t *http2Server) Close() error {
t.mu.Unlock()
t.controlBuf.finish()
close(t.done)
- err := t.conn.Close()
+ if err := t.conn.Close(); err != nil && logger.V(logLevel) {
+ logger.Infof("transport: error closing conn during Close: %v", err)
+ }
if channelz.IsOn() {
channelz.RemoveEntry(t.channelzID)
}
@@ -1087,7 +1157,6 @@ func (t *http2Server) Close() error {
connEnd := &stats.ConnEnd{}
t.stats.HandleConn(t.ctx, connEnd)
}
- return err
}
// deleteStream deletes the stream s from transport's active streams.
@@ -1152,17 +1221,13 @@ func (t *http2Server) RemoteAddr() net.Addr {
}
func (t *http2Server) Drain() {
- t.drain(http2.ErrCodeNo, []byte{})
-}
-
-func (t *http2Server) drain(code http2.ErrCode, debugData []byte) {
t.mu.Lock()
defer t.mu.Unlock()
if t.drainChan != nil {
return
}
t.drainChan = make(chan struct{})
- t.controlBuf.put(&goAway{code: code, debugData: debugData, headsUp: true})
+ t.controlBuf.put(&goAway{code: http2.ErrCodeNo, debugData: []byte{}, headsUp: true})
}
var goAwayPing = &ping{data: [8]byte{1, 6, 1, 8, 0, 3, 3, 9}}
diff --git a/vendor/google.golang.org/grpc/internal/transport/http_util.go b/vendor/google.golang.org/grpc/internal/transport/http_util.go
index c7dee140c..d8247bcdf 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http_util.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http_util.go
@@ -39,7 +39,6 @@ import (
spb "google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
- "google.golang.org/grpc/internal/grpcutil"
"google.golang.org/grpc/status"
)
@@ -96,53 +95,6 @@ var (
logger = grpclog.Component("transport")
)
-type parsedHeaderData struct {
- encoding string
- // statusGen caches the stream status received from the trailer the server
- // sent. Client side only. Do not access directly. After all trailers are
- // parsed, use the status method to retrieve the status.
- statusGen *status.Status
- // rawStatusCode and rawStatusMsg are set from the raw trailer fields and are not
- // intended for direct access outside of parsing.
- rawStatusCode *int
- rawStatusMsg string
- httpStatus *int
- // Server side only fields.
- timeoutSet bool
- timeout time.Duration
- method string
- httpMethod string
- // key-value metadata map from the peer.
- mdata map[string][]string
- statsTags []byte
- statsTrace []byte
- contentSubtype string
-
- // isGRPC field indicates whether the peer is speaking gRPC (otherwise HTTP).
- //
- // We are in gRPC mode (peer speaking gRPC) if:
- // * We are client side and have already received a HEADER frame that indicates gRPC peer.
- // * The header contains valid a content-type, i.e. a string starts with "application/grpc"
- // And we should handle error specific to gRPC.
- //
- // Otherwise (i.e. a content-type string starts without "application/grpc", or does not exist), we
- // are in HTTP fallback mode, and should handle error specific to HTTP.
- isGRPC bool
- grpcErr error
- httpErr error
- contentTypeErr string
-}
-
-// decodeState configures decoding criteria and records the decoded data.
-type decodeState struct {
- // whether decoding on server side or not
- serverSide bool
-
- // Records the states during HPACK decoding. It will be filled with info parsed from HTTP HEADERS
- // frame once decodeHeader function has been invoked and returned.
- data parsedHeaderData
-}
-
// isReservedHeader checks whether hdr belongs to HTTP2 headers
// reserved by gRPC protocol. Any other headers are classified as the
// user-specified metadata.
@@ -180,14 +132,6 @@ func isWhitelistedHeader(hdr string) bool {
}
}
-func (d *decodeState) status() *status.Status {
- if d.data.statusGen == nil {
- // No status-details were provided; generate status using code/msg.
- d.data.statusGen = status.New(codes.Code(int32(*(d.data.rawStatusCode))), d.data.rawStatusMsg)
- }
- return d.data.statusGen
-}
-
const binHdrSuffix = "-bin"
func encodeBinHeader(v []byte) string {
@@ -217,168 +161,16 @@ func decodeMetadataHeader(k, v string) (string, error) {
return v, nil
}
-func (d *decodeState) decodeHeader(frame *http2.MetaHeadersFrame) (http2.ErrCode, error) {
- // frame.Truncated is set to true when framer detects that the current header
- // list size hits MaxHeaderListSize limit.
- if frame.Truncated {
- return http2.ErrCodeFrameSize, status.Error(codes.Internal, "peer header list size exceeded limit")
- }
-
- for _, hf := range frame.Fields {
- d.processHeaderField(hf)
- }
-
- if d.data.isGRPC {
- if d.data.grpcErr != nil {
- return http2.ErrCodeProtocol, d.data.grpcErr
- }
- if d.serverSide {
- return http2.ErrCodeNo, nil
- }
- if d.data.rawStatusCode == nil && d.data.statusGen == nil {
- // gRPC status doesn't exist.
- // Set rawStatusCode to be unknown and return nil error.
- // So that, if the stream has ended this Unknown status
- // will be propagated to the user.
- // Otherwise, it will be ignored. In which case, status from
- // a later trailer, that has StreamEnded flag set, is propagated.
- code := int(codes.Unknown)
- d.data.rawStatusCode = &code
- }
- return http2.ErrCodeNo, nil
- }
-
- // HTTP fallback mode
- if d.data.httpErr != nil {
- return http2.ErrCodeProtocol, d.data.httpErr
- }
-
- var (
- code = codes.Internal // when header does not include HTTP status, return INTERNAL
- ok bool
- )
-
- if d.data.httpStatus != nil {
- code, ok = HTTPStatusConvTab[*(d.data.httpStatus)]
- if !ok {
- code = codes.Unknown
- }
- }
-
- return http2.ErrCodeProtocol, status.Error(code, d.constructHTTPErrMsg())
-}
-
-// constructErrMsg constructs error message to be returned in HTTP fallback mode.
-// Format: HTTP status code and its corresponding message + content-type error message.
-func (d *decodeState) constructHTTPErrMsg() string {
- var errMsgs []string
-
- if d.data.httpStatus == nil {
- errMsgs = append(errMsgs, "malformed header: missing HTTP status")
- } else {
- errMsgs = append(errMsgs, fmt.Sprintf("%s: HTTP status code %d", http.StatusText(*(d.data.httpStatus)), *d.data.httpStatus))
- }
-
- if d.data.contentTypeErr == "" {
- errMsgs = append(errMsgs, "transport: missing content-type field")
- } else {
- errMsgs = append(errMsgs, d.data.contentTypeErr)
- }
-
- return strings.Join(errMsgs, "; ")
-}
-
-func (d *decodeState) addMetadata(k, v string) {
- if d.data.mdata == nil {
- d.data.mdata = make(map[string][]string)
+func decodeGRPCStatusDetails(rawDetails string) (*status.Status, error) {
+ v, err := decodeBinHeader(rawDetails)
+ if err != nil {
+ return nil, err
}
- d.data.mdata[k] = append(d.data.mdata[k], v)
-}
-
-func (d *decodeState) processHeaderField(f hpack.HeaderField) {
- switch f.Name {
- case "content-type":
- contentSubtype, validContentType := grpcutil.ContentSubtype(f.Value)
- if !validContentType {
- d.data.contentTypeErr = fmt.Sprintf("transport: received the unexpected content-type %q", f.Value)
- return
- }
- d.data.contentSubtype = contentSubtype
- // TODO: do we want to propagate the whole content-type in the metadata,
- // or come up with a way to just propagate the content-subtype if it was set?
- // ie {"content-type": "application/grpc+proto"} or {"content-subtype": "proto"}
- // in the metadata?
- d.addMetadata(f.Name, f.Value)
- d.data.isGRPC = true
- case "grpc-encoding":
- d.data.encoding = f.Value
- case "grpc-status":
- code, err := strconv.Atoi(f.Value)
- if err != nil {
- d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status: %v", err)
- return
- }
- d.data.rawStatusCode = &code
- case "grpc-message":
- d.data.rawStatusMsg = decodeGrpcMessage(f.Value)
- case "grpc-status-details-bin":
- v, err := decodeBinHeader(f.Value)
- if err != nil {
- d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
- return
- }
- s := &spb.Status{}
- if err := proto.Unmarshal(v, s); err != nil {
- d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err)
- return
- }
- d.data.statusGen = status.FromProto(s)
- case "grpc-timeout":
- d.data.timeoutSet = true
- var err error
- if d.data.timeout, err = decodeTimeout(f.Value); err != nil {
- d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed time-out: %v", err)
- }
- case ":path":
- d.data.method = f.Value
- case ":status":
- code, err := strconv.Atoi(f.Value)
- if err != nil {
- d.data.httpErr = status.Errorf(codes.Internal, "transport: malformed http-status: %v", err)
- return
- }
- d.data.httpStatus = &code
- case "grpc-tags-bin":
- v, err := decodeBinHeader(f.Value)
- if err != nil {
- d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-tags-bin: %v", err)
- return
- }
- d.data.statsTags = v
- d.addMetadata(f.Name, string(v))
- case "grpc-trace-bin":
- v, err := decodeBinHeader(f.Value)
- if err != nil {
- d.data.grpcErr = status.Errorf(codes.Internal, "transport: malformed grpc-trace-bin: %v", err)
- return
- }
- d.data.statsTrace = v
- d.addMetadata(f.Name, string(v))
- case ":method":
- d.data.httpMethod = f.Value
- default:
- if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) {
- break
- }
- v, err := decodeMetadataHeader(f.Name, f.Value)
- if err != nil {
- if logger.V(logLevel) {
- logger.Errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err)
- }
- return
- }
- d.addMetadata(f.Name, v)
+ st := &spb.Status{}
+ if err = proto.Unmarshal(v, st); err != nil {
+ return nil, err
}
+ return status.FromProto(st), nil
}
type timeoutUnit uint8
diff --git a/vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go b/vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go
index 96967428b..7bb53cff1 100644
--- a/vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go
+++ b/vendor/google.golang.org/grpc/internal/transport/networktype/networktype.go
@@ -17,7 +17,7 @@
*/
// Package networktype declares the network type to be used in the default
-// dailer. Attribute of a resolver.Address.
+// dialer. Attribute of a resolver.Address.
package networktype
import (
diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go
index 6cc1031fd..141981264 100644
--- a/vendor/google.golang.org/grpc/internal/transport/transport.go
+++ b/vendor/google.golang.org/grpc/internal/transport/transport.go
@@ -532,12 +532,6 @@ type ServerConfig struct {
HeaderTableSize *uint32
}
-// NewServerTransport creates a ServerTransport with conn or non-nil error
-// if it fails.
-func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) {
- return newHTTP2Server(conn, config)
-}
-
// ConnectOptions covers all relevant options for communicating with the server.
type ConnectOptions struct {
// UserAgent is the application user agent.
@@ -694,7 +688,7 @@ type ServerTransport interface {
// Close tears down the transport. Once it is called, the transport
// should not be accessed any more. All the pending streams and their
// handlers will be terminated asynchronously.
- Close() error
+ Close()
// RemoteAddr returns the remote network address.
RemoteAddr() net.Addr
diff --git a/vendor/google.golang.org/grpc/metadata/metadata.go b/vendor/google.golang.org/grpc/metadata/metadata.go
index e4cbea917..3604c7819 100644
--- a/vendor/google.golang.org/grpc/metadata/metadata.go
+++ b/vendor/google.golang.org/grpc/metadata/metadata.go
@@ -93,12 +93,16 @@ func (md MD) Copy() MD {
}
// Get obtains the values for a given key.
+//
+// k is converted to lowercase before searching in md.
func (md MD) Get(k string) []string {
k = strings.ToLower(k)
return md[k]
}
// Set sets the value of a given key with a slice of values.
+//
+// k is converted to lowercase before storing in md.
func (md MD) Set(k string, vals ...string) {
if len(vals) == 0 {
return
@@ -107,7 +111,10 @@ func (md MD) Set(k string, vals ...string) {
md[k] = vals
}
-// Append adds the values to key k, not overwriting what was already stored at that key.
+// Append adds the values to key k, not overwriting what was already stored at
+// that key.
+//
+// k is converted to lowercase before storing in md.
func (md MD) Append(k string, vals ...string) {
if len(vals) == 0 {
return
@@ -116,9 +123,17 @@ func (md MD) Append(k string, vals ...string) {
md[k] = append(md[k], vals...)
}
+// Delete removes the values for a given key k which is converted to lowercase
+// before removing it from md.
+func (md MD) Delete(k string) {
+ k = strings.ToLower(k)
+ delete(md, k)
+}
+
// Join joins any number of mds into a single MD.
-// The order of values for each key is determined by the order in which
-// the mds containing those values are presented to Join.
+//
+// The order of values for each key is determined by the order in which the mds
+// containing those values are presented to Join.
func Join(mds ...MD) MD {
out := MD{}
for _, md := range mds {
@@ -145,8 +160,8 @@ func NewOutgoingContext(ctx context.Context, md MD) context.Context {
}
// AppendToOutgoingContext returns a new context with the provided kv merged
-// with any existing metadata in the context. Please refer to the
-// documentation of Pairs for a description of kv.
+// with any existing metadata in the context. Please refer to the documentation
+// of Pairs for a description of kv.
func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context {
if len(kv)%2 == 1 {
panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv)))
@@ -159,20 +174,34 @@ func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context
return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added})
}
-// FromIncomingContext returns the incoming metadata in ctx if it exists. The
-// returned MD should not be modified. Writing to it may cause races.
-// Modification should be made to copies of the returned MD.
-func FromIncomingContext(ctx context.Context) (md MD, ok bool) {
- md, ok = ctx.Value(mdIncomingKey{}).(MD)
- return
+// FromIncomingContext returns the incoming metadata in ctx if it exists.
+//
+// All keys in the returned MD are lowercase.
+func FromIncomingContext(ctx context.Context) (MD, bool) {
+ md, ok := ctx.Value(mdIncomingKey{}).(MD)
+ if !ok {
+ return nil, false
+ }
+ out := MD{}
+ for k, v := range md {
+ // We need to manually convert all keys to lower case, because MD is a
+ // map, and there's no guarantee that the MD attached to the context is
+ // created using our helper functions.
+ key := strings.ToLower(k)
+ out[key] = v
+ }
+ return out, true
}
-// FromOutgoingContextRaw returns the un-merged, intermediary contents
-// of rawMD. Remember to perform strings.ToLower on the keys. The returned
-// MD should not be modified. Writing to it may cause races. Modification
-// should be made to copies of the returned MD.
+// FromOutgoingContextRaw returns the un-merged, intermediary contents of rawMD.
+//
+// Remember to perform strings.ToLower on the keys, for both the returned MD (MD
+// is a map, there's no guarantee it's created using our helper functions) and
+// the extra kv pairs (AppendToOutgoingContext doesn't turn them into
+// lowercase).
//
-// This is intended for gRPC-internal use ONLY.
+// This is intended for gRPC-internal use ONLY. Users should use
+// FromOutgoingContext instead.
func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
if !ok {
@@ -182,16 +211,23 @@ func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) {
return raw.md, raw.added, true
}
-// FromOutgoingContext returns the outgoing metadata in ctx if it exists. The
-// returned MD should not be modified. Writing to it may cause races.
-// Modification should be made to copies of the returned MD.
+// FromOutgoingContext returns the outgoing metadata in ctx if it exists.
+//
+// All keys in the returned MD are lowercase.
func FromOutgoingContext(ctx context.Context) (MD, bool) {
raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD)
if !ok {
return nil, false
}
- out := raw.md.Copy()
+ out := MD{}
+ for k, v := range raw.md {
+ // We need to manually convert all keys to lower case, because MD is a
+ // map, and there's no guarantee that the MD attached to the context is
+ // created using our helper functions.
+ key := strings.ToLower(k)
+ out[key] = v
+ }
for _, added := range raw.added {
if len(added)%2 == 1 {
panic(fmt.Sprintf("metadata: FromOutgoingContext got an odd number of input pairs for metadata: %d", len(added)))
diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go
index a58174b6f..0878ada9d 100644
--- a/vendor/google.golang.org/grpc/picker_wrapper.go
+++ b/vendor/google.golang.org/grpc/picker_wrapper.go
@@ -147,7 +147,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
logger.Error("subconn returned from pick is not *acBalancerWrapper")
continue
}
- if t, ok := acw.getAddrConn().getReadyTransport(); ok {
+ if t := acw.getAddrConn().getReadyTransport(); t != nil {
if channelz.IsOn() {
return t, doneChannelzWrapper(acw, pickResult.Done), nil
}
diff --git a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go
index 4118de571..2c47cd54f 100644
--- a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go
+++ b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go
@@ -39,6 +39,8 @@ type ccResolverWrapper struct {
resolver resolver.Resolver
done *grpcsync.Event
curState resolver.State
+
+ incomingMu sync.Mutex // Synchronizes all the incoming calls.
}
// newCCResolverWrapper uses the resolver.Builder to build a Resolver and
@@ -90,6 +92,8 @@ func (ccr *ccResolverWrapper) close() {
}
func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
+ ccr.incomingMu.Lock()
+ defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return nil
}
@@ -105,6 +109,8 @@ func (ccr *ccResolverWrapper) UpdateState(s resolver.State) error {
}
func (ccr *ccResolverWrapper) ReportError(err error) {
+ ccr.incomingMu.Lock()
+ defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return
}
@@ -114,6 +120,8 @@ func (ccr *ccResolverWrapper) ReportError(err error) {
// NewAddress is called by the resolver implementation to send addresses to gRPC.
func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
+ ccr.incomingMu.Lock()
+ defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return
}
@@ -128,6 +136,8 @@ func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) {
// NewServiceConfig is called by the resolver implementation to send service
// configs to gRPC.
func (ccr *ccResolverWrapper) NewServiceConfig(sc string) {
+ ccr.incomingMu.Lock()
+ defer ccr.incomingMu.Unlock()
if ccr.done.HasFired() {
return
}
diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go
index 6db356fa5..87987a2e6 100644
--- a/vendor/google.golang.org/grpc/rpc_util.go
+++ b/vendor/google.golang.org/grpc/rpc_util.go
@@ -258,7 +258,8 @@ func (o PeerCallOption) after(c *callInfo, attempt *csAttempt) {
}
// WaitForReady configures the action to take when an RPC is attempted on broken
-// connections or unreachable servers. If waitForReady is false, the RPC will fail
+// connections or unreachable servers. If waitForReady is false and the
+// connection is in the TRANSIENT_FAILURE state, the RPC will fail
// immediately. Otherwise, the RPC client will block the call until a
// connection is available (or the call is canceled or times out) and will
// retry the call if it fails due to a transient error. gRPC will not retry if
@@ -828,26 +829,28 @@ func Errorf(c codes.Code, format string, a ...interface{}) error {
// toRPCErr converts an error into an error from the status package.
func toRPCErr(err error) error {
- if err == nil || err == io.EOF {
+ switch err {
+ case nil, io.EOF:
return err
- }
- if err == io.ErrUnexpectedEOF {
+ case context.DeadlineExceeded:
+ return status.Error(codes.DeadlineExceeded, err.Error())
+ case context.Canceled:
+ return status.Error(codes.Canceled, err.Error())
+ case io.ErrUnexpectedEOF:
return status.Error(codes.Internal, err.Error())
}
- if _, ok := status.FromError(err); ok {
- return err
- }
+
switch e := err.(type) {
case transport.ConnectionError:
return status.Error(codes.Unavailable, e.Desc)
- default:
- switch err {
- case context.DeadlineExceeded:
- return status.Error(codes.DeadlineExceeded, err.Error())
- case context.Canceled:
- return status.Error(codes.Canceled, err.Error())
- }
+ case *transport.NewStreamError:
+ return toRPCErr(e.Err)
+ }
+
+ if _, ok := status.FromError(err); ok {
+ return err
}
+
return status.Error(codes.Unknown, err.Error())
}
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 0a151dee4..0251f48da 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -844,10 +844,16 @@ func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) {
// ErrConnDispatched means that the connection was dispatched away from
// gRPC; those connections should be left open.
if err != credentials.ErrConnDispatched {
- s.mu.Lock()
- s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
- s.mu.Unlock()
- channelz.Warningf(logger, s.channelzID, "grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
+ // In deployments where a gRPC server runs behind a cloud load
+ // balancer which performs regular TCP level health checks, the
+ // connection is closed immediately by the latter. Skipping the
+ // error here will help reduce log clutter.
+ if err != io.EOF {
+ s.mu.Lock()
+ s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err)
+ s.mu.Unlock()
+ channelz.Warningf(logger, s.channelzID, "grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err)
+ }
rawConn.Close()
}
rawConn.SetDeadline(time.Time{})
@@ -857,6 +863,7 @@ func (s *Server) handleRawConn(lisAddr string, rawConn net.Conn) {
// Finish handshaking (HTTP2)
st := s.newHTTP2Transport(conn, authInfo)
if st == nil {
+ conn.Close()
return
}
@@ -897,7 +904,7 @@ func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) tr
MaxHeaderListSize: s.opts.maxHeaderListSize,
HeaderTableSize: s.opts.headerTableSize,
}
- st, err := transport.NewServerTransport("http2", c, config)
+ st, err := transport.NewServerTransport(c, config)
if err != nil {
s.mu.Lock()
s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err)
@@ -1109,22 +1116,24 @@ func chainUnaryServerInterceptors(s *Server) {
} else if len(interceptors) == 1 {
chainedInt = interceptors[0]
} else {
- chainedInt = func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
- return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
- }
+ chainedInt = chainUnaryInterceptors(interceptors)
}
s.opts.unaryInt = chainedInt
}
-// getChainUnaryHandler recursively generate the chained UnaryHandler
-func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
- if curr == len(interceptors)-1 {
- return finalHandler
- }
-
- return func(ctx context.Context, req interface{}) (interface{}, error) {
- return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
+func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor {
+ return func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
+ var i int
+ var next UnaryHandler
+ next = func(ctx context.Context, req interface{}) (interface{}, error) {
+ if i == len(interceptors)-1 {
+ return interceptors[i](ctx, req, info, handler)
+ }
+ i++
+ return interceptors[i-1](ctx, req, info, next)
+ }
+ return next(ctx, req)
}
}
@@ -1138,7 +1147,9 @@ func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.
if sh != nil {
beginTime := time.Now()
statsBegin = &stats.Begin{
- BeginTime: beginTime,
+ BeginTime: beginTime,
+ IsClientStream: false,
+ IsServerStream: false,
}
sh.HandleRPC(stream.Context(), statsBegin)
}
@@ -1390,22 +1401,24 @@ func chainStreamServerInterceptors(s *Server) {
} else if len(interceptors) == 1 {
chainedInt = interceptors[0]
} else {
- chainedInt = func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
- return interceptors[0](srv, ss, info, getChainStreamHandler(interceptors, 0, info, handler))
- }
+ chainedInt = chainStreamInterceptors(interceptors)
}
s.opts.streamInt = chainedInt
}
-// getChainStreamHandler recursively generate the chained StreamHandler
-func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, info *StreamServerInfo, finalHandler StreamHandler) StreamHandler {
- if curr == len(interceptors)-1 {
- return finalHandler
- }
-
- return func(srv interface{}, ss ServerStream) error {
- return interceptors[curr+1](srv, ss, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler))
+func chainStreamInterceptors(interceptors []StreamServerInterceptor) StreamServerInterceptor {
+ return func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
+ var i int
+ var next StreamHandler
+ next = func(srv interface{}, ss ServerStream) error {
+ if i == len(interceptors)-1 {
+ return interceptors[i](srv, ss, info, handler)
+ }
+ i++
+ return interceptors[i-1](srv, ss, info, next)
+ }
+ return next(srv, ss)
}
}
@@ -1418,7 +1431,9 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
if sh != nil {
beginTime := time.Now()
statsBegin = &stats.Begin{
- BeginTime: beginTime,
+ BeginTime: beginTime,
+ IsClientStream: sd.ClientStreams,
+ IsServerStream: sd.ServerStreams,
}
sh.HandleRPC(stream.Context(), statsBegin)
}
@@ -1521,6 +1536,8 @@ func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transp
}
}
+ ss.ctx = newContextWithRPCInfo(ss.ctx, false, ss.codec, ss.cp, ss.comp)
+
if trInfo != nil {
trInfo.tr.LazyLog(&trInfo.firstLine, false)
}
@@ -1588,7 +1605,7 @@ func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Str
trInfo.tr.SetError()
}
errDesc := fmt.Sprintf("malformed method name: %q", stream.Method())
- if err := t.WriteStatus(stream, status.New(codes.ResourceExhausted, errDesc)); err != nil {
+ if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil {
if trInfo != nil {
trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true)
trInfo.tr.SetError()
diff --git a/vendor/google.golang.org/grpc/stats/stats.go b/vendor/google.golang.org/grpc/stats/stats.go
index 63e476ee7..a5ebeeb69 100644
--- a/vendor/google.golang.org/grpc/stats/stats.go
+++ b/vendor/google.golang.org/grpc/stats/stats.go
@@ -45,6 +45,10 @@ type Begin struct {
BeginTime time.Time
// FailFast indicates if this RPC is failfast.
FailFast bool
+ // IsClientStream indicates whether the RPC is a client streaming RPC.
+ IsClientStream bool
+ // IsServerStream indicates whether the RPC is a server streaming RPC.
+ IsServerStream bool
}
// IsClient indicates if the stats information is from client side.
diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go
index 1f3e70d2c..e224af12d 100644
--- a/vendor/google.golang.org/grpc/stream.go
+++ b/vendor/google.golang.org/grpc/stream.go
@@ -295,9 +295,11 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client
ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method, FailFast: c.failFast})
beginTime = time.Now()
begin := &stats.Begin{
- Client: true,
- BeginTime: beginTime,
- FailFast: c.failFast,
+ Client: true,
+ BeginTime: beginTime,
+ FailFast: c.failFast,
+ IsClientStream: desc.ClientStreams,
+ IsServerStream: desc.ServerStreams,
}
sh.HandleRPC(ctx, begin)
}
@@ -419,12 +421,9 @@ func (a *csAttempt) newStream() error {
cs.callHdr.PreviousAttempts = cs.numRetries
s, err := a.t.NewStream(cs.ctx, cs.callHdr)
if err != nil {
- if _, ok := err.(transport.PerformedIOError); ok {
- // Return without converting to an RPC error so retry code can
- // inspect.
- return err
- }
- return toRPCErr(err)
+ // Return without converting to an RPC error so retry code can
+ // inspect.
+ return err
}
cs.attempt.s = s
cs.attempt.p = &parser{r: s}
@@ -523,19 +522,28 @@ func (cs *clientStream) commitAttempt() {
// shouldRetry returns nil if the RPC should be retried; otherwise it returns
// the error that should be returned by the operation.
func (cs *clientStream) shouldRetry(err error) error {
- unprocessed := false
if cs.attempt.s == nil {
- pioErr, ok := err.(transport.PerformedIOError)
- if ok {
- // Unwrap error.
- err = toRPCErr(pioErr.Err)
- } else {
- unprocessed = true
+ // Error from NewClientStream.
+ nse, ok := err.(*transport.NewStreamError)
+ if !ok {
+ // Unexpected, but assume no I/O was performed and the RPC is not
+ // fatal, so retry indefinitely.
+ return nil
}
- if !ok && !cs.callInfo.failFast {
- // In the event of a non-IO operation error from NewStream, we
- // never attempted to write anything to the wire, so we can retry
- // indefinitely for non-fail-fast RPCs.
+
+ // Unwrap and convert error.
+ err = toRPCErr(nse.Err)
+
+ // Never retry DoNotRetry errors, which indicate the RPC should not be
+ // retried due to max header list size violation, etc.
+ if nse.DoNotRetry {
+ return err
+ }
+
+ // In the event of a non-IO operation error from NewStream, we never
+ // attempted to write anything to the wire, so we can retry
+ // indefinitely.
+ if !nse.PerformedIO {
return nil
}
}
@@ -544,6 +552,7 @@ func (cs *clientStream) shouldRetry(err error) error {
return err
}
// Wait for the trailers.
+ unprocessed := false
if cs.attempt.s != nil {
<-cs.attempt.s.Done()
unprocessed = cs.attempt.s.Unprocessed()
@@ -632,7 +641,7 @@ func (cs *clientStream) shouldRetry(err error) error {
// Returns nil if a retry was performed and succeeded; error otherwise.
func (cs *clientStream) retryLocked(lastErr error) error {
for {
- cs.attempt.finish(lastErr)
+ cs.attempt.finish(toRPCErr(lastErr))
if err := cs.shouldRetry(lastErr); err != nil {
cs.commitAttemptLocked()
return err
@@ -659,7 +668,11 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func())
for {
if cs.committed {
cs.mu.Unlock()
- return op(cs.attempt)
+ // toRPCErr is used in case the error from the attempt comes from
+ // NewClientStream, which intentionally doesn't return a status
+ // error to allow for further inspection; all other errors should
+ // already be status errors.
+ return toRPCErr(op(cs.attempt))
}
a := cs.attempt
cs.mu.Unlock()
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index bfe5cf887..e3510e10f 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
package grpc
// Version is the current grpc version.
-const Version = "1.38.0"
+const Version = "1.40.0"
diff --git a/vendor/google.golang.org/grpc/vet.sh b/vendor/google.golang.org/grpc/vet.sh
index 1a0dbd7ee..5eaa8b05d 100644
--- a/vendor/google.golang.org/grpc/vet.sh
+++ b/vendor/google.golang.org/grpc/vet.sh
@@ -32,26 +32,14 @@ PATH="${HOME}/go/bin:${GOROOT}/bin:${PATH}"
go version
if [[ "$1" = "-install" ]]; then
- # Check for module support
- if go help mod >& /dev/null; then
- # Install the pinned versions as defined in module tools.
- pushd ./test/tools
- go install \
- golang.org/x/lint/golint \
- golang.org/x/tools/cmd/goimports \
- honnef.co/go/tools/cmd/staticcheck \
- github.com/client9/misspell/cmd/misspell
- popd
- else
- # Ye olde `go get` incantation.
- # Note: this gets the latest version of all tools (vs. the pinned versions
- # with Go modules).
- go get -u \
- golang.org/x/lint/golint \
- golang.org/x/tools/cmd/goimports \
- honnef.co/go/tools/cmd/staticcheck \
- github.com/client9/misspell/cmd/misspell
- fi
+ # Install the pinned versions as defined in module tools.
+ pushd ./test/tools
+ go install \
+ golang.org/x/lint/golint \
+ golang.org/x/tools/cmd/goimports \
+ honnef.co/go/tools/cmd/staticcheck \
+ github.com/client9/misspell/cmd/misspell
+ popd
if [[ -z "${VET_SKIP_PROTO}" ]]; then
if [[ "${TRAVIS}" = "true" ]]; then
PROTOBUF_VERSION=3.14.0
diff --git a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go
index 8fb1d9e08..179d6e8fc 100644
--- a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go
+++ b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go
@@ -744,9 +744,6 @@ func (d decoder) skipValue() error {
// Skip items. This will not validate whether skipped values are
// of the same type or not, same behavior as C++
// TextFormat::Parser::AllowUnknownField(true) version 3.8.0.
- if err := d.skipValue(); err != nil {
- return err
- }
}
}
}
diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go
index aa66bdd06..da289ccce 100644
--- a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go
+++ b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go
@@ -263,3 +263,8 @@ func (e *Encoder) Snapshot() encoderState {
func (e *Encoder) Reset(es encoderState) {
e.encoderState = es
}
+
+// AppendString appends the escaped form of the input string to b.
+func AppendString(b []byte, s string) []byte {
+ return appendString(b, s, false)
+}
diff --git a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go
index 3759b010c..029feeefd 100644
--- a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go
+++ b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go
@@ -440,6 +440,13 @@ func legacyMerge(in piface.MergeInput) piface.MergeOutput {
if !ok {
return piface.MergeOutput{}
}
+ if !in.Source.IsValid() {
+ // Legacy Marshal methods may not function on nil messages.
+ // Check for a typed nil source only after we confirm that
+ // legacy Marshal/Unmarshal methods are present, for
+ // consistency.
+ return piface.MergeOutput{Flags: piface.MergeComplete}
+ }
b, err := marshaler.Marshal()
if err != nil {
return piface.MergeOutput{}
diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go
index 5879131da..14e774fb2 100644
--- a/vendor/google.golang.org/protobuf/internal/version/version.go
+++ b/vendor/google.golang.org/protobuf/internal/version/version.go
@@ -52,8 +52,8 @@ import (
// 10. Send out the CL for review and submit it.
const (
Major = 1
- Minor = 26
- Patch = 0
+ Minor = 27
+ Patch = 1
PreRelease = ""
)
diff --git a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go
index 66dcbcd0d..59f024c44 100644
--- a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go
+++ b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go
@@ -94,7 +94,8 @@ type Files struct {
// Note that enum values are in the top-level since that are in the same
// scope as the parent enum.
descsByName map[protoreflect.FullName]interface{}
- filesByPath map[string]protoreflect.FileDescriptor
+ filesByPath map[string][]protoreflect.FileDescriptor
+ numFiles int
}
type packageDescriptor struct {
@@ -117,17 +118,16 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error {
r.descsByName = map[protoreflect.FullName]interface{}{
"": &packageDescriptor{},
}
- r.filesByPath = make(map[string]protoreflect.FileDescriptor)
+ r.filesByPath = make(map[string][]protoreflect.FileDescriptor)
}
path := file.Path()
- if prev := r.filesByPath[path]; prev != nil {
+ if prev := r.filesByPath[path]; len(prev) > 0 {
r.checkGenProtoConflict(path)
err := errors.New("file %q is already registered", file.Path())
- err = amendErrorWithCaller(err, prev, file)
- if r == GlobalFiles && ignoreConflict(file, err) {
- err = nil
+ err = amendErrorWithCaller(err, prev[0], file)
+ if !(r == GlobalFiles && ignoreConflict(file, err)) {
+ return err
}
- return err
}
for name := file.Package(); name != ""; name = name.Parent() {
@@ -168,7 +168,8 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error {
rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) {
r.descsByName[d.FullName()] = d
})
- r.filesByPath[path] = file
+ r.filesByPath[path] = append(r.filesByPath[path], file)
+ r.numFiles++
return nil
}
@@ -308,6 +309,7 @@ func (s *nameSuffix) Pop() (name protoreflect.Name) {
// FindFileByPath looks up a file by the path.
//
// This returns (nil, NotFound) if not found.
+// This returns an error if multiple files have the same path.
func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) {
if r == nil {
return nil, NotFound
@@ -316,13 +318,19 @@ func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error)
globalMutex.RLock()
defer globalMutex.RUnlock()
}
- if fd, ok := r.filesByPath[path]; ok {
- return fd, nil
+ fds := r.filesByPath[path]
+ switch len(fds) {
+ case 0:
+ return nil, NotFound
+ case 1:
+ return fds[0], nil
+ default:
+ return nil, errors.New("multiple files named %q", path)
}
- return nil, NotFound
}
-// NumFiles reports the number of registered files.
+// NumFiles reports the number of registered files,
+// including duplicate files with the same name.
func (r *Files) NumFiles() int {
if r == nil {
return 0
@@ -331,10 +339,11 @@ func (r *Files) NumFiles() int {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
- return len(r.filesByPath)
+ return r.numFiles
}
// RangeFiles iterates over all registered files while f returns true.
+// If multiple files have the same name, RangeFiles iterates over all of them.
// The iteration order is undefined.
func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
if r == nil {
@@ -344,9 +353,11 @@ func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) {
globalMutex.RLock()
defer globalMutex.RUnlock()
}
- for _, file := range r.filesByPath {
- if !f(file) {
- return
+ for _, files := range r.filesByPath {
+ for _, file := range files {
+ if !f(file) {
+ return
+ }
}
}
}
diff --git a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go
index f77239fc3..abe4ab511 100644
--- a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go
+++ b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go
@@ -43,7 +43,6 @@ package descriptorpb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
- protoiface "google.golang.org/protobuf/runtime/protoiface"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
@@ -829,15 +828,6 @@ func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{3}
}
-var extRange_ExtensionRangeOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use ExtensionRangeOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*ExtensionRangeOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_ExtensionRangeOptions
-}
-
func (x *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption {
if x != nil {
return x.UninterpretedOption
@@ -1520,15 +1510,6 @@ func (*FileOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{10}
}
-var extRange_FileOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use FileOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*FileOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_FileOptions
-}
-
func (x *FileOptions) GetJavaPackage() string {
if x != nil && x.JavaPackage != nil {
return *x.JavaPackage
@@ -1776,15 +1757,6 @@ func (*MessageOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{11}
}
-var extRange_MessageOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use MessageOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*MessageOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_MessageOptions
-}
-
func (x *MessageOptions) GetMessageSetWireFormat() bool {
if x != nil && x.MessageSetWireFormat != nil {
return *x.MessageSetWireFormat
@@ -1930,15 +1902,6 @@ func (*FieldOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{12}
}
-var extRange_FieldOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use FieldOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*FieldOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_FieldOptions
-}
-
func (x *FieldOptions) GetCtype() FieldOptions_CType {
if x != nil && x.Ctype != nil {
return *x.Ctype
@@ -2030,15 +1993,6 @@ func (*OneofOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{13}
}
-var extRange_OneofOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use OneofOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*OneofOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_OneofOptions
-}
-
func (x *OneofOptions) GetUninterpretedOption() []*UninterpretedOption {
if x != nil {
return x.UninterpretedOption
@@ -2101,15 +2055,6 @@ func (*EnumOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{14}
}
-var extRange_EnumOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use EnumOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*EnumOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_EnumOptions
-}
-
func (x *EnumOptions) GetAllowAlias() bool {
if x != nil && x.AllowAlias != nil {
return *x.AllowAlias
@@ -2183,15 +2128,6 @@ func (*EnumValueOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{15}
}
-var extRange_EnumValueOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use EnumValueOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*EnumValueOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_EnumValueOptions
-}
-
func (x *EnumValueOptions) GetDeprecated() bool {
if x != nil && x.Deprecated != nil {
return *x.Deprecated
@@ -2258,15 +2194,6 @@ func (*ServiceOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{16}
}
-var extRange_ServiceOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use ServiceOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*ServiceOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_ServiceOptions
-}
-
func (x *ServiceOptions) GetDeprecated() bool {
if x != nil && x.Deprecated != nil {
return *x.Deprecated
@@ -2335,15 +2262,6 @@ func (*MethodOptions) Descriptor() ([]byte, []int) {
return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{17}
}
-var extRange_MethodOptions = []protoiface.ExtensionRangeV1{
- {Start: 1000, End: 536870911},
-}
-
-// Deprecated: Use MethodOptions.ProtoReflect.Descriptor.ExtensionRanges instead.
-func (*MethodOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 {
- return extRange_MethodOptions
-}
-
func (x *MethodOptions) GetDeprecated() bool {
if x != nil && x.Deprecated != nil {
return *x.Deprecated
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 830de276b..9b6bd34bf 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -10,7 +10,7 @@ github.com/Microsoft/go-winio/backuptar
github.com/Microsoft/go-winio/pkg/guid
github.com/Microsoft/go-winio/pkg/security
github.com/Microsoft/go-winio/vhd
-# github.com/Microsoft/hcsshim v0.8.22
+# github.com/Microsoft/hcsshim v0.9.0
github.com/Microsoft/hcsshim
github.com/Microsoft/hcsshim/computestorage
github.com/Microsoft/hcsshim/internal/cow
@@ -45,9 +45,11 @@ github.com/buger/goterm
github.com/cespare/xxhash/v2
# github.com/checkpoint-restore/checkpointctl v0.0.0-20210922093614-c31748bec9f2
github.com/checkpoint-restore/checkpointctl/lib
-# github.com/checkpoint-restore/go-criu/v5 v5.1.0
+# github.com/checkpoint-restore/go-criu/v5 v5.2.0
github.com/checkpoint-restore/go-criu/v5
+github.com/checkpoint-restore/go-criu/v5/magic
github.com/checkpoint-restore/go-criu/v5/rpc
+github.com/checkpoint-restore/go-criu/v5/stats
# github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
github.com/chzyer/readline
# github.com/container-orchestrated-devices/container-device-interface v0.0.0-20210325223243-f99e8b6c10b9
@@ -55,7 +57,7 @@ github.com/container-orchestrated-devices/container-device-interface/pkg
github.com/container-orchestrated-devices/container-device-interface/specs-go
# github.com/containerd/cgroups v1.0.1
github.com/containerd/cgroups/stats/v1
-# github.com/containerd/containerd v1.5.5
+# github.com/containerd/containerd v1.5.7
github.com/containerd/containerd/errdefs
github.com/containerd/containerd/log
github.com/containerd/containerd/pkg/userns
@@ -95,7 +97,7 @@ github.com/containers/buildah/pkg/rusage
github.com/containers/buildah/pkg/sshagent
github.com/containers/buildah/pkg/util
github.com/containers/buildah/util
-# github.com/containers/common v0.46.1-0.20211008123044-d846f5aaec0e
+# github.com/containers/common v0.46.1-0.20211110143743-73e7b462c358
github.com/containers/common/libimage
github.com/containers/common/libimage/manifests
github.com/containers/common/pkg/apparmor
@@ -107,7 +109,9 @@ github.com/containers/common/pkg/chown
github.com/containers/common/pkg/completion
github.com/containers/common/pkg/config
github.com/containers/common/pkg/defaultnet
+github.com/containers/common/pkg/download
github.com/containers/common/pkg/filters
+github.com/containers/common/pkg/flag
github.com/containers/common/pkg/manifests
github.com/containers/common/pkg/parse
github.com/containers/common/pkg/report
@@ -200,7 +204,7 @@ github.com/containers/psgo/internal/dev
github.com/containers/psgo/internal/host
github.com/containers/psgo/internal/proc
github.com/containers/psgo/internal/process
-# github.com/containers/storage v1.37.0
+# github.com/containers/storage v1.37.1-0.20211014130921-5c5bf639ed01
github.com/containers/storage
github.com/containers/storage/drivers
github.com/containers/storage/drivers/aufs
@@ -284,7 +288,7 @@ github.com/docker/distribution/registry/client/auth/challenge
github.com/docker/distribution/registry/client/transport
github.com/docker/distribution/registry/storage/cache
github.com/docker/distribution/registry/storage/cache/memory
-# github.com/docker/docker v20.10.9+incompatible
+# github.com/docker/docker v20.10.10+incompatible
github.com/docker/docker/api
github.com/docker/docker/api/types
github.com/docker/docker/api/types/blkiodev
@@ -345,7 +349,7 @@ github.com/ghodss/yaml
github.com/go-logr/logr
# github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0
github.com/go-task/slim-sprig
-# github.com/godbus/dbus/v5 v5.0.5
+# github.com/godbus/dbus/v5 v5.0.6
github.com/godbus/dbus/v5
# github.com/gogo/protobuf v1.3.2
github.com/gogo/protobuf/gogoproto
@@ -488,7 +492,7 @@ github.com/onsi/ginkgo/reporters/stenographer
github.com/onsi/ginkgo/reporters/stenographer/support/go-colorable
github.com/onsi/ginkgo/reporters/stenographer/support/go-isatty
github.com/onsi/ginkgo/types
-# github.com/onsi/gomega v1.16.0
+# github.com/onsi/gomega v1.17.0 => github.com/onsi/gomega v1.16.0
github.com/onsi/gomega
github.com/onsi/gomega/format
github.com/onsi/gomega/gbytes
@@ -517,7 +521,7 @@ github.com/opencontainers/runc/libcontainer/userns
github.com/opencontainers/runc/libcontainer/utils
# github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
github.com/opencontainers/runtime-spec/specs-go
-# github.com/opencontainers/runtime-tools v0.9.0
+# github.com/opencontainers/runtime-tools v0.9.1-0.20211020193359-09d837bf40a7
github.com/opencontainers/runtime-tools/error
github.com/opencontainers/runtime-tools/filepath
github.com/opencontainers/runtime-tools/generate
@@ -658,7 +662,7 @@ golang.org/x/crypto/ssh/agent
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf
golang.org/x/crypto/ssh/knownhosts
golang.org/x/crypto/ssh/terminal
-# golang.org/x/net v0.0.0-20210520170846-37e1c6afe023
+# golang.org/x/net v0.0.0-20210825183410-e898025ed96a
golang.org/x/net/context
golang.org/x/net/html
golang.org/x/net/html/atom
@@ -708,7 +712,7 @@ golang.org/x/text/unicode/norm
golang.org/x/tools/go/ast/inspector
# google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c
google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.38.0
+# google.golang.org/grpc v1.40.0
google.golang.org/grpc
google.golang.org/grpc/attributes
google.golang.org/grpc/backoff
@@ -753,7 +757,7 @@ google.golang.org/grpc/serviceconfig
google.golang.org/grpc/stats
google.golang.org/grpc/status
google.golang.org/grpc/tap
-# google.golang.org/protobuf v1.26.0
+# google.golang.org/protobuf v1.27.1
google.golang.org/protobuf/encoding/prototext
google.golang.org/protobuf/encoding/protowire
google.golang.org/protobuf/internal/descfmt
@@ -798,10 +802,10 @@ gopkg.in/tomb.v1
gopkg.in/yaml.v2
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
gopkg.in/yaml.v3
-# k8s.io/api v0.22.2
+# k8s.io/api v0.22.3
k8s.io/api/apps/v1
k8s.io/api/core/v1
-# k8s.io/apimachinery v0.22.2
+# k8s.io/apimachinery v0.22.3
k8s.io/apimachinery/pkg/api/resource
k8s.io/apimachinery/pkg/apis/meta/v1
k8s.io/apimachinery/pkg/conversion