diff options
229 files changed, 4558 insertions, 1167 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 292f83951..6d9ccfc78 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -39,7 +39,7 @@ env: UBUNTU_NAME: "ubuntu-20" PRIOR_UBUNTU_NAME: "ubuntu-19" - _BUILT_IMAGE_SUFFIX: "libpod-6268069335007232" # From the packer output of 'build_vm_images_script' + _BUILT_IMAGE_SUFFIX: "libpod-6508632441356288" FEDORA_CACHE_IMAGE_NAME: "${FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}" PRIOR_FEDORA_CACHE_IMAGE_NAME: "${PRIOR_FEDORA_NAME}-${_BUILT_IMAGE_SUFFIX}" UBUNTU_CACHE_IMAGE_NAME: "${UBUNTU_NAME}-${_BUILT_IMAGE_SUFFIX}" @@ -72,10 +72,6 @@ env: GCE_SSH_USERNAME: cirrus-ci # Name where this repositories cloud resources are located GCP_PROJECT_ID: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f] - RELEASE_GCPJSON: ENCRYPTED[789d8f7e9a5972ce350fd8e60f1032ccbf4a35c3938b604774b711aad280e12c21faf10e25af1e0ba33597ffb9e39e46] - RELEASE_GCPNAME: ENCRYPTED[417d50488a4bd197bcc925ba6574de5823b97e68db1a17e3a5fde4bcf26576987345e75f8d9ea1c15a156b4612c072a1] - RELEASE_GCPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f] - # Default VM to use unless set or modified by task @@ -134,8 +130,7 @@ gating_task: # not break. It also verifies all sub-commands have man pages. build_script: - '/usr/local/bin/entrypoint.sh podman |& ${TIMESTAMP}' - # FIXME - #- 'cd $GOSRC && ./hack/podman-commands.sh |& ${TIMESTAMP}' + - 'cd $GOSRC && ./hack/podman-commands.sh |& ${TIMESTAMP}' # N/B: need 'clean' so some committed files are re-generated. - '/usr/local/bin/entrypoint.sh clean podman-remote |& ${TIMESTAMP}' - '/usr/local/bin/entrypoint.sh clean podman xref_helpmsgs_manpages BUILDTAGS="exclude_graphdriver_devicemapper selinux seccomp" |& ${TIMESTAMP}' @@ -423,25 +418,20 @@ testing_task: - name: "test ${PRIOR_FEDORA_NAME}" gce_instance: image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" - # TODO: - # - name: "test ${UBUNTU_NAME}" - # gce_instance: - # image_name: "${UBUNTU_CACHE_IMAGE_NAME}" - # - name: "test ${PRIOR_UBUNTU_NAME}" - # gce_instance: - # image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}" + - name: "test ${UBUNTU_NAME}" + gce_instance: + image_name: "${UBUNTU_CACHE_IMAGE_NAME}" + - name: "test ${PRIOR_UBUNTU_NAME}" + gce_instance: + image_name: "${PRIOR_UBUNTU_CACHE_IMAGE_NAME}" timeout_in: 120m env: ADD_SECOND_PARTITION: 'true' matrix: - - name: remote - env: - TEST_REMOTE_CLIENT: 'true' - - name: local - env: - TEST_REMOTE_CLIENT: 'false' + - TEST_REMOTE_CLIENT: 'true' + - TEST_REMOTE_CLIENT: 'false' networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' setup_environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' @@ -488,12 +478,8 @@ special_testing_rootless_task: ADD_SECOND_PARTITION: 'true' SPECIALMODE: 'rootless' # See docs matrix: - - name: remote - env: - TEST_REMOTE_CLIENT: 'true' - - name: local - env: - TEST_REMOTE_CLIENT: 'false' + - TEST_REMOTE_CLIENT: 'true' + - TEST_REMOTE_CLIENT: 'false' timeout_in: 60m @@ -512,7 +498,6 @@ special_testing_rootless_task: special_testing_in_podman_task: - skip: $CI == 'true' alias: "special_testing_in_podman" depends_on: - "gating" @@ -603,6 +588,7 @@ special_testing_bindings_task: env: SPECIALMODE: 'bindings' # See docs + ADD_SECOND_PARTITION: 'true' # More root fs space is required timeout_in: 40m @@ -696,27 +682,19 @@ verify_test_built_images_task: env: ADD_SECOND_PARTITION: 'true' matrix: - - name: remote - env: - TEST_REMOTE_CLIENT: 'true' - - name: local - env: - TEST_REMOTE_CLIENT: 'false' + - TEST_REMOTE_CLIENT: 'true' + - TEST_REMOTE_CLIENT: 'false' matrix: - # Required env. var. by check_image_script PACKER_BUILDER_NAME: "${FEDORA_NAME}" PACKER_BUILDER_NAME: "${PRIOR_FEDORA_NAME}" + PACKER_BUILDER_NAME: "${UBUNTU_NAME}" PACKER_BUILDER_NAME: "${PRIOR_UBUNTU_NAME}" - # Multiple test failures on ${UBUNTU_CACHE_IMAGE_NAME} - # PACKER_BUILDER_NAME: "${UBUNTU_NAME}" networking_script: '${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/networking.sh' installed_packages_script: '$SCRIPT_BASE/logcollector.sh packages' environment_script: '$SCRIPT_BASE/setup_environment.sh |& ${TIMESTAMP}' - # Verify expectations once per image - check_image_script: >- - [[ "$TEST_REMOTE_CLIENT" == "false" ]] || \ - $SCRIPT_BASE/check_image.sh |& ${TIMESTAMP} + # Verify expectations of built images + check_image_script: '$SCRIPT_BASE/check_image.sh |& ${TIMESTAMP}' # Note: A truncated form of normal testing. It only needs to confirm new images # "probably" work. A full round of testing will happen again after $*_CACHE_IMAGE_NAME # are updated in this or another PR (w/o '***CIRRUS: TEST IMAGES***'). @@ -734,6 +712,10 @@ docs_task: depends_on: - "gating" + env: + RELEASE_GCPJSON: ENCRYPTED[789d8f7e9a5972ce350fd8e60f1032ccbf4a35c3938b604774b711aad280e12c21faf10e25af1e0ba33597ffb9e39e46] + RELEASE_GCPNAME: ENCRYPTED[417d50488a4bd197bcc925ba6574de5823b97e68db1a17e3a5fde4bcf26576987345e75f8d9ea1c15a156b4612c072a1] + RELEASE_GCPROJECT: ENCRYPTED[7c80e728e046b1c76147afd156a32c1c57d4a1ac1eab93b7e68e718c61ca8564fc61fef815952b8ae0a64e7034b8fe4f] script: - "$SCRIPT_BASE/build_swagger.sh |& ${TIMESTAMP}" diff --git a/.golangci.yml b/.golangci.yml index 5480b02bb..33a8b4f59 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -7,6 +7,7 @@ run: - contrib - dependencies - test + - pkg/spec - pkg/varlink - pkg/varlinkapi skip-files: @@ -21,7 +22,6 @@ linters: - gochecknoinits - goconst - gocyclo - - golint - gosec - lll - maligned @@ -22,7 +22,7 @@ ETCDIR ?= /etc TMPFILESDIR ?= ${PREFIX}/lib/tmpfiles.d SYSTEMDDIR ?= ${PREFIX}/lib/systemd/system USERSYSTEMDDIR ?= ${PREFIX}/lib/systemd/user -REMOTETAGS ?= !ABISupport remote exclude_graphdriver_btrfs btrfs_noversion exclude_graphdriver_devicemapper containers_image_openpgp +REMOTETAGS ?= remote exclude_graphdriver_btrfs btrfs_noversion exclude_graphdriver_devicemapper containers_image_openpgp BUILDTAGS ?= \ $(shell hack/apparmor_tag.sh) \ $(shell hack/btrfs_installed_tag.sh) \ @@ -44,7 +44,7 @@ ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true) GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor endif -BUILDTAGS_CROSS ?= ABISupport containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay +BUILDTAGS_CROSS ?= containers_image_openpgp exclude_graphdriver_btrfs exclude_graphdriver_devicemapper exclude_graphdriver_overlay ifneq (,$(findstring varlink,$(BUILDTAGS))) PODMAN_VARLINK_DEPENDENCIES = pkg/varlink/iopodman.go endif @@ -88,8 +88,8 @@ RELEASE_DIST_VER ?= $(shell hack/get_release_info.sh DIST_VER) RELEASE_ARCH ?= $(shell hack/get_release_info.sh ARCH) RELEASE_BASENAME := $(shell hack/get_release_info.sh BASENAME) -# If non-empty, logs all output from varlink during remote system testing -VARLINK_LOG ?= +# If non-empty, logs all output from server during remote system testing +PODMAN_SERVER_LOG ?= # If GOPATH not specified, use one in the local directory ifeq ($(GOPATH),) @@ -185,7 +185,7 @@ ifeq (,$(findstring systemd,$(BUILDTAGS))) @echo "Podman is being compiled without the systemd build tag. Install libsystemd on \ Ubuntu or systemd-devel on rpm based distro for journald support." endif - $(GO_BUILD) $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "ABISupport $(BUILDTAGS)" -o $@ $(PROJECT)/cmd/podman + $(GO_BUILD) $(BUILDFLAGS) -gcflags '$(GCFLAGS)' -asmflags '$(ASMFLAGS)' -ldflags '$(LDFLAGS_PODMAN)' -tags "$(BUILDTAGS)" -o $@ $(PROJECT)/cmd/podman .PHONY: podman podman: bin/podman @@ -357,22 +357,28 @@ localsystem: remotesystem: # Wipe existing config, database, and cache: start with clean slate. $(RM) -rf ${HOME}/.local/share/containers ${HOME}/.config/containers - # Start varlink server using tmp socket; loop-wait for it; + # Start podman server using tmp socket; loop-wait for it; # test podman-remote; kill server, clean up tmp socket file. - # varlink server spews copious unhelpful output; ignore it. + # podman server spews copious unhelpful output; ignore it. + # FIXME FIXME FIXME: remove 'exit 0' after #6538 and #6539 are fixed + exit 0;\ rc=0;\ if timeout -v 1 true; then \ SOCK_FILE=$(shell mktemp --dry-run --tmpdir podman.XXXXXX);\ - export PODMAN_SOCKEY=unix:$$SOCK_FILE; \ - ./bin/podman system service --timeout=0 $$PODMAN_VARLINK_ADDRESS &> $(if $(VARLINK_LOG),$(VARLINK_LOG),/dev/null) & \ + export PODMAN_SOCKET=unix:$$SOCK_FILE; \ + ./bin/podman system service --timeout=0 $$PODMAN_SOCKET &> $(if $(PODMAN_SERVER_LOG),$(PODMAN_SERVER_LOG),/dev/null) & \ retry=5;\ while [[ $$retry -ge 0 ]]; do\ echo Waiting for server...;\ sleep 1;\ - ./bin/podman-remote --remote $(SOCK_FILE) info &>/dev/null && break;\ + ./bin/podman-remote --url $$PODMAN_SOCKET info &>/dev/null && break;\ retry=$$(expr $$retry - 1);\ done;\ - env PODMAN=./bin/podman-remote bats test/system/ ;\ + if [[ $$retry -lt 0 ]]; then\ + echo "Error: ./bin/podman system service did not come up on $$SOCK_FILE" >&2;\ + exit 1;\ + fi;\ + env PODMAN="./bin/podman-remote --url $$PODMAN_SOCKET" bats test/system/ ;\ rc=$$?;\ kill %1;\ rm -f $$SOCK_FILE;\ @@ -612,7 +618,7 @@ uninstall: GIT_CHECK_EXCLUDE="./vendor:docs/make.bat" $(GOBIN)/git-validation -run DCO,short-subject,dangling-whitespace -range $(EPOCH_TEST_COMMIT)..$(HEAD) .PHONY: install.tools -install.tools: .install.gitvalidation .install.md2man .install.ginkgo .install.golangci-lint ## Install needed tools +install.tools: .install.gitvalidation .install.md2man .install.ginkgo .install.golangci-lint .install.bats ## Install needed tools define go-get env GO111MODULE=off \ @@ -635,6 +641,10 @@ endef .install.golangci-lint: .gopathok VERSION=1.18.0 GOBIN=$(GOBIN) sh ./hack/install_golangci.sh +.PHONY: .install.bats +.install.bats: .gopathok + VERSION=v1.1.0 ./hack/install_bats.sh + .PHONY: .install.pre-commit .install.pre-commit: if [ -z "$(PRE_COMMIT)" ]; then \ diff --git a/changelog.txt b/changelog.txt index 47a99ba64..f17e0ee75 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,78 @@ +- Changelog for v2.0.0-rc5 (2020-06-10) + * Fix Id->ID where possible for lint + * Fixup issues found by golint + * podman-events: clarify streaming behaviour + * Cirrus: Include packages for containers/conmon CI + * Ensure signal validation happens first in pod kill + * Bump github.com/json-iterator/go from 1.1.9 to 1.1.10 + * Bump github.com/containers/common from 0.12.0 to 0.13.0 + * Improve swagger+CORS metadata docs + * Ensure Conmon is alive before waiting for exit file + * Bump github.com/stretchr/testify from 1.6.0 to 1.6.1 + * e2e: disable checkpoint test on Ubuntu + * force bats version to v1.1.0 + * Enable Ubuntu tests in CI + * Modify py test to start stop system service for each test + * Add parallel operation to `podman stop` + * Fix handling of systemd. + * Add parallel execution code for container operations + * Fix handling of ThrottleWriteIOPSDevice + * Bump github.com/seccomp/containers-golang from 0.4.1 to 0.5.0 + * Strip defaults from namespace flags + * Ensure that containers in pods properly set hostname + * Adds docker py regression test. + * Turn on the podman-commands script to verify man pages + * Attempt to turn on special_testing_in_podman tests + * Bump to v2.0.0-dev + +- Changelog for v2.0.0-rc4 (2020-06-04) + * /images/.../json: fix port parsing + * BATS and APIv2: more tests and tweaks + * Vendor in container/storage v1.20.2 + * add socket information to podman info + * Namespace fields were set with bogus values + * When stopping containers locally, ensure cleanup runs + * Remove use of ABISupport buildtag + * fix remote test --ignore & turn on more tests + * Ensure that image/container inspect are specialized + * turn on remote stop_test + * V2 Add support for ssh authentication methods + * Add a few CVE entries to changelog.txt + * Add more Remote tests + * RHEL8 and Centos8 don't have oci-runtime yet + * test.apiv2: add test cases for committing an image from a container + * Turn on remote rm_test --cidfile + * Properly follow linked namespace container for stats + * Fix a segfault in `podman inspect -l` w/ no containers + * Remove reference to "upcoming" RHEL 7.7 + * Bump Conmon in COPR spec + * Enable detached exec for remote + * check --user range for rootless containers + * images --no-trunc: fix ID formatting + * make env handling os dependent + * Bump github.com/containers/conmon + * Bump github.com/onsi/ginkgo from 1.12.2 to 1.12.3 + * Update vendor containers/psgo + * Bump github.com/opencontainers/runc from 1.0.0-rc9 to 1.0.0-rc90 + * Bump github.com/coreos/go-systemd/v22 from 22.0.0 to 22.1.0 + * Combine the code of dealing with 'readonly' and 'ro'. + * Add bindings for exec and enable attached remote + * Add information on detach-keys + * system tests : more tests + * Add support for format {{.Label}} + * turn on remote testing for images. podman-remote build now works. + * Add invalid value to error message + * Fix leak of empty tarball + * Update man pages for --ip with CNI networks + * [CI:DOCS] update httpd location in tutorial + * default build without `varlink` tag + * Bump to v2.0.0-dev + * compat handlers: add X-Registry-Auth header support + * Don't build code on remoteclient + * v2 copy endpoints + * Bump github.com/rootless-containers/rootlesskit from 0.9.4 to 0.9.5 + * system tests: enable skopeo REGISTRY_AUTH_FILE + - Changelog for v2.0.0-rc3 (2020-05-29) * Bump github.com/stretchr/testify from 1.5.1 to 1.6.0 * V2 verify JSON output is consistent and doesn't drift @@ -555,7 +630,7 @@ * podmanv2: implement pod top * v2 api: implement pods top endpoint * podmanv2 commit - * Bump to buildah v1.14.5 + * Bump to buildah v1.14.5 (Edit 2020-06-03: Addresses CVE-2020-10696) * Add support for containers.conf * API v2 tests: usability improvements * Sanitize port parsing for pods in play kube @@ -878,7 +953,7 @@ * rootlessport: drop Pdeathsig in favor of Kill * rootlessport: fix potential hang * add pkg/seccomp - * Do not copy up when volume is not empty + * Do not copy up when volume is not empty (Edit 2020-06-03: Addresses CVE-2020-1726) * api: pull: fix reference parsing * cmd/podman/pull: refactor code * stats: add SystemUsage @@ -1573,7 +1648,7 @@ * get runtime for podman-remote push earlier * rootless: report the correct error * Report errors when trying to pause rootless containers - * Do not support wildcards on cp + * Do not support wildcards on cp (Edit 2020-06-03: Addresses CVE-2019-18466) * Podman-remote run should wait for exit code * Use exit code constants * exec: Register resize func a bit later diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go index 7086dc839..86cd51643 100644 --- a/cmd/podman/common/create.go +++ b/cmd/podman/common/create.go @@ -50,7 +50,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "Drop capabilities from the container", ) createFlags.String( - "cgroupns", containerConfig.CgroupNS(), + "cgroupns", "", "cgroup namespace to use", ) createFlags.StringVar( @@ -244,7 +244,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "Keep STDIN open even if not attached", ) createFlags.String( - "ipc", containerConfig.IPCNS(), + "ipc", "", "IPC namespace to use", ) createFlags.StringVar( @@ -325,7 +325,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { ) // markFlagHidden(createFlags, "override-os") createFlags.String( - "pid", containerConfig.PidNS(), + "pid", "", "PID namespace to use", ) createFlags.Int64Var( @@ -424,7 +424,7 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "Sysctl options", ) createFlags.StringVar( - &cf.SystemdD, + &cf.Systemd, "systemd", "true", `Run container in systemd mode ("true"|"false"|"always")`, ) @@ -454,11 +454,11 @@ func GetCreateFlags(cf *ContainerCLIOpts) *pflag.FlagSet { "Username or UID (format: <name|uid>[:<group|gid>])", ) createFlags.String( - "userns", containerConfig.Containers.UserNS, + "userns", "", "User namespace to use", ) createFlags.String( - "uts", containerConfig.Containers.UTSNS, + "uts", "", "UTS namespace to use", ) createFlags.StringArrayVar( diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go index 8b38e3b47..4cba5daf7 100644 --- a/cmd/podman/common/create_opts.go +++ b/cmd/podman/common/create_opts.go @@ -85,7 +85,7 @@ type ContainerCLIOpts struct { SubUIDName string SubGIDName string Sysctl []string - SystemdD string + Systemd string TmpFS []string TTY bool UIDMap []string diff --git a/cmd/podman/common/default.go b/cmd/podman/common/default.go index 7233b2091..6e5994b18 100644 --- a/cmd/podman/common/default.go +++ b/cmd/podman/common/default.go @@ -16,5 +16,5 @@ var ( // DefaultImageVolume default value DefaultImageVolume = "bind" // Pull in configured json library - json = registry.JsonLibrary() + json = registry.JSONLibrary() ) diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 1fabff378..2286e67de 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -3,7 +3,6 @@ package common import ( "fmt" "os" - "path/filepath" "strconv" "strings" "time" @@ -285,16 +284,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.NetNS = c.Net.Network } - // STOP SIGNAL - signalString := "TERM" if sig := c.StopSignal; len(sig) > 0 { - signalString = sig - } - stopSignal, err := util.ParseSignal(signalString) - if err != nil { - return err + stopSignal, err := util.ParseSignal(sig) + if err != nil { + return err + } + s.StopSignal = &stopSignal } - s.StopSignal = &stopSignal // ENVIRONMENT VARIABLES // @@ -439,25 +435,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string s.ImageVolumeMode = "anonymous" } - systemd := c.SystemdD == "always" - if !systemd && command != nil { - x, err := strconv.ParseBool(c.SystemdD) - if err != nil { - return errors.Wrapf(err, "cannot parse bool %s", c.SystemdD) - } - if x && (command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd")) { - systemd = true - } - } - if systemd { - if s.StopSignal == nil { - stopSignal, err = util.ParseSignal("RTMIN+3") - if err != nil { - return errors.Wrapf(err, "error parsing systemd signal") - } - s.StopSignal = &stopSignal - } - } + s.Systemd = c.Systemd if s.ResourceLimits == nil { s.ResourceLimits = &specs.LinuxResources{} } @@ -691,7 +669,7 @@ func makeHealthCheckFromCli(inCmd, interval string, retries uint, timeout, start hc.Interval = intervalDuration if retries < 1 { - return nil, errors.New("healthcheck-retries must be greater than 0.") + return nil, errors.New("healthcheck-retries must be greater than 0") } hc.Retries = int(retries) timeoutDuration, err := time.ParseDuration(timeout) diff --git a/cmd/podman/common/util.go b/cmd/podman/common/util.go index a3626b4e4..0d9f3ba26 100644 --- a/cmd/podman/common/util.go +++ b/cmd/podman/common/util.go @@ -71,14 +71,44 @@ func createPortBindings(ports []string) ([]specgen.PortMapping, error) { return nil, errors.Errorf("invalid port format - protocol can only be specified once") } - splitPort := strings.Split(splitProto[0], ":") + remainder := splitProto[0] + haveV6 := false + + // Check for an IPv6 address in brackets + splitV6 := strings.Split(remainder, "]") + switch len(splitV6) { + case 1: + // Do nothing, proceed as before + case 2: + // We potentially have an IPv6 address + haveV6 = true + if !strings.HasPrefix(splitV6[0], "[") { + return nil, errors.Errorf("invalid port format - IPv6 addresses must be enclosed by []") + } + if !strings.HasPrefix(splitV6[1], ":") { + return nil, errors.Errorf("invalid port format - IPv6 address must be followed by a colon (':')") + } + ipNoPrefix := strings.TrimPrefix(splitV6[0], "[") + hostIP = &ipNoPrefix + remainder = strings.TrimPrefix(splitV6[1], ":") + default: + return nil, errors.Errorf("invalid port format - at most one IPv6 address can be specified in a --publish") + } + + splitPort := strings.Split(remainder, ":") switch len(splitPort) { case 1: + if haveV6 { + return nil, errors.Errorf("invalid port format - must provide host and destination port if specifying an IP") + } ctrPort = splitPort[0] case 2: hostPort = &(splitPort[0]) ctrPort = splitPort[1] case 3: + if haveV6 { + return nil, errors.Errorf("invalid port format - when v6 address specified, must be [ipv6]:hostPort:ctrPort") + } hostIP = &(splitPort[0]) hostPort = &(splitPort[1]) ctrPort = splitPort[2] diff --git a/cmd/podman/containers/attach.go b/cmd/podman/containers/attach.go index 9f29d1664..9ef9d79f0 100644 --- a/cmd/podman/containers/attach.go +++ b/cmd/podman/containers/attach.go @@ -18,7 +18,7 @@ var ( Short: "Attach to a running container", Long: attachDescription, RunE: attach, - Args: validate.IdOrLatestArgs, + Args: validate.IDOrLatestArgs, Example: `podman attach ctrID podman attach 1234 podman attach --no-stdin foobar`, @@ -29,7 +29,7 @@ var ( Short: attachCommand.Short, Long: attachCommand.Long, RunE: attachCommand.RunE, - Args: validate.IdOrLatestArgs, + Args: validate.IDOrLatestArgs, Example: `podman container attach ctrID podman container attach 1234 podman container attach --no-stdin foobar`, diff --git a/cmd/podman/containers/container.go b/cmd/podman/containers/container.go index a102318fb..3ff341dcd 100644 --- a/cmd/podman/containers/container.go +++ b/cmd/podman/containers/container.go @@ -10,7 +10,7 @@ import ( var ( // Pull in configured json library - json = registry.JsonLibrary() + json = registry.JSONLibrary() // Command: podman _container_ containerCmd = &cobra.Command{ diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index c8007bc2f..ed09585ba 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -161,24 +161,25 @@ func createInit(c *cobra.Command) error { if c.Flag("no-hosts").Changed && c.Flag("add-host").Changed { return errors.Errorf("--no-hosts and --add-host cannot be set together") } - if c.Flag("userns").Changed { - cliVals.UserNS = c.Flag("userns").Value.String() - } - if c.Flag("ipc").Changed { - cliVals.IPC = c.Flag("ipc").Value.String() - } - if c.Flag("uts").Changed { - cliVals.UTS = c.Flag("uts").Value.String() - } - if c.Flag("pid").Changed { - cliVals.PID = c.Flag("pid").Value.String() + cliVals.UserNS = c.Flag("userns").Value.String() + // if user did not modify --userns flag and did turn on + // uid/gid mappsings, set userns flag to "private" + if !c.Flag("userns").Changed && cliVals.UserNS == "host" { + if len(cliVals.UIDMap) > 0 || + len(cliVals.GIDMap) > 0 || + cliVals.SubUIDName != "" || + cliVals.SubGIDName != "" { + cliVals.UserNS = "private" + } } + + cliVals.IPC = c.Flag("ipc").Value.String() + cliVals.UTS = c.Flag("uts").Value.String() + cliVals.PID = c.Flag("pid").Value.String() + cliVals.CGroupsNS = c.Flag("cgroupns").Value.String() if !c.Flag("pids-limit").Changed { cliVals.PIDsLimit = -1 } - if c.Flag("cgroupns").Changed { - cliVals.CGroupsNS = c.Flag("cgroupns").Value.String() - } if c.Flag("entrypoint").Changed { val := c.Flag("entrypoint").Value.String() cliVals.Entrypoint = &val diff --git a/cmd/podman/containers/diff.go b/cmd/podman/containers/diff.go index 59b788010..33b1c1126 100644 --- a/cmd/podman/containers/diff.go +++ b/cmd/podman/containers/diff.go @@ -13,7 +13,7 @@ var ( // podman container _diff_ diffCmd = &cobra.Command{ Use: "diff [flags] CONTAINER", - Args: validate.IdOrLatestArgs, + Args: validate.IDOrLatestArgs, Short: "Inspect changes on container's file systems", Long: `Displays changes on a container filesystem. The container will be compared to its parent layer.`, RunE: diff, diff --git a/cmd/podman/containers/exec.go b/cmd/podman/containers/exec.go index 41f100768..ce48af618 100644 --- a/cmd/podman/containers/exec.go +++ b/cmd/podman/containers/exec.go @@ -84,7 +84,7 @@ func init() { } func exec(cmd *cobra.Command, args []string) error { - var nameOrId string + var nameOrID string if len(args) == 0 && !execOpts.Latest { return errors.New("exec requires the name or ID of a container or the --latest flag") @@ -92,7 +92,7 @@ func exec(cmd *cobra.Command, args []string) error { execOpts.Cmd = args if !execOpts.Latest { execOpts.Cmd = args[1:] - nameOrId = args[0] + nameOrID = args[0] } // Validate given environment variables execOpts.Envs = make(map[string]string) @@ -122,12 +122,12 @@ func exec(cmd *cobra.Command, args []string) error { streams.AttachOutput = true streams.AttachError = true - exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrId, execOpts, streams) + exitCode, err := registry.ContainerEngine().ContainerExec(registry.GetContext(), nameOrID, execOpts, streams) registry.SetExitCode(exitCode) return err } - id, err := registry.ContainerEngine().ContainerExecDetached(registry.GetContext(), nameOrId, execOpts) + id, err := registry.ContainerEngine().ContainerExecDetached(registry.GetContext(), nameOrID, execOpts) if err != nil { return err } diff --git a/cmd/podman/containers/inspect.go b/cmd/podman/containers/inspect.go index 4549a4ef6..8556ebe83 100644 --- a/cmd/podman/containers/inspect.go +++ b/cmd/podman/containers/inspect.go @@ -26,9 +26,15 @@ func init() { Command: inspectCmd, Parent: containerCmd, }) - inspectOpts = inspect.AddInspectFlagSet(inspectCmd) + inspectOpts = new(entities.InspectOptions) + flags := inspectCmd.Flags() + flags.BoolVarP(&inspectOpts.Size, "size", "s", false, "Display total file size") + flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format the output to a Go template or json") + flags.BoolVarP(&inspectOpts.Latest, "latest", "l", false, "Act on the latest container Podman is aware of") } func inspectExec(cmd *cobra.Command, args []string) error { + // Force container type + inspectOpts.Type = inspect.ContainerType return inspect.Inspect(args, *inspectOpts) } diff --git a/cmd/podman/containers/ps.go b/cmd/podman/containers/ps.go index 4d12d2534..a29b4da3d 100644 --- a/cmd/podman/containers/ps.go +++ b/cmd/podman/containers/ps.go @@ -67,7 +67,7 @@ func listFlagSet(flags *pflag.FlagSet) { flags.BoolVar(&listOpts.Sync, "sync", false, "Sync container state with OCI runtime") flags.UintVarP(&listOpts.Watch, "watch", "w", 0, "Watch the ps output on an interval in seconds") - sort := validate.ChoiceValue(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") + sort := validate.Value(&listOpts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") flags.Var(sort, "sort", "Sort output by: "+sort.Choices()) if registry.IsRemote() { diff --git a/cmd/podman/containers/stats.go b/cmd/podman/containers/stats.go index c61b161e4..11aa3a4d2 100644 --- a/cmd/podman/containers/stats.go +++ b/cmd/podman/containers/stats.go @@ -87,13 +87,13 @@ func init() { func checkStatOptions(cmd *cobra.Command, args []string) error { opts := 0 if statsOptions.All { - opts += 1 + opts++ } if statsOptions.Latest { - opts += 1 + opts++ } if len(args) > 0 { - opts += 1 + opts++ } if opts > 1 { return errors.Errorf("--all, --latest and containers cannot be used together") @@ -219,9 +219,9 @@ func combineHumanValues(a, b uint64) string { func outputJSON(stats []*containerStats) error { type jstat struct { - Id string `json:"id"` + Id string `json:"id"` //nolint Name string `json:"name"` - CpuPercent string `json:"cpu_percent"` + CpuPercent string `json:"cpu_percent"` //nolint MemUsage string `json:"mem_usage"` MemPerc string `json:"mem_percent"` NetIO string `json:"net_io"` diff --git a/cmd/podman/containers/stop.go b/cmd/podman/containers/stop.go index 22c487961..0f2a91af0 100644 --- a/cmd/podman/containers/stop.go +++ b/cmd/podman/containers/stop.go @@ -85,9 +85,8 @@ func stop(cmd *cobra.Command, args []string) error { var ( errs utils.OutputErrors ) - stopOptions.Timeout = containerConfig.Engine.StopTimeout if cmd.Flag("time").Changed { - stopOptions.Timeout = stopTimeout + stopOptions.Timeout = &stopTimeout } responses, err := registry.ContainerEngine().ContainerStop(context.Background(), args, stopOptions) diff --git a/cmd/podman/containers/wait.go b/cmd/podman/containers/wait.go index 1f4d4159b..115bb3eea 100644 --- a/cmd/podman/containers/wait.go +++ b/cmd/podman/containers/wait.go @@ -23,9 +23,8 @@ var ( Short: "Block on one or more containers", Long: waitDescription, RunE: wait, - Args: validate.IdOrLatestArgs, - Example: `podman wait --latest - podman wait --interval 5000 ctrID + Args: validate.IDOrLatestArgs, + Example: `podman wait --interval 5000 ctrID podman wait ctrID1 ctrID2`, } @@ -34,9 +33,8 @@ var ( Short: waitCommand.Short, Long: waitCommand.Long, RunE: waitCommand.RunE, - Args: validate.IdOrLatestArgs, - Example: `podman container wait --latest - podman container wait --interval 5000 ctrID + Args: validate.IDOrLatestArgs, + Example: `podman container wait --interval 5000 ctrID podman container wait ctrID1 ctrID2`, } ) @@ -48,11 +46,9 @@ var ( func waitFlags(flags *pflag.FlagSet) { flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion") - flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on") - if registry.IsRemote() { - // TODO: This is the same as V1. We could skip creating the flag altogether in V2... - _ = flags.MarkHidden("latest") + if !registry.IsRemote() { + flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of") } } diff --git a/cmd/podman/diff.go b/cmd/podman/diff.go index 1ff2fce40..d635ea57a 100644 --- a/cmd/podman/diff.go +++ b/cmd/podman/diff.go @@ -18,7 +18,7 @@ var ( diffDescription = `Displays changes on a container or image's filesystem. The container or image will be compared to its parent layer.` diffCmd = &cobra.Command{ Use: "diff [flags] {CONTAINER_ID | IMAGE_ID}", - Args: validate.IdOrLatestArgs, + Args: validate.IDOrLatestArgs, Short: "Display the changes of object's file system", Long: diffDescription, TraverseChildren: true, diff --git a/cmd/podman/images/image.go b/cmd/podman/images/image.go index 790c16c05..ebef126c0 100644 --- a/cmd/podman/images/image.go +++ b/cmd/podman/images/image.go @@ -9,7 +9,7 @@ import ( var ( // Pull in configured json library - json = registry.JsonLibrary() + json = registry.JSONLibrary() // Command: podman _image_ imageCmd = &cobra.Command{ diff --git a/cmd/podman/images/inspect.go b/cmd/podman/images/inspect.go index 8c727eb07..f6a10ba44 100644 --- a/cmd/podman/images/inspect.go +++ b/cmd/podman/images/inspect.go @@ -27,11 +27,12 @@ func init() { Command: inspectCmd, Parent: imageCmd, }) - inspectOpts = inspect.AddInspectFlagSet(inspectCmd) + inspectOpts = new(entities.InspectOptions) flags := inspectCmd.Flags() - _ = flags.MarkHidden("latest") // Shared with container-inspect but not wanted here. + flags.StringVarP(&inspectOpts.Format, "format", "f", "json", "Format the output to a Go template or json") } func inspectExec(cmd *cobra.Command, args []string) error { + inspectOpts.Type = inspect.ImageType return inspect.Inspect(args, *inspectOpts) } diff --git a/cmd/podman/images/list.go b/cmd/podman/images/list.go index 23757104b..236ae15b4 100644 --- a/cmd/podman/images/list.go +++ b/cmd/podman/images/list.go @@ -100,7 +100,7 @@ func images(cmd *cobra.Command, args []string) error { switch { case listFlag.quiet: - return writeId(summaries) + return writeID(summaries) case cmd.Flag("format").Changed && listFlag.format == "json": return writeJSON(summaries) default: @@ -108,7 +108,7 @@ func images(cmd *cobra.Command, args []string) error { } } -func writeId(imageS []*entities.ImageSummary) error { +func writeID(imageS []*entities.ImageSummary) error { var ids = map[string]struct{}{} for _, e := range imageS { i := "sha256:" + e.ID diff --git a/cmd/podman/networks/list.go b/cmd/podman/networks/list.go index 24604c055..498a4dc18 100644 --- a/cmd/podman/networks/list.go +++ b/cmd/podman/networks/list.go @@ -33,8 +33,8 @@ var ( var ( networkListOptions entities.NetworkListOptions - headers string = "NAME\tVERSION\tPLUGINS\n" - defaultListRow string = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" + headers = "NAME\tVERSION\tPLUGINS\n" + defaultListRow = "{{.Name}}\t{{.Version}}\t{{.Plugins}}\n" ) func networkListFlags(flags *pflag.FlagSet) { @@ -57,7 +57,7 @@ func init() { func networkList(cmd *cobra.Command, args []string) error { var ( - nlprs []NetworkListPrintReports + nlprs []ListPrintReports ) // validate the filter pattern. @@ -83,7 +83,7 @@ func networkList(cmd *cobra.Command, args []string) error { } for _, r := range responses { - nlprs = append(nlprs, NetworkListPrintReports{r}) + nlprs = append(nlprs, ListPrintReports{r}) } row := networkListOptions.Format @@ -125,14 +125,14 @@ func jsonOut(responses []*entities.NetworkListReport) error { return nil } -type NetworkListPrintReports struct { +type ListPrintReports struct { *entities.NetworkListReport } -func (n NetworkListPrintReports) Version() string { +func (n ListPrintReports) Version() string { return n.CNIVersion } -func (n NetworkListPrintReports) Plugins() string { +func (n ListPrintReports) Plugins() string { return network.GetCNIPlugins(n.NetworkConfigList) } diff --git a/cmd/podman/pods/create.go b/cmd/podman/pods/create.go index 62b5b849e..5ed5fa57c 100644 --- a/cmd/podman/pods/create.go +++ b/cmd/podman/pods/create.go @@ -73,8 +73,8 @@ func aliasNetworkFlag(_ *pflag.FlagSet, name string) pflag.NormalizedName { func create(cmd *cobra.Command, args []string) error { var ( - err error - podIdFile *os.File + err error + podIDFD *os.File ) createOptions.Labels, err = parse.GetAllLabels(labelFile, labels) if err != nil { @@ -101,15 +101,15 @@ func create(cmd *cobra.Command, args []string) error { } if cmd.Flag("pod-id-file").Changed { - podIdFile, err = util.OpenExclusiveFile(podIDFile) + podIDFD, err = util.OpenExclusiveFile(podIDFile) if err != nil && os.IsExist(err) { return errors.Errorf("pod id file exists. Ensure another pod is not using it or delete %s", podIDFile) } if err != nil { return errors.Errorf("error opening pod-id-file %s", podIDFile) } - defer errorhandling.CloseQuiet(podIdFile) - defer errorhandling.SyncQuiet(podIdFile) + defer errorhandling.CloseQuiet(podIDFD) + defer errorhandling.SyncQuiet(podIDFD) } createOptions.Net, err = common.NetFlagsToNetOptions(cmd) diff --git a/cmd/podman/pods/pod.go b/cmd/podman/pods/pod.go index ed265ef90..9dc538c71 100644 --- a/cmd/podman/pods/pod.go +++ b/cmd/podman/pods/pod.go @@ -10,7 +10,7 @@ import ( var ( // Pull in configured json library - json = registry.JsonLibrary() + json = registry.JSONLibrary() // Command: podman _pod_ podCmd = &cobra.Command{ diff --git a/cmd/podman/pods/ps.go b/cmd/podman/pods/ps.go index 1385ff270..bcd1db84c 100644 --- a/cmd/podman/pods/ps.go +++ b/cmd/podman/pods/ps.go @@ -195,7 +195,7 @@ func (l ListPodReporter) ID() string { } // Id returns the Pod id -func (l ListPodReporter) Id() string { +func (l ListPodReporter) Id() string { //nolint if noTrunc { return l.ListPodsReport.Id } @@ -209,7 +209,7 @@ func (l ListPodReporter) InfraID() string { // InfraId returns the infra container id for the pod // depending on trunc -func (l ListPodReporter) InfraId() string { +func (l ListPodReporter) InfraId() string { //nolint if len(l.ListPodsReport.InfraId) == 0 { return "" } @@ -252,7 +252,7 @@ func sortPodPsOutput(sortBy string, lprs []*entities.ListPodsReport) error { case "created": sort.Sort(podPsSortedCreated{lprs}) case "id": - sort.Sort(podPsSortedId{lprs}) + sort.Sort(podPsSortedID{lprs}) case "name": sort.Sort(podPsSortedName{lprs}) case "number": @@ -276,9 +276,9 @@ func (a podPsSortedCreated) Less(i, j int) bool { return a.lprSort[i].Created.After(a.lprSort[j].Created) } -type podPsSortedId struct{ lprSort } +type podPsSortedID struct{ lprSort } -func (a podPsSortedId) Less(i, j int) bool { return a.lprSort[i].Id < a.lprSort[j].Id } +func (a podPsSortedID) Less(i, j int) bool { return a.lprSort[i].Id < a.lprSort[j].Id } type podPsSortedNumber struct{ lprSort } diff --git a/cmd/podman/pods/stats.go b/cmd/podman/pods/stats.go index d3950fdbc..d14632f01 100644 --- a/cmd/podman/pods/stats.go +++ b/cmd/podman/pods/stats.go @@ -71,7 +71,7 @@ func stats(cmd *cobra.Command, args []string) error { } format := statsOptions.Format - doJson := strings.ToLower(format) == formats.JSONString + doJSON := strings.ToLower(format) == formats.JSONString header := getPodStatsHeader(format) for { @@ -80,7 +80,7 @@ func stats(cmd *cobra.Command, args []string) error { return err } // Print the stats in the requested format and configuration. - if doJson { + if doJSON { if err := printJSONPodStats(reports); err != nil { return err } diff --git a/cmd/podman/registry/config_abi.go b/cmd/podman/registry/config_abi.go index 55430e1bf..4a909c17e 100644 --- a/cmd/podman/registry/config_abi.go +++ b/cmd/podman/registry/config_abi.go @@ -1,4 +1,4 @@ -// +build ABISupport +// +build !remote package registry diff --git a/cmd/podman/registry/config_tunnel.go b/cmd/podman/registry/config_tunnel.go index 29e744dac..bb3da947e 100644 --- a/cmd/podman/registry/config_tunnel.go +++ b/cmd/podman/registry/config_tunnel.go @@ -1,4 +1,4 @@ -// +build !ABISupport +// +build remote package registry diff --git a/cmd/podman/registry/json.go b/cmd/podman/registry/json.go index f25406c3c..a8a1623f5 100644 --- a/cmd/podman/registry/json.go +++ b/cmd/podman/registry/json.go @@ -11,8 +11,8 @@ var ( jsonSync sync.Once ) -// JsonLibrary provides a "encoding/json" compatible API -func JsonLibrary() jsoniter.API { +// JSONLibrary provides a "encoding/json" compatible API +func JSONLibrary() jsoniter.API { jsonSync.Do(func() { json = jsoniter.ConfigCompatibleWithStandardLibrary }) diff --git a/cmd/podman/report/report.go b/cmd/podman/report/report.go index 8392f10e0..ce349ef35 100644 --- a/cmd/podman/report/report.go +++ b/cmd/podman/report/report.go @@ -3,4 +3,4 @@ package report import "github.com/containers/libpod/cmd/podman/registry" // Pull in configured json library -var json = registry.JsonLibrary() +var json = registry.JSONLibrary() diff --git a/cmd/podman/root.go b/cmd/podman/root.go index 3796b8e27..4f834e87d 100644 --- a/cmd/podman/root.go +++ b/cmd/podman/root.go @@ -4,12 +4,14 @@ import ( "fmt" "os" "path" + "runtime" "runtime/pprof" "strings" "github.com/containers/libpod/cmd/podman/registry" "github.com/containers/libpod/cmd/podman/validate" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/parallel" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/tracing" "github.com/containers/libpod/version" @@ -103,6 +105,11 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { cfg := registry.PodmanConfig() + // Help is a special case, no need for more setup + if cmd.Name() == "help" { + return nil + } + // Prep the engines if _, err := registry.NewImageEngine(cmd, args); err != nil { return err @@ -112,10 +119,10 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { } if cmd.Flag("cpu-profile").Changed { - f, err := os.Create(cfg.CpuProfile) + f, err := os.Create(cfg.CPUProfile) if err != nil { return errors.Wrapf(err, "unable to create cpu profiling file %s", - cfg.CpuProfile) + cfg.CPUProfile) } if err := pprof.StartCPUProfile(f); err != nil { return err @@ -132,6 +139,13 @@ func persistentPreRunE(cmd *cobra.Command, args []string) error { opentracing.StartSpanFromContext(cfg.SpanCtx, cmd.Name()) } + if cfg.MaxWorks <= 0 { + return errors.Errorf("maximum workers must be set to a positive number (got %d)", cfg.MaxWorks) + } + if err := parallel.SetMaxThreads(uint(cfg.MaxWorks)); err != nil { + return err + } + // Setup Rootless environment, IFF: // 1) in ABI mode // 2) running as non-root @@ -150,6 +164,11 @@ func persistentPostRunE(cmd *cobra.Command, args []string) error { // TODO: Remove trace statement in podman V2.1 logrus.Debugf("Called %s.PersistentPostRunE(%s)", cmd.Name(), strings.Join(os.Args, " ")) + // Help is a special case, no need for more cleanup + if cmd.Name() == "help" { + return nil + } + cfg := registry.PodmanConfig() if cmd.Flag("cpu-profile").Changed { pprof.StopCPUProfile() @@ -191,19 +210,22 @@ func loggingHook() { func rootFlags(opts *entities.PodmanConfig, flags *pflag.FlagSet) { // V2 flags - flags.StringVarP(&opts.Uri, "remote", "r", registry.DefaultAPIAddress(), "URL to access Podman service") - flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file") + flags.BoolVarP(&opts.Remote, "remote", "r", false, "Access remote Podman service (default false)") + // TODO Read uri from containers.config when available + flags.StringVar(&opts.URI, "url", registry.DefaultAPIAddress(), "URL to access Podman service (CONTAINER_HOST)") + flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file, (CONTAINER_SSHKEY)") + flags.StringVar(&opts.PassPhrase, "passphrase", "", "passphrase for identity file (not secure, CONTAINER_PASSPHRASE), ssh-agent always supported") cfg := opts.Config flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, "Cgroup manager to use (\"cgroupfs\"|\"systemd\")") - flags.StringVar(&opts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results") + flags.StringVar(&opts.CPUProfile, "cpu-profile", "", "Path for the cpu profiling results") flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary") flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network") flags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks") flags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file") flags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`) flags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)") - flags.IntVar(&opts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations") + flags.IntVar(&opts.MaxWorks, "max-workers", (runtime.NumCPU()*3)+1, "The maximum number of workers for parallel operations") flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system") flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored") flags.StringVar(&opts.RegistriesConf, "registries-conf", "", "Path to a registries.conf to use for image processing") diff --git a/cmd/podman/system/df.go b/cmd/podman/system/df.go index 8fe035209..9318bba12 100644 --- a/cmd/podman/system/df.go +++ b/cmd/podman/system/df.go @@ -63,7 +63,7 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { dfSummaries []*dfSummary active int size, reclaimable int64 - format string = "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n" + format = "{{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}\n" w io.Writer = os.Stdout ) @@ -74,7 +74,7 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { for _, i := range reports.Images { if i.Containers > 0 { - active += 1 + active++ } size += i.Size if i.Containers < 1 { @@ -99,7 +99,7 @@ func printSummary(reports *entities.SystemDfReport, userFormat string) error { ) for _, c := range reports.Containers { if c.Status == "running" { - conActive += 1 + conActive++ } else { conReclaimable += c.RWSize } diff --git a/cmd/podman/system/events.go b/cmd/podman/system/events.go index 27e80138e..c401c5a92 100644 --- a/cmd/podman/system/events.go +++ b/cmd/podman/system/events.go @@ -17,8 +17,10 @@ import ( ) var ( - eventsDescription = "Monitor podman events" - eventsCommand = &cobra.Command{ + eventsDescription = `Monitor podman events. + + By default, streaming mode is used, printing new events as they occur. Previous events can be listed via --since and --until.` + eventsCommand = &cobra.Command{ Use: "events", Args: validate.NoArgs, Short: "Show podman events", diff --git a/cmd/podman/system/service.go b/cmd/podman/system/service.go index 1b07ee301..ecd17c251 100644 --- a/cmd/podman/system/service.go +++ b/cmd/podman/system/service.go @@ -64,7 +64,7 @@ func aliasTimeoutFlag(_ *pflag.FlagSet, name string) pflag.NormalizedName { } func service(cmd *cobra.Command, args []string) error { - apiURI, err := resolveApiURI(args) + apiURI, err := resolveAPIURI(args) if err != nil { return err } @@ -103,7 +103,7 @@ func service(cmd *cobra.Command, args []string) error { return restService(opts, cmd.Flags(), registry.PodmanConfig()) } -func resolveApiURI(_url []string) (string, error) { +func resolveAPIURI(_url []string) (string, error) { // When determining _*THE*_ listening endpoint -- // 1) User input wins always // 2) systemd socket activation diff --git a/cmd/podman/system/service_abi.go b/cmd/podman/system/service_abi.go index 501650839..f5386c4f1 100644 --- a/cmd/podman/system/service_abi.go +++ b/cmd/podman/system/service_abi.go @@ -1,4 +1,4 @@ -// +build ABISupport,!remote +// +build linux,!remote package system diff --git a/cmd/podman/system/service_unsupported.go b/cmd/podman/system/service_unsupported.go deleted file mode 100644 index 82272c882..000000000 --- a/cmd/podman/system/service_unsupported.go +++ /dev/null @@ -1,14 +0,0 @@ -// +build !ABISupport,!remote - -package system - -import ( - "errors" - - "github.com/containers/libpod/pkg/domain/entities" - "github.com/spf13/pflag" -) - -func restService(opts entities.ServiceOptions, flags *pflag.FlagSet, cfg *entities.PodmanConfig) error { - return errors.New("not supported") -} diff --git a/cmd/podman/system/system.go b/cmd/podman/system/system.go index d9691ad2a..acf41a32d 100644 --- a/cmd/podman/system/system.go +++ b/cmd/podman/system/system.go @@ -9,7 +9,7 @@ import ( var ( // Pull in configured json library - json = registry.JsonLibrary() + json = registry.JSONLibrary() // Command: podman _system_ systemCmd = &cobra.Command{ diff --git a/cmd/podman/validate/args.go b/cmd/podman/validate/args.go index 14b4d7897..69240798f 100644 --- a/cmd/podman/validate/args.go +++ b/cmd/podman/validate/args.go @@ -23,8 +23,8 @@ func SubCommandExists(cmd *cobra.Command, args []string) error { return errors.Errorf("missing command '%[1]s COMMAND'\nTry '%[1]s --help' for more information.", cmd.CommandPath()) } -// IdOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag -func IdOrLatestArgs(cmd *cobra.Command, args []string) error { +// IDOrLatestArgs used to validate a nameOrId was provided or the "--latest" flag +func IDOrLatestArgs(cmd *cobra.Command, args []string) error { if len(args) > 1 || (len(args) == 0 && !cmd.Flag("latest").Changed) { return fmt.Errorf("`%s` requires a name, id or the \"--latest\" flag", cmd.CommandPath()) } diff --git a/cmd/podman/validate/choice.go b/cmd/podman/validate/choice.go index 572c5f4a5..8bb21c591 100644 --- a/cmd/podman/validate/choice.go +++ b/cmd/podman/validate/choice.go @@ -6,28 +6,28 @@ import ( ) // Honors cobra.Value interface -type choiceValue struct { +type ChoiceValue struct { value *string choices []string } -// ChoiceValue may be used in cobra FlagSet methods Var/VarP/VarPF() to select from a set of values +// Value may be used in cobra FlagSet methods Var/VarP/VarPF() to select from a set of values // // Example: // created := validate.ChoiceValue(&opts.Sort, "command", "created", "id", "image", "names", "runningfor", "size", "status") // flags.Var(created, "sort", "Sort output by: "+created.Choices()) -func ChoiceValue(p *string, choices ...string) *choiceValue { - return &choiceValue{ +func Value(p *string, choices ...string) *ChoiceValue { + return &ChoiceValue{ value: p, choices: choices, } } -func (c *choiceValue) String() string { +func (c *ChoiceValue) String() string { return *c.value } -func (c *choiceValue) Set(value string) error { +func (c *ChoiceValue) Set(value string) error { for _, v := range c.choices { if v == value { *c.value = value @@ -37,10 +37,10 @@ func (c *choiceValue) Set(value string) error { return fmt.Errorf("%q is not a valid value. Choose from: %q", value, c.Choices()) } -func (c *choiceValue) Choices() string { +func (c *ChoiceValue) Choices() string { return strings.Join(c.choices, ", ") } -func (c *choiceValue) Type() string { +func (c *ChoiceValue) Type() string { return "choice" } diff --git a/cmd/podman/volumes/create.go b/cmd/podman/volumes/create.go index 1bec8d0e7..16ac3771e 100644 --- a/cmd/podman/volumes/create.go +++ b/cmd/podman/volumes/create.go @@ -67,6 +67,6 @@ func create(cmd *cobra.Command, args []string) error { if err != nil { return err } - fmt.Println(response.IdOrName) + fmt.Println(response.IDOrName) return nil } diff --git a/cmd/podman/volumes/volume.go b/cmd/podman/volumes/volume.go index 3e90d178c..12947a6b1 100644 --- a/cmd/podman/volumes/volume.go +++ b/cmd/podman/volumes/volume.go @@ -9,7 +9,7 @@ import ( var ( // Pull in configured json library - json = registry.JsonLibrary() + json = registry.JSONLibrary() // Command: podman _volume_ volumeCmd = &cobra.Command{ diff --git a/contrib/cirrus/README.md b/contrib/cirrus/README.md index 541cf2f54..c8ec766e7 100644 --- a/contrib/cirrus/README.md +++ b/contrib/cirrus/README.md @@ -167,26 +167,50 @@ env: ### `docs` Task -Builds swagger API documentation YAML and uploads to google storage for both -PR's (for testing the process) and after a merge into any branch. For PR's +Builds swagger API documentation YAML and uploads to google storage (an online +service for storing unstructured data) for both +PR's (for testing the process) and the master branch. For PR's the YAML is uploaded into a [dedicated short-pruning cycle -bucket.](https://storage.googleapis.com/libpod-pr-releases/) For branches, -a [separate bucket is -used.](https://storage.googleapis.com/libpod-master-releases) -In both cases the filename includes the source -PR number or branch name. - -***Note***: [The online documentation](http://docs.podman.io/en/latest/_static/api.html) -is presented through javascript on the client-side. This requires CORS to be properly -configured on the bucket, for the `http://docs.podman.io` origin. Please see -[Configuring CORS on a bucket](https://cloud.google.com/storage/docs/configuring-cors#configure-cors-bucket) -for details. This may be performed by anybody with admin access to the google storage bucket, -using the following JSON: +bucket.](https://storage.googleapis.com/libpod-pr-releases/) for testing purposes +only. For the master branch, a [separate bucket is +used](https://storage.googleapis.com/libpod-master-releases) and provides the +content rendered on [the API Reference page](https://docs.podman.io/en/latest/_static/api.html) + +The online API reference is presented by javascript to the client. To prevent hijacking +of the client by malicious data, the [javascript utilises CORS](https://cloud.google.com/storage/docs/cross-origin). +This CORS metadata is served by `https://storage.googleapis.com` when configured correctly. +It will appear in [the request and response headers from the +client](https://cloud.google.com/storage/docs/configuring-cors#troubleshooting) when accessing +the API reference page. + +However, when the CORS metadata is missing or incorrectly configured, clients will receive an +error-message similar to: + +![Javascript Stack Trace Image](swagger_stack_trace.png) + +For documentation built by Read The Docs from the master branch, CORS metadata is +set on the `libpod-master-releases` storage bucket. Viewing or setting the CORS +metadata on the bucket requires having locally [installed and +configured the google-cloud SDK](https://cloud.google.com/sdk/docs). It also requires having +admin access to the google-storage bucket. Contact a project owner for help if you are +unsure of your permissions or need help resolving an error similar to the picture above. + +Assuming the SDK is installed, and you have the required admin access, the following command +will display the current CORS metadata: + +``` +gsutil cors get gs://libpod-master-releases +``` + +To function properly (allow client "trust" of content from `storage.googleapis.com`) the followiing +metadata JSON should be used. Following the JSON, is an example of the command used to set this +metadata on the libpod-master-releases bucket. For additional information about configuring CORS +please referr to [the google-storage documentation](https://cloud.google.com/storage/docs/configuring-cors). ```JSON [ { - "origin": ["http://docs.podman.io"], + "origin": ["http://docs.podman.io", "https://docs.podman.io"], "responseHeader": ["Content-Type"], "method": ["GET"], "maxAgeSeconds": 600 @@ -194,6 +218,14 @@ using the following JSON: ] ``` +``` +gsutil cors set /path/to/file.json gs://libpod-master-releases +``` + +***Note:*** The CORS metadata does _NOT_ change after the `docs` task uploads a new swagger YAML +file. Therefore, if it is not functioning or misconfigured, a person must have altered it or +changes were made to the referring site (e.g. `docs.podman.io`). + ## Base-images Base-images are VM disk-images specially prepared for executing as GCE VMs. diff --git a/contrib/cirrus/check_image.sh b/contrib/cirrus/check_image.sh index 5423f67d6..0d33e55bf 100755 --- a/contrib/cirrus/check_image.sh +++ b/contrib/cirrus/check_image.sh @@ -6,7 +6,7 @@ source $(dirname $0)/lib.sh EVIL_UNITS="$($CIRRUS_WORKING_DIR/$PACKER_BASE/systemd_banish.sh --list)" -req_env_var PACKER_BUILDER_NAME TEST_REMOTE_CLIENT EVIL_UNITS OS_RELEASE_ID +req_env_var PACKER_BUILDER_NAME TEST_REMOTE_CLIENT EVIL_UNITS OS_RELEASE_ID CG_FS_TYPE NFAILS=0 echo "Validating VM image" @@ -22,7 +22,8 @@ item_test 'Minimum available memory' $MEM_FREE -ge $MIN_MEM_MB || let "NFAILS+=1 # We're testing a custom-built podman; make sure there isn't a distro-provided # binary anywhere; that could potentially taint our results. -item_test "remove_packaged_podman_files() did it's job" -z "$(type -P podman)" || let "NFAILS+=1" +remove_packaged_podman_files +item_test "remove_packaged_podman_files() does it's job" -z "$(type -P podman)" || let "NFAILS+=1" # Integration Tests require varlink in Fedora item_test "The varlink executable is present" -x "$(type -P varlink)" || let "NFAILS+=1" @@ -39,8 +40,10 @@ for REQ_UNIT in google-accounts-daemon.service \ google-shutdown-scripts.service \ google-startup-scripts.service do - item_test "required $REQ_UNIT enabled" \ - "$(systemctl list-unit-files --no-legend $REQ_UNIT)" = "$REQ_UNIT enabled" || let "NFAILS+=1" + # enabled/disabled appears at the end of the line, on some Ubuntu's it appears twice + service_status=$(systemctl list-unit-files --no-legend $REQ_UNIT | tac -s ' ' | head -1) + item_test "required $REQ_UNIT status is enabled" \ + "$service_status" = "enabled" || let "NFAILS+=1" done for evil_unit in $EVIL_UNITS @@ -50,19 +53,28 @@ do item_test "No $evil_unit unit is present or active:" "$unit_status" -ne "0" || let "NFAILS+=1" done -if [[ "$OS_RELEASE_ID" == "ubuntu" ]] && [[ -x "/usr/lib/cri-o-runc/sbin/runc" ]] -then - SAMESAME=$(diff --brief /usr/lib/cri-o-runc/sbin/runc /usr/bin/runc &> /dev/null; echo $?) - item_test "On ubuntu /usr/bin/runc is /usr/lib/cri-o-runc/sbin/runc" "$SAMESAME" -eq "0" || let "NFAILS+=1" -fi - -if [[ "$OS_RELEASE_ID" == "ubuntu" ]] -then - item_test "On ubuntu, no periodic apt crap is enabled" -z "$(egrep $PERIODIC_APT_RE /etc/apt/apt.conf.d/*)" -fi - echo "Checking items specific to ${PACKER_BUILDER_NAME}${BUILT_IMAGE_SUFFIX}" case "$PACKER_BUILDER_NAME" in + ubuntu*) + item_test "On ubuntu, no periodic apt crap is enabled" -z "$(egrep $PERIODIC_APT_RE /etc/apt/apt.conf.d/*)" + ;; + fedora*) + # Only runc -OR- crun should be installed, never both + case "$CG_FS_TYPE" in + tmpfs) + HAS=runc + HAS_NOT=crun + ;; + cgroup2fs) + HAS=crun + HAS_NOT=runc + ;; + esac + HAS_RC=$(rpm -qV $HAS &> /dev/null; echo $?) + HAS_NOT_RC=$(rpm -qV $HAS_NOT &> /dev/null; echo $?) + item_test "With a cgroups-fs type $CG_FS_TYPE, the $HAS package is installed" $HAS_RC -eq 0 + item_test "With a cgroups-fs type $CG_FS_TYPE, the $HAS_NOT package is not installed" $HAS_NOT_RC -ne 0 + ;; xfedora*) echo "Kernel Command-line: $(cat /proc/cmdline)" item_test \ diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh index c92f123fd..33e9fbc6b 100755 --- a/contrib/cirrus/integration_test.sh +++ b/contrib/cirrus/integration_test.sh @@ -6,6 +6,11 @@ source $(dirname $0)/lib.sh req_env_var GOSRC SCRIPT_BASE OS_RELEASE_ID OS_RELEASE_VER CONTAINER_RUNTIME VARLINK_LOG +LOCAL_OR_REMOTE=local +if [[ "$TEST_REMOTE_CLIENT" = "true" ]]; then + LOCAL_OR_REMOTE=remote +fi + # Our name must be of the form xxxx_test or xxxx_test.sh, where xxxx is # the test suite to run; currently (2019-05) the only option is 'integration' # but pr2947 intends to add 'system'. @@ -18,7 +23,7 @@ cd "$GOSRC" case "$SPECIALMODE" in in_podman) - ${CONTAINER_RUNTIME} run --rm --privileged --net=host \ + ${CONTAINER_RUNTIME} run --rm --privileged --net=host --cgroupns=host \ -v $GOSRC:$GOSRC:Z \ --workdir $GOSRC \ -e "CGROUP_MANAGER=cgroupfs" \ @@ -34,7 +39,7 @@ case "$SPECIALMODE" in req_env_var ROOTLESS_USER ssh $ROOTLESS_USER@localhost \ -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \ - -o CheckHostIP=no $GOSRC/$SCRIPT_BASE/rootless_test.sh ${TESTSUITE} + -o CheckHostIP=no $GOSRC/$SCRIPT_BASE/rootless_test.sh ${TESTSUITE} ${LOCAL_OR_REMOTE} ;; endpoint) make @@ -52,12 +57,8 @@ case "$SPECIALMODE" in make make install PREFIX=/usr ETCDIR=/etc make test-binaries - if [[ "$TEST_REMOTE_CLIENT" == "true" ]] - then - make remote${TESTSUITE} VARLINK_LOG=$VARLINK_LOG - else - make local${TESTSUITE} - fi + make .install.bats + make ${LOCAL_OR_REMOTE}${TESTSUITE} PODMAN_SERVER_LOG=$PODMAN_SERVER_LOG ;; *) die 110 "Unsupported \$SPECIALMODE: $SPECIALMODE" diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index cc5a3ffa7..66e8060cf 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -39,6 +39,8 @@ PACKER_BASE=${PACKER_BASE:-./contrib/cirrus/packer} # Important filepaths SETUP_MARKER_FILEPATH="${SETUP_MARKER_FILEPATH:-/var/tmp/.setup_environment_sh_complete}" AUTHOR_NICKS_FILEPATH="${CIRRUS_WORKING_DIR}/${SCRIPT_BASE}/git_authors_to_irc_nicks.csv" +# Downloaded, but not installed packages. +PACKAGE_DOWNLOAD_DIR=/var/cache/download # Log remote-client system test varlink output here export VARLINK_LOG=/var/tmp/varlink.log @@ -422,7 +424,7 @@ remove_packaged_podman_files() { then LISTING_CMD="$SUDO dpkg-query -L podman" else - LISTING_CMD='$SUDO rpm -ql podman' + LISTING_CMD="$SUDO rpm -ql podman" fi # yum/dnf/dpkg may list system directories, only remove files @@ -437,6 +439,14 @@ remove_packaged_podman_files() { sync && echo 3 > /proc/sys/vm/drop_caches } +# The version of CRI-O and Kubernetes must always match +get_kubernetes_version(){ + # TODO: Look up the kube RPM/DEB version installed, or in $PACKAGE_DOWNLOAD_DIR + # and retrieve the major-minor version directly. + local KUBERNETES_VERSION="1.15" + echo "$KUBERNETES_VERSION" +} + canonicalize_image_names() { req_env_var IMGNAMES echo "Adding all current base images to \$IMGNAMES for timestamp update" @@ -479,6 +489,7 @@ _finalize() { fi echo "Re-initializing so next boot does 'first-boot' setup again." cd / + $SUDO rm -rf $GOPATH/src # Actual source will be cloned at runtime $SUDO rm -rf /var/lib/cloud/instanc* $SUDO rm -rf /root/.ssh/* $SUDO rm -rf /etc/ssh/*key* diff --git a/contrib/cirrus/packer/fedora_packaging.sh b/contrib/cirrus/packer/fedora_packaging.sh index e80d48bc8..aecaaef93 100644 --- a/contrib/cirrus/packer/fedora_packaging.sh +++ b/contrib/cirrus/packer/fedora_packaging.sh @@ -11,6 +11,8 @@ echo "Updating/Installing repos and packages for $OS_REL_VER" source $GOSRC/$SCRIPT_BASE/lib.sh +req_env_var GOSRC SCRIPT_BASE BIGTO INSTALL_AUTOMATION_VERSION FEDORA_BASE_IMAGE PRIOR_FEDORA_BASE_IMAGE + # Pre-req. to install automation tooing $LILTO $SUDO dnf install -y git @@ -35,7 +37,7 @@ fi $BIGTO ooe.sh $SUDO dnf update -y -REMOVE_PACKAGES=() +REMOVE_PACKAGES=(runc) INSTALL_PACKAGES=(\ autoconf automake @@ -50,8 +52,11 @@ INSTALL_PACKAGES=(\ containernetworking-plugins containers-common criu + crun + curl device-mapper-devel dnsmasq + e2fsprogs-devel emacs-nox file findutils @@ -60,16 +65,26 @@ INSTALL_PACKAGES=(\ gcc git glib2-devel + glibc-devel glibc-static gnupg go-md2man golang + gpgme gpgme-devel + grubby + hostname iproute iptables jq + krb5-workstation + libassuan libassuan-devel + libblkid-devel libcap-devel + libffi-devel + libgpg-error-devel + libguestfs-tools libmsi1 libnet libnet-devel @@ -79,56 +94,60 @@ INSTALL_PACKAGES=(\ libselinux-devel libtool libvarlink-util + libxml2-devel + libxslt-devel lsof make + mlocate msitools + nfs-utils nmap-ncat + openssl + openssl-devel ostree-devel pandoc + pkgconfig podman + policycoreutils procps-ng protobuf protobuf-c protobuf-c-devel protobuf-devel - python + python2 + python3-PyYAML python3-dateutil python3-psutil python3-pytoml + python3-libsemanage + python3-libselinux + python3-libvirt + redhat-rpm-config + rpcbind rsync + sed selinux-policy-devel skopeo skopeo-containers slirp4netns + socat + tar unzip vim wget which xz zip + zlib-devel +) +DOWNLOAD_PACKAGES=(\ + "cri-o-$(get_kubernetes_version)*" + cri-tools + "kubernetes-$(get_kubernetes_version)*" + runc + oci-umount + parallel ) - -case "$OS_RELEASE_VER" in - 30) - INSTALL_PACKAGES+=(\ - atomic-registries - golang-github-cpuguy83-go-md2man - python2-future - runc - ) - REMOVE_PACKAGES+=(crun) - ;; - 31) - INSTALL_PACKAGES+=(crun) - REMOVE_PACKAGES+=(runc) - ;; - 32) - INSTALL_PACKAGES+=(crun) - REMOVE_PACKAGES+=(runc) - ;; - *) - bad_os_id_ver ;; -esac echo "Installing general build/test dependencies for Fedora '$OS_RELEASE_VER'" $BIGTO ooe.sh $SUDO dnf install -y ${INSTALL_PACKAGES[@]} @@ -136,6 +155,18 @@ $BIGTO ooe.sh $SUDO dnf install -y ${INSTALL_PACKAGES[@]} [[ ${#REMOVE_PACKAGES[@]} -eq 0 ]] || \ $LILTO ooe.sh $SUDO dnf erase -y ${REMOVE_PACKAGES[@]} -export GOPATH="$(mktemp -d)" -trap "$SUDO rm -rf $GOPATH" EXIT -ooe.sh $SUDO $GOSRC/hack/install_catatonit.sh +if [[ ${#DOWNLOAD_PACKAGES[@]} -gt 0 ]]; then + echo "Downloading packages for optional installation at runtime, as needed." + # Required for cri-o + ooe.sh $SUDO dnf -y module enable cri-o:$(get_kubernetes_version) + $SUDO mkdir -p "$PACKAGE_DOWNLOAD_DIR" + cd "$PACKAGE_DOWNLOAD_DIR" + $LILTO ooe.sh $SUDO dnf download -y --resolve ${DOWNLOAD_PACKAGES[@]} + ls -la "$PACKAGE_DOWNLOAD_DIR/" +fi + +echo "Installing runtime tooling" +# Save some runtime by having these already available +cd $GOSRC +$SUDO make install.tools +$SUDO $GOSRC/hack/install_catatonit.sh diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index 3830b3bc4..25b568e8a 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -12,11 +12,11 @@ req_env_var SCRIPT_BASE PACKER_BASE INSTALL_AUTOMATION_VERSION PACKER_BUILDER_NA workaround_bfq_bug -# Do not enable update-stesting on the previous Fedora release -if [[ "$FEDORA_BASE_IMAGE" =~ "${OS_RELEASE_ID}-cloud-base-${OS_RELEASE_VER}" ]]; then - DISABLE_UPDATES_TESTING=0 -else +# Do not enable updates-testing on the previous Fedora release +if [[ "$PRIOR_FEDORA_BASE_IMAGE" =~ "${OS_RELEASE_ID}-cloud-base-${OS_RELEASE_VER}" ]]; then DISABLE_UPDATES_TESTING=1 +else + DISABLE_UPDATES_TESTING=0 fi bash $PACKER_BASE/fedora_packaging.sh diff --git a/contrib/cirrus/packer/ubuntu_packaging.sh b/contrib/cirrus/packer/ubuntu_packaging.sh index fd0280230..09f9aab9f 100644 --- a/contrib/cirrus/packer/ubuntu_packaging.sh +++ b/contrib/cirrus/packer/ubuntu_packaging.sh @@ -11,6 +11,8 @@ echo "Updating/Installing repos and packages for $OS_REL_VER" source $GOSRC/$SCRIPT_BASE/lib.sh +req_env_var GOSRC SCRIPT_BASE BIGTO SUDOAPTGET INSTALL_AUTOMATION_VERSION + echo "Updating/configuring package repositories." $BIGTO $SUDOAPTGET update @@ -99,6 +101,7 @@ INSTALL_PACKAGES=(\ protobuf-c-compiler protobuf-compiler python-protobuf + python2 python3-dateutil python3-pip python3-psutil @@ -118,6 +121,11 @@ INSTALL_PACKAGES=(\ zip zlib1g-dev ) +DOWNLOAD_PACKAGES=(\ + cri-o-$(get_kubernetes_version) + cri-tools + parallel +) # These aren't resolvable on Ubuntu 20 if [[ "$OS_RELEASE_VER" -le 19 ]]; then @@ -137,16 +145,15 @@ echo "Installing general testing and system dependencies" $LILTO ooe.sh $SUDOAPTGET update $BIGTO ooe.sh $SUDOAPTGET install ${INSTALL_PACKAGES[@]} -export GOPATH="$(mktemp -d)" -trap "$SUDO rm -rf $GOPATH" EXIT -echo "Installing cataonit and libseccomp.sudo" -cd $GOSRC -ooe.sh $SUDO hack/install_catatonit.sh -ooe.sh $SUDO make install.libseccomp.sudo - -CRIO_RUNC_PATH="/usr/lib/cri-o-runc/sbin/runc" -if $SUDO dpkg -L cri-o-runc | grep -m 1 -q "$CRIO_RUNC_PATH" -then - echo "Linking $CRIO_RUNC_PATH to /usr/bin/runc for ease of testing." - $SUDO ln -f "$CRIO_RUNC_PATH" "/usr/bin/runc" +if [[ ${#DOWNLOAD_PACKAGES[@]} -gt 0 ]]; then + echo "Downloading packages for optional installation at runtime, as needed." + $SUDO ln -s /var/cache/apt/archives "$PACKAGE_DOWNLOAD_DIR" + $LILTO ooe.sh $SUDOAPTGET install --download-only ${DOWNLOAD_PACKAGES[@]} + ls -la "$PACKAGE_DOWNLOAD_DIR/" fi + +echo "Installing runtime tooling" +cd $GOSRC +$SUDO hack/install_catatonit.sh +$SUDO make install.libseccomp.sudo +$SUDO make install.tools diff --git a/contrib/cirrus/rootless_test.sh b/contrib/cirrus/rootless_test.sh index 3f45aac84..9e1b1d911 100755 --- a/contrib/cirrus/rootless_test.sh +++ b/contrib/cirrus/rootless_test.sh @@ -2,14 +2,6 @@ set -e -remote=0 - -# The TEST_REMOTE_CLIENT environment variable decides whether -# to test varlink -if [[ "$TEST_REMOTE_CLIENT" == "true" ]]; then - remote=1 -fi - source $(dirname $0)/lib.sh if [[ "$UID" == "0" ]] @@ -18,11 +10,8 @@ then exit 1 fi -# Which set of tests to run; possible alternative is "system" -TESTSUITE=integration -if [[ -n "$*" ]]; then - TESTSUITE="$1" -fi +TESTSUITE=${1?Missing TESTSUITE argument (arg1)} +LOCAL_OR_REMOTE=${2?Missing LOCAL_OR_REMOTE argument (arg2)} # Ensure environment setup correctly req_env_var GOSRC ROOTLESS_USER @@ -31,7 +20,6 @@ echo "." echo "Hello, my name is $USER and I live in $PWD can I be your friend?" echo "." -export PODMAN_VARLINK_ADDRESS=unix:/tmp/podman-$(id -u) show_env_vars set -x @@ -39,8 +27,4 @@ cd "$GOSRC" make make varlink_generate make test-binaries -if [ $remote -eq 0 ]; then - make local${TESTSUITE} -else - make remote${TESTSUITE} -fi +make ${LOCAL_OR_REMOTE}${TESTSUITE} diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 25b7ff941..323e7c35b 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -39,6 +39,17 @@ done cd "${GOSRC}/" case "${OS_RELEASE_ID}" in ubuntu) + apt-get update + apt-get install -y containers-common + sed -ie 's/^\(# \)\?apparmor_profile =.*/apparmor_profile = ""/' /etc/containers/containers.conf + if [[ "$OS_RELEASE_VER" == "19" ]]; then + apt-get purge -y --auto-remove golang* + apt-get install -y golang-1.13 + ln -s /usr/lib/go-1.13/bin/go /usr/bin/go + fi + if [[ "$OS_RELEASE_VER" == "20" ]]; then + apt-get install -y python-is-python3 + fi ;; fedora) # All SELinux distros need this for systemd-in-a-container @@ -78,14 +89,6 @@ case "$CG_FS_TYPE" in warn "Forcing testing with crun instead of runc" X=$(echo "export OCI_RUNTIME=/usr/bin/crun" | \ tee -a /etc/environment) && eval "$X" && echo "$X" - - if [[ "$OS_RELEASE_ID" == "fedora" ]]; then - warn "Upgrading to the latest crun" - # Normally not something to do for stable testing - # but crun is new, and late-breaking fixes may be required - # on short notice - dnf update -y crun containers-common - fi ;; *) die 110 "Unsure how to handle cgroup filesystem type '$CG_FS_TYPE'" diff --git a/contrib/cirrus/swagger_stack_trace.png b/contrib/cirrus/swagger_stack_trace.png Binary files differnew file mode 100644 index 000000000..6aa063bab --- /dev/null +++ b/contrib/cirrus/swagger_stack_trace.png diff --git a/contrib/gate/Dockerfile b/contrib/gate/Dockerfile index f86709b00..657b5accf 100644 --- a/contrib/gate/Dockerfile +++ b/contrib/gate/Dockerfile @@ -13,7 +13,7 @@ COPY . $GOSRC # Note: adding conmon and crun so podman command checks will work RUN dnf -y install \ $(grep "^[^#]" $GOSRC/contrib/dependencies.txt) diffutils containers-common fuse-overlayfs conmon crun runc --exclude container-selinux; \ - sed -i -e 's|^#mount_program|mount_program|g' /etc/containers/storage.conf \ + sed -i -e 's|^#mount_program|mount_program|g' -e 's/# size.*/skip_mount_home = "true"/g' /etc/containers/storage.conf \ && dnf clean all # Install dependencies diff --git a/contrib/spec/podman.spec.in b/contrib/spec/podman.spec.in index 9ab0db3ae..8d3cba612 100644 --- a/contrib/spec/podman.spec.in +++ b/contrib/spec/podman.spec.in @@ -80,8 +80,12 @@ Requires: iptables %if 0%{?rhel} <= 7 Requires: container-selinux %else +%if 0%{?rhel} || 0%{?centos} +Requires: runc +%else Requires: oci-runtime Recommends: crun +%endif Recommends: container-selinux Recommends: slirp4netns Recommends: fuse-overlayfs diff --git a/docs/Readme.md b/docs/Readme.md index 987a5b8e4..9d3b9d06f 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -30,10 +30,26 @@ link on that page. ## API Reference The [latest online documentation](http://docs.podman.io/en/latest/_static/api.html) is -automatically generated from committed upstream sources. There is a short-duration -cache involved, in case old content or an error is returned, try clearing your browser -cache or returning to the site after 10-30 minutes. - -***Maintainers Note***: Please refer to [the Cirrus-CI tasks -documentation](../contrib/cirrus/README.md#docs-task) for -important operational details. +automatically generated by two cooperating automation systems based on committed upstream +source code. Firstly, [the Cirrus-CI docs task](../contrib/cirrus/README.md#docs-task) builds +`pkg/api/swagger.yaml` and uploads it to a public-facing location (Google Storage Bucket - +an online service for storing unstructured data). Second, [Read The Docs](readthedocs.com) +reacts to the github.com repository change, building the content for the [libpod documentation +site](https://podman.readthedocs.io/). This site includes for the API section, +some javascript which consumes the uploaded `swagger.yaml` file directly from the Google +Storage Bucket. + +Since there are multiple systems and local cache is involved, it's possible that updates to +documentation (especially the swagger/API docs) will lag by 10-or-so minutes. However, +because the client (i.e. your web browser) is fetching content from multiple locations that +do not share a common domain, accessing the API section may show a stack-trace similar to +the following: + +![Javascript Stack Trace Image](../contrib/cirrus/swagger_stack_trace.png) + +If reloading the page, or clearing your local cache does not fix the problem, it is +likely caused by broken metadata needed to protect clients from cross-site-scripting +style attacks. Please [notify a maintainer](https://github.com/containers/libpod#communications) +so they may investigate how/why the swagger.yaml file's CORS-metadata is incorrect. See +[the Cirrus-CI tasks documentation](../contrib/cirrus/README.md#docs-task) for +details regarding this situation. diff --git a/docs/source/markdown/podman-events.1.md b/docs/source/markdown/podman-events.1.md index a05047684..abfc6e9c1 100644 --- a/docs/source/markdown/podman-events.1.md +++ b/docs/source/markdown/podman-events.1.md @@ -15,6 +15,8 @@ value to `file`. Only `file` and `journald` are accepted. A `none` logger is al available but this logging mechanism completely disables events; nothing will be reported by `podman events`. +By default, streaming mode is used, printing new events as they occur. Previous events can be listed via `--since` and `--until`. + The *container* event type will report the follow statuses: * attach * checkpoint diff --git a/docs/source/markdown/podman-info.1.md b/docs/source/markdown/podman-info.1.md index 24ab97c91..19dd61c15 100644 --- a/docs/source/markdown/podman-info.1.md +++ b/docs/source/markdown/podman-info.1.md @@ -71,6 +71,9 @@ host: commit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657 spec: 1.0.1-dev os: linux + remoteSocket: + exists: false + path: /run/user/1000/podman/podman.sock rootless: true slirp4netns: executable: /bin/slirp4netns @@ -179,6 +182,10 @@ Run podman info with JSON formatted response: "version": "runc version 1.0.0-rc8+dev\ncommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657\nspec: 1.0.1-dev" }, "os": "linux", + "remoteSocket": { + "path": "/run/user/1000/podman/podman.sock", + "exists": false + }, "rootless": true, "slirp4netns": { "executable": "/bin/slirp4netns", diff --git a/docs/source/markdown/podman.1.md b/docs/source/markdown/podman.1.md index 02f23e6cc..8dc486b65 100644 --- a/docs/source/markdown/podman.1.md +++ b/docs/source/markdown/podman.1.md @@ -61,6 +61,9 @@ Podman and libpod currently support an additional `precreate` state which is cal **--identity**=*path* Path to SSH identity file +**--passphrase**=*secret* +pass phrase for SSH identity file + **--log-level**=*level* Log messages above specified level: debug, info, warn, error (default), fatal or panic (default: "error") @@ -73,18 +76,21 @@ When namespace is set, created containers and pods will join the given namespace **--network-cmd-path**=*path* Path to the command binary to use for setting up a network. It is currently only used for setting up a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable. -**--remote**, **-r**=*url* -URL to access Podman service (default "unix:/run/user/3267/podman/podman.sock") +**--remote**, **-r** +Access Podman service will be remote + +**--url**=*value* +URL to access Podman service (default from `containers.conf`, rootless "unix://run/user/$UID/podman/podman.sock" or as root "unix://run/podman/podman.sock). **--root**=*value* Storage root dir in which data, including images, is stored (default: "/var/lib/containers/storage" for UID 0, "$HOME/.local/share/containers/storage" for other users). -Default root dir is configured in `/etc/containers/storage.conf`. +Default root dir configured in `/etc/containers/storage.conf`. **--runroot**=*value* Storage state directory where all state information is stored (default: "/var/run/containers/storage" for UID 0, "/var/run/user/$UID/run" for other users). -Default state dir is configured in `/etc/containers/storage.conf`. +Default state dir configured in `/etc/containers/storage.conf`. **--runtime**=*value* @@ -11,11 +11,11 @@ require ( github.com/containernetworking/cni v0.7.2-0.20200304161608-4fae32b84921 github.com/containernetworking/plugins v0.8.6 github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224 - github.com/containers/common v0.12.0 - github.com/containers/conmon v2.0.16+incompatible + github.com/containers/common v0.13.0 + github.com/containers/conmon v2.0.17+incompatible github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 github.com/containers/psgo v1.5.1 - github.com/containers/storage v1.20.1 + github.com/containers/storage v1.20.2 github.com/coreos/go-systemd/v22 v22.1.0 github.com/cri-o/ocicni v0.2.0 github.com/cyphar/filepath-securejoin v0.2.2 @@ -33,7 +33,7 @@ require ( github.com/gorilla/schema v1.1.0 github.com/hashicorp/go-multierror v1.0.0 github.com/hpcloud/tail v1.0.0 - github.com/json-iterator/go v1.1.9 + github.com/json-iterator/go v1.1.10 github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 github.com/onsi/ginkgo v1.12.3 github.com/onsi/gomega v1.10.1 @@ -47,11 +47,11 @@ require ( github.com/pkg/errors v0.9.1 github.com/pmezard/go-difflib v1.0.0 github.com/rootless-containers/rootlesskit v0.9.5 - github.com/seccomp/containers-golang v0.4.1 + github.com/seccomp/containers-golang v0.5.0 github.com/sirupsen/logrus v1.6.0 github.com/spf13/cobra v0.0.7 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.6.0 + github.com/stretchr/testify v1.6.1 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/uber/jaeger-client-go v2.23.1+incompatible github.com/uber/jaeger-lib v2.2.0+incompatible // indirect @@ -70,10 +70,10 @@ github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHV github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224 h1:EqwBZRqyUYvU7JOmmSSPviSaAoUP1wN0cefXXDZ9ATo= github.com/containers/buildah v1.14.9-0.20200523094741-de0f541d9224/go.mod h1:5ZkWjOuK90yl55L5R+purJNLfUo0VUr8pstJazNtYck= github.com/containers/common v0.11.2/go.mod h1:2w3QE6VUmhltGYW4wV00h4okq1Crs7hNI1ZD2I0QRUY= -github.com/containers/common v0.12.0 h1:LR/sYyzFa22rFhfu6J9dEYhVkrWjagUigz/ewHhHL9s= -github.com/containers/common v0.12.0/go.mod h1:PKlahPDnQQYcXuIw5qq8mq6yNuCHBtgABphzy6pN0iI= -github.com/containers/conmon v2.0.16+incompatible h1:QFOlb9Id4WoJ24BelCFWwDSPTquwKMp3L3g2iGmRTq4= -github.com/containers/conmon v2.0.16+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= +github.com/containers/common v0.13.0 h1:+7FHpPNz3YR2YcVIVNnPg2sVrXytxNgNHbd3n7SosL0= +github.com/containers/common v0.13.0/go.mod h1:LJlijBz9zi7pJqZvlbxCOsw6qNn31rzb7Zo6NBJNQxU= +github.com/containers/conmon v2.0.17+incompatible h1:8BooocmNIwjOwAUGAoDD6fi3u0RrFyQ/fDkQzdiVtrI= +github.com/containers/conmon v2.0.17+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= github.com/containers/image/v5 v5.4.3/go.mod h1:pN0tvp3YbDd7BWavK2aE0mvJUqVd2HmhPjekyWSFm0U= github.com/containers/image/v5 v5.4.4/go.mod h1:g7cxNXitiLi6pEr9/L9n/0wfazRuhDKXU15kV86N8h8= github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 h1:K1ez+qAi9hCMHv/akPF4ddZumQTq/PBGf2Nzc7e+7lI= @@ -89,6 +89,8 @@ github.com/containers/storage v1.19.1/go.mod h1:KbXjSwKnx17ejOsjFcCXSf78mCgZkQSL github.com/containers/storage v1.19.2/go.mod h1:gYCp3jzgXkvubO0rI14QAjz5Mxm/qKJgLmHFyqayDnw= github.com/containers/storage v1.20.1 h1:2XE4eRIqSa6YjhAZjNwIkIKE6+Miy+5WV8l1KzY2ZKk= github.com/containers/storage v1.20.1/go.mod h1:RoKzO8KSDogCT6c06rEbanZTcKYxshorB33JikEGc3A= +github.com/containers/storage v1.20.2 h1:tw/uKRPDnmVrluIzer3dawTFG/bTJLP8IEUyHFhltYk= +github.com/containers/storage v1.20.2/go.mod h1:oOB9Ie8OVPojvoaKWEGSEtHbXUAs+tSyr7RO7ZGteMc= 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 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= @@ -246,8 +248,8 @@ github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBv 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.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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= @@ -257,6 +259,8 @@ github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqj github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.6 h1:SP6zavvTG3YjOosWePXFDlExpKIWMTO4SE/Y8MZB2vI= github.com/klauspost/compress v1.10.6/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= +github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.3/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= @@ -314,8 +318,6 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ 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.12.2 h1:Ke9m3h2Hu0wsZ45yewCqhYr3Z+emcNTuLY2nMWCkrSI= -github.com/onsi/ginkgo v1.12.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.12.3 h1:+RYp9QczoWz9zfUyLP/5SLXQVhfr6gZOoKGfQqHuLZQ= github.com/onsi/ginkgo v1.12.3/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= @@ -403,6 +405,8 @@ github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8q github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/seccomp/containers-golang v0.4.1 h1:6hsmsP8Y9T6PWKJELqAkRWkc6Te60+zK64avkjInd44= github.com/seccomp/containers-golang v0.4.1/go.mod h1:5fP9lgyYyklJ8fg8Geq193G1QLe0ikf34z+hZKIjmnE= +github.com/seccomp/containers-golang v0.5.0 h1:uUMOZIz/7TUiEO6h4ursAJY5JT55AzYiN/X5GOj9rvY= +github.com/seccomp/containers-golang v0.5.0/go.mod h1:5fP9lgyYyklJ8fg8Geq193G1QLe0ikf34z+hZKIjmnE= github.com/seccomp/libseccomp-golang v0.9.1 h1:NJjM5DNFOs0s3kYE1WUOr6G8V97sdt46rlXTMfXGWBo= 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= @@ -439,6 +443,8 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +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 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -560,6 +566,7 @@ golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/hack/golangci-lint.sh b/hack/golangci-lint.sh index f4e60d8f5..8c81a3743 100755 --- a/hack/golangci-lint.sh +++ b/hack/golangci-lint.sh @@ -5,12 +5,12 @@ declare -A BUILD_TAGS # TODO: add systemd tag BUILD_TAGS[default]="apparmor,seccomp,selinux" -BUILD_TAGS[abi]="${BUILD_TAGS[default]},ABISupport,varlink,!remoteclient" -BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},!ABISupport,varlink,remoteclient" +BUILD_TAGS[abi]="${BUILD_TAGS[default]},varlink,!remoteclient" +BUILD_TAGS[tunnel]="${BUILD_TAGS[default]},remote,varlink,remoteclient" declare -A SKIP_DIRS SKIP_DIRS[abi]="" -# TODO: add "ABISupport" build tag to pkg/api +# TODO: add "remote" build tag to pkg/api SKIP_DIRS[tunnel]="pkg/api" [[ $1 == run ]] && shift diff --git a/hack/install_bats.sh b/hack/install_bats.sh new file mode 100755 index 000000000..00ded07a9 --- /dev/null +++ b/hack/install_bats.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +set -e + +die() { echo "${1:-No error message given} (from $(basename $0))"; exit 1; } + +buildDir=$(mktemp -d) +git clone https://github.com/bats-core/bats-core $buildDir + +pushd $buildDir +pwd +git reset --hard ${VERSION} +./install.sh /usr/local +popd + +rm -rf $buildDir diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 43e873bd6..f6fc3c1a4 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -1209,13 +1209,35 @@ func (c *Container) stop(timeout uint) error { } } + // Check if conmon is still alive. + // If it is not, we won't be getting an exit file. + conmonAlive, err := c.ociRuntime.CheckConmonRunning(c) + if err != nil { + return err + } + if err := c.ociRuntime.StopContainer(c, timeout, all); err != nil { return err } + c.newContainerEvent(events.Stop) + c.state.PID = 0 c.state.ConmonPID = 0 c.state.StoppedByUser = true + + if !conmonAlive { + // Conmon is dead, so we can't epect an exit code. + c.state.ExitCode = -1 + c.state.FinishedTime = time.Now() + c.state.State = define.ContainerStateStopped + if err := c.save(); err != nil { + logrus.Errorf("Error saving container %s status: %v", c.ID(), err) + } + + return errors.Wrapf(define.ErrConmonDead, "container %s conmon process missing, cannot retrieve exit code", c.ID()) + } + if err := c.save(); err != nil { return errors.Wrapf(err, "error saving container %s state after stopping", c.ID()) } @@ -1225,8 +1247,6 @@ func (c *Container) stop(timeout uint) error { return err } - c.newContainerEvent(events.Stop) - return nil } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index d08e012a6..e3ca3f6b2 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -392,7 +392,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } for _, i := range c.config.Spec.Linux.Namespaces { - if i.Type == spec.UTSNamespace { + if i.Type == spec.UTSNamespace && i.Path == "" { hostname := c.Hostname() g.SetHostname(hostname) g.AddProcessEnv("HOSTNAME", hostname) @@ -591,7 +591,8 @@ func (c *Container) addNamespaceContainer(g *generate.Generator, ns LinuxNS, ctr if specNS == spec.UTSNamespace { hostname := nsCtr.Hostname() - g.SetHostname(hostname) + // Joining an existing namespace, cannot set the hostname + g.SetHostname("") g.AddProcessEnv("HOSTNAME", hostname) } @@ -1171,6 +1172,15 @@ func (c *Container) makeBindMounts() error { // finally, save it in the new container c.state.BindMounts["/etc/hosts"] = hostsPath } + + if !hasCurrentUserMapped(c) { + if err := makeAccessible(resolvPath, c.RootUID(), c.RootGID()); err != nil { + return err + } + if err := makeAccessible(hostsPath, c.RootUID(), c.RootGID()); err != nil { + return err + } + } } else { if !c.config.UseImageResolvConf { newResolv, err := c.generateResolvConf() diff --git a/libpod/container_log.go b/libpod/container_log.go index bfa303e84..c3a84d048 100644 --- a/libpod/container_log.go +++ b/libpod/container_log.go @@ -19,7 +19,7 @@ func (r *Runtime) Log(containers []*Container, options *logs.LogOptions, logChan return nil } -// ReadLog reads a containers log based on the input options and returns loglines over a channel +// ReadLog reads a containers log based on the input options and returns loglines over a channel. func (c *Container) ReadLog(options *logs.LogOptions, logChannel chan *logs.LogLine) error { // TODO Skip sending logs until journald logs can be read // TODO make this not a magic string @@ -61,7 +61,7 @@ func (c *Container) readFromLogFile(options *logs.LogOptions, logChannel chan *l partial += nll.Msg continue } else if !nll.Partial() && len(partial) > 1 { - nll.Msg = partial + nll.Msg = partial + nll.Msg partial = "" } nll.CID = c.ID() diff --git a/libpod/define/errors.go b/libpod/define/errors.go index 16df2a1cc..083553b7e 100644 --- a/libpod/define/errors.go +++ b/libpod/define/errors.go @@ -141,6 +141,9 @@ var ( // ErrConmonOutdated indicates the version of conmon found (whether via the configuration or $PATH) // is out of date for the current podman version ErrConmonOutdated = errors.New("outdated conmon version") + // ErrConmonDead indicates that the container's conmon process has been + // killed, preventing normal operation. + ErrConmonDead = errors.New("conmon process killed") // ErrImageInUse indicates the requested operation failed because the image was in use ErrImageInUse = errors.New("image is being used") diff --git a/libpod/define/info.go b/libpod/define/info.go index 906aa523f..f136936f7 100644 --- a/libpod/define/info.go +++ b/libpod/define/info.go @@ -27,6 +27,7 @@ type HostInfo struct { MemTotal int64 `json:"memTotal"` OCIRuntime *OCIRuntimeInfo `json:"ociRuntime"` OS string `json:"os"` + RemoteSocket *RemoteSocket `json:"remoteSocket,omitempty"` Rootless bool `json:"rootless"` RuntimeInfo map[string]interface{} `json:"runtimeInfo,omitempty"` Slirp4NetNS SlirpInfo `json:"slirp4netns,omitempty"` @@ -36,6 +37,12 @@ type HostInfo struct { Linkmode string `json:"linkmode"` } +// RemoteSocket describes information about the API socket +type RemoteSocket struct { + Path string `json:"path,omitempty"` + Exists bool `json:"exists,omitempty"` +} + // SlirpInfo describes the slirp exectuable that // is being being used. type SlirpInfo struct { diff --git a/libpod/filters/pods.go b/libpod/filters/pods.go index 9bf436eab..0edb9fbf2 100644 --- a/libpod/filters/pods.go +++ b/libpod/filters/pods.go @@ -57,13 +57,13 @@ func GeneratePodFilterFunc(filter, filterValue string) ( return nil, errors.Errorf("%s is not a valid status", filterValue) } return func(p *libpod.Pod) bool { - ctr_statuses, err := p.Status() + ctrStatuses, err := p.Status() if err != nil { return false } - for _, ctr_status := range ctr_statuses { - state := ctr_status.String() - if ctr_status == define.ContainerStateConfigured { + for _, ctrStatus := range ctrStatuses { + state := ctrStatus.String() + if ctrStatus == define.ContainerStateConfigured { state = "created" } if state == filterValue { diff --git a/libpod/image/filters.go b/libpod/image/filters.go index 747eba165..9d99fb344 100644 --- a/libpod/image/filters.go +++ b/libpod/image/filters.go @@ -102,8 +102,8 @@ func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter { } } -// IdFilter allows you to filter by image Id -func IdFilter(idFilter string) ResultFilter { +// IDFilter allows you to filter by image Id +func IDFilter(idFilter string) ResultFilter { return func(i *Image) bool { return i.ID() == idFilter } @@ -172,7 +172,7 @@ func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilt case "reference": filterFuncs = append(filterFuncs, ReferenceFilter(ctx, splitFilter[1])) case "id": - filterFuncs = append(filterFuncs, IdFilter(splitFilter[1])) + filterFuncs = append(filterFuncs, IDFilter(splitFilter[1])) default: return nil, errors.Errorf("invalid filter %s ", splitFilter[0]) } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 83344ebbe..0c9d28701 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -550,7 +550,7 @@ func getContainerNetNS(ctr *Container) (string, error) { if err = c.syncContainer(); err != nil { return "", err } - return c.state.NetNS.Path(), nil + return getContainerNetNS(c) } return "", nil } diff --git a/libpod/oci.go b/libpod/oci.go index 684a7ba42..c2f0041b1 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -107,6 +107,13 @@ type OCIRuntime interface { // error. CheckpointContainer(ctr *Container, options ContainerCheckpointOptions) error + // CheckConmonRunning verifies that the given container's Conmon + // instance is still running. Runtimes without Conmon, or systems where + // the PID of conmon is not available, should mock this as True. + // True indicates that Conmon for the instance is running, False + // indicates it is not. + CheckConmonRunning(ctr *Container) (bool, error) + // SupportsCheckpoint returns whether this OCI runtime // implementation supports the CheckpointContainer() operation. SupportsCheckpoint() bool diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go index 9c92b036e..0921a532b 100644 --- a/libpod/oci_conmon_linux.go +++ b/libpod/oci_conmon_linux.go @@ -669,6 +669,31 @@ func (r *ConmonOCIRuntime) CheckpointContainer(ctr *Container, options Container return utils.ExecCmdWithStdStreams(os.Stdin, os.Stdout, os.Stderr, nil, r.path, args...) } +func (r *ConmonOCIRuntime) CheckConmonRunning(ctr *Container) (bool, error) { + if ctr.state.ConmonPID == 0 { + // If the container is running or paused, assume Conmon is + // running. We didn't record Conmon PID on some old versions, so + // that is likely what's going on... + // Unusual enough that we should print a warning message though. + if ctr.ensureState(define.ContainerStateRunning, define.ContainerStatePaused) { + logrus.Warnf("Conmon PID is not set, but container is running!") + return true, nil + } + // Container's not running, so conmon PID being unset is + // expected. Conmon is not running. + return false, nil + } + + // We have a conmon PID. Ping it with signal 0. + if err := unix.Kill(ctr.state.ConmonPID, 0); err != nil { + if err == unix.ESRCH { + return false, nil + } + return false, errors.Wrapf(err, "error pinging container %s conmon with signal 0", ctr.ID()) + } + return true, nil +} + // SupportsCheckpoint checks if the OCI runtime supports checkpointing // containers. func (r *ConmonOCIRuntime) SupportsCheckpoint() bool { diff --git a/libpod/oci_missing.go b/libpod/oci_missing.go index 4da16876c..90e90cc6c 100644 --- a/libpod/oci_missing.go +++ b/libpod/oci_missing.go @@ -163,6 +163,11 @@ func (r *MissingRuntime) CheckpointContainer(ctr *Container, options ContainerCh return r.printError() } +// CheckConmonRunning is not available as the runtime is missing +func (r *MissingRuntime) CheckConmonRunning(ctr *Container) (bool, error) { + return false, r.printError() +} + // SupportsCheckpoint returns false as checkpointing requires a working runtime func (r *MissingRuntime) SupportsCheckpoint() bool { return false diff --git a/libpod/oci_util.go b/libpod/oci_util.go index 53567d2d0..8b40dad81 100644 --- a/libpod/oci_util.go +++ b/libpod/oci_util.go @@ -36,14 +36,30 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { var files []*os.File notifySCTP := false for _, i := range ports { + isV6 := net.ParseIP(i.HostIP).To4() == nil + if i.HostIP == "" { + isV6 = false + } switch i.Protocol { case "udp": - addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + 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") } - server, err := net.ListenUDP("udp", addr) + 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") } @@ -54,12 +70,24 @@ func bindPorts(ports []ocicni.PortMapping) ([]*os.File, error) { files = append(files, f) case "tcp": - addr, err := net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", i.HostIP, i.HostPort)) + 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") } - server, err := net.ListenTCP("tcp4", addr) + 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") } diff --git a/libpod/pod.go b/libpod/pod.go index 34ceef5ef..8afaa6052 100644 --- a/libpod/pod.go +++ b/libpod/pod.go @@ -171,6 +171,11 @@ func (p *Pod) SharesCgroup() bool { return p.config.UsePodCgroupNS } +// Hostname returns the hostname of the pod. +func (p *Pod) Hostname() string { + return p.config.Hostname +} + // CgroupPath returns the path to the pod's CGroup func (p *Pod) CgroupPath() (string, error) { p.lock.Lock() diff --git a/libpod/pod_api.go b/libpod/pod_api.go index 0be9f2573..e2c4b515d 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -490,7 +490,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) { Namespace: p.Namespace(), Created: p.CreatedTime(), State: podState, - Hostname: "", + Hostname: p.config.Hostname, Labels: p.Labels(), CreateCgroup: false, CgroupParent: p.CgroupParent(), diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index 655b42e51..aa91dff03 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -464,9 +464,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool, } } - // Check that the container's in a good state to be removed + // Check that the container's in a good state to be removed. if c.state.State == define.ContainerStateRunning { - if err := c.stop(c.StopTimeout()); err != nil { + // Ignore ErrConmonDead - we couldn't retrieve the container's + // exit code properly, but it's still stopped. + if err := c.stop(c.StopTimeout()); err != nil && errors.Cause(err) != define.ErrConmonDead { return errors.Wrapf(err, "cannot remove container %s as it could not be stopped", c.ID()) } } diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go index cea4bd0f6..b90f3d625 100644 --- a/pkg/api/handlers/compat/containers.go +++ b/pkg/api/handlers/compat/containers.go @@ -348,7 +348,7 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) { } func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error) { - imageId, imageName := l.Image() + imageID, imageName := l.Image() var ( err error @@ -378,7 +378,7 @@ func LibpodToContainer(l *libpod.Container, sz bool) (*handlers.Container, error ID: l.ID(), Names: []string{fmt.Sprintf("/%s", l.Name())}, Image: imageName, - ImageID: imageId, + ImageID: imageID, Command: strings.Join(l.Command(), " "), Created: l.CreatedTime().Unix(), Ports: nil, diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index b64ed0036..ce9ff1b19 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -224,7 +224,7 @@ func CreateImageFromSrc(w http.ResponseWriter, r *http.Request) { Status string `json:"status"` Progress string `json:"progress"` ProgressDetail map[string]string `json:"progressDetail"` - Id string `json:"id"` + Id string `json:"id"` //nolint }{ Status: iid, ProgressDetail: map[string]string{}, @@ -289,7 +289,7 @@ func CreateImageFromImage(w http.ResponseWriter, r *http.Request) { Error string `json:"error"` Progress string `json:"progress"` ProgressDetail map[string]string `json:"progressDetail"` - Id string `json:"id"` + Id string `json:"id"` //nolint }{ Status: fmt.Sprintf("pulling image (%s) from %s", img.Tag, strings.Join(img.Names(), ", ")), ProgressDetail: map[string]string{}, diff --git a/pkg/api/handlers/compat/images_build.go b/pkg/api/handlers/compat/images_build.go index e9d8fd719..6cc766a38 100644 --- a/pkg/api/handlers/compat/images_build.go +++ b/pkg/api/handlers/compat/images_build.go @@ -59,10 +59,10 @@ func BuildImage(w http.ResponseWriter, r *http.Request) { ForceRm bool `schema:"forcerm"` Memory int64 `schema:"memory"` MemSwap int64 `schema:"memswap"` - CpuShares uint64 `schema:"cpushares"` - CpuSetCpus string `schema:"cpusetcpus"` - CpuPeriod uint64 `schema:"cpuperiod"` - CpuQuota int64 `schema:"cpuquota"` + CpuShares uint64 `schema:"cpushares"` //nolint + CpuSetCpus string `schema:"cpusetcpus"` //nolint + CpuPeriod uint64 `schema:"cpuperiod"` //nolint + CpuQuota int64 `schema:"cpuquota"` //nolint BuildArgs string `schema:"buildargs"` ShmSize int `schema:"shmsize"` Squash bool `schema:"squash"` diff --git a/pkg/api/handlers/compat/info.go b/pkg/api/handlers/compat/info.go index e9756a03f..d4a933c54 100644 --- a/pkg/api/handlers/compat/info.go +++ b/pkg/api/handlers/compat/info.go @@ -177,7 +177,7 @@ func getContainersState(r *libpod.Runtime) map[define.ContainerStatus]int { if err != nil { continue } - states[state] += 1 + states[state]++ } } return states diff --git a/pkg/api/handlers/compat/networks.go b/pkg/api/handlers/compat/networks.go index c52ca093f..8734ba405 100644 --- a/pkg/api/handlers/compat/networks.go +++ b/pkg/api/handlers/compat/networks.go @@ -20,10 +20,6 @@ import ( "github.com/pkg/errors" ) -type CompatInspectNetwork struct { - types.NetworkResource -} - func InspectNetwork(w http.ResponseWriter, r *http.Request) { runtime := r.Context().Value("runtime").(*libpod.Runtime) diff --git a/pkg/api/handlers/compat/ping.go b/pkg/api/handlers/compat/ping.go index abee3d8e8..d275c4a02 100644 --- a/pkg/api/handlers/compat/ping.go +++ b/pkg/api/handlers/compat/ping.go @@ -14,13 +14,13 @@ import ( // Clients will use the Header availability to test which backend engine is in use. // Note: Additionally handler supports GET and HEAD methods func Ping(w http.ResponseWriter, r *http.Request) { - w.Header().Set("API-Version", utils.ApiVersion[utils.CompatTree][utils.CurrentApiVersion].String()) + w.Header().Set("API-Version", utils.APIVersion[utils.CompatTree][utils.CurrentAPIVersion].String()) w.Header().Set("BuildKit-Version", "") w.Header().Set("Docker-Experimental", "true") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Pragma", "no-cache") - w.Header().Set("Libpod-API-Version", utils.ApiVersion[utils.LibpodTree][utils.CurrentApiVersion].String()) + w.Header().Set("Libpod-API-Version", utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String()) w.Header().Set("Libpod-Buildha-Version", buildah.Version) w.WriteHeader(http.StatusOK) diff --git a/pkg/api/handlers/compat/version.go b/pkg/api/handlers/compat/version.go index bfc226bb8..3164b16b9 100644 --- a/pkg/api/handlers/compat/version.go +++ b/pkg/api/handlers/compat/version.go @@ -34,14 +34,14 @@ func VersionHandler(w http.ResponseWriter, r *http.Request) { Name: "Podman Engine", Version: versionInfo.Version, Details: map[string]string{ - "APIVersion": utils.ApiVersion[utils.LibpodTree][utils.CurrentApiVersion].String(), + "APIVersion": utils.APIVersion[utils.LibpodTree][utils.CurrentAPIVersion].String(), "Arch": goRuntime.GOARCH, "BuildTime": time.Unix(versionInfo.Built, 0).Format(time.RFC3339), "Experimental": "true", "GitCommit": versionInfo.GitCommit, "GoVersion": versionInfo.GoVersion, "KernelVersion": infoData.Host.Kernel, - "MinAPIVersion": utils.ApiVersion[utils.LibpodTree][utils.MinimalApiVersion].String(), + "MinAPIVersion": utils.APIVersion[utils.LibpodTree][utils.MinimalAPIVersion].String(), "Os": goRuntime.GOOS, }, }} diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go index 03b86275d..e46cd8837 100644 --- a/pkg/api/handlers/decoder.go +++ b/pkg/api/handlers/decoder.go @@ -17,7 +17,7 @@ func NewAPIDecoder() *schema.Decoder { d := schema.NewDecoder() d.IgnoreUnknownKeys(true) - d.RegisterConverter(map[string][]string{}, convertUrlValuesString) + d.RegisterConverter(map[string][]string{}, convertURLValuesString) d.RegisterConverter(time.Time{}, convertTimeString) var Signal syscall.Signal @@ -35,12 +35,12 @@ func NewAPIDecoder() *schema.Decoder { // panic(err) // } // payload = url.QueryEscape(payload) -func convertUrlValuesString(query string) reflect.Value { +func convertURLValuesString(query string) reflect.Value { f := map[string][]string{} err := json.Unmarshal([]byte(query), &f) if err != nil { - logrus.Infof("convertUrlValuesString: Failed to Unmarshal %s: %s", query, err.Error()) + logrus.Infof("convertURLValuesString: Failed to Unmarshal %s: %s", query, err.Error()) } return reflect.ValueOf(f) diff --git a/pkg/api/handlers/libpod/pods.go b/pkg/api/handlers/libpod/pods.go index c3f8d5d66..7d4d03144 100644 --- a/pkg/api/handlers/libpod/pods.go +++ b/pkg/api/handlers/libpod/pods.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func PodCreate(w http.ResponseWriter, r *http.Request) { @@ -31,11 +32,11 @@ func PodCreate(w http.ResponseWriter, r *http.Request) { } pod, err := generate.MakePod(&psg, runtime) if err != nil { - http_code := http.StatusInternalServerError + httpCode := http.StatusInternalServerError if errors.Cause(err) == define.ErrPodExists { - http_code = http.StatusConflict + httpCode = http.StatusConflict } - utils.Error(w, "Something went wrong.", http_code, err) + utils.Error(w, "Something went wrong.", httpCode, err) return } utils.WriteResponse(w, http.StatusCreated, handlers.IDResponse{ID: pod.ID()}) @@ -375,6 +376,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) { sig, err := util.ParseSignal(signal) if err != nil { utils.InternalServerError(w, errors.Wrapf(err, "unable to parse signal value")) + return } name := utils.GetName(r) pod, err := runtime.LookupPod(name) @@ -382,6 +384,7 @@ func PodKill(w http.ResponseWriter, r *http.Request) { utils.PodNotFound(w, name, err) return } + logrus.Debugf("Killing pod %s with signal %d", pod.ID(), sig) podStates, err := pod.Status() if err != nil { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, err) diff --git a/pkg/api/handlers/libpod/volumes.go b/pkg/api/handlers/libpod/volumes.go index c42ca407b..b5574b87b 100644 --- a/pkg/api/handlers/libpod/volumes.go +++ b/pkg/api/handlers/libpod/volumes.go @@ -46,7 +46,7 @@ func CreateVolume(w http.ResponseWriter, r *http.Request) { volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(input.Label)) } if len(input.Options) > 0 { - parsedOptions, err := parse.ParseVolumeOptions(input.Options) + parsedOptions, err := parse.VolumeOptions(input.Options) if err != nil { utils.InternalServerError(w, err) return diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index aa3d0fe91..79aeff2f8 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -334,16 +334,25 @@ func ImageDataToImageInspect(ctx context.Context, l *libpodImage.Image) (*ImageI func portsToPortSet(input map[string]struct{}) (nat.PortSet, error) { ports := make(nat.PortSet) for k := range input { - npTCP, err := nat.NewPort("tcp", k) - if err != nil { - return nil, errors.Wrapf(err, "unable to create tcp port from %s", k) - } - npUDP, err := nat.NewPort("udp", k) - if err != nil { - return nil, errors.Wrapf(err, "unable to create udp port from %s", k) + proto, port := nat.SplitProtoPort(k) + switch proto { + // See the OCI image spec for details: + // https://github.com/opencontainers/image-spec/blob/e562b04403929d582d449ae5386ff79dd7961a11/config.md#properties + case "tcp", "": + p, err := nat.NewPort("tcp", port) + if err != nil { + return nil, errors.Wrapf(err, "unable to create tcp port from %s", k) + } + ports[p] = struct{}{} + case "udp": + p, err := nat.NewPort("udp", port) + if err != nil { + return nil, errors.Wrapf(err, "unable to create tcp port from %s", k) + } + ports[p] = struct{}{} + default: + return nil, errors.Errorf("invalid port proto %q in %q", proto, k) } - ports[npTCP] = struct{}{} - ports[npUDP] = struct{}{} } return ports, nil } diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index a46b308b5..4bcac6e72 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -62,7 +62,7 @@ func WaitContainer(w http.ResponseWriter, r *http.Request) (int32, error) { func CreateContainer(ctx context.Context, w http.ResponseWriter, runtime *libpod.Runtime, cc *createconfig.CreateConfig) { var pod *libpod.Pod - ctr, err := createconfig.CreateContainerFromCreateConfig(runtime, cc, ctx, pod) + ctr, err := createconfig.CreateContainerFromCreateConfig(ctx, runtime, cc, pod) if err != nil { Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "CreateContainerFromCreateConfig()")) return diff --git a/pkg/api/handlers/utils/handler.go b/pkg/api/handlers/utils/handler.go index 2f4a54b98..62fdc05dd 100644 --- a/pkg/api/handlers/utils/handler.go +++ b/pkg/api/handlers/utils/handler.go @@ -28,27 +28,27 @@ const ( // CompatTree supports Libpod endpoints CompatTree - // CurrentApiVersion announces what is the current API level - CurrentApiVersion = VersionLevel(iota) - // MinimalApiVersion announces what is the oldest API level supported - MinimalApiVersion + // CurrentAPIVersion announces what is the current API level + CurrentAPIVersion = VersionLevel(iota) + // MinimalAPIVersion announces what is the oldest API level supported + MinimalAPIVersion ) var ( // See https://docs.docker.com/engine/api/v1.40/ // libpod compat handlers are expected to honor docker API versions - // ApiVersion provides the current and minimal API versions for compat and libpod endpoint trees + // APIVersion provides the current and minimal API versions for compat and libpod endpoint trees // Note: GET|HEAD /_ping is never versioned and provides the API-Version and Libpod-API-Version headers to allow // clients to shop for the Version they wish to support - ApiVersion = map[VersionTree]map[VersionLevel]semver.Version{ + APIVersion = map[VersionTree]map[VersionLevel]semver.Version{ LibpodTree: { - CurrentApiVersion: semver.MustParse("1.0.0"), - MinimalApiVersion: semver.MustParse("1.0.0"), + CurrentAPIVersion: semver.MustParse("1.0.0"), + MinimalAPIVersion: semver.MustParse("1.0.0"), }, CompatTree: { - CurrentApiVersion: semver.MustParse("1.40.0"), - MinimalApiVersion: semver.MustParse("1.24.0"), + CurrentAPIVersion: semver.MustParse("1.40.0"), + MinimalAPIVersion: semver.MustParse("1.24.0"), }, } @@ -103,8 +103,8 @@ func SupportedVersionWithDefaults(r *http.Request) (semver.Version, error) { } return SupportedVersion(r, - fmt.Sprintf(">=%s <=%s", ApiVersion[tree][MinimalApiVersion].String(), - ApiVersion[tree][CurrentApiVersion].String())) + fmt.Sprintf(">=%s <=%s", APIVersion[tree][MinimalAPIVersion].String(), + APIVersion[tree][CurrentAPIVersion].String())) } // WriteResponse encodes the given value as JSON or string and renders it for http client diff --git a/pkg/api/handlers/utils/handler_test.go b/pkg/api/handlers/utils/handler_test.go index 6009432b5..d9fd22b80 100644 --- a/pkg/api/handlers/utils/handler_test.go +++ b/pkg/api/handlers/utils/handler_test.go @@ -12,12 +12,12 @@ import ( func TestSupportedVersion(t *testing.T) { req, err := http.NewRequest("GET", - fmt.Sprintf("/v%s/libpod/testing/versions", ApiVersion[LibpodTree][CurrentApiVersion]), + fmt.Sprintf("/v%s/libpod/testing/versions", APIVersion[LibpodTree][CurrentAPIVersion]), nil) if err != nil { t.Fatal(err) } - req = mux.SetURLVars(req, map[string]string{"version": ApiVersion[LibpodTree][CurrentApiVersion].String()}) + req = mux.SetURLVars(req, map[string]string{"version": APIVersion[LibpodTree][CurrentAPIVersion].String()}) rr := httptest.NewRecorder() handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/handlers/utils/pods.go b/pkg/api/handlers/utils/pods.go index 5b6f6d34d..4a5cbd05c 100644 --- a/pkg/api/handlers/utils/pods.go +++ b/pkg/api/handlers/utils/pods.go @@ -54,7 +54,7 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport if err != nil { return nil, err } - infraId, err := pod.InfraContainerID() + infraID, err := pod.InfraContainerID() if err != nil { return nil, err } @@ -65,7 +65,7 @@ func GetPods(w http.ResponseWriter, r *http.Request) ([]*entities.ListPodsReport Name: pod.Name(), Namespace: pod.Namespace(), Status: status, - InfraId: infraId, + InfraId: infraID, Labels: pod.Labels(), } for _, ctr := range ctrs { diff --git a/pkg/api/server/handler_api.go b/pkg/api/server/handler_api.go index 7a7db12f3..dbdb7f17b 100644 --- a/pkg/api/server/handler_api.go +++ b/pkg/api/server/handler_api.go @@ -34,9 +34,9 @@ func (s *APIServer) APIHandler(h http.HandlerFunc) http.HandlerFunc { } // TODO: Use r.ConnContext when ported to go 1.13 - c := context.WithValue(r.Context(), "decoder", s.Decoder) - c = context.WithValue(c, "runtime", s.Runtime) - c = context.WithValue(c, "shutdownFunc", s.Shutdown) + c := context.WithValue(r.Context(), "decoder", s.Decoder) //nolint + c = context.WithValue(c, "runtime", s.Runtime) //nolint + c = context.WithValue(c, "shutdownFunc", s.Shutdown) //nolint r = r.WithContext(c) h(w, r) diff --git a/pkg/api/server/server.go b/pkg/api/server/server.go index 499a4c58a..bd6a99b96 100644 --- a/pkg/api/server/server.go +++ b/pkg/api/server/server.go @@ -257,7 +257,7 @@ func (t *IdleTracker) ConnState(conn net.Conn, state http.ConnState) { if oldActive == 0 { t.timer.Stop() } - t.total += 1 + t.total++ case http.StateIdle, http.StateClosed: delete(t.active, conn) // Restart the timer if we've become idle diff --git a/pkg/bindings/bindings.go b/pkg/bindings/bindings.go index 7e2a444bd..94f7a45d0 100644 --- a/pkg/bindings/bindings.go +++ b/pkg/bindings/bindings.go @@ -5,11 +5,16 @@ // This package exposes a series of methods that allow users to firstly // create their connection with the API endpoints. Once the connection // is established, users can then manage the Podman container runtime. - package bindings import ( + "errors" + "fmt" + "io" + "os" + "github.com/blang/semver" + "golang.org/x/crypto/ssh/terminal" ) var ( @@ -22,6 +27,43 @@ var ( pFalse = false PFalse = &pFalse - // _*YES*- podman will fail to run if this value is wrong + // APIVersion - podman will fail to run if this value is wrong APIVersion = semver.MustParse("1.0.0") ) + +// readPassword prompts for a secret and returns value input by user from stdin +// Unlike terminal.ReadPassword(), $(echo $SECRET | podman...) is supported. +// Additionally, all input after `<secret>/n` is queued to podman command. +func readPassword(prompt string) (pw []byte, err error) { + fd := int(os.Stdin.Fd()) + if terminal.IsTerminal(fd) { + fmt.Fprint(os.Stderr, prompt) + pw, err = terminal.ReadPassword(fd) + fmt.Fprintln(os.Stderr) + return + } + + var b [1]byte + for { + n, err := os.Stdin.Read(b[:]) + // terminal.ReadPassword discards any '\r', so we do the same + if n > 0 && b[0] != '\r' { + if b[0] == '\n' { + return pw, nil + } + pw = append(pw, b[0]) + // limit size, so that a wrong input won't fill up the memory + if len(pw) > 1024 { + err = errors.New("password too long, 1024 byte limit") + } + } + if err != nil { + // terminal.ReadPassword accepts EOF-terminated passwords + // if non-empty, so we do the same + if err == io.EOF && len(pw) > 0 { + err = nil + } + return pw, err + } + } +} diff --git a/pkg/bindings/connection.go b/pkg/bindings/connection.go index c26093a7f..aa7f3707c 100644 --- a/pkg/bindings/connection.go +++ b/pkg/bindings/connection.go @@ -13,6 +13,7 @@ import ( "path/filepath" "strconv" "strings" + "sync" "time" "github.com/blang/semver" @@ -20,6 +21,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" "k8s.io/client-go/util/homedir" ) @@ -29,6 +31,8 @@ var ( Host: "d", Path: "/v" + APIVersion.String() + "/libpod", } + passPhrase []byte + phraseSync sync.Once ) type APIResponse struct { @@ -37,7 +41,7 @@ type APIResponse struct { } type Connection struct { - Uri *url.URL + URI *url.URL Client *http.Client } @@ -61,6 +65,10 @@ func JoinURL(elements ...string) string { return "/" + strings.Join(elements, "/") } +func NewConnection(ctx context.Context, uri string) (context.Context, error) { + return NewConnectionWithIdentity(ctx, uri, "") +} + // NewConnection takes a URI as a string and returns a context with the // Connection embedded as a value. This context needs to be passed to each // endpoint to work correctly. @@ -69,23 +77,28 @@ func JoinURL(elements ...string) string { // For example tcp://localhost:<port> // or unix:///run/podman/podman.sock // or ssh://<user>@<host>[:port]/run/podman/podman.sock?secure=True -func NewConnection(ctx context.Context, uri string, identity ...string) (context.Context, error) { +func NewConnectionWithIdentity(ctx context.Context, uri string, passPhrase string, identities ...string) (context.Context, error) { var ( err error secure bool ) - if v, found := os.LookupEnv("PODMAN_HOST"); found { + if v, found := os.LookupEnv("CONTAINER_HOST"); found && uri == "" { uri = v } - if v, found := os.LookupEnv("PODMAN_SSHKEY"); found { - identity = []string{v} + if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found && len(identities) == 0 { + identities = append(identities, v) + } + + if v, found := os.LookupEnv("CONTAINER_PASSPHRASE"); found && passPhrase == "" { + passPhrase = v } _url, err := url.Parse(uri) if err != nil { - return nil, errors.Wrapf(err, "Value of PODMAN_HOST is not a valid url: %s", uri) + return nil, errors.Wrapf(err, "Value of CONTAINER_HOST is not a valid url: %s", uri) } + // TODO Fill in missing defaults for _url... // Now we setup the http Client to use the connection above var connection Connection @@ -95,7 +108,7 @@ func NewConnection(ctx context.Context, uri string, identity ...string) (context if err != nil { secure = false } - connection, err = sshClient(_url, identity[0], secure) + connection, err = sshClient(_url, secure, passPhrase, identities...) case "unix": if !strings.HasPrefix(uri, "unix:///") { // autofix unix://path_element vs unix:///path_element @@ -124,7 +137,7 @@ func NewConnection(ctx context.Context, uri string, identity ...string) (context func tcpClient(_url *url.URL) (Connection, error) { connection := Connection{ - Uri: _url, + URI: _url, } connection.Client = &http.Client{ Transport: &http.Transport{ @@ -172,10 +185,31 @@ func pingNewConnection(ctx context.Context) error { return errors.Errorf("ping response was %q", response.StatusCode) } -func sshClient(_url *url.URL, identity string, secure bool) (Connection, error) { - auth, err := publicKey(identity) - if err != nil { - return Connection{}, errors.Wrapf(err, "Failed to parse identity %s: %v\n", _url.String(), identity) +func sshClient(_url *url.URL, secure bool, passPhrase string, identities ...string) (Connection, error) { + var authMethods []ssh.AuthMethod + + for _, i := range identities { + auth, err := publicKey(i, []byte(passPhrase)) + if err != nil { + fmt.Fprint(os.Stderr, errors.Wrapf(err, "failed to parse identity %q", i).Error()+"\n") + continue + } + authMethods = append(authMethods, auth) + } + + if sock, found := os.LookupEnv("SSH_AUTH_SOCK"); found { + logrus.Debugf("Found SSH_AUTH_SOCK %q, ssh-agent signer enabled", sock) + + c, err := net.Dial("unix", sock) + if err != nil { + return Connection{}, err + } + a := agent.NewClient(c) + authMethods = append(authMethods, ssh.PublicKeysCallback(a.Signers)) + } + + if pw, found := _url.User.Password(); found { + authMethods = append(authMethods, ssh.Password(pw)) } callback := ssh.InsecureIgnoreHostKey() @@ -195,7 +229,7 @@ func sshClient(_url *url.URL, identity string, secure bool) (Connection, error) net.JoinHostPort(_url.Hostname(), port), &ssh.ClientConfig{ User: _url.User.Username(), - Auth: []ssh.AuthMethod{auth}, + Auth: authMethods, HostKeyCallback: callback, HostKeyAlgorithms: []string{ ssh.KeyAlgoRSA, @@ -212,7 +246,7 @@ func sshClient(_url *url.URL, identity string, secure bool) (Connection, error) return Connection{}, errors.Wrapf(err, "Connection to bastion host (%s) failed.", _url.String()) } - connection := Connection{Uri: _url} + connection := Connection{URI: _url} connection.Client = &http.Client{ Transport: &http.Transport{ DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { @@ -223,7 +257,7 @@ func sshClient(_url *url.URL, identity string, secure bool) (Connection, error) } func unixClient(_url *url.URL) (Connection, error) { - connection := Connection{Uri: _url} + connection := Connection{URI: _url} connection.Client = &http.Client{ Transport: &http.Transport{ DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { @@ -307,7 +341,7 @@ func (h *APIResponse) IsServerError() bool { return h.Response.StatusCode/100 == 5 } -func publicKey(path string) (ssh.AuthMethod, error) { +func publicKey(path string, passphrase []byte) (ssh.AuthMethod, error) { key, err := ioutil.ReadFile(path) if err != nil { return nil, err @@ -315,12 +349,30 @@ func publicKey(path string) (ssh.AuthMethod, error) { signer, err := ssh.ParsePrivateKey(key) if err != nil { - return nil, err + if _, ok := err.(*ssh.PassphraseMissingError); !ok { + return nil, err + } + if len(passphrase) == 0 { + phraseSync.Do(promptPassphrase) + passphrase = passPhrase + } + signer, err = ssh.ParsePrivateKeyWithPassphrase(key, passphrase) + if err != nil { + return nil, err + } } - return ssh.PublicKeys(signer), nil } +func promptPassphrase() { + phrase, err := readPassword("Key Passphrase: ") + if err != nil { + passPhrase = []byte{} + return + } + passPhrase = phrase +} + func hostKey(host string) ssh.PublicKey { // parse OpenSSH known_hosts file // ssh or use ssh-keyscan to get initial key diff --git a/pkg/bindings/containers/attach.go b/pkg/bindings/containers/attach.go index b7f35c30d..44c7f4002 100644 --- a/pkg/bindings/containers/attach.go +++ b/pkg/bindings/containers/attach.go @@ -25,7 +25,7 @@ import ( ) // Attach attaches to a running container -func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stream *bool, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool) error { +func Attach(ctx context.Context, nameOrID string, detachKeys *string, logs, stream *bool, stdin io.Reader, stdout io.Writer, stderr io.Writer, attachReady chan bool) error { isSet := struct { stdin bool stdout bool @@ -52,7 +52,7 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre } // Do we need to wire in stdin? - ctnr, err := Inspect(ctx, nameOrId, bindings.PFalse) + ctnr, err := Inspect(ctx, nameOrID, bindings.PFalse) if err != nil { return err } @@ -115,7 +115,7 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre IdleConnTimeout: time.Duration(0), } conn.Client.Transport = t - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/attach", params, headers, nameOrId) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/attach", params, headers, nameOrID) if err != nil { return err } @@ -129,7 +129,7 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre winCtx, winCancel := context.WithCancel(ctx) defer winCancel() - go attachHandleResize(ctx, winCtx, winChange, false, nameOrId, file) + go attachHandleResize(ctx, winCtx, winChange, false, nameOrID, file) } // If we are attaching around a start, we need to "signal" @@ -243,13 +243,13 @@ func DemuxFrame(r io.Reader, buffer []byte, length int) (frame []byte, err error } // ResizeContainerTTY sets container's TTY height and width in characters -func ResizeContainerTTY(ctx context.Context, nameOrId string, height *int, width *int) error { - return resizeTTY(ctx, bindings.JoinURL("containers", nameOrId, "resize"), height, width) +func ResizeContainerTTY(ctx context.Context, nameOrID string, height *int, width *int) error { + return resizeTTY(ctx, bindings.JoinURL("containers", nameOrID, "resize"), height, width) } // ResizeExecTTY sets session's TTY height and width in characters -func ResizeExecTTY(ctx context.Context, nameOrId string, height *int, width *int) error { - return resizeTTY(ctx, bindings.JoinURL("exec", nameOrId, "resize"), height, width) +func ResizeExecTTY(ctx context.Context, nameOrID string, height *int, width *int) error { + return resizeTTY(ctx, bindings.JoinURL("exec", nameOrID, "resize"), height, width) } // resizeTTY set size of TTY of container diff --git a/pkg/bindings/containers/checkpoint.go b/pkg/bindings/containers/checkpoint.go index f483a9297..916ec8071 100644 --- a/pkg/bindings/containers/checkpoint.go +++ b/pkg/bindings/containers/checkpoint.go @@ -10,9 +10,9 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -// Checkpoint checkpoints the given container (identified by nameOrId). All additional +// Checkpoint checkpoints the given container (identified by nameOrID). All additional // options are options and allow for more fine grained control of the checkpoint process. -func Checkpoint(ctx context.Context, nameOrId string, keep, leaveRunning, tcpEstablished, ignoreRootFS *bool, export *string) (*entities.CheckpointReport, error) { +func Checkpoint(ctx context.Context, nameOrID string, keep, leaveRunning, tcpEstablished, ignoreRootFS *bool, export *string) (*entities.CheckpointReport, error) { var report entities.CheckpointReport conn, err := bindings.GetClient(ctx) if err != nil { @@ -34,16 +34,16 @@ func Checkpoint(ctx context.Context, nameOrId string, keep, leaveRunning, tcpEst if export != nil { params.Set("export", *export) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/checkpoint", params, nil, nameOrID) if err != nil { return nil, err } return &report, response.Process(&report) } -// Restore restores a checkpointed container to running. The container is identified by the nameOrId option. All +// Restore restores a checkpointed container to running. The container is identified by the nameOrID option. All // additional options are optional and allow finer control of the restore processs. -func Restore(ctx context.Context, nameOrId string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) { +func Restore(ctx context.Context, nameOrID string, keep, tcpEstablished, ignoreRootFS, ignoreStaticIP, ignoreStaticMAC *bool, name, importArchive *string) (*entities.RestoreReport, error) { var report entities.RestoreReport conn, err := bindings.GetClient(ctx) if err != nil { @@ -71,7 +71,7 @@ func Restore(ctx context.Context, nameOrId string, keep, tcpEstablished, ignoreR if importArchive != nil { params.Set("import", *importArchive) } - response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodPost, "/containers/%s/restore", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/containers/commit.go b/pkg/bindings/containers/commit.go index 780d42272..1a9ddc970 100644 --- a/pkg/bindings/containers/commit.go +++ b/pkg/bindings/containers/commit.go @@ -10,16 +10,16 @@ import ( "github.com/containers/libpod/pkg/bindings" ) -// Commit creates a container image from a container. The container is defined by nameOrId. Use +// Commit creates a container image from a container. The container is defined by nameOrID. Use // the CommitOptions for finer grain control on characteristics of the resulting image. -func Commit(ctx context.Context, nameOrId string, options CommitOptions) (handlers.IDResponse, error) { +func Commit(ctx context.Context, nameOrID string, options CommitOptions) (handlers.IDResponse, error) { id := handlers.IDResponse{} conn, err := bindings.GetClient(ctx) if err != nil { return id, err } params := url.Values{} - params.Set("container", nameOrId) + params.Set("container", nameOrID) if options.Author != nil { params.Set("author", *options.Author) } diff --git a/pkg/bindings/containers/diff.go b/pkg/bindings/containers/diff.go index 06a828c30..e7a50248a 100644 --- a/pkg/bindings/containers/diff.go +++ b/pkg/bindings/containers/diff.go @@ -9,13 +9,13 @@ import ( ) // Diff provides the changes between two container layers -func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { +func Diff(ctx context.Context, nameOrID string) ([]archive.Change, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodGet, "/containers/%s/changes", nil, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/generate/generate.go b/pkg/bindings/generate/generate.go index 161b722f3..5e4be4896 100644 --- a/pkg/bindings/generate/generate.go +++ b/pkg/bindings/generate/generate.go @@ -10,7 +10,7 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { +func Kube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/bindings/images/diff.go b/pkg/bindings/images/diff.go index e2d344ea0..25cbde188 100644 --- a/pkg/bindings/images/diff.go +++ b/pkg/bindings/images/diff.go @@ -9,13 +9,13 @@ import ( ) // Diff provides the changes between two container layers -func Diff(ctx context.Context, nameOrId string) ([]archive.Change, error) { +func Diff(ctx context.Context, nameOrID string) ([]archive.Change, error) { conn, err := bindings.GetClient(ctx) if err != nil { return nil, err } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/changes", nil, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index e0802a6e1..a82a9080b 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -80,7 +80,7 @@ func GetImage(ctx context.Context, nameOrID string, size *bool) (*entities.Image } // Tree retrieves a "tree" based representation of the given image -func Tree(ctx context.Context, nameOrId string, whatRequires *bool) (*entities.ImageTreeReport, error) { +func Tree(ctx context.Context, nameOrID string, whatRequires *bool) (*entities.ImageTreeReport, error) { var report entities.ImageTreeReport conn, err := bindings.GetClient(ctx) if err != nil { @@ -90,7 +90,7 @@ func Tree(ctx context.Context, nameOrId string, whatRequires *bool) (*entities.I if whatRequires != nil { params.Set("size", strconv.FormatBool(*whatRequires)) } - response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrId) + response, err := conn.DoRequest(nil, http.MethodGet, "/images/%s/tree", params, nil, nameOrID) if err != nil { return nil, err } diff --git a/pkg/bindings/play/play.go b/pkg/bindings/play/play.go index 288cca454..9a4f56b6d 100644 --- a/pkg/bindings/play/play.go +++ b/pkg/bindings/play/play.go @@ -13,7 +13,7 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { +func Kube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { var report entities.PlayKubeReport conn, err := bindings.GetClient(ctx) if err != nil { diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index fd94d93be..c5e11f188 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -85,9 +85,9 @@ func (a psSortedCommand) Less(i, j int) bool { return strings.Join(a.SortListContainers[i].Command, " ") < strings.Join(a.SortListContainers[j].Command, " ") } -type psSortedId struct{ SortListContainers } +type psSortedID struct{ SortListContainers } -func (a psSortedId) Less(i, j int) bool { +func (a psSortedID) Less(i, j int) bool { return a.SortListContainers[i].ID < a.SortListContainers[j].ID } @@ -139,7 +139,7 @@ func (a PsSortedCreateTime) Less(i, j int) bool { func SortPsOutput(sortBy string, psOutput SortListContainers) (SortListContainers, error) { switch sortBy { case "id": - sort.Sort(psSortedId{psOutput}) + sort.Sort(psSortedID{psOutput}) case "image": sort.Sort(psSortedImage{psOutput}) case "command": diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 8d85a9b23..b4d8e6c29 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -56,7 +56,7 @@ type WaitOptions struct { } type WaitReport struct { - Id string + Id string //nolint Error error ExitCode int32 } @@ -76,7 +76,7 @@ type PauseUnPauseOptions struct { type PauseUnpauseReport struct { Err error - Id string + Id string //nolint } type StopOptions struct { @@ -84,12 +84,12 @@ type StopOptions struct { CIDFiles []string Ignore bool Latest bool - Timeout uint + Timeout *uint } type StopReport struct { Err error - Id string + Id string //nolint } type TopOptions struct { @@ -110,7 +110,7 @@ type KillOptions struct { type KillReport struct { Err error - Id string + Id string //nolint } type RestartOptions struct { @@ -122,7 +122,7 @@ type RestartOptions struct { type RestartReport struct { Err error - Id string + Id string //nolint } type RmOptions struct { @@ -137,7 +137,7 @@ type RmOptions struct { type RmReport struct { Err error - Id string + Id string //nolint } type ContainerInspectReport struct { @@ -157,7 +157,7 @@ type CommitOptions struct { } type CommitReport struct { - Id string + Id string //nolint } type ContainerExportOptions struct { @@ -176,7 +176,7 @@ type CheckpointOptions struct { type CheckpointReport struct { Err error - Id string + Id string //nolint } type RestoreOptions struct { @@ -193,11 +193,11 @@ type RestoreOptions struct { type RestoreReport struct { Err error - Id string + Id string //nolint } type ContainerCreateReport struct { - Id string + Id string //nolint } // AttachOptions describes the cli and other values @@ -263,7 +263,7 @@ type ContainerStartOptions struct { // ContainerStartReport describes the response from starting // containers from the cli type ContainerStartReport struct { - Id string + Id string //nolint RawInput string Err error ExitCode int @@ -303,7 +303,7 @@ type ContainerRunOptions struct { // a container type ContainerRunReport struct { ExitCode int - Id string + Id string //nolint } // ContainerCleanupOptions are the CLI values for the @@ -320,7 +320,7 @@ type ContainerCleanupOptions struct { // container cleanup type ContainerCleanupReport struct { CleanErr error - Id string + Id string //nolint RmErr error RmiErr error } @@ -336,7 +336,7 @@ type ContainerInitOptions struct { // container init type ContainerInitReport struct { Err error - Id string + Id string //nolint } //ContainerMountOptions describes the input values for mounting containers @@ -358,7 +358,7 @@ type ContainerUnmountOptions struct { // ContainerMountReport describes the response from container mount type ContainerMountReport struct { Err error - Id string + Id string //nolint Name string Path string } @@ -366,7 +366,7 @@ type ContainerMountReport struct { // ContainerUnmountReport describes the response from umounting a container type ContainerUnmountReport struct { Err error - Id string + Id string //nolint } // ContainerPruneOptions describes the options needed @@ -392,7 +392,7 @@ type ContainerPortOptions struct { // ContainerPortReport describes the output needed for // the CLI to output ports type ContainerPortReport struct { - Id string + Id string //nolint Ports []ocicni.PortMapping } diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index db58befa5..1f056bad7 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -39,18 +39,20 @@ type PodmanConfig struct { CGroupUsage string // rootless code determines Usage message ConmonPath string // --conmon flag will set Engine.ConmonPath - CpuProfile string // Hidden: Should CPU profile be taken + CPUProfile string // Hidden: Should CPU profile be taken EngineMode EngineMode // ABI or Tunneling mode Identities []string // ssh identities for connecting to server MaxWorks int // maximum number of parallel threads + PassPhrase string // ssh passphrase for identity for connecting to server RegistriesConf string // allows for specifying a custom registries.conf + Remote bool // Connection to Podman API Service will use RESTful API RuntimePath string // --runtime flag will set Engine.RuntimePath + Span opentracing.Span // tracing object SpanCloser io.Closer // Close() for tracing object SpanCtx context.Context // context to use when tracing - Span opentracing.Span // tracing object Syslog bool // write to StdOut and Syslog, not supported when tunneling Trace bool // Hidden: Trace execution - Uri string // URI to API Service + URI string // URI to RESTful API Service Runroot string StorageDriver string diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 3d5161745..979df7581 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -12,25 +12,25 @@ import ( type ContainerEngine interface { AutoUpdate(ctx context.Context, options AutoUpdateOptions) (*AutoUpdateReport, []error) Config(ctx context.Context) (*config.Config, error) - ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error + ContainerAttach(ctx context.Context, nameOrID string, options AttachOptions) error ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) - ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) + ContainerCommit(ctx context.Context, nameOrID string, options CommitOptions) (*CommitReport, error) ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) - ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error) - ContainerExec(ctx context.Context, nameOrId string, options ExecOptions, streams define.AttachStreams) (int, error) + ContainerDiff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) + ContainerExec(ctx context.Context, nameOrID string, options ExecOptions, streams define.AttachStreams) (int, error) ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error) - ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) - ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error + ContainerExists(ctx context.Context, nameOrID string) (*BoolReport, error) + ContainerExport(ctx context.Context, nameOrID string, options ContainerExportOptions) error ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error) ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error) ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error) ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error - ContainerMount(ctx context.Context, nameOrIds []string, options ContainerMountOptions) ([]*ContainerMountReport, error) + ContainerMount(ctx context.Context, nameOrIDs []string, options ContainerMountOptions) ([]*ContainerMountReport, error) ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) - ContainerPort(ctx context.Context, nameOrId string, options ContainerPortOptions) ([]*ContainerPortReport, error) + ContainerPort(ctx context.Context, nameOrID string, options ContainerPortOptions) ([]*ContainerPortReport, error) ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error) ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) @@ -41,14 +41,14 @@ type ContainerEngine interface { ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) error ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) - ContainerUnmount(ctx context.Context, nameOrIds []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error) + ContainerUnmount(ctx context.Context, nameOrIDs []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) Events(ctx context.Context, opts EventsOptions) error GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error) GenerateKube(ctx context.Context, nameOrID string, opts GenerateKubeOptions) (*GenerateKubeReport, error) SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error) - HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) + HealthCheckRun(ctx context.Context, nameOrID string, options HealthCheckOptions) (*define.HealthCheckResults, error) Info(ctx context.Context) (*define.Info, error) NetworkCreate(ctx context.Context, name string, options NetworkCreateOptions) (*NetworkCreateReport, error) NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error) @@ -56,7 +56,7 @@ type ContainerEngine interface { NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error) PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) - PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) + PodExists(ctx context.Context, nameOrID string) (*BoolReport, error) PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) @@ -75,7 +75,7 @@ type ContainerEngine interface { Unshare(ctx context.Context, args []string) error VarlinkService(ctx context.Context, opts ServiceOptions) error Version(ctx context.Context) (*SystemVersionReport, error) - VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error) + VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IDOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) VolumePrune(ctx context.Context, opts VolumePruneOptions) ([]*VolumePruneReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 7d7099838..60fb20b6e 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -9,9 +9,9 @@ import ( type ImageEngine interface { Build(ctx context.Context, containerFiles []string, opts BuildOptions) (*BuildReport, error) Config(ctx context.Context) (*config.Config, error) - Diff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error) - Exists(ctx context.Context, nameOrId string) (*BoolReport, error) - History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error) + Diff(ctx context.Context, nameOrID string, options DiffOptions) (*DiffReport, error) + Exists(ctx context.Context, nameOrID string) (*BoolReport, error) + History(ctx context.Context, nameOrID string, opts ImageHistoryOptions) (*ImageHistoryReport, error) Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) Inspect(ctx context.Context, namesOrIDs []string, opts InspectOptions) ([]*ImageInspectReport, error) List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) @@ -20,14 +20,14 @@ type ImageEngine interface { Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error) Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, []error) - Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error + Save(ctx context.Context, nameOrID string, tags []string, options ImageSaveOptions) error Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error) SetTrust(ctx context.Context, args []string, options SetTrustOptions) error 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 - Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error) - Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error + Tag(ctx context.Context, nameOrID string, tags []string, options ImageTagOptions) error + Tree(ctx context.Context, nameOrID string, options ImageTreeOptions) (*ImageTreeReport, error) + Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error ManifestCreate(ctx context.Context, names, images []string, opts ManifestCreateOptions) (string, error) ManifestInspect(ctx context.Context, name string) ([]byte, error) ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) diff --git a/pkg/domain/entities/filters.go b/pkg/domain/entities/filters.go index c7e227244..2ddbffbcd 100644 --- a/pkg/domain/entities/filters.go +++ b/pkg/domain/entities/filters.go @@ -20,14 +20,14 @@ type Names interface { Names() []string } -// IdOrName interface allows filters to access ID() or Name() of object -type IdOrNamed interface { +// IDOrName interface allows filters to access ID() or Name() of object +type IDOrNamed interface { Identifier Named } -// IdOrName interface allows filters to access ID() or Names() of object -type IdOrNames interface { +// IDOrName interface allows filters to access ID() or Names() of object +type IDOrNames interface { Identifier Names } @@ -42,11 +42,11 @@ func CompileImageFilters(filters url.Values) ImageFilter { for name, targets := range filters { switch name { case "id": - fns = append(fns, FilterIdFn(targets)) + fns = append(fns, FilterIDFn(targets)) case "name": fns = append(fns, FilterNamesFn(targets)) case "idOrName": - fns = append(fns, FilterIdOrNameFn(targets)) + fns = append(fns, FilterIDOrNameFn(targets)) } } @@ -66,11 +66,11 @@ func CompileContainerFilters(filters url.Values) ContainerFilter { for name, targets := range filters { switch name { case "id": - fns = append(fns, FilterIdFn(targets)) + fns = append(fns, FilterIDFn(targets)) case "name": fns = append(fns, FilterNameFn(targets)) case "idOrName": - fns = append(fns, FilterIdOrNameFn(targets)) + fns = append(fns, FilterIDOrNameFn(targets)) } } @@ -89,7 +89,7 @@ func CompileVolumeFilters(filters url.Values) VolumeFilter { for name, targets := range filters { if name == "id" { - fns = append(fns, FilterIdFn(targets)) + fns = append(fns, FilterIDFn(targets)) } } @@ -103,7 +103,7 @@ func CompileVolumeFilters(filters url.Values) VolumeFilter { } } -func FilterIdFn(id []string) func(Identifier) bool { +func FilterIDFn(id []string) func(Identifier) bool { return func(obj Identifier) bool { for _, v := range id { if strings.Contains(obj.Id(), v) { @@ -138,8 +138,8 @@ func FilterNamesFn(name []string) func(Names) bool { } } -func FilterIdOrNameFn(id []string) func(IdOrNamed) bool { - return func(obj IdOrNamed) bool { +func FilterIDOrNameFn(id []string) func(IDOrNamed) bool { + return func(obj IDOrNamed) bool { for _, v := range id { if strings.Contains(obj.Id(), v) || strings.Contains(obj.Name(), v) { return true diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 19a2c87f5..81f52fef5 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -45,13 +45,13 @@ type Image struct { HealthCheck *manifest.Schema2HealthConfig `json:",omitempty"` } -func (i *Image) Id() string { +func (i *Image) Id() string { //nolint return i.ID } type ImageSummary struct { ID string `json:"Id"` - ParentId string `json:",omitempty"` + ParentId string `json:",omitempty"` // nolint RepoTags []string `json:",omitempty"` Created time.Time `json:",omitempty"` Size int64 `json:",omitempty"` @@ -70,7 +70,7 @@ type ImageSummary struct { History []string `json:",omitempty"` } -func (i *ImageSummary) Id() string { +func (i *ImageSummary) Id() string { //nolint return i.ID } @@ -266,7 +266,7 @@ type ImageImportOptions struct { } type ImageImportReport struct { - Id string + Id string //nolint } type ImageSaveOptions struct { @@ -299,7 +299,7 @@ type ShowTrustReport struct { Raw []byte SystemRegistriesDirPath string JSONOutput []byte - Policies []*trust.TrustPolicy + Policies []*trust.Policy } // SetTrustOptions describes the CLI options for setting trust diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index 37acba6e6..a85333c75 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -17,15 +17,15 @@ type PodKillOptions struct { type PodKillReport struct { Errs []error - Id string + Id string //nolint } type ListPodsReport struct { Cgroup string Containers []*ListPodContainer Created time.Time - Id string - InfraId string + Id string //nolint + InfraId string //nolint Name string Namespace string Status string @@ -33,7 +33,7 @@ type ListPodsReport struct { } type ListPodContainer struct { - Id string + Id string //nolint Names string Status string } @@ -45,7 +45,7 @@ type PodPauseOptions struct { type PodPauseReport struct { Errs []error - Id string + Id string //nolint } type PodunpauseOptions struct { @@ -55,7 +55,7 @@ type PodunpauseOptions struct { type PodUnpauseReport struct { Errs []error - Id string + Id string //nolint } type PodStopOptions struct { @@ -67,7 +67,7 @@ type PodStopOptions struct { type PodStopReport struct { Errs []error - Id string + Id string //nolint } type PodRestartOptions struct { @@ -77,7 +77,7 @@ type PodRestartOptions struct { type PodRestartReport struct { Errs []error - Id string + Id string //nolint } type PodStartOptions struct { @@ -87,7 +87,7 @@ type PodStartOptions struct { type PodStartReport struct { Errs []error - Id string + Id string //nolint } type PodRmOptions struct { @@ -99,7 +99,7 @@ type PodRmOptions struct { type PodRmReport struct { Err error - Id string + Id string //nolint } type PodCreateOptions struct { @@ -115,7 +115,7 @@ type PodCreateOptions struct { } type PodCreateReport struct { - Id string + Id string //nolint } func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { @@ -155,7 +155,7 @@ type PodPruneOptions struct { type PodPruneReport struct { Err error - Id string + Id string //nolint } type PodTopOptions struct { diff --git a/pkg/domain/entities/set.go b/pkg/domain/entities/set.go index c8d6cb1a9..1d31d82f9 100644 --- a/pkg/domain/entities/set.go +++ b/pkg/domain/entities/set.go @@ -4,12 +4,12 @@ import ( "strings" ) -type stringSet struct { +type StringSet struct { m map[string]struct{} } -func NewStringSet(elem ...string) *stringSet { - s := &stringSet{} +func NewStringSet(elem ...string) *StringSet { + s := &StringSet{} s.m = make(map[string]struct{}, len(elem)) for _, e := range elem { s.Add(e) @@ -17,20 +17,20 @@ func NewStringSet(elem ...string) *stringSet { return s } -func (s *stringSet) Add(elem string) { +func (s *StringSet) Add(elem string) { s.m[elem] = struct{}{} } -func (s *stringSet) Remove(elem string) { +func (s *StringSet) Remove(elem string) { delete(s.m, elem) } -func (s *stringSet) Contains(elem string) bool { +func (s *StringSet) Contains(elem string) bool { _, ok := s.m[elem] return ok } -func (s *stringSet) Elements() []string { +func (s *StringSet) Elements() []string { keys := make([]string, len(s.m)) i := 0 for k := range s.m { @@ -40,6 +40,6 @@ func (s *stringSet) Elements() []string { return keys } -func (s *stringSet) String() string { +func (s *StringSet) String() string { return strings.Join(s.Elements(), ", ") } diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index 21ab025de..622f74838 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -11,7 +11,7 @@ import ( ) type Container struct { - IdOrNamed + IDOrNamed } type Volume struct { @@ -19,7 +19,7 @@ type Volume struct { } type Report struct { - Id []string + Id []string //nolint Err map[string]error } diff --git a/pkg/domain/entities/volumes.go b/pkg/domain/entities/volumes.go index 23c066083..7cf7d82a2 100644 --- a/pkg/domain/entities/volumes.go +++ b/pkg/domain/entities/volumes.go @@ -16,9 +16,9 @@ type VolumeCreateOptions struct { Options map[string]string `schema:"opts"` } -type IdOrNameResponse struct { +type IDOrNameResponse struct { // The Id or Name of an object - IdOrName string + IDOrName string } type VolumeConfigResponse struct { @@ -63,7 +63,7 @@ type VolumeRmOptions struct { type VolumeRmReport struct { Err error - Id string + Id string //nolint } type VolumeInspectOptions struct { @@ -80,7 +80,7 @@ type VolumePruneOptions struct { type VolumePruneReport struct { Err error - Id string + Id string //nolint } type VolumeListOptions struct { diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 706fcec47..4d6d0d59a 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -23,6 +23,7 @@ import ( "github.com/containers/libpod/pkg/checkpoint" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi/terminal" + "github.com/containers/libpod/pkg/parallel" "github.com/containers/libpod/pkg/ps" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/signal" @@ -44,8 +45,10 @@ func getContainersAndInputByContext(all, latest bool, names []string, runtime *l ctrs, err = runtime.GetAllContainers() case latest: ctr, err = runtime.GetLatestContainer() - rawInput = append(rawInput, ctr.ID()) - ctrs = append(ctrs, ctr) + if err == nil { + rawInput = append(rawInput, ctr.ID()) + ctrs = append(ctrs, ctr) + } default: for _, n := range names { ctr, e := runtime.LookupContainer(n) @@ -72,8 +75,8 @@ func getContainersByContext(all, latest bool, names []string, runtime *libpod.Ru } // TODO: Should return *entities.ContainerExistsReport, error -func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { - _, err := ic.Libpod.LookupContainer(nameOrId) +func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + _, err := ic.Libpod.LookupContainer(nameOrID) if err != nil && errors.Cause(err) != define.ErrNoSuchCtr { return nil, err } @@ -159,26 +162,33 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { return nil, err } - for _, con := range ctrs { - report := entities.StopReport{Id: con.ID()} - err = con.StopWithTimeout(options.Timeout) + errMap, err := parallel.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { + var err error + if options.Timeout != nil { + err = c.StopWithTimeout(*options.Timeout) + } else { + err = c.Stop() + } if err != nil { - // These first two are considered non-fatal under the right conditions - if errors.Cause(err) == define.ErrCtrStopped { - logrus.Debugf("Container %s is already stopped", con.ID()) - reports = append(reports, &report) - continue - - } else if options.All && errors.Cause(err) == define.ErrCtrStateInvalid { - logrus.Debugf("Container %s is not running, could not stop", con.ID()) - reports = append(reports, &report) - continue + switch { + case errors.Cause(err) == define.ErrCtrStopped: + logrus.Debugf("Container %s is already stopped", c.ID()) + case options.All && errors.Cause(err) == define.ErrCtrStateInvalid: + logrus.Debugf("Container %s is not running, could not stop", c.ID()) + default: + return err } - report.Err = err - reports = append(reports, &report) - continue } - reports = append(reports, &report) + return c.Cleanup(ctx) + }) + if err != nil { + return nil, err + } + for ctr, err := range errMap { + report := new(entities.StopReport) + report.Id = ctr.ID() + report.Err = err + reports = append(reports, report) } return reports, nil } @@ -313,21 +323,25 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return reports, nil } - for _, c := range ctrs { - report := entities.RmReport{Id: c.ID()} + errMap, err := parallel.ContainerOp(ctx, ctrs, func(c *libpod.Container) error { err := ic.Libpod.RemoveContainer(ctx, c, options.Force, options.Volumes) if err != nil { if options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr { logrus.Debugf("Ignoring error (--allow-missing): %v", err) - reports = append(reports, &report) - continue + return nil } logrus.Debugf("Failed to remove container %s: %s", c.ID(), err.Error()) - report.Err = err - reports = append(reports, &report) - continue } - reports = append(reports, &report) + return err + }) + if err != nil { + return nil, err + } + for ctr, err := range errMap { + report := new(entities.RmReport) + report.Id = ctr.ID() + report.Err = err + reports = append(reports, report) } return reports, nil } @@ -370,11 +384,11 @@ func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.To return report, err } -func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, options entities.CommitOptions) (*entities.CommitReport, error) { +func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) { var ( mimeType string ) - ctr, err := ic.Libpod.LookupContainer(nameOrId) + ctr, err := ic.Libpod.LookupContainer(nameOrID) if err != nil { return nil, err } @@ -415,8 +429,8 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, return &entities.CommitReport{Id: newImage.ID()}, nil } -func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error { - ctr, err := ic.Libpod.LookupContainer(nameOrId) +func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error { + ctr, err := ic.Libpod.LookupContainer(nameOrID) if err != nil { return err } @@ -514,8 +528,8 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: ctr.ID()}, nil } -func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { - ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) +func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) if err != nil { return err } @@ -577,12 +591,12 @@ func checkExecPreserveFDs(options entities.ExecOptions) (int, error) { return ec, nil } -func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { ec, err := checkExecPreserveFDs(options) if err != nil { return ec, err } - ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) if err != nil { return ec, err } @@ -594,12 +608,12 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o return define.TranslateExecErrorToExitCode(ec, err), err } -func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrId string, options entities.ExecOptions) (string, error) { +func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { _, err := checkExecPreserveFDs(options) if err != nil { return "", err } - ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrID}, ic.Libpod) if err != nil { return "", err } @@ -753,15 +767,15 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C } // ContainerDiff provides changes to given container -func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, opts entities.DiffOptions) (*entities.DiffReport, error) { +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, opts entities.DiffOptions) (*entities.DiffReport, error) { if opts.Latest { ctnr, err := ic.Libpod.GetLatestContainer() if err != nil { return nil, errors.Wrap(err, "unable to get latest container") } - nameOrId = ctnr.ID() + nameOrID = ctnr.ID() } - changes, err := ic.Libpod.GetDiff("", nameOrId) + changes, err := ic.Libpod.GetDiff("", nameOrID) return &entities.DiffReport{Changes: changes}, err } @@ -963,7 +977,7 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { +func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { if os.Geteuid() != 0 { if driver := ic.Libpod.StorageConfig().GraphDriverName; driver != "vfs" { // Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part @@ -980,7 +994,7 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []strin } } var reports []*entities.ContainerMountReport - ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIds, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod) if err != nil { return nil, err } @@ -1015,9 +1029,9 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { +func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { var reports []*entities.ContainerUnmountReport - ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIds, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIDs, ic.Libpod) if err != nil { return nil, err } @@ -1050,9 +1064,9 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { return ic.Libpod.GetConfig() } -func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { +func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { var reports []*entities.ContainerPortReport - ctrs, err := getContainersByContext(options.All, options.Latest, []string{nameOrId}, ic.Libpod) + ctrs, err := getContainersByContext(options.All, options.Latest, []string{nameOrID}, ic.Libpod) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go index 4e925ef56..dfa9a6fa5 100644 --- a/pkg/domain/infra/abi/healthcheck.go +++ b/pkg/domain/infra/abi/healthcheck.go @@ -7,8 +7,8 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { - status, err := ic.Libpod.HealthCheck(nameOrId) +func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrID string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { + status, err := ic.Libpod.HealthCheck(nameOrID) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index d8af4d339..67f331aac 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -38,8 +38,8 @@ import ( // SignatureStoreDir defines default directory to store signatures const SignatureStoreDir = "/var/lib/containers/sigstore" -func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { - _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) +func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { + _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil && errors.Cause(err) != define.ErrNoSuchImage { return nil, err } @@ -65,8 +65,8 @@ func (ir *ImageEngine) pruneImagesHelper(ctx context.Context, all bool, filters return &report, nil } -func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { - image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) +func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { + image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return nil, err } @@ -261,8 +261,8 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri nil) } -// func (r *imageRuntime) Delete(ctx context.Context, nameOrId string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { -// image, err := r.libpod.ImageEngine().NewFromLocal(nameOrId) +// func (r *imageRuntime) Delete(ctx context.Context, nameOrID string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { +// image, err := r.libpod.ImageEngine().NewFromLocal(nameOrID) // if err != nil { // return nil, err // } @@ -292,8 +292,8 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri // return &report, nil // } -func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error { - newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) +func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return err } @@ -305,8 +305,8 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, return nil } -func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error { - newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) +func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return err } @@ -356,16 +356,16 @@ func (ir *ImageEngine) Import(ctx context.Context, opts entities.ImageImportOpti return &entities.ImageImportReport{Id: id}, nil } -func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error { - newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) +func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { + newImage, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return err } - return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress) + return newImage.Save(ctx, nameOrID, options.Format, options.Output, tags, options.Quiet, options.Compress) } -func (ir *ImageEngine) Diff(_ context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := ir.Libpod.GetDiff("", nameOrId) +func (ir *ImageEngine) Diff(_ context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := ir.Libpod.GetDiff("", nameOrID) if err != nil { return nil, err } @@ -420,8 +420,8 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts return &entities.BuildReport{ID: id}, nil } -func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { - img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) +func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { + img, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrID) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 6e311dec7..a2b5fc0fc 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -1,4 +1,4 @@ -// +build ABISupport +// +build !remote package abi diff --git a/pkg/domain/infra/abi/parse/parse.go b/pkg/domain/infra/abi/parse/parse.go index 6c0e1ee55..2320c6a32 100644 --- a/pkg/domain/infra/abi/parse/parse.go +++ b/pkg/domain/infra/abi/parse/parse.go @@ -12,7 +12,7 @@ import ( // Handle volume options from CLI. // Parse "o" option to find UID, GID. -func ParseVolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) { +func VolumeOptions(opts map[string]string) ([]libpod.VolumeCreateOption, error) { libpodOptions := []libpod.VolumeCreateOption{} volumeOptions := make(map[string]string) diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go index e06251ca7..f5b93c51b 100644 --- a/pkg/domain/infra/abi/play.go +++ b/pkg/domain/infra/abi/play.go @@ -297,7 +297,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY if err != nil { return nil, err } - ctr, err := createconfig.CreateContainerFromCreateConfig(ic.Libpod, conf, ctx, pod) + ctr, err := createconfig.CreateContainerFromCreateConfig(ctx, ic.Libpod, conf, pod) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index 320880920..eb6f1e191 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -45,8 +45,8 @@ func getPodsByContext(all, latest bool, pods []string, runtime *libpod.Runtime) return outpods, err } -func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { - _, err := ic.Libpod.LookupPod(nameOrId) +func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + _, err := ic.Libpod.LookupPod(nameOrID) if err != nil && errors.Cause(err) != define.ErrNoSuchPod { return nil, err } @@ -347,7 +347,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti Status: state.String(), }) } - infraId, err := p.InfraContainerID() + infraID, err := p.InfraContainerID() if err != nil { return nil, err } @@ -356,7 +356,7 @@ func (ic *ContainerEngine) PodPs(ctx context.Context, options entities.PodPSOpti Containers: lpcs, Created: p.CreatedTime(), Id: p.ID(), - InfraId: infraId, + InfraId: infraID, Name: p.Name(), Namespace: p.Namespace(), Status: status, diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 52dfaba7d..b91dd513d 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -25,7 +25,38 @@ import ( ) func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { - return ic.Libpod.Info() + info, err := ic.Libpod.Info() + if err != nil { + return nil, err + } + xdg, err := util.GetRuntimeDir() + if err != nil { + return nil, err + } + if len(xdg) == 0 { + // If no xdg is returned, assume root socket + xdg = "/run" + } + + // Glue the socket path together + socketPath := filepath.Join(xdg, "podman", "podman.sock") + rs := define.RemoteSocket{ + Path: socketPath, + Exists: false, + } + + // Check if the socket exists + if fi, err := os.Stat(socketPath); err == nil { + if fi.Mode()&os.ModeSocket != 0 { + rs.Exists = true + } + } + // TODO + // it was suggested future versions of this could perform + // a ping on the socket for greater confidence the socket is + // actually active. + info.Host.RemoteSocket = &rs + return info, err } func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) error { @@ -307,7 +338,7 @@ func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.System } for _, viu := range inUse { if util.StringInSlice(viu, runningContainers) { - consInUse += 1 + consInUse++ } } report := entities.SystemDfVolumeReport{ @@ -345,12 +376,12 @@ func (se *SystemEngine) Renumber(ctx context.Context, flags *pflag.FlagSet, conf return nil } -func (s SystemEngine) Migrate(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig, options entities.SystemMigrateOptions) error { +func (se SystemEngine) Migrate(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig, options entities.SystemMigrateOptions) error { return nil } -func (s SystemEngine) Shutdown(ctx context.Context) { - if err := s.Libpod.Shutdown(false); err != nil { +func (se SystemEngine) Shutdown(ctx context.Context) { + if err := se.Libpod.Shutdown(false); err != nil { logrus.Error(err) } } diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go index 5b89c91d9..03986ad0e 100644 --- a/pkg/domain/infra/abi/trust.go +++ b/pkg/domain/infra/abi/trust.go @@ -112,8 +112,8 @@ func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options enti return ioutil.WriteFile(policyPath, data, 0644) } -func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.TrustPolicy, error) { - var output []*trust.TrustPolicy +func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.Policy, error) { + var output []*trust.Policy registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) if err != nil { @@ -121,7 +121,7 @@ func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistri } if len(policyContentStruct.Default) > 0 { - defaultPolicyStruct := trust.TrustPolicy{ + defaultPolicyStruct := trust.Policy{ Name: "* (default)", RepoName: "default", Type: trustTypeDescription(policyContentStruct.Default[0].Type), @@ -130,7 +130,7 @@ func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistri } for _, transval := range policyContentStruct.Transports { for repo, repoval := range transval { - tempTrustShowOutput := trust.TrustPolicy{ + tempTrustShowOutput := trust.Policy{ Name: repo, RepoName: repo, Type: repoval[0].Type, diff --git a/pkg/domain/infra/abi/volumes.go b/pkg/domain/infra/abi/volumes.go index 91b2440df..a311e0c4e 100644 --- a/pkg/domain/infra/abi/volumes.go +++ b/pkg/domain/infra/abi/volumes.go @@ -10,7 +10,7 @@ import ( "github.com/pkg/errors" ) -func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IdOrNameResponse, error) { +func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) { var ( volumeOptions []libpod.VolumeCreateOption ) @@ -24,7 +24,7 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(opts.Label)) } if len(opts.Options) > 0 { - parsedOptions, err := parse.ParseVolumeOptions(opts.Options) + parsedOptions, err := parse.VolumeOptions(opts.Options) if err != nil { return nil, err } @@ -34,7 +34,7 @@ func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.Volum if err != nil { return nil, err } - return &entities.IdOrNameResponse{IdOrName: vol.Name()}, nil + return &entities.IDOrNameResponse{IDOrName: vol.Name()}, nil } func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) { diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go index 67c1cd534..60d0c6e86 100644 --- a/pkg/domain/infra/runtime_abi.go +++ b/pkg/domain/infra/runtime_abi.go @@ -1,4 +1,4 @@ -// +build ABISupport +// +build !remote package infra @@ -20,7 +20,7 @@ func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine, r, err := NewLibpodRuntime(facts.FlagSet, facts) return r, err case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), facts.Uri, facts.Identities...) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.PassPhrase, facts.Identities...) return &tunnel.ContainerEngine{ClientCxt: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) @@ -33,7 +33,7 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) r, err := NewLibpodImageRuntime(facts.FlagSet, facts) return r, err case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), facts.Uri, facts.Identities...) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.PassPhrase, facts.Identities...) return &tunnel.ImageEngine{ClientCxt: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) diff --git a/pkg/domain/infra/runtime_abi_unsupported.go b/pkg/domain/infra/runtime_abi_unsupported.go index c4e25e990..3d7d457fc 100644 --- a/pkg/domain/infra/runtime_abi_unsupported.go +++ b/pkg/domain/infra/runtime_abi_unsupported.go @@ -1,4 +1,4 @@ -// +build !ABISupport +// +build remote package infra diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index a57eadc63..2f2b0f90f 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -1,4 +1,4 @@ -// +build ABISupport +// +build !remote package infra diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go index e7002e20f..fed9b1008 100644 --- a/pkg/domain/infra/runtime_proxy.go +++ b/pkg/domain/infra/runtime_proxy.go @@ -1,4 +1,4 @@ -// +build ABISupport +// +build !remote package infra diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go index 752218aaf..24a93b888 100644 --- a/pkg/domain/infra/runtime_tunnel.go +++ b/pkg/domain/infra/runtime_tunnel.go @@ -1,4 +1,4 @@ -// +build !ABISupport +// +build remote package infra @@ -16,7 +16,7 @@ func NewContainerEngine(facts *entities.PodmanConfig) (entities.ContainerEngine, case entities.ABIMode: return nil, fmt.Errorf("direct runtime not supported") case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), facts.Uri, facts.Identities...) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.PassPhrase, facts.Identities...) return &tunnel.ContainerEngine{ClientCxt: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) @@ -28,7 +28,7 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) case entities.ABIMode: return nil, fmt.Errorf("direct image runtime not supported") case entities.TunnelMode: - ctx, err := bindings.NewConnection(context.Background(), facts.Uri, facts.Identities...) + ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.PassPhrase, facts.Identities...) return &tunnel.ImageEngine{ClientCxt: ctx}, err } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index af9d94293..68a8b0329 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "io/ioutil" "os" "strconv" "strings" @@ -25,8 +26,8 @@ func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, return errors.New("not implemented") } -func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { - exists, err := containers.Exists(ic.ClientCxt, nameOrId) +func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + exists, err := containers.Exists(ic.ClientCxt, nameOrID) return &entities.BoolReport{Value: exists}, err } @@ -85,13 +86,21 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin var ( reports []*entities.StopReport ) + for _, cidFile := range options.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, errors.Wrapf(err, "error reading CIDFile %s", cidFile) + } + id := strings.Split(string(content), "\n")[0] + namesOrIds = append(namesOrIds, id) + } ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil { + if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { return nil, err } for _, c := range ctrs { report := entities.StopReport{Id: c.ID} - if err = containers.Stop(ic.ClientCxt, c.ID, &options.Timeout); err != nil { + if err = containers.Stop(ic.ClientCxt, c.ID, options.Timeout); err != nil { // These first two are considered non-fatal under the right conditions if errors.Cause(err).Error() == define.ErrCtrStopped.Error() { logrus.Debugf("Container %s is already stopped", c.ID) @@ -162,8 +171,16 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, var ( reports []*entities.RmReport ) + for _, cidFile := range options.CIDFiles { + content, err := ioutil.ReadFile(cidFile) + if err != nil { + return nil, errors.Wrapf(err, "error reading CIDFile %s", cidFile) + } + id := strings.Split(string(content), "\n")[0] + namesOrIds = append(namesOrIds, id) + } ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil { + if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchCtr) { return nil, err } // TODO there is no endpoint for container eviction. Need to discuss @@ -213,10 +230,10 @@ func (ic *ContainerEngine) ContainerTop(ctx context.Context, options entities.To return &entities.StringSliceReport{Value: topOutput}, nil } -func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, options entities.CommitOptions) (*entities.CommitReport, error) { +func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrID string, options entities.CommitOptions) (*entities.CommitReport, error) { var ( repo string - tag string = "latest" + tag = "latest" ) if len(options.ImageName) > 0 { ref, err := reference.Parse(options.ImageName) @@ -242,14 +259,14 @@ func (ic *ContainerEngine) ContainerCommit(ctx context.Context, nameOrId string, Repo: &repo, Tag: &tag, } - response, err := containers.Commit(ic.ClientCxt, nameOrId, commitOpts) + response, err := containers.Commit(ic.ClientCxt, nameOrID, commitOpts) if err != nil { return nil, err } return &entities.CommitReport{Id: response.ID}, nil } -func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, options entities.ContainerExportOptions) error { +func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrID string, options entities.ContainerExportOptions) error { var ( err error w io.Writer @@ -260,7 +277,7 @@ func (ic *ContainerEngine) ContainerExport(ctx context.Context, nameOrId string, return err } } - return containers.Export(ic.ClientCxt, nameOrId, w) + return containers.Export(ic.ClientCxt, nameOrID, w) } func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options entities.CheckpointOptions) ([]*entities.CheckpointReport, error) { @@ -340,7 +357,7 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: response.ID}, nil } -func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIds []string, options entities.ContainerLogsOptions) error { +func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIDs []string, options entities.ContainerLogsOptions) error { since := options.Since.Format(time.RFC3339) tail := strconv.FormatInt(options.Tail, 10) stdout := options.Writer != nil @@ -358,7 +375,7 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIds []string, outCh := make(chan string) ctx, cancel := context.WithCancel(context.Background()) go func() { - err = containers.Logs(ic.ClientCxt, nameOrIds[0], opts, outCh, outCh) + err = containers.Logs(ic.ClientCxt, nameOrIDs[0], opts, outCh, outCh) cancel() }() @@ -372,8 +389,8 @@ func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIds []string, } } -func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { - return containers.Attach(ic.ClientCxt, nameOrId, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil) +func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrID string, options entities.AttachOptions) error { + return containers.Attach(ic.ClientCxt, nameOrID, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil) } func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig { @@ -398,10 +415,10 @@ func makeExecConfig(options entities.ExecOptions) *handlers.ExecCreateConfig { return createConfig } -func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrID string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { createConfig := makeExecConfig(options) - sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrId, createConfig) + sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrID, createConfig) if err != nil { return 125, err } @@ -418,10 +435,10 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o return inspectOut.ExitCode, nil } -func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrId string, options entities.ExecOptions) (string, error) { +func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { createConfig := makeExecConfig(options) - sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrId, createConfig) + sessionID, err := containers.ExecCreate(ic.ClientCxt, nameOrID, createConfig) if err != nil { return "", err } @@ -508,8 +525,8 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta return &report, err } -func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := containers.Diff(ic.ClientCxt, nameOrId) +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := containers.Diff(ic.ClientCxt, nameOrID) return &entities.DiffReport{Changes: changes}, err } @@ -538,11 +555,11 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin return reports, nil } -func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { +func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIDs []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { return nil, errors.New("mounting containers is not supported for remote clients") } -func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { +func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIDs []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { return nil, errors.New("unmounting containers is not supported for remote clients") } @@ -550,13 +567,13 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { return config.Default() } -func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { +func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrID string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { var ( reports []*entities.ContainerPortReport namesOrIds []string ) - if len(nameOrId) > 0 { - namesOrIds = append(namesOrIds, nameOrId) + if len(nameOrID) > 0 { + namesOrIds = append(namesOrIds, nameOrID) } ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { diff --git a/pkg/domain/infra/tunnel/generate.go b/pkg/domain/infra/tunnel/generate.go index eb5587f89..519dc5907 100644 --- a/pkg/domain/infra/tunnel/generate.go +++ b/pkg/domain/infra/tunnel/generate.go @@ -13,5 +13,5 @@ func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, } func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) { - return generate.GenerateKube(ic.ClientCxt, nameOrID, options) + return generate.Kube(ic.ClientCxt, nameOrID, options) } diff --git a/pkg/domain/infra/tunnel/healthcheck.go b/pkg/domain/infra/tunnel/healthcheck.go index e589489b3..56bdd6759 100644 --- a/pkg/domain/infra/tunnel/healthcheck.go +++ b/pkg/domain/infra/tunnel/healthcheck.go @@ -8,6 +8,6 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { - return containers.RunHealthCheck(ic.ClientCxt, nameOrId) +func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrID string, options entities.HealthCheckOptions) (*define.HealthCheckResults, error) { + return containers.RunHealthCheck(ic.ClientCxt, nameOrID) } diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 862c7a5d6..2bbc0e7a5 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -13,11 +13,11 @@ import ( "github.com/pkg/errors" ) -func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]entities.ListContainer, error) { +func getContainersByContext(contextWithConnection context.Context, all bool, namesOrIDs []string) ([]entities.ListContainer, error) { var ( cons []entities.ListContainer ) - if all && len(namesOrIds) > 0 { + if all && len(namesOrIDs) > 0 { return nil, errors.New("cannot lookup containers and all") } c, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, nil, bindings.PTrue) @@ -27,7 +27,7 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam if all { return c, err } - for _, id := range namesOrIds { + for _, id := range namesOrIDs { var found bool for _, con := range c { if id == con.ID || strings.HasPrefix(con.ID, id) || util.StringInSlice(id, con.Names) { @@ -43,11 +43,11 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam return cons, nil } -func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIds []string) ([]*entities.ListPodsReport, error) { +func getPodsByContext(contextWithConnection context.Context, all bool, namesOrIDs []string) ([]*entities.ListPodsReport, error) { var ( sPods []*entities.ListPodsReport ) - if all && len(namesOrIds) > 0 { + if all && len(namesOrIDs) > 0 { return nil, errors.New("cannot lookup specific pods and all") } @@ -58,17 +58,17 @@ func getPodsByContext(contextWithConnection context.Context, all bool, namesOrId if all { return fPods, nil } - for _, nameOrId := range namesOrIds { + for _, nameOrID := range namesOrIDs { var found bool for _, f := range fPods { - if f.Name == nameOrId || strings.HasPrefix(f.Id, nameOrId) { + if f.Name == nameOrID || strings.HasPrefix(f.Id, nameOrID) { sPods = append(sPods, f) found = true break } } if !found { - return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrId) + return nil, errors.Wrapf(define.ErrNoSuchPod, "unable to find pod %q", nameOrID) } } return sPods, nil diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index c300e74d0..fc7ac0aa8 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -18,8 +18,8 @@ import ( "github.com/pkg/errors" ) -func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { - found, err := images.Exists(ir.ClientCxt, nameOrId) +func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.BoolReport, error) { + found, err := images.Exists(ir.ClientCxt, nameOrID) return &entities.BoolReport{Value: found}, err } @@ -50,8 +50,8 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) return is, nil } -func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { - results, err := images.History(ir.ClientCxt, nameOrId) +func (ir *ImageEngine) History(ctx context.Context, nameOrID string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { + results, err := images.History(ir.ClientCxt, nameOrID) if err != nil { return nil, err } @@ -98,7 +98,7 @@ func (ir *ImageEngine) Pull(ctx context.Context, rawImage string, options entiti return &entities.ImagePullReport{Images: pulledImages}, nil } -func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, options entities.ImageTagOptions) error { +func (ir *ImageEngine) Tag(ctx context.Context, nameOrID string, tags []string, options entities.ImageTagOptions) error { for _, newTag := range tags { var ( tag, repo string @@ -114,19 +114,19 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, repo = r.Name() } if len(repo) < 1 { - return errors.Errorf("invalid image name %q", nameOrId) + return errors.Errorf("invalid image name %q", nameOrID) } - if err := images.Tag(ir.ClientCxt, nameOrId, tag, repo); err != nil { + if err := images.Tag(ir.ClientCxt, nameOrID, tag, repo); err != nil { return err } } return nil } -func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error { +func (ir *ImageEngine) Untag(ctx context.Context, nameOrID string, tags []string, options entities.ImageUntagOptions) error { // Remove all tags if none are provided if len(tags) == 0 { - newImage, err := images.GetImage(ir.ClientCxt, nameOrId, bindings.PFalse) + newImage, err := images.GetImage(ir.ClientCxt, nameOrID, bindings.PFalse) if err != nil { return err } @@ -148,9 +148,9 @@ func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string repo = r.Name() } if len(repo) < 1 { - return errors.Errorf("invalid image name %q", nameOrId) + return errors.Errorf("invalid image name %q", nameOrID) } - if err := images.Untag(ir.ClientCxt, nameOrId, tag, repo); err != nil { + if err := images.Untag(ir.ClientCxt, nameOrID, tag, repo); err != nil { return err } } @@ -199,7 +199,7 @@ func (ir *ImageEngine) Push(ctx context.Context, source string, destination stri return images.Push(ir.ClientCxt, source, destination, options) } -func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, options entities.ImageSaveOptions) error { +func (ir *ImageEngine) Save(ctx context.Context, nameOrID string, tags []string, options entities.ImageSaveOptions) error { var ( f *os.File err error @@ -217,7 +217,7 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, return err } - exErr := images.Export(ir.ClientCxt, nameOrId, f, &options.Format, &options.Compress) + exErr := images.Export(ir.ClientCxt, nameOrID, f, &options.Format, &options.Compress) if err := f.Close(); err != nil { return err } @@ -250,8 +250,8 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } // Diff reports the changes to the given image -func (ir *ImageEngine) Diff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { - changes, err := images.Diff(ir.ClientCxt, nameOrId) +func (ir *ImageEngine) Diff(ctx context.Context, nameOrID string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := images.Diff(ir.ClientCxt, nameOrID) if err != nil { return nil, err } @@ -277,8 +277,8 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts return images.Build(ir.ClientCxt, containerFiles, opts, tarfile) } -func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { - return images.Tree(ir.ClientCxt, nameOrId, &opts.WhatRequires) +func (ir *ImageEngine) Tree(ctx context.Context, nameOrID string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { + return images.Tree(ir.ClientCxt, nameOrID, &opts.WhatRequires) } // Shutdown Libpod engine diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go index 15383a703..5f6bc4a2a 100644 --- a/pkg/domain/infra/tunnel/play.go +++ b/pkg/domain/infra/tunnel/play.go @@ -8,5 +8,5 @@ import ( ) func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) { - return play.PlayKube(ic.ClientCxt, path, options) + return play.Kube(ic.ClientCxt, path, options) } diff --git a/pkg/domain/infra/tunnel/pods.go b/pkg/domain/infra/tunnel/pods.go index c193c6752..5ca4a6a80 100644 --- a/pkg/domain/infra/tunnel/pods.go +++ b/pkg/domain/infra/tunnel/pods.go @@ -3,14 +3,16 @@ package tunnel import ( "context" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/bindings/pods" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" ) -func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { - exists, err := pods.Exists(ic.ClientCxt, nameOrId) +func (ic *ContainerEngine) PodExists(ctx context.Context, nameOrID string) (*entities.BoolReport, error) { + exists, err := pods.Exists(ic.ClientCxt, nameOrID) return &entities.BoolReport{Value: exists}, err } @@ -18,6 +20,12 @@ func (ic *ContainerEngine) PodKill(ctx context.Context, namesOrIds []string, opt var ( reports []*entities.PodKillReport ) + + _, err := util.ParseSignal(options.Signal) + if err != nil { + return nil, err + } + foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) if err != nil { return nil, err @@ -86,10 +94,10 @@ func (ic *ContainerEngine) PodUnpause(ctx context.Context, namesOrIds []string, func (ic *ContainerEngine) PodStop(ctx context.Context, namesOrIds []string, options entities.PodStopOptions) ([]*entities.PodStopReport, error) { var ( reports []*entities.PodStopReport - timeout int = -1 + timeout = -1 ) foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil { + if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } if options.Timeout != -1 { @@ -155,7 +163,7 @@ func (ic *ContainerEngine) PodStart(ctx context.Context, namesOrIds []string, op func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, options entities.PodRmOptions) ([]*entities.PodRmReport, error) { var reports []*entities.PodRmReport foundPods, err := getPodsByContext(ic.ClientCxt, options.All, namesOrIds) - if err != nil { + if err != nil && !(options.Ignore && errors.Cause(err) == define.ErrNoSuchPod) { return nil, err } for _, p := range foundPods { diff --git a/pkg/domain/infra/tunnel/volumes.go b/pkg/domain/infra/tunnel/volumes.go index e48a7fa7c..5b65c66ea 100644 --- a/pkg/domain/infra/tunnel/volumes.go +++ b/pkg/domain/infra/tunnel/volumes.go @@ -7,12 +7,12 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IdOrNameResponse, error) { +func (ic *ContainerEngine) VolumeCreate(ctx context.Context, opts entities.VolumeCreateOptions) (*entities.IDOrNameResponse, error) { response, err := volumes.Create(ic.ClientCxt, opts) if err != nil { return nil, err } - return &entities.IdOrNameResponse{IdOrName: response.Name}, nil + return &entities.IDOrNameResponse{IDOrName: response.Name}, nil } func (ic *ContainerEngine) VolumeRm(ctx context.Context, namesOrIds []string, opts entities.VolumeRmOptions) ([]*entities.VolumeRmReport, error) { diff --git a/pkg/domain/utils/utils.go b/pkg/domain/utils/utils.go index c17769f62..ee213e1b6 100644 --- a/pkg/domain/utils/utils.go +++ b/pkg/domain/utils/utils.go @@ -31,7 +31,7 @@ func ToLibpodFilters(f url.Values) (filters []string) { return } -func ToUrlValues(f []string) (filters url.Values) { +func ToURLValues(f []string) (filters url.Values) { filters = make(url.Values) for _, v := range f { t := strings.SplitN(v, "=", 2) diff --git a/pkg/network/network.go b/pkg/network/network.go index 526ee92d8..3ff664316 100644 --- a/pkg/network/network.go +++ b/pkg/network/network.go @@ -14,7 +14,7 @@ import ( ) // DefaultNetworkDriver is the default network type used -var DefaultNetworkDriver string = "bridge" +var DefaultNetworkDriver = "bridge" // SupportedNetworkDrivers describes the list of supported drivers var SupportedNetworkDrivers = []string{DefaultNetworkDriver} diff --git a/pkg/parallel/parallel.go b/pkg/parallel/parallel.go new file mode 100644 index 000000000..c9e4da50d --- /dev/null +++ b/pkg/parallel/parallel.go @@ -0,0 +1,44 @@ +package parallel + +import ( + "sync" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "golang.org/x/sync/semaphore" +) + +var ( + // Maximum number of jobs that will be used. + // Set a low, but non-zero, default. We'll be overriding it by default + // anyways. + numThreads uint = 8 + // Semaphore to control thread creation and ensure numThreads is + // respected. + jobControl *semaphore.Weighted + // Lock to control changing the semaphore - we don't want to do it + // while anyone is using it. + jobControlLock sync.RWMutex +) + +// SetMaxThreads sets the number of threads that will be used for parallel jobs. +func SetMaxThreads(threads uint) error { + if threads == 0 { + return errors.New("must give a non-zero number of threads to execute with") + } + + jobControlLock.Lock() + defer jobControlLock.Unlock() + + numThreads = threads + jobControl = semaphore.NewWeighted(int64(threads)) + logrus.Infof("Setting parallel job count to %d", threads) + + return nil +} + +// GetMaxThreads returns the current number of threads that will be used for +// parallel jobs. +func GetMaxThreads() uint { + return numThreads +} diff --git a/pkg/parallel/parallel_linux.go b/pkg/parallel/parallel_linux.go new file mode 100644 index 000000000..472571972 --- /dev/null +++ b/pkg/parallel/parallel_linux.go @@ -0,0 +1,57 @@ +package parallel + +import ( + "context" + "sync" + + "github.com/containers/libpod/libpod" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +// ContainerOp performs the given function on the given set of +// containers, using a number of parallel threads. +// If no error is returned, each container specified in ctrs will have an entry +// in the resulting map; containers with no error will be set to nil. +func ContainerOp(ctx context.Context, ctrs []*libpod.Container, applyFunc func(*libpod.Container) error) (map[*libpod.Container]error, error) { + jobControlLock.RLock() + defer jobControlLock.RUnlock() + + // We could use a sync.Map but given Go's lack of generic I'd rather + // just use a lock on a normal map... + // The expectation is that most of the time is spent in applyFunc + // anyways. + var ( + errMap = make(map[*libpod.Container]error) + errLock sync.Mutex + allDone sync.WaitGroup + ) + + for _, ctr := range ctrs { + // Block until a thread is available + if err := jobControl.Acquire(ctx, 1); err != nil { + return nil, errors.Wrapf(err, "error acquiring job control semaphore") + } + + allDone.Add(1) + + c := ctr + go func() { + logrus.Debugf("Launching job on container %s", c.ID()) + + err := applyFunc(c) + errLock.Lock() + errMap[c] = err + errLock.Unlock() + + allDone.Done() + jobControl.Release(1) + }() + } + + allDone.Wait() + + return errMap, nil +} + +// TODO: Add an Enqueue() function that returns a promise diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index 2cf30a59e..e19c582b5 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -399,7 +399,7 @@ func AddPrivilegedDevices(g *generate.Generator) error { return addPrivilegedDevices(g) } -func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *CreateConfig, ctx context.Context, pod *libpod.Pod) (*libpod.Container, error) { +func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *CreateConfig, pod *libpod.Pod) (*libpod.Container, error) { runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod) if err != nil { return nil, err diff --git a/pkg/specgen/config_linux.go b/pkg/specgen/config_linux.go deleted file mode 100644 index 82a371492..000000000 --- a/pkg/specgen/config_linux.go +++ /dev/null @@ -1,93 +0,0 @@ -package specgen - -//func createBlockIO() (*spec.LinuxBlockIO, error) { -// var ret *spec.LinuxBlockIO -// bio := &spec.LinuxBlockIO{} -// if c.Resources.BlkioWeight > 0 { -// ret = bio -// bio.Weight = &c.Resources.BlkioWeight -// } -// if len(c.Resources.BlkioWeightDevice) > 0 { -// var lwds []spec.LinuxWeightDevice -// ret = bio -// for _, i := range c.Resources.BlkioWeightDevice { -// wd, err := ValidateweightDevice(i) -// if err != nil { -// return ret, errors.Wrapf(err, "invalid values for blkio-weight-device") -// } -// wdStat, err := GetStatFromPath(wd.Path) -// if err != nil { -// return ret, errors.Wrapf(err, "error getting stat from path %q", wd.Path) -// } -// lwd := spec.LinuxWeightDevice{ -// Weight: &wd.Weight, -// } -// lwd.Major = int64(unix.Major(wdStat.Rdev)) -// lwd.Minor = int64(unix.Minor(wdStat.Rdev)) -// lwds = append(lwds, lwd) -// } -// bio.WeightDevice = lwds -// } -// if len(c.Resources.DeviceReadBps) > 0 { -// ret = bio -// readBps, err := makeThrottleArray(c.Resources.DeviceReadBps, bps) -// if err != nil { -// return ret, err -// } -// bio.ThrottleReadBpsDevice = readBps -// } -// if len(c.Resources.DeviceWriteBps) > 0 { -// ret = bio -// writeBpds, err := makeThrottleArray(c.Resources.DeviceWriteBps, bps) -// if err != nil { -// return ret, err -// } -// bio.ThrottleWriteBpsDevice = writeBpds -// } -// if len(c.Resources.DeviceReadIOps) > 0 { -// ret = bio -// readIOps, err := makeThrottleArray(c.Resources.DeviceReadIOps, iops) -// if err != nil { -// return ret, err -// } -// bio.ThrottleReadIOPSDevice = readIOps -// } -// if len(c.Resources.DeviceWriteIOps) > 0 { -// ret = bio -// writeIOps, err := makeThrottleArray(c.Resources.DeviceWriteIOps, iops) -// if err != nil { -// return ret, err -// } -// bio.ThrottleWriteIOPSDevice = writeIOps -// } -// return ret, nil -//} - -//func makeThrottleArray(throttleInput []string, rateType int) ([]spec.LinuxThrottleDevice, error) { -// var ( -// ltds []spec.LinuxThrottleDevice -// t *throttleDevice -// err error -// ) -// for _, i := range throttleInput { -// if rateType == bps { -// t, err = validateBpsDevice(i) -// } else { -// t, err = validateIOpsDevice(i) -// } -// if err != nil { -// return []spec.LinuxThrottleDevice{}, err -// } -// ltdStat, err := GetStatFromPath(t.path) -// if err != nil { -// return ltds, errors.Wrapf(err, "error getting stat from path %q", t.path) -// } -// ltd := spec.LinuxThrottleDevice{ -// Rate: t.rate, -// } -// ltd.Major = int64(unix.Major(ltdStat.Rdev)) -// ltd.Minor = int64(unix.Minor(ltdStat.Rdev)) -// ltds = append(ltds, ltd) -// } -// return ltds, nil -//} diff --git a/pkg/specgen/container_validate.go b/pkg/specgen/container_validate.go index 75da38c0e..45179343b 100644 --- a/pkg/specgen/container_validate.go +++ b/pkg/specgen/container_validate.go @@ -10,7 +10,7 @@ import ( var ( // ErrInvalidSpecConfig describes an error that the given SpecGenerator is invalid - ErrInvalidSpecConfig error = errors.New("invalid configuration") + ErrInvalidSpecConfig = errors.New("invalid configuration") // SystemDValues describes the only values that SystemD can be SystemDValues = []string{"true", "false", "always"} // ImageVolumeModeValues describes the only values that ImageVolumeMode can be @@ -38,7 +38,7 @@ func (s *SpecGenerator) Validate() error { } // systemd values must be true, false, or always if len(s.ContainerBasicConfig.Systemd) > 0 && !util.StringInSlice(strings.ToLower(s.ContainerBasicConfig.Systemd), SystemDValues) { - return errors.Wrapf(ErrInvalidSpecConfig, "SystemD values must be one of %s", strings.Join(SystemDValues, ",")) + return errors.Wrapf(ErrInvalidSpecConfig, "--systemd values must be one of %q", strings.Join(SystemDValues, ", ")) } // diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index a217125f4..3d70571d5 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -194,7 +194,7 @@ func finishThrottleDevices(s *specgen.SpecGenerator) error { s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice = append(s.ResourceLimits.BlockIO.ThrottleReadIOPSDevice, v) } } - if iops := s.ThrottleWriteBpsDevice; len(iops) > 0 { + if iops := s.ThrottleWriteIOPSDevice; len(iops) > 0 { for k, v := range iops { statT := unix.Stat_t{} if err := unix.Stat(k, &statT); err != nil { diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go index 7ddfed339..33075b543 100644 --- a/pkg/specgen/generate/container_create.go +++ b/pkg/specgen/generate/container_create.go @@ -3,12 +3,14 @@ package generate import ( "context" "os" + "path/filepath" "github.com/containers/common/pkg/config" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/libpod/pkg/util" "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -114,7 +116,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener } options = append(options, libpod.WithExitCommand(exitCommandArgs)) - runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts) + runtimeSpec, err := SpecGenToOCI(ctx, s, rt, rtc, newImage, finalMounts, pod) if err != nil { return nil, err } @@ -128,7 +130,41 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. if s.Stdin { options = append(options, libpod.WithStdin()) } - if len(s.Systemd) > 0 { + + useSystemd := false + switch s.Systemd { + case "always": + useSystemd = true + case "false": + break + case "", "true": + command := s.Command + if len(command) == 0 { + command, err = img.Cmd(ctx) + if err != nil { + return nil, err + } + } + + if len(command) > 0 { + if command[0] == "/usr/sbin/init" || command[0] == "/sbin/init" || (filepath.Base(command[0]) == "systemd") { + useSystemd = true + } + } + default: + return nil, errors.Wrapf(err, "invalid value %q systemd option requires 'true, false, always'", s.Systemd) + } + if useSystemd { + // is StopSignal was not set by the user then set it to systemd + // expected StopSigal + if s.StopSignal == nil { + stopSignal, err := util.ParseSignal("RTMIN+3") + if err != nil { + return nil, errors.Wrapf(err, "error parsing systemd signal") + } + s.StopSignal = &stopSignal + } + options = append(options, libpod.WithSystemd()) } if len(s.Name) > 0 { @@ -194,7 +230,7 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen. options = append(options, libpod.WithPrivileged(s.Privileged)) // Get namespace related options - namespaceOptions, err := GenerateNamespaceOptions(ctx, s, rt, pod, img) + namespaceOptions, err := namespaceOptions(ctx, s, rt, pod, img) if err != nil { return nil, err } diff --git a/pkg/specgen/generate/namespaces.go b/pkg/specgen/generate/namespaces.go index 138d9e0cd..e67afe1bf 100644 --- a/pkg/specgen/generate/namespaces.go +++ b/pkg/specgen/generate/namespaces.go @@ -72,13 +72,13 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod) return toReturn, errors.Wrapf(define.ErrInvalidArg, "invalid namespace type %q passed", nsType) } -// GenerateNamespaceOptions generates container creation options for all +// namespaceOptions generates container creation options for all // namespaces in a SpecGenerator. // Pod is the pod the container will join. May be nil is the container is not // joining a pod. // TODO: Consider grouping options that are not directly attached to a namespace // elsewhere. -func GenerateNamespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, img *image.Image) ([]libpod.CtrCreateOption, error) { +func namespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.Pod, img *image.Image) ([]libpod.CtrCreateOption, error) { toReturn := []libpod.CtrCreateOption{} // If pod is not nil, get infra container. @@ -265,7 +265,7 @@ func GenerateNamespaceOptions(ctx context.Context, s *specgen.SpecGenerator, rt return toReturn, nil } -func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime) error { +func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt *libpod.Runtime, pod *libpod.Pod) error { // PID switch s.PidNS.NSMode { case specgen.Path: @@ -326,6 +326,8 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt hostname := s.Hostname if hostname == "" { switch { + case s.UtsNS.NSMode == specgen.FromPod: + hostname = pod.Hostname() case s.UtsNS.NSMode == specgen.FromContainer: utsCtr, err := rt.LookupContainer(s.UtsNS.Value) if err != nil { diff --git a/pkg/specgen/generate/oci.go b/pkg/specgen/generate/oci.go index 11b18e2d0..266abd28d 100644 --- a/pkg/specgen/generate/oci.go +++ b/pkg/specgen/generate/oci.go @@ -118,7 +118,7 @@ func makeCommand(ctx context.Context, s *specgen.SpecGenerator, img *image.Image return finalCommand, nil } -func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount) (*spec.Spec, error) { +func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, newImage *image.Image, mounts []spec.Mount, pod *libpod.Pod) (*spec.Spec, error) { var ( inUserNS bool ) @@ -300,7 +300,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt } // NAMESPACES - if err := specConfigureNamespaces(s, &g, rt); err != nil { + if err := specConfigureNamespaces(s, &g, rt, pod); err != nil { return nil, err } configSpec := g.Config diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index 640447e71..2d57cdb91 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -7,7 +7,7 @@ import ( var ( // ErrInvalidPodSpecConfig describes an error given when the podspecgenerator is invalid - ErrInvalidPodSpecConfig error = errors.New("invalid pod spec") + ErrInvalidPodSpecConfig = errors.New("invalid pod spec") // containerConfig has the default configurations defined in containers.conf containerConfig = util.DefaultContainerConfig() ) diff --git a/pkg/systemd/generate/systemdgen_test.go b/pkg/systemd/generate/systemdgen_test.go index 3269405a6..cc5db5e24 100644 --- a/pkg/systemd/generate/systemdgen_test.go +++ b/pkg/systemd/generate/systemdgen_test.go @@ -170,7 +170,7 @@ Type=forking [Install] WantedBy=multi-user.target default.target` - goodIdNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service + goodIDNew := `# container-639c53578af4d84b8800b4635fa4e680ee80fd67e0e6a2d4eea48d1e3230f401.service # autogenerated by Podman CI [Unit] @@ -323,7 +323,7 @@ WantedBy=multi-user.target default.target` New: true, CreateCommand: []string{"I'll get stripped", "container", "run", "awesome-image:latest"}, }, - goodIdNew, + goodIDNew, false, }, } diff --git a/pkg/trust/config.go b/pkg/trust/config.go index 0bafc722b..164df2a90 100644 --- a/pkg/trust/config.go +++ b/pkg/trust/config.go @@ -1,7 +1,7 @@ package trust -// Trust Policy describes a basic trust policy configuration -type TrustPolicy struct { +// Policy describes a basic trust policy configuration +type Policy struct { Name string `json:"name"` RepoName string `json:"repo_name,omitempty"` Keys []string `json:"keys,omitempty"` diff --git a/pkg/varlinkapi/create.go b/pkg/varlinkapi/create.go index 571ce6115..d921130e7 100644 --- a/pkg/varlinkapi/create.go +++ b/pkg/varlinkapi/create.go @@ -220,7 +220,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. } } - ctr, err := CreateContainerFromCreateConfig(runtime, createConfig, ctx, pod) + ctr, err := CreateContainerFromCreateConfig(ctx, runtime, createConfig, pod) if err != nil { return nil, nil, err } @@ -909,7 +909,7 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. return config, nil } -func CreateContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context, pod *libpod.Pod) (*libpod.Container, error) { +func CreateContainerFromCreateConfig(ctx context.Context, r *libpod.Runtime, createConfig *cc.CreateConfig, pod *libpod.Pod) (*libpod.Container, error) { runtimeSpec, options, err := createConfig.MakeContainerConfig(r, pod) if err != nil { return nil, err diff --git a/pkg/varlinkapi/volumes.go b/pkg/varlinkapi/volumes.go index aa0eb1fb5..3b6276287 100644 --- a/pkg/varlinkapi/volumes.go +++ b/pkg/varlinkapi/volumes.go @@ -25,7 +25,7 @@ func (i *VarlinkAPI) VolumeCreate(call iopodman.VarlinkCall, options iopodman.Vo volumeOptions = append(volumeOptions, libpod.WithVolumeLabels(options.Labels)) } if len(options.Options) > 0 { - parsedOptions, err := parse.ParseVolumeOptions(options.Options) + parsedOptions, err := parse.VolumeOptions(options.Options) if err != nil { return call.ReplyErrorOccurred(err.Error()) } diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 7fb39b221..60f6d97aa 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -19,7 +19,7 @@ t GET libpod/containers/json 200 length=0 t GET libpod/containers/json?all=true 200 \ length=1 \ - .[0].Id~[0-9a-f]\\{12\\} \ + .[0].Id~[0-9a-f]\\{64\\} \ .[0].Image=$IMAGE \ .[0].Command[0]="true" \ .[0].State~\\\(exited\\\|stopped\\\) \ @@ -30,4 +30,56 @@ cid=$(jq -r '.[0].Id' <<<"$output") t DELETE libpod/containers/$cid 204 +CNAME=myfoo +podman run --name $CNAME $IMAGE -td top +t GET libpod/containers/json?all=true 200 \ + .[0].Id~[0-9a-f]\\{64\\} +cid=$(jq -r '.[0].Id' <<<"$output") + +# No such container +t POST "libpod/commit?container=nonesuch" '' 404 + +# Comment can only be used with docker format, not OCI +cparam="repo=newrepo&comment=foo&author=bob" +t POST "libpod/commit?container=$CNAME&$cparam" '' 500 + +# Commit a new image from the container +t POST "libpod/commit?container=$CNAME" '' 200 \ + .Id~[0-9a-f]\\{64\\} +iid=$(jq -r '.Id' <<<"$output") +t GET libpod/images/$iid/json 200 \ + .RepoTags[0]=null \ + .Author="" \ + .Comment="" + +# Commit a new image w/o tag +cparam="repo=newrepo&comment=foo&author=bob&format=docker" +t POST "libpod/commit?container=$CNAME&$cparam" '' 200 +t GET libpod/images/newrepo:latest/json 200 \ + .RepoTags[0]=localhost/newrepo:latest \ + .Author=bob \ + .Comment=foo + +# Commit a new image w/ specified tag and author +cparam="repo=newrepo&tag=v1&author=alice" +t POST "libpod/commit?container=$cid&$cparam&pause=false" '' 200 +t GET libpod/images/newrepo:v1/json 200 \ + .RepoTags[0]=localhost/newrepo:v1 \ + .Author=alice + +# Commit a new image w/ full parameters +cparam="repo=newrepo&tag=v2&comment=bar&author=eric" +cparam="$cparam&format=docker&changes=CMD=/bin/foo" +t POST "libpod/commit?container=${cid:0:12}&$cparam&pause=true" '' 200 +t GET libpod/images/newrepo:v2/json 200 \ + .RepoTags[0]=localhost/newrepo:v2 \ + .Author=eric \ + .Comment=bar \ + .Config.Cmd[-1]="/bin/foo" + +t DELETE images/localhost/newrepo:latest?force=true 200 +t DELETE images/localhost/newrepo:v1?force=true 200 +t DELETE images/localhost/newrepo:v2?force=true 200 +t DELETE libpod/containers/$cid 204 + # vim: filetype=sh diff --git a/test/dockerpy/README.md b/test/dockerpy/README.md new file mode 100644 index 000000000..32e426d58 --- /dev/null +++ b/test/dockerpy/README.md @@ -0,0 +1,30 @@ +# Dockerpy regression test + +Python test suite to validate Podman endpoints using dockerpy library + +Running tests +============= +To run the tests locally in your sandbox: + +#### Run the entire test + +``` +sudo PYTHONPATH=/usr/bin/python python -m dockerpy.images +``` + +Passing the -v option to your test script will instruct unittest.main() to enable a higher level of verbosity, and produce detailed output: + +``` +sudo PYTHONPATH=/usr/bin/python python -m unittest -v dockerpy.images +``` +#### Run a specific test class + +``` +sudo PYTHONPATH=/usr/bin/python python -m unittest -v dockerpy.images.TestImages +``` + +#### Run a specific test within the test class + +``` +sudo PYTHONPATH=/usr/bin/python python -m unittest -v dockerpy.images.TestImages.test_list_images +``` diff --git a/test/dockerpy/__init__.py b/test/dockerpy/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/dockerpy/__init__.py diff --git a/test/dockerpy/common.py b/test/dockerpy/common.py new file mode 100644 index 000000000..fdacb49be --- /dev/null +++ b/test/dockerpy/common.py @@ -0,0 +1,68 @@ +import docker +import subprocess +import os +import sys +import time +from docker import Client +from . import constant + +alpineDict = { + "name": "docker.io/library/alpine:latest", + "shortName": "alpine", + "tarballName": "alpine.tar"} + +def get_client(): + client = docker.Client(base_url="http://localhost:8080",timeout=15) + return client + +client = get_client() + +def podman(): + binary = os.getenv("PODMAN_BINARY") + if binary is None: + binary = "bin/podman" + return binary + +def restore_image_from_cache(): + client.load_image(constant.ImageCacheDir+alpineDict["tarballName"]) + +def run_top_container(): + client.pull(constant.ALPINE) + c = client.create_container(constant.ALPINE,name=constant.TOP) + client.start(container=c.get("Id")) + +def enable_sock(TestClass): + TestClass.podman = subprocess.Popen( + [ + podman(), "system", "service", "tcp:localhost:8080", + "--log-level=debug", "--time=0" + ], + shell=False, + stdin=subprocess.DEVNULL, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + time.sleep(2) + +def terminate_connection(TestClass): + TestClass.podman.terminate() + stdout, stderr = TestClass.podman.communicate(timeout=0.5) + if stdout: + print("\nService Stdout:\n" + stdout.decode('utf-8')) + if stderr: + print("\nService Stderr:\n" + stderr.decode('utf-8')) + + if TestClass.podman.returncode > 0: + sys.stderr.write("podman exited with error code {}\n".format( + TestClass.podman.returncode)) + sys.exit(2) + +def remove_all_containers(): + containers = client.containers(quiet=True) + for c in containers: + client.remove_container(container=c.get("Id"), force=True) + +def remove_all_images(): + allImages = client.images() + for image in allImages: + client.remove_image(image,force=True) diff --git a/test/dockerpy/constant.py b/test/dockerpy/constant.py new file mode 100644 index 000000000..8a3f1d984 --- /dev/null +++ b/test/dockerpy/constant.py @@ -0,0 +1,13 @@ +BB = "docker.io/library/busybox:latest" +NGINX = "docker.io/library/nginx:latest" +ALPINE = "docker.io/library/alpine:latest" +ALPINE_SHORTNAME = "alpine" +ALPINELISTTAG = "docker.io/library/alpine:3.10.2" +ALPINELISTDIGEST = "docker.io/library/alpine@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb" +ALPINEAMD64DIGEST = "docker.io/library/alpine@sha256:acd3ca9941a85e8ed16515bfc5328e4e2f8c128caa72959a58a127b7801ee01f" +ALPINEAMD64ID = "961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4" +ALPINEARM64DIGEST = "docker.io/library/alpine@sha256:db7f3dcef3d586f7dd123f107c93d7911515a5991c4b9e51fa2a43e46335a43e" +ALPINEARM64ID = "915beeae46751fc564998c79e73a1026542e945ca4f73dc841d09ccc6c2c0672" +infra = "k8s.gcr.io/pause:3.2" +TOP = "top" +ImageCacheDir = "/tmp/podman/imagecachedir" diff --git a/test/dockerpy/containers.py b/test/dockerpy/containers.py new file mode 100644 index 000000000..d70ec932c --- /dev/null +++ b/test/dockerpy/containers.py @@ -0,0 +1,46 @@ + +import unittest +import docker +import requests +import os +from docker import Client +from . import constant +from . import common + +client = common.get_client() + +class TestContainers(unittest.TestCase): + + podman = None + + def setUp(self): + super().setUp() + common.run_top_container() + + def tearDown(self): + common.remove_all_containers() + common.remove_all_images() + return super().tearDown() + + @classmethod + def setUpClass(cls): + super().setUpClass() + common.enable_sock(cls) + + @classmethod + def tearDownClass(cls): + common.terminate_connection(cls) + return super().tearDownClass() + + def test_inspect_container(self): + # Inspect bogus container + with self.assertRaises(requests.HTTPError): + client.inspect_container("dummy") + # Inspect valid container + container = client.inspect_container(constant.TOP) + self.assertIn(constant.TOP , container["Name"]) + + +if __name__ == '__main__': + # Setup temporary space + unittest.main() diff --git a/test/dockerpy/images.py b/test/dockerpy/images.py new file mode 100644 index 000000000..1e07d25c7 --- /dev/null +++ b/test/dockerpy/images.py @@ -0,0 +1,151 @@ + +import unittest +import docker +import requests +import os +from docker import Client +from . import constant +from . import common + +client = common.get_client() + +class TestImages(unittest.TestCase): + + podman = None + def setUp(self): + super().setUp() + client.pull(constant.ALPINE) + + def tearDown(self): + common.remove_all_images() + return super().tearDown() + + @classmethod + def setUpClass(cls): + super().setUpClass() + common.enable_sock(cls) + + + @classmethod + def tearDownClass(cls): + common.terminate_connection(cls) + return super().tearDownClass() + + +# Inspect Image + + def test_inspect_image(self): + # Check for error with wrong image name + with self.assertRaises(requests.HTTPError): + client.inspect_image("dummy") + alpine_image = client.inspect_image(constant.ALPINE) + self.assertIn(constant.ALPINE, alpine_image["RepoTags"]) + +# Tag Image + + # Validates if invalid image name is given a bad response is encountered. + def test_tag_invalid_image(self): + with self.assertRaises(requests.HTTPError): + client.tag("dummy","demo") + + + + # Validates if the image is tagged successfully. + def test_tag_valid_image(self): + client.tag(constant.ALPINE,"demo",constant.ALPINE_SHORTNAME) + alpine_image = client.inspect_image(constant.ALPINE) + for x in alpine_image["RepoTags"]: + if("demo:alpine" in x): + self.assertTrue + self.assertFalse + + # Validates if name updates when the image is retagged. + @unittest.skip("dosent work now") + def test_retag_valid_image(self): + client.tag(constant.ALPINE_SHORTNAME, "demo","rename") + alpine_image = client.inspect_image(constant.ALPINE) + self.assertNotIn("demo:test", alpine_image["RepoTags"]) + +# List Image + # List All Images + def test_list_images(self): + allImages = client.images() + self.assertEqual(len(allImages), 1) + # Add more images + client.pull(constant.BB) + client.pull(constant.NGINX) + allImages = client.images() + self.assertEqual(len(allImages) , 3) + + + # List images with filter + filters = {'reference':'alpine'} + allImages = client.images(filters = filters) + self.assertEqual(len(allImages) , 1) + +# Search Image + def test_search_image(self): + response = client.search("alpine") + for i in response: + # Alpine found + if "docker.io/library/alpine" in i["Name"]: + self.assertTrue + self.assertFalse + +# Image Exist (No docker-py support yet) + +# Remove Image + def test_remove_image(self): + # Check for error with wrong image name + with self.assertRaises(requests.HTTPError): + client.remove_image("dummy") + allImages = client.images() + self.assertEqual(len(allImages) , 1) + alpine_image = client.inspect_image(constant.ALPINE) + client.remove_image(alpine_image) + allImages = client.images() + self.assertEqual(len(allImages) , 0) + +# Image History + def test_image_history(self): + # Check for error with wrong image name + with self.assertRaises(requests.HTTPError): + client.remove_image("dummy") + imageHistory = client.history(constant.ALPINE) + alpine_image = client.inspect_image(constant.ALPINE) + for h in imageHistory: + if h["Id"] in alpine_image["Id"]: + self.assertTrue + self.assertFalse + +# Prune Image (No docker-py support yet) + +# Export Image + + def test_export_image(self): + client.pull(constant.BB) + file = os.path.join(constant.ImageCacheDir , "busybox.tar") + if not os.path.exists(constant.ImageCacheDir): + os.makedirs(constant.ImageCacheDir) + # Check for error with wrong image name + with self.assertRaises(requests.HTTPError): + client.get_image("dummy") + response = client.get_image(constant.BB) + image_tar = open(file,mode="wb") + image_tar.write(response.data) + image_tar.close() + os.stat(file) + +# Import|Load Image + + def test_import_image(self): + allImages = client.images() + self.assertEqual(len(allImages), 1) + file = os.path.join(constant.ImageCacheDir , "busybox.tar") + client.import_image_from_file(filename=file) + allImages = client.images() + self.assertEqual(len(allImages), 2) + +if __name__ == '__main__': + # Setup temporary space + unittest.main() diff --git a/test/e2e/checkpoint_test.go b/test/e2e/checkpoint_test.go index e6a3d2f7a..663205209 100644 --- a/test/e2e/checkpoint_test.go +++ b/test/e2e/checkpoint_test.go @@ -232,6 +232,8 @@ var _ = Describe("Podman checkpoint", func() { }) It("podman checkpoint container with established tcp connections", func() { + // Broken on Ubuntu. + SkipIfNotFedora() localRunString := getRunString([]string{redis}) session := podmanTest.Podman(localRunString) session.WaitWithDefaultTimeout() diff --git a/test/e2e/cp_test.go b/test/e2e/cp_test.go index f95f8646c..6ae54ba34 100644 --- a/test/e2e/cp_test.go +++ b/test/e2e/cp_test.go @@ -141,6 +141,8 @@ var _ = Describe("Podman cp", func() { }) It("podman cp stdin/stdout", func() { + SkipIfRemote() + Skip("Looks like SkipIfRemote() is not working") session := podmanTest.Podman([]string{"create", ALPINE, "ls", "foo"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/create_test.go b/test/e2e/create_test.go index 0a6373bfa..f40472a7c 100644 --- a/test/e2e/create_test.go +++ b/test/e2e/create_test.go @@ -301,7 +301,6 @@ var _ = Describe("Podman create", func() { }) It("podman create --authfile with nonexist authfile", func() { - SkipIfRemote() session := podmanTest.PodmanNoCache([]string{"create", "--authfile", "/tmp/nonexist", "--name=foo", ALPINE}) session.WaitWithDefaultTimeout() Expect(session).To(Not(Equal(0))) diff --git a/test/e2e/diff_test.go b/test/e2e/diff_test.go index d273f9463..fbbe49eac 100644 --- a/test/e2e/diff_test.go +++ b/test/e2e/diff_test.go @@ -61,7 +61,6 @@ var _ = Describe("Podman diff", func() { }) It("podman diff container and committed image", func() { - SkipIfRemote() session := podmanTest.Podman([]string{"run", "--name=diff-test", ALPINE, "touch", "/tmp/diff-test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -82,11 +81,10 @@ var _ = Describe("Podman diff", func() { }) It("podman diff latest container", func() { - SkipIfRemote() - session := podmanTest.Podman([]string{"run", "--name=diff-test", ALPINE, "touch", "/tmp/diff-test"}) + session := podmanTest.Podman([]string{"run", "--name", "diff-test", ALPINE, "touch", "/tmp/diff-test"}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) - session = podmanTest.Podman([]string{"diff", "-l"}) + session = podmanTest.Podman([]string{"diff", "diff-test"}) session.WaitWithDefaultTimeout() containerDiff := session.OutputToStringArray() sort.Strings(containerDiff) diff --git a/test/e2e/exists_test.go b/test/e2e/exists_test.go index e26fad51d..8f3b371d8 100644 --- a/test/e2e/exists_test.go +++ b/test/e2e/exists_test.go @@ -112,7 +112,6 @@ var _ = Describe("Podman image|container exists", func() { }) It("podman pod does not exist in local storage", func() { // The exit code for non-existing pod is incorrect (125 vs 1) - SkipIfRemote() session := podmanTest.Podman([]string{"pod", "exists", "foobar"}) session.WaitWithDefaultTimeout() Expect(session).Should(Exit(1)) diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go index 395cc1ea4..7872a9fbf 100644 --- a/test/e2e/generate_kube_test.go +++ b/test/e2e/generate_kube_test.go @@ -254,6 +254,8 @@ var _ = Describe("Podman generate kube", func() { }) It("podman generate with user and reimport kube on pod", func() { + // This test fails on ubuntu due to https://github.com/seccomp/containers-golang/pull/27 + SkipIfNotFedora() podName := "toppod" _, rc, _ := podmanTest.CreatePod(podName) Expect(rc).To(Equal(0)) diff --git a/test/e2e/healthcheck_run_test.go b/test/e2e/healthcheck_run_test.go index 8e63d9f4c..d8e3f045f 100644 --- a/test/e2e/healthcheck_run_test.go +++ b/test/e2e/healthcheck_run_test.go @@ -42,7 +42,6 @@ var _ = Describe("Podman healthcheck run", func() { }) It("podman disable healthcheck with --no-healthcheck on valid container", func() { - SkipIfRemote() session := podmanTest.Podman([]string{"run", "-dt", "--no-healthcheck", "--name", "hc", healthcheck}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -52,7 +51,6 @@ var _ = Describe("Podman healthcheck run", func() { }) It("podman disable healthcheck with --health-cmd=none on valid container", func() { - SkipIfRemote() session := podmanTest.Podman([]string{"run", "-dt", "--health-cmd", "none", "--name", "hc", healthcheck}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go index 77cfe4fd3..62f69f1c1 100644 --- a/test/e2e/inspect_test.go +++ b/test/e2e/inspect_test.go @@ -132,28 +132,27 @@ var _ = Describe("Podman inspect", func() { }) It("podman inspect with mount filters", func() { - SkipIfRemote() - ctrSession := podmanTest.Podman([]string{"create", "-v", "/tmp:/test1", ALPINE, "top"}) + ctrSession := podmanTest.Podman([]string{"create", "--name", "test", "-v", "/tmp:/test1", ALPINE, "top"}) ctrSession.WaitWithDefaultTimeout() Expect(ctrSession.ExitCode()).To(Equal(0)) - inspectSource := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Source}}"}) + inspectSource := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Source}}"}) inspectSource.WaitWithDefaultTimeout() Expect(inspectSource.ExitCode()).To(Equal(0)) Expect(inspectSource.OutputToString()).To(Equal("/tmp")) - inspectSrc := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Src}}"}) + inspectSrc := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Src}}"}) inspectSrc.WaitWithDefaultTimeout() Expect(inspectSrc.ExitCode()).To(Equal(0)) Expect(inspectSrc.OutputToString()).To(Equal("/tmp")) - inspectDestination := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Destination}}"}) + inspectDestination := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Destination}}"}) inspectDestination.WaitWithDefaultTimeout() Expect(inspectDestination.ExitCode()).To(Equal(0)) Expect(inspectDestination.OutputToString()).To(Equal("/test1")) - inspectDst := podmanTest.Podman([]string{"inspect", "-l", "--format", "{{(index .Mounts 0).Dst}}"}) + inspectDst := podmanTest.Podman([]string{"inspect", "test", "--format", "{{(index .Mounts 0).Dst}}"}) inspectDst.WaitWithDefaultTimeout() Expect(inspectDst.ExitCode()).To(Equal(0)) Expect(inspectDst.OutputToString()).To(Equal("/test1")) @@ -171,4 +170,57 @@ var _ = Describe("Podman inspect", func() { Expect(imageData[0].HealthCheck.Interval).To(BeNumerically("==", 60000000000)) Expect(imageData[0].HealthCheck.Test).To(Equal([]string{"CMD-SHELL", "curl -f http://localhost/ || exit 1"})) }) + + It("podman inspect --latest with no container fails", func() { + SkipIfRemote() + + session := podmanTest.Podman([]string{"inspect", "--latest"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Not(Equal(0))) + }) + + It("podman [image,container] inspect on image", func() { + baseInspect := podmanTest.Podman([]string{"inspect", ALPINE}) + baseInspect.WaitWithDefaultTimeout() + Expect(baseInspect.ExitCode()).To(Equal(0)) + baseJSON := baseInspect.InspectImageJSON() + Expect(len(baseJSON)).To(Equal(1)) + + ctrInspect := podmanTest.Podman([]string{"container", "inspect", ALPINE}) + ctrInspect.WaitWithDefaultTimeout() + Expect(ctrInspect.ExitCode()).To(Not(Equal(0))) + + imageInspect := podmanTest.Podman([]string{"image", "inspect", ALPINE}) + imageInspect.WaitWithDefaultTimeout() + Expect(imageInspect.ExitCode()).To(Equal(0)) + imageJSON := imageInspect.InspectImageJSON() + Expect(len(imageJSON)).To(Equal(1)) + + Expect(baseJSON[0].ID).To(Equal(imageJSON[0].ID)) + }) + + It("podman [image, container] inspect on container", func() { + ctrName := "testCtr" + create := podmanTest.Podman([]string{"create", "--name", ctrName, ALPINE, "sh"}) + create.WaitWithDefaultTimeout() + Expect(create.ExitCode()).To(Equal(0)) + + baseInspect := podmanTest.Podman([]string{"inspect", ctrName}) + baseInspect.WaitWithDefaultTimeout() + Expect(baseInspect.ExitCode()).To(Equal(0)) + baseJSON := baseInspect.InspectContainerToJSON() + Expect(len(baseJSON)).To(Equal(1)) + + ctrInspect := podmanTest.Podman([]string{"container", "inspect", ctrName}) + ctrInspect.WaitWithDefaultTimeout() + Expect(ctrInspect.ExitCode()).To(Equal(0)) + ctrJSON := ctrInspect.InspectContainerToJSON() + Expect(len(ctrJSON)).To(Equal(1)) + + imageInspect := podmanTest.Podman([]string{"image", "inspect", ctrName}) + imageInspect.WaitWithDefaultTimeout() + Expect(imageInspect.ExitCode()).To(Not(Equal(0))) + + Expect(baseJSON[0].ID).To(Equal(ctrJSON[0].ID)) + }) }) diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go index dde853413..b94b6e267 100644 --- a/test/e2e/libpod_suite_remote_test.go +++ b/test/e2e/libpod_suite_remote_test.go @@ -36,7 +36,7 @@ func SkipIfRootlessV2() { // Podman is the exec call to podman on the filesystem func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration { - var remoteArgs = []string{"--remote", p.RemoteSocket} + var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} remoteArgs = append(remoteArgs, args...) podmanSession := p.PodmanBase(remoteArgs, false, false) return &PodmanSessionIntegration{podmanSession} @@ -44,7 +44,7 @@ func (p *PodmanTestIntegration) Podman(args []string) *PodmanSessionIntegration // 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", p.RemoteSocket} + var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} remoteArgs = append(remoteArgs, args...) podmanSession := p.PodmanAsUserBase(remoteArgs, 0, 0, "", nil, false, false, extraFiles) return &PodmanSessionIntegration{podmanSession} @@ -52,7 +52,7 @@ func (p *PodmanTestIntegration) PodmanExtraFiles(args []string, extraFiles []*os // PodmanNoCache calls podman with out adding the imagecache func (p *PodmanTestIntegration) PodmanNoCache(args []string) *PodmanSessionIntegration { - var remoteArgs = []string{"--remote", p.RemoteSocket} + var remoteArgs = []string{"--remote", "--url", p.RemoteSocket} remoteArgs = append(remoteArgs, args...) podmanSession := p.PodmanBase(remoteArgs, false, true) return &PodmanSessionIntegration{podmanSession} diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index e56db54a2..a7d5783cb 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -238,7 +238,6 @@ var _ = Describe("Podman pod create", func() { }) It("podman create pod with IP address", func() { - SkipIfRemote() SkipIfRootless() name := "test" ip := GetRandomIPAddress() diff --git a/test/e2e/pod_rm_test.go b/test/e2e/pod_rm_test.go index e10b3c98f..4060e1268 100644 --- a/test/e2e/pod_rm_test.go +++ b/test/e2e/pod_rm_test.go @@ -213,7 +213,6 @@ var _ = Describe("Podman pod rm", func() { }) It("podman rm --ignore bogus pod and a running pod", func() { - SkipIfRemote() _, ec, podid1 := podmanTest.CreatePod("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/pod_stats_test.go b/test/e2e/pod_stats_test.go index 9bba59073..778b44d1e 100644 --- a/test/e2e/pod_stats_test.go +++ b/test/e2e/pod_stats_test.go @@ -178,4 +178,21 @@ var _ = Describe("Podman pod stats", func() { Expect(stats).To(ExitWithError()) }) + It("podman stats on net=host post", func() { + // --net=host not supported for rootless pods at present + SkipIfRootless() + podName := "testPod" + podCreate := podmanTest.Podman([]string{"pod", "create", "--net=host", "--name", podName}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + ctrRun := podmanTest.Podman([]string{"run", "-d", "--pod", podName, ALPINE, "top"}) + ctrRun.WaitWithDefaultTimeout() + Expect(ctrRun.ExitCode()).To(Equal(0)) + + stats := podmanTest.Podman([]string{"pod", "stats", "--format", "json", "--no-stream", podName}) + stats.WaitWithDefaultTimeout() + Expect(stats.ExitCode()).To(Equal(0)) + Expect(stats.IsJSONOutputValid()).To(BeTrue()) + }) }) diff --git a/test/e2e/pod_stop_test.go b/test/e2e/pod_stop_test.go index 298f3da2f..0a46b07c9 100644 --- a/test/e2e/pod_stop_test.go +++ b/test/e2e/pod_stop_test.go @@ -39,7 +39,6 @@ var _ = Describe("Podman pod stop", func() { }) It("podman pod stop --ignore bogus pod", func() { - SkipIfRemote() session := podmanTest.Podman([]string{"pod", "stop", "--ignore", "123"}) session.WaitWithDefaultTimeout() @@ -60,7 +59,6 @@ var _ = Describe("Podman pod stop", func() { }) It("podman stop --ignore bogus pod and a running pod", func() { - SkipIfRemote() _, ec, podid1 := podmanTest.CreatePod("") Expect(ec).To(Equal(0)) diff --git a/test/e2e/ps_test.go b/test/e2e/ps_test.go index 12ce4661f..0dc8e01af 100644 --- a/test/e2e/ps_test.go +++ b/test/e2e/ps_test.go @@ -362,8 +362,6 @@ var _ = Describe("Podman ps", func() { }) It("podman --pod with a non-empty pod name", func() { - SkipIfRemote() - podName := "testPodName" _, ec, podid := podmanTest.CreatePod(podName) Expect(ec).To(Equal(0)) diff --git a/test/e2e/pull_test.go b/test/e2e/pull_test.go index 96340ef30..ac882927f 100644 --- a/test/e2e/pull_test.go +++ b/test/e2e/pull_test.go @@ -351,7 +351,6 @@ var _ = Describe("Podman pull", func() { }) It("podman pull from docker with nonexist --authfile", func() { - SkipIfRemote() session := podmanTest.PodmanNoCache([]string{"pull", "--authfile", "/tmp/nonexist", ALPINE}) session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Not(Equal(0))) diff --git a/test/e2e/rm_test.go b/test/e2e/rm_test.go index 87e3de922..d0dbd527a 100644 --- a/test/e2e/rm_test.go +++ b/test/e2e/rm_test.go @@ -141,7 +141,6 @@ var _ = Describe("Podman rm", func() { }) It("podman rm --cidfile", func() { - SkipIfRemote() tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) @@ -164,7 +163,6 @@ var _ = Describe("Podman rm", func() { }) It("podman rm multiple --cidfile", func() { - SkipIfRemote() tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) @@ -235,7 +233,6 @@ var _ = Describe("Podman rm", func() { }) It("podman rm --ignore bogus container and a running container", func() { - SkipIfRemote() session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 9db2f5d49..4fad85f00 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -129,6 +129,32 @@ var _ = Describe("Podman run networking", func() { Expect(inspectOut[0].NetworkSettings.Ports[0].HostIP).To(Equal("127.0.0.1")) }) + It("podman run -p [::1]:8080:80/udp", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", "[::1]:8080:80/udp", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports[0].HostPort).To(Equal(int32(8080))) + Expect(inspectOut[0].NetworkSettings.Ports[0].ContainerPort).To(Equal(int32(80))) + Expect(inspectOut[0].NetworkSettings.Ports[0].Protocol).To(Equal("udp")) + Expect(inspectOut[0].NetworkSettings.Ports[0].HostIP).To(Equal("::1")) + }) + + It("podman run -p [::1]:8080:80/tcp", func() { + name := "testctr" + session := podmanTest.Podman([]string{"create", "-t", "-p", "[::1]:8080:80/tcp", "--name", name, ALPINE, "/bin/sh"}) + session.WaitWithDefaultTimeout() + inspectOut := podmanTest.InspectContainer(name) + Expect(len(inspectOut)).To(Equal(1)) + Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1)) + Expect(inspectOut[0].NetworkSettings.Ports[0].HostPort).To(Equal(int32(8080))) + Expect(inspectOut[0].NetworkSettings.Ports[0].ContainerPort).To(Equal(int32(80))) + Expect(inspectOut[0].NetworkSettings.Ports[0].Protocol).To(Equal("tcp")) + Expect(inspectOut[0].NetworkSettings.Ports[0].HostIP).To(Equal("::1")) + }) + It("podman run --expose 80 -P", func() { name := "testctr" session := podmanTest.Podman([]string{"create", "-t", "--expose", "80", "-P", "--name", name, ALPINE, "/bin/sh"}) diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index cd78a54e1..4ed0b6293 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -233,7 +233,6 @@ var _ = Describe("Podman stop", func() { }) It("podman stop --cidfile", func() { - SkipIfRemote() tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) @@ -258,7 +257,6 @@ var _ = Describe("Podman stop", func() { }) It("podman stop multiple --cidfile", func() { - SkipIfRemote() tmpDir, err := ioutil.TempDir("", "") Expect(err).To(BeNil()) diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index 1275670eb..a35e5113a 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/containers/libpod/pkg/cgroups" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -82,13 +81,6 @@ WantedBy=multi-user.target }) It("podman run container with systemd PID1", func() { - cgroupsv2, err := cgroups.IsCgroup2UnifiedMode() - Expect(err).To(BeNil()) - if cgroupsv2 { - // TODO: Find a way to enable this for v2 - Skip("systemd test does not work in cgroups V2 mode yet") - } - systemdImage := "fedora" pull := podmanTest.Podman([]string{"pull", systemdImage}) pull.WaitWithDefaultTimeout() diff --git a/test/e2e/volume_rm_test.go b/test/e2e/volume_rm_test.go index 6f2020828..742d4e0dc 100644 --- a/test/e2e/volume_rm_test.go +++ b/test/e2e/volume_rm_test.go @@ -48,7 +48,6 @@ var _ = Describe("Podman volume rm", func() { }) It("podman volume rm with --force flag", func() { - SkipIfRemote() session := podmanTest.Podman([]string{"create", "-v", "myvol:/myvol", ALPINE, "ls"}) cid := session.OutputToString() session.WaitWithDefaultTimeout() diff --git a/test/system/080-pause.bats b/test/system/080-pause.bats new file mode 100644 index 000000000..4ec0906f4 --- /dev/null +++ b/test/system/080-pause.bats @@ -0,0 +1,58 @@ +#!/usr/bin/env bats -*- bats -*- +# +# tests for podman pause/unpause functionality +# + +load helpers + +@test "podman pause/unpause" { + skip_if_rootless "pause does not work rootless" + + cname=$(random_string 10) + run_podman run -d --name $cname $IMAGE \ + sh -c 'while :;do date +%s;sleep 1;done' + cid="$output" + # Wait for first time value + wait_for_output '[0-9]\{10,\}' $cid + + # Pause container, sleep a bit, unpause, sleep again to give process + # time to write a new post-restart time value. Pause by CID, unpause + # by name, just to exercise code paths. While paused, check 'ps' + # and 'inspect', then check again after restarting. + run_podman pause $cid + run_podman inspect --format '{{.State.Status}}' $cid + is "$output" "paused" "podman inspect .State.Status" + sleep 3 + run_podman ps -a --format '{{.ID}} {{.Names}} {{.Status}}' + is "$output" "${cid:0:12} $cname paused" "podman ps on paused container" + run_podman unpause $cname + run_podman ps -a --format '{{.ID}} {{.Names}} {{.Status}}' + is "$output" "${cid:0:12} $cname Up .*" "podman ps on resumed container" + sleep 1 + + # Get full logs, and iterate through them computing delta_t between entries + run_podman logs $cid + i=1 + max_delta=0 + while [ $i -lt ${#lines[*]} ]; do + this_delta=$(( ${lines[$i]} - ${lines[$(($i - 1))]} )) + if [ $this_delta -gt $max_delta ]; then + max_delta=$this_delta + fi + i=$(( $i + 1 )) + done + + # There should be a 3-4 second gap, *maybe* 5. Never 1 or 2, that + # would imply that the container never paused. + is "$max_delta" "[3456]" "delta t between paused and restarted" + + run_podman rm -f $cname + + # Pause/unpause on nonexistent name or id - these should all fail + run_podman 125 pause $cid + run_podman 125 pause $cname + run_podman 125 unpause $cid + run_podman 125 unpause $cname +} + +# vim: filetype=sh diff --git a/test/system/200-pod.bats b/test/system/200-pod.bats index 0d14ca990..9a6b39057 100644 --- a/test/system/200-pod.bats +++ b/test/system/200-pod.bats @@ -73,6 +73,12 @@ function teardown() { run_podman run -d --pod $podname $IMAGE nc -l -p $port cid1="$output" + # (While we're here, test the 'Pod' field of 'podman ps'. Expect two ctrs) + run_podman ps --format '{{.Pod}}' + newline=" +" + is "$output" "${podid:0:12}${newline}${podid:0:12}" "sdfdsf" + # Talker: send the message via common port on localhost message=$(random_string 15) run_podman run --rm --pod $podname $IMAGE \ diff --git a/test/system/220-healthcheck.bats b/test/system/220-healthcheck.bats new file mode 100644 index 000000000..e649ad3d2 --- /dev/null +++ b/test/system/220-healthcheck.bats @@ -0,0 +1,116 @@ +#!/usr/bin/env bats -*- bats -*- +# +# tests for podman healthcheck +# +# + +load helpers + + +# Helper function: run 'podman inspect' and check various given fields +function _check_health { + local testname="$1" + local tests="$2" + + run_podman inspect --format json healthcheck_c + + parse_table "$tests" | while read field expect;do + # (kludge to deal with parse_table and empty strings) + if [ "$expect" = "''" ]; then expect=""; fi + + actual=$(jq -r ".[0].State.Healthcheck.$field" <<<"$output") + is "$actual" "$expect" "$testname - .State.Healthcheck.$field" + done +} + + +@test "podman healthcheck" { + + # Create an image with a healthcheck script; said script will + # pass until the file /uh-oh gets created (by us, via exec) + cat >${PODMAN_TMPDIR}/healthcheck <<EOF +#!/bin/sh + +if test -e /uh-oh; then + echo "Uh-oh on stdout!" + echo "Uh-oh on stderr!" >&2 + exit 1 +else + echo "Life is Good on stdout" + echo "Life is Good on stderr" >&2 + exit 0 +fi +EOF + + cat >${PODMAN_TMPDIR}/entrypoint <<EOF +#!/bin/sh + +while :; do + sleep 1 +done +EOF + + cat >${PODMAN_TMPDIR}/Containerfile <<EOF +FROM $IMAGE + +COPY healthcheck /healthcheck +COPY entrypoint /entrypoint + +RUN chmod 755 /healthcheck /entrypoint + +CMD ["/entrypoint"] +EOF + + run_podman build -t healthcheck_i ${PODMAN_TMPDIR} + + # Run that healthcheck image. + run_podman run -d --name healthcheck_c \ + --health-cmd /healthcheck \ + --health-interval 1s \ + --health-retries 3 \ + healthcheck_i + + # We can't check for 'starting' because a 1-second interval is too + # short; it could run healthcheck before we get to our first check. + # + # So, just force a healthcheck run, then confirm that it's running. + run_podman healthcheck run healthcheck_c + is "$output" "healthy" "output from 'podman healthcheck run'" + + _check_health "All healthy" " +Status | healthy +FailingStreak | 0 +Log[-1].ExitCode | 0 +Log[-1].Output | +" + + # Force a failure + run_podman exec healthcheck_c touch /uh-oh + sleep 2 + + _check_health "First failure" " +Status | healthy +FailingStreak | [123] +Log[-1].ExitCode | 1 +Log[-1].Output | +" + + # After three successive failures, container should no longer be healthy + sleep 5 + _check_health "Three or more failures" " +Status | unhealthy +FailingStreak | [3456] +Log[-1].ExitCode | 1 +Log[-1].Output | +" + + # healthcheck should now fail, with exit status 1 and 'unhealthy' output + run_podman 1 healthcheck run healthcheck_c + is "$output" "unhealthy" "output from 'podman healthcheck run'" + + # Clean up + run_podman rm -f healthcheck_c + run_podman rmi healthcheck_i +} + +# vim: filetype=sh diff --git a/test/utils/utils.go b/test/utils/utils.go index 1d59e5468..0597cd292 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -73,7 +73,7 @@ func (p *PodmanTest) PodmanAsUserBase(args []string, uid, gid uint32, cwd string podmanBinary = p.RemotePodmanBinary } if p.RemoteTest { - podmanOptions = append([]string{"--remote", p.RemoteSocket}, podmanOptions...) + podmanOptions = append([]string{"--remote", "--url", p.RemoteSocket}, podmanOptions...) } if env == nil { fmt.Printf("Running: %s %s\n", podmanBinary, strings.Join(podmanOptions, " ")) diff --git a/vendor/github.com/containers/common/pkg/config/config.go b/vendor/github.com/containers/common/pkg/config/config.go index d0b56c7f6..9657ecb69 100644 --- a/vendor/github.com/containers/common/pkg/config/config.go +++ b/vendor/github.com/containers/common/pkg/config/config.go @@ -269,6 +269,9 @@ type EngineConfig struct { // RemoteURI containers connection information used to connect to remote system. RemoteURI string `toml:"remote_uri,omitempty"` + // Identity key file for RemoteURI + RemoteIdentity string `toml:"remote_identity,omitempty"` + // RuntimePath is the path to OCI runtime binary for launching containers. // The first path pointing to a valid file will be used This is used only // when there are no OCIRuntime/OCIRuntimes defined. It is used only to be diff --git a/vendor/github.com/containers/storage/VERSION b/vendor/github.com/containers/storage/VERSION index 0044d6cb9..769e37e15 100644 --- a/vendor/github.com/containers/storage/VERSION +++ b/vendor/github.com/containers/storage/VERSION @@ -1 +1 @@ -1.20.1 +1.20.2 diff --git a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go index f1c941f11..ff6e297f4 100644 --- a/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go +++ b/vendor/github.com/containers/storage/drivers/devmapper/deviceset.go @@ -1544,8 +1544,8 @@ func getDeviceMajorMinor(file *os.File) (uint64, uint64, error) { } dev := stat.Rdev - majorNum := major(dev) - minorNum := minor(dev) + majorNum := major(uint64(dev)) + minorNum := minor(uint64(dev)) logrus.Debugf("devmapper: Major:Minor for device: %s is:%v:%v", file.Name(), majorNum, minorNum) return majorNum, minorNum, nil diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go index 2906e3e08..930a57a97 100644 --- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go +++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go @@ -311,6 +311,9 @@ func parseOptions(options []string) (*overlayOptions, error) { return nil, fmt.Errorf("overlay: can't stat program %s: %v", val, err) } o.mountProgram = val + case "overlay2.skip_mount_home", "overlay.skip_mount_home", ".skip_mount_home": + logrus.Debugf("overlay: skip_mount_home=%s", val) + o.skipMountHome, err = strconv.ParseBool(val) case ".ignore_chown_errors", "overlay2.ignore_chown_errors", "overlay.ignore_chown_errors": logrus.Debugf("overlay: ignore_chown_errors=%s", val) o.ignoreChownErrors, err = strconv.ParseBool(val) diff --git a/vendor/github.com/containers/storage/go.mod b/vendor/github.com/containers/storage/go.mod index a7d9ade60..01ac1827b 100644 --- a/vendor/github.com/containers/storage/go.mod +++ b/vendor/github.com/containers/storage/go.mod @@ -6,23 +6,23 @@ require ( github.com/Microsoft/hcsshim v0.8.9 github.com/docker/go-units v0.4.0 github.com/hashicorp/go-multierror v1.0.0 - github.com/klauspost/compress v1.10.5 + github.com/klauspost/compress v1.10.7 github.com/klauspost/pgzip v1.2.4 github.com/mattn/go-shellwords v1.0.10 github.com/mistifyio/go-zfs v2.1.1+incompatible github.com/opencontainers/go-digest v1.0.0 - github.com/opencontainers/runc v1.0.0-rc9 + github.com/opencontainers/runc v1.0.0-rc90 github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700 - github.com/opencontainers/selinux v1.5.1 + github.com/opencontainers/selinux v1.5.2 github.com/pkg/errors v0.9.1 github.com/pquerna/ffjson v0.0.0-20181028064349-e517b90714f7 github.com/sirupsen/logrus v1.6.0 - github.com/stretchr/testify v1.5.1 + github.com/stretchr/testify v1.6.0 github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 github.com/tchap/go-patricia v2.3.0+incompatible github.com/vbatts/tar-split v0.11.1 golang.org/x/net v0.0.0-20191004110552-13f9640d40b9 - golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 + golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 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 eab0fd61e..2b5a279c6 100644 --- a/vendor/github.com/containers/storage/go.sum +++ b/vendor/github.com/containers/storage/go.sum @@ -8,6 +8,7 @@ github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg3 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f h1:tSNMc+rJDfmYntojat8lljbt1mgKNpTxUZJsSzJ9Y1s= github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1 h1:uict5mhHFTzKLUCufdSLym7z/J0CbBJT59lYbP9wtbg= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= @@ -15,12 +16,14 @@ github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +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/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/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/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/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= @@ -29,6 +32,7 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU 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.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= @@ -42,8 +46,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ 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/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.10.5 h1:7q6vHIqubShURwQz8cQK6yIe/xC3IF0Vm7TGfqjewrc= -github.com/klauspost/compress v1.10.5/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.10.7 h1:7rix8v8GpI3ZBb0nSozFRgbtXKv+hOe+qfEpZqybrAg= +github.com/klauspost/compress v1.10.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/pgzip v1.2.4 h1:TQ7CNpYKovDOmqzRHKxJh0BeaBI7UdQZYc6p7pMQh1A= github.com/klauspost/pgzip v1.2.4/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -62,12 +66,12 @@ github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1 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/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9 h1:/k06BMULKF5hidyoZymkoDCzdJzltZpz/UU4LguQVtc= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc90 h1:4+xo8mtWixbHoEm451+WJNUrq12o2/tDsyK9Vgc/NcA= +github.com/opencontainers/runc v1.0.0-rc90/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700 h1:eNUVfm/RFLIi1G7flU5/ZRTHvd4kcVuzfRnL6OFlzCI= github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.5.1 h1:jskKwSMFYqyTrHEuJgQoUlTcId0av64S6EWObrIfn5Y= -github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= +github.com/opencontainers/selinux v1.5.2 h1:F6DgIsjgBIcDksLW4D5RG9bXok6oqZ3nvMwj4ZoFu/Q= +github.com/opencontainers/selinux v1.5.2/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= 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= @@ -81,15 +85,17 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= +github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 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/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5 h1:MCfT24H3f//U5+UCrZp1/riVO3B50BovxtDiNn0XKkk= github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/vbatts/tar-split v0.11.1 h1:0Odu65rhcZ3JZaPHxl7tCI3V/C/Q9Zf82UFravl02dE= github.com/vbatts/tar-split v0.11.1/go.mod h1:LEuURwDEiWjRjwu46yU3KVGuUdVv/dcnpcEPSzR8z6g= @@ -120,8 +126,8 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2 h1:/J2nHFg1MTqaRLFO7M+J78ASNsJoz3r0cvHBPQ77fsE= -golang.org/x/sys v0.0.0-20191127021746-63cb32ae39b2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -145,6 +151,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/containers/storage/pkg/config/config.go b/vendor/github.com/containers/storage/pkg/config/config.go index 1ac6c04e3..4a35997ea 100644 --- a/vendor/github.com/containers/storage/pkg/config/config.go +++ b/vendor/github.com/containers/storage/pkg/config/config.go @@ -2,8 +2,6 @@ package config import ( "fmt" - - "github.com/sirupsen/logrus" ) // ThinpoolOptionsConfig represents the "storage.options.thinpool" @@ -269,11 +267,11 @@ func GetGraphDriverOptions(driverName string, options OptionsConfig) []string { } else if options.Size != "" { doptions = append(doptions, fmt.Sprintf("%s.size=%s", driverName, options.Size)) } - - if options.Overlay.SkipMountHome != "" || options.SkipMountHome != "" { - logrus.Warn("skip_mount_home option is no longer supported, ignoring option") + if options.Overlay.SkipMountHome != "" { + doptions = append(doptions, fmt.Sprintf("%s.skip_mount_home=%s", driverName, options.Overlay.SkipMountHome)) + } else if options.SkipMountHome != "" { + doptions = append(doptions, fmt.Sprintf("%s.skip_mount_home=%s", driverName, options.SkipMountHome)) } - case "vfs": if options.Vfs.IgnoreChownErrors != "" { doptions = append(doptions, fmt.Sprintf("%s.ignore_chown_errors=%s", driverName, options.Vfs.IgnoreChownErrors)) diff --git a/vendor/github.com/containers/storage/storage.conf b/vendor/github.com/containers/storage/storage.conf index c7f9b2cf8..19909e9c6 100644 --- a/vendor/github.com/containers/storage/storage.conf +++ b/vendor/github.com/containers/storage/storage.conf @@ -76,6 +76,9 @@ additionalimagestores = [ # mountopt specifies comma separated list of extra mount options mountopt = "nodev" +# Set to skip a PRIVATE bind mount on the storage home directory. +# skip_mount_home = "false" + # Size is used to set a maximum size of the container image. # size = "" diff --git a/vendor/github.com/containers/storage/store.go b/vendor/github.com/containers/storage/store.go index eaf622f43..263f4edb2 100644 --- a/vendor/github.com/containers/storage/store.go +++ b/vendor/github.com/containers/storage/store.go @@ -3481,6 +3481,9 @@ func ReloadConfigurationFile(configFile string, storeOptions *StoreOptions) { if config.Storage.Options.MountProgram != "" { storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, fmt.Sprintf("%s.mount_program=%s", config.Storage.Driver, config.Storage.Options.MountProgram)) } + if config.Storage.Options.SkipMountHome != "" { + storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, fmt.Sprintf("%s.skip_mount_home=%s", config.Storage.Driver, config.Storage.Options.SkipMountHome)) + } if config.Storage.Options.IgnoreChownErrors != "" { storeOptions.GraphDriverOptions = append(storeOptions.GraphDriverOptions, fmt.Sprintf("%s.ignore_chown_errors=%s", config.Storage.Driver, config.Storage.Options.IgnoreChownErrors)) } diff --git a/vendor/github.com/json-iterator/go/README.md b/vendor/github.com/json-iterator/go/README.md index 50d56ffbf..52b111d5f 100644 --- a/vendor/github.com/json-iterator/go/README.md +++ b/vendor/github.com/json-iterator/go/README.md @@ -1,5 +1,5 @@ [![Sourcegraph](https://sourcegraph.com/github.com/json-iterator/go/-/badge.svg)](https://sourcegraph.com/github.com/json-iterator/go?badge) -[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/json-iterator/go) +[![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](https://pkg.go.dev/github.com/json-iterator/go) [![Build Status](https://travis-ci.org/json-iterator/go.svg?branch=master)](https://travis-ci.org/json-iterator/go) [![codecov](https://codecov.io/gh/json-iterator/go/branch/master/graph/badge.svg)](https://codecov.io/gh/json-iterator/go) [![rcard](https://goreportcard.com/badge/github.com/json-iterator/go)](https://goreportcard.com/report/github.com/json-iterator/go) @@ -18,16 +18,16 @@ Source code: https://github.com/json-iterator/go-benchmark/blob/master/src/githu Raw Result (easyjson requires static code generation) -| | ns/op | allocation bytes | allocation times | -| --- | --- | --- | --- | -| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | -| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | -| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | -| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | -| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | -| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | +| | ns/op | allocation bytes | allocation times | +| --------------- | ----------- | ---------------- | ---------------- | +| std decode | 35510 ns/op | 1960 B/op | 99 allocs/op | +| easyjson decode | 8499 ns/op | 160 B/op | 4 allocs/op | +| jsoniter decode | 5623 ns/op | 160 B/op | 3 allocs/op | +| std encode | 2213 ns/op | 712 B/op | 5 allocs/op | +| easyjson encode | 883 ns/op | 576 B/op | 3 allocs/op | +| jsoniter encode | 837 ns/op | 384 B/op | 4 allocs/op | -Always benchmark with your own workload. +Always benchmark with your own workload. The result depends heavily on the data input. # Usage @@ -41,10 +41,10 @@ import "encoding/json" json.Marshal(&data) ``` -with +with ```go -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Marshal(&data) @@ -60,7 +60,7 @@ json.Unmarshal(input, &data) with ```go -import "github.com/json-iterator/go" +import jsoniter "github.com/json-iterator/go" var json = jsoniter.ConfigCompatibleWithStandardLibrary json.Unmarshal(input, &data) @@ -78,10 +78,10 @@ go get github.com/json-iterator/go Contributors -* [thockin](https://github.com/thockin) -* [mattn](https://github.com/mattn) -* [cch123](https://github.com/cch123) -* [Oleg Shaldybin](https://github.com/olegshaldybin) -* [Jason Toffaletti](https://github.com/toffaletti) +- [thockin](https://github.com/thockin) +- [mattn](https://github.com/mattn) +- [cch123](https://github.com/cch123) +- [Oleg Shaldybin](https://github.com/olegshaldybin) +- [Jason Toffaletti](https://github.com/toffaletti) Report issue or pull request, or email taowen@gmail.com, or [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/json-iterator/Lobby) diff --git a/vendor/github.com/json-iterator/go/any_str.go b/vendor/github.com/json-iterator/go/any_str.go index a4b93c78c..1f12f6612 100644 --- a/vendor/github.com/json-iterator/go/any_str.go +++ b/vendor/github.com/json-iterator/go/any_str.go @@ -64,7 +64,6 @@ func (any *stringAny) ToInt64() int64 { flag := 1 startPos := 0 - endPos := 0 if any.val[0] == '+' || any.val[0] == '-' { startPos = 1 } @@ -73,6 +72,7 @@ func (any *stringAny) ToInt64() int64 { flag = -1 } + endPos := startPos for i := startPos; i < len(any.val); i++ { if any.val[i] >= '0' && any.val[i] <= '9' { endPos = i + 1 @@ -98,7 +98,6 @@ func (any *stringAny) ToUint64() uint64 { } startPos := 0 - endPos := 0 if any.val[0] == '-' { return 0 @@ -107,6 +106,7 @@ func (any *stringAny) ToUint64() uint64 { startPos = 1 } + endPos := startPos for i := startPos; i < len(any.val); i++ { if any.val[i] >= '0' && any.val[i] <= '9' { endPos = i + 1 diff --git a/vendor/github.com/json-iterator/go/config.go b/vendor/github.com/json-iterator/go/config.go index 8c58fcba5..2adcdc3b7 100644 --- a/vendor/github.com/json-iterator/go/config.go +++ b/vendor/github.com/json-iterator/go/config.go @@ -183,11 +183,11 @@ func (cfg *frozenConfig) validateJsonRawMessage(extension EncoderExtension) { encoder := &funcEncoder{func(ptr unsafe.Pointer, stream *Stream) { rawMessage := *(*json.RawMessage)(ptr) iter := cfg.BorrowIterator([]byte(rawMessage)) + defer cfg.ReturnIterator(iter) iter.Read() - if iter.Error != nil { + if iter.Error != nil && iter.Error != io.EOF { stream.WriteRaw("null") } else { - cfg.ReturnIterator(iter) stream.WriteRaw(string(rawMessage)) } }, func(ptr unsafe.Pointer) bool { diff --git a/vendor/github.com/json-iterator/go/iter_object.go b/vendor/github.com/json-iterator/go/iter_object.go index b65137114..58ee89c84 100644 --- a/vendor/github.com/json-iterator/go/iter_object.go +++ b/vendor/github.com/json-iterator/go/iter_object.go @@ -150,7 +150,7 @@ func (iter *Iterator) ReadObjectCB(callback func(*Iterator, string) bool) bool { if c == '}' { return iter.decrementDepth() } - iter.ReportError("ReadObjectCB", `expect " after }, but found `+string([]byte{c})) + iter.ReportError("ReadObjectCB", `expect " after {, but found `+string([]byte{c})) iter.decrementDepth() return false } @@ -206,7 +206,7 @@ func (iter *Iterator) ReadMapCB(callback func(*Iterator, string) bool) bool { if c == '}' { return iter.decrementDepth() } - iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c})) + iter.ReportError("ReadMapCB", `expect " after {, but found `+string([]byte{c})) iter.decrementDepth() return false } diff --git a/vendor/github.com/json-iterator/go/reflect_extension.go b/vendor/github.com/json-iterator/go/reflect_extension.go index 80320cd64..74a97bfe5 100644 --- a/vendor/github.com/json-iterator/go/reflect_extension.go +++ b/vendor/github.com/json-iterator/go/reflect_extension.go @@ -475,7 +475,7 @@ func calcFieldNames(originalFieldName string, tagProvidedFieldName string, whole fieldNames = []string{tagProvidedFieldName} } // private? - isNotExported := unicode.IsLower(rune(originalFieldName[0])) + isNotExported := unicode.IsLower(rune(originalFieldName[0])) || originalFieldName[0] == '_' if isNotExported { fieldNames = []string{} } diff --git a/vendor/github.com/json-iterator/go/reflect_map.go b/vendor/github.com/json-iterator/go/reflect_map.go index 9e2b623fe..582967130 100644 --- a/vendor/github.com/json-iterator/go/reflect_map.go +++ b/vendor/github.com/json-iterator/go/reflect_map.go @@ -49,6 +49,33 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { return decoder } } + + ptrType := reflect2.PtrTo(typ) + if ptrType.Implements(unmarshalerType) { + return &referenceDecoder{ + &unmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(unmarshalerType) { + return &unmarshalerDecoder{ + valType: typ, + } + } + if ptrType.Implements(textUnmarshalerType) { + return &referenceDecoder{ + &textUnmarshalerDecoder{ + valType: ptrType, + }, + } + } + if typ.Implements(textUnmarshalerType) { + return &textUnmarshalerDecoder{ + valType: typ, + } + } + switch typ.Kind() { case reflect.String: return decoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) @@ -63,31 +90,6 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { typ = reflect2.DefaultTypeOfKind(typ.Kind()) return &numericMapKeyDecoder{decoderOfType(ctx, typ)} default: - ptrType := reflect2.PtrTo(typ) - if ptrType.Implements(unmarshalerType) { - return &referenceDecoder{ - &unmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(unmarshalerType) { - return &unmarshalerDecoder{ - valType: typ, - } - } - if ptrType.Implements(textUnmarshalerType) { - return &referenceDecoder{ - &textUnmarshalerDecoder{ - valType: ptrType, - }, - } - } - if typ.Implements(textUnmarshalerType) { - return &textUnmarshalerDecoder{ - valType: typ, - } - } return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)} } } @@ -103,6 +105,19 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { return encoder } } + + if typ == textMarshalerType { + return &directTextMarshalerEncoder{ + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + if typ.Implements(textMarshalerType) { + return &textMarshalerEncoder{ + valType: typ, + stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), + } + } + switch typ.Kind() { case reflect.String: return encoderOfType(ctx, reflect2.DefaultTypeOfKind(reflect.String)) @@ -117,17 +132,6 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { typ = reflect2.DefaultTypeOfKind(typ.Kind()) return &numericMapKeyEncoder{encoderOfType(ctx, typ)} default: - if typ == textMarshalerType { - return &directTextMarshalerEncoder{ - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } - if typ.Implements(textMarshalerType) { - return &textMarshalerEncoder{ - valType: typ, - stringEncoder: ctx.EncoderOf(reflect2.TypeOf("")), - } - } if typ.Kind() == reflect.Interface { return &dynamicMapKeyEncoder{ctx, typ} } @@ -163,10 +167,6 @@ func (decoder *mapDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { if c == '}' { return } - if c != '"' { - iter.ReportError("ReadMapCB", `expect " after }, but found `+string([]byte{c})) - return - } iter.unreadByte() key := decoder.keyType.UnsafeNew() decoder.keyDecoder.Decode(key, iter) diff --git a/vendor/github.com/json-iterator/go/reflect_optional.go b/vendor/github.com/json-iterator/go/reflect_optional.go index 43ec71d6d..fa71f4748 100644 --- a/vendor/github.com/json-iterator/go/reflect_optional.go +++ b/vendor/github.com/json-iterator/go/reflect_optional.go @@ -2,7 +2,6 @@ package jsoniter import ( "github.com/modern-go/reflect2" - "reflect" "unsafe" ) @@ -10,9 +9,6 @@ func decoderOfOptional(ctx *ctx, typ reflect2.Type) ValDecoder { ptrType := typ.(*reflect2.UnsafePtrType) elemType := ptrType.Elem() decoder := decoderOfType(ctx, elemType) - if ctx.prefix == "" && elemType.Kind() == reflect.Ptr { - return &dereferenceDecoder{elemType, decoder} - } return &OptionalDecoder{elemType, decoder} } diff --git a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go index 5ad5cc561..d7eb0eb5c 100644 --- a/vendor/github.com/json-iterator/go/reflect_struct_decoder.go +++ b/vendor/github.com/json-iterator/go/reflect_struct_decoder.go @@ -507,7 +507,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) for c = ','; c == ','; c = iter.nextToken() { decoder.decodeOneField(ptr, iter) } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } if c != '}' { @@ -588,7 +588,7 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -622,7 +622,7 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -660,7 +660,7 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -702,7 +702,7 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -748,7 +748,7 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -798,7 +798,7 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -852,7 +852,7 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -910,7 +910,7 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -972,7 +972,7 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() @@ -1038,7 +1038,7 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator break } } - if iter.Error != nil && iter.Error != io.EOF { + if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) } iter.decrementDepth() diff --git a/vendor/github.com/json-iterator/go/stream.go b/vendor/github.com/json-iterator/go/stream.go index 17662fded..23d8a3ad6 100644 --- a/vendor/github.com/json-iterator/go/stream.go +++ b/vendor/github.com/json-iterator/go/stream.go @@ -103,14 +103,14 @@ func (stream *Stream) Flush() error { if stream.Error != nil { return stream.Error } - n, err := stream.out.Write(stream.buf) + _, err := stream.out.Write(stream.buf) if err != nil { if stream.Error == nil { stream.Error = err } return err } - stream.buf = stream.buf[n:] + stream.buf = stream.buf[:0] return nil } @@ -177,7 +177,6 @@ func (stream *Stream) WriteEmptyObject() { func (stream *Stream) WriteMore() { stream.writeByte(',') stream.writeIndention(0) - stream.Flush() } // WriteArrayStart write [ with possible indention diff --git a/vendor/github.com/klauspost/compress/huff0/decompress.go b/vendor/github.com/klauspost/compress/huff0/decompress.go index 97ae66a4a..fb42a398b 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress.go @@ -155,20 +155,70 @@ func ReadTable(in []byte, s *Scratch) (s2 *Scratch, remain []byte, err error) { // The length of the supplied input must match the end of a block exactly. // Before this is called, the table must be initialized with ReadTable unless // the encoder re-used the table. +// deprecated: Use the stateless Decoder() to get a concurrent version. func (s *Scratch) Decompress1X(in []byte) (out []byte, err error) { - if len(s.dt.single) == 0 { + if cap(s.Out) < s.MaxDecodedSize { + s.Out = make([]byte, s.MaxDecodedSize) + } + s.Out = s.Out[:0:s.MaxDecodedSize] + s.Out, err = s.Decoder().Decompress1X(s.Out, in) + return s.Out, err +} + +// Decompress4X will decompress a 4X encoded stream. +// Before this is called, the table must be initialized with ReadTable unless +// the encoder re-used the table. +// The length of the supplied input must match the end of a block exactly. +// The destination size of the uncompressed data must be known and provided. +// deprecated: Use the stateless Decoder() to get a concurrent version. +func (s *Scratch) Decompress4X(in []byte, dstSize int) (out []byte, err error) { + if dstSize > s.MaxDecodedSize { + return nil, ErrMaxDecodedSizeExceeded + } + if cap(s.Out) < dstSize { + s.Out = make([]byte, s.MaxDecodedSize) + } + s.Out = s.Out[:0:dstSize] + s.Out, err = s.Decoder().Decompress4X(s.Out, in) + return s.Out, err +} + +// Decoder will return a stateless decoder that can be used by multiple +// decompressors concurrently. +// Before this is called, the table must be initialized with ReadTable. +// The Decoder is still linked to the scratch buffer so that cannot be reused. +// However, it is safe to discard the scratch. +func (s *Scratch) Decoder() *Decoder { + return &Decoder{ + dt: s.dt, + actualTableLog: s.actualTableLog, + } +} + +// Decoder provides stateless decoding. +type Decoder struct { + dt dTable + actualTableLog uint8 +} + +// Decompress1X will decompress a 1X encoded stream. +// The cap of the output buffer will be the maximum decompressed size. +// The length of the supplied input must match the end of a block exactly. +func (d *Decoder) Decompress1X(dst, src []byte) ([]byte, error) { + if len(d.dt.single) == 0 { return nil, errors.New("no table loaded") } var br bitReader - err = br.init(in) + err := br.init(src) if err != nil { - return nil, err + return dst, err } - s.Out = s.Out[:0] + maxDecodedSize := cap(dst) + dst = dst[:0] decode := func() byte { - val := br.peekBitsFast(s.actualTableLog) /* note : actualTableLog >= 1 */ - v := s.dt.single[val] + val := br.peekBitsFast(d.actualTableLog) /* note : actualTableLog >= 1 */ + v := d.dt.single[val] br.bitsRead += uint8(v.entry) return uint8(v.entry >> 8) } @@ -180,88 +230,80 @@ func (s *Scratch) Decompress1X(in []byte) (out []byte, err error) { // Avoid bounds check by always having full sized table. const tlSize = 1 << tableLogMax const tlMask = tlSize - 1 - dt := s.dt.single[:tlSize] + dt := d.dt.single[:tlSize] // Use temp table to avoid bound checks/append penalty. - var tmp = s.huffWeight[:256] + var buf [256]byte var off uint8 for br.off >= 8 { br.fillFast() - tmp[off+0] = hasDec(dt[br.peekBitsFast(s.actualTableLog)&tlMask]) - tmp[off+1] = hasDec(dt[br.peekBitsFast(s.actualTableLog)&tlMask]) + buf[off+0] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask]) + buf[off+1] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask]) br.fillFast() - tmp[off+2] = hasDec(dt[br.peekBitsFast(s.actualTableLog)&tlMask]) - tmp[off+3] = hasDec(dt[br.peekBitsFast(s.actualTableLog)&tlMask]) + buf[off+2] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask]) + buf[off+3] = hasDec(dt[br.peekBitsFast(d.actualTableLog)&tlMask]) off += 4 if off == 0 { - if len(s.Out)+256 > s.MaxDecodedSize { + if len(dst)+256 > maxDecodedSize { br.close() return nil, ErrMaxDecodedSizeExceeded } - s.Out = append(s.Out, tmp...) + dst = append(dst, buf[:]...) } } - if len(s.Out)+int(off) > s.MaxDecodedSize { + if len(dst)+int(off) > maxDecodedSize { br.close() return nil, ErrMaxDecodedSizeExceeded } - s.Out = append(s.Out, tmp[:off]...) + dst = append(dst, buf[:off]...) for !br.finished() { br.fill() - if len(s.Out) >= s.MaxDecodedSize { + if len(dst) >= maxDecodedSize { br.close() return nil, ErrMaxDecodedSizeExceeded } - s.Out = append(s.Out, decode()) + dst = append(dst, decode()) } - return s.Out, br.close() + return dst, br.close() } // Decompress4X will decompress a 4X encoded stream. -// Before this is called, the table must be initialized with ReadTable unless -// the encoder re-used the table. // The length of the supplied input must match the end of a block exactly. -// The destination size of the uncompressed data must be known and provided. -func (s *Scratch) Decompress4X(in []byte, dstSize int) (out []byte, err error) { +// The *capacity* of the dst slice must match the destination size of +// the uncompressed data exactly. +func (s *Decoder) Decompress4X(dst, src []byte) ([]byte, error) { if len(s.dt.single) == 0 { return nil, errors.New("no table loaded") } - if len(in) < 6+(4*1) { + if len(src) < 6+(4*1) { return nil, errors.New("input too small") } - if dstSize > s.MaxDecodedSize { - return nil, ErrMaxDecodedSizeExceeded - } - // TODO: We do not detect when we overrun a buffer, except if the last one does. var br [4]bitReader start := 6 for i := 0; i < 3; i++ { - length := int(in[i*2]) | (int(in[i*2+1]) << 8) - if start+length >= len(in) { + length := int(src[i*2]) | (int(src[i*2+1]) << 8) + if start+length >= len(src) { return nil, errors.New("truncated input (or invalid offset)") } - err = br[i].init(in[start : start+length]) + err := br[i].init(src[start : start+length]) if err != nil { return nil, err } start += length } - err = br[3].init(in[start:]) + err := br[3].init(src[start:]) if err != nil { return nil, err } - // Prepare output - if cap(s.Out) < dstSize { - s.Out = make([]byte, 0, dstSize) - } - s.Out = s.Out[:dstSize] // destination, offset to match first output - dstOut := s.Out + dstSize := cap(dst) + dst = dst[:dstSize] + out := dst dstEvery := (dstSize + 3) / 4 const tlSize = 1 << tableLogMax @@ -276,7 +318,7 @@ func (s *Scratch) Decompress4X(in []byte, dstSize int) (out []byte, err error) { } // Use temp table to avoid bound checks/append penalty. - var tmp = s.huffWeight[:256] + var buf [256]byte var off uint8 var decoded int @@ -300,8 +342,8 @@ bigloop: val2 := br[stream].peekBitsFast(s.actualTableLog) v2 := single[val2&tlMask] - tmp[off+bufoff*stream+1] = uint8(v2.entry >> 8) - tmp[off+bufoff*stream] = uint8(v.entry >> 8) + buf[off+bufoff*stream+1] = uint8(v2.entry >> 8) + buf[off+bufoff*stream] = uint8(v.entry >> 8) br[stream].bitsRead += uint8(v2.entry) } @@ -313,8 +355,8 @@ bigloop: val2 := br[stream].peekBitsFast(s.actualTableLog) v2 := single[val2&tlMask] - tmp[off+bufoff*stream+1] = uint8(v2.entry >> 8) - tmp[off+bufoff*stream] = uint8(v.entry >> 8) + buf[off+bufoff*stream+1] = uint8(v2.entry >> 8) + buf[off+bufoff*stream] = uint8(v.entry >> 8) br[stream].bitsRead += uint8(v2.entry) } @@ -326,8 +368,8 @@ bigloop: val2 := br[stream].peekBitsFast(s.actualTableLog) v2 := single[val2&tlMask] - tmp[off+bufoff*stream+1] = uint8(v2.entry >> 8) - tmp[off+bufoff*stream] = uint8(v.entry >> 8) + buf[off+bufoff*stream+1] = uint8(v2.entry >> 8) + buf[off+bufoff*stream] = uint8(v.entry >> 8) br[stream].bitsRead += uint8(v2.entry) } @@ -339,8 +381,8 @@ bigloop: val2 := br[stream].peekBitsFast(s.actualTableLog) v2 := single[val2&tlMask] - tmp[off+bufoff*stream+1] = uint8(v2.entry >> 8) - tmp[off+bufoff*stream] = uint8(v.entry >> 8) + buf[off+bufoff*stream+1] = uint8(v2.entry >> 8) + buf[off+bufoff*stream] = uint8(v.entry >> 8) br[stream].bitsRead += uint8(v2.entry) } @@ -350,30 +392,30 @@ bigloop: if bufoff > dstEvery { return nil, errors.New("corruption detected: stream overrun 1") } - copy(dstOut, tmp[:bufoff]) - copy(dstOut[dstEvery:], tmp[bufoff:bufoff*2]) - copy(dstOut[dstEvery*2:], tmp[bufoff*2:bufoff*3]) - copy(dstOut[dstEvery*3:], tmp[bufoff*3:bufoff*4]) + copy(out, buf[:bufoff]) + copy(out[dstEvery:], buf[bufoff:bufoff*2]) + copy(out[dstEvery*2:], buf[bufoff*2:bufoff*3]) + copy(out[dstEvery*3:], buf[bufoff*3:bufoff*4]) off = 0 - dstOut = dstOut[bufoff:] + out = out[bufoff:] decoded += 256 // There must at least be 3 buffers left. - if len(dstOut) < dstEvery*3 { + if len(out) < dstEvery*3 { return nil, errors.New("corruption detected: stream overrun 2") } } } if off > 0 { ioff := int(off) - if len(dstOut) < dstEvery*3+ioff { + if len(out) < dstEvery*3+ioff { return nil, errors.New("corruption detected: stream overrun 3") } - copy(dstOut, tmp[:off]) - copy(dstOut[dstEvery:dstEvery+ioff], tmp[bufoff:bufoff*2]) - copy(dstOut[dstEvery*2:dstEvery*2+ioff], tmp[bufoff*2:bufoff*3]) - copy(dstOut[dstEvery*3:dstEvery*3+ioff], tmp[bufoff*3:bufoff*4]) + copy(out, buf[:off]) + copy(out[dstEvery:dstEvery+ioff], buf[bufoff:bufoff*2]) + copy(out[dstEvery*2:dstEvery*2+ioff], buf[bufoff*2:bufoff*3]) + copy(out[dstEvery*3:dstEvery*3+ioff], buf[bufoff*3:bufoff*4]) decoded += int(off) * 4 - dstOut = dstOut[off:] + out = out[off:] } // Decode remaining. @@ -382,10 +424,10 @@ bigloop: br := &br[i] for !br.finished() { br.fill() - if offset >= len(dstOut) { + if offset >= len(out) { return nil, errors.New("corruption detected: stream overrun 4") } - dstOut[offset] = decode(br) + out[offset] = decode(br) offset++ } decoded += offset - dstEvery*i @@ -397,7 +439,7 @@ bigloop: if dstSize != decoded { return nil, errors.New("corruption detected: short output block") } - return s.Out, nil + return dst, nil } // matches will compare a decoding table to a coding table. diff --git a/vendor/github.com/klauspost/compress/zstd/README.md b/vendor/github.com/klauspost/compress/zstd/README.md index bc977a302..f2a80b5d0 100644 --- a/vendor/github.com/klauspost/compress/zstd/README.md +++ b/vendor/github.com/klauspost/compress/zstd/README.md @@ -309,6 +309,20 @@ The decoder can be used for *concurrent* decompression of multiple buffers. It will only allow a certain number of concurrent operations to run. To tweak that yourself use the `WithDecoderConcurrency(n)` option when creating the decoder. +### Dictionaries + +Data compressed with [dictionaries](https://github.com/facebook/zstd#the-case-for-small-data-compression) can be decompressed. + +Dictionaries are added individually to Decoders. +Dictionaries are generated by the `zstd --train` command and contains an initial state for the decoder. +To add a dictionary use the `RegisterDict(data)` with the dictionary data before starting any decompression. + +The dictionary will be used automatically for the data that specifies them. + +A re-used Decoder will still contain the dictionaries registered. + +When registering a dictionary with the same ID it will override the existing. + ### Allocation-less operation The decoder has been designed to operate without allocations after a warmup. diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go index 19181caea..4a14242c7 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -461,26 +461,22 @@ func (b *blockDec) decodeCompressed(hist *history) error { if huff == nil { huff = &huff0.Scratch{} } - huff.Out = b.literalBuf[:0] huff, literals, err = huff0.ReadTable(literals, huff) if err != nil { println("reading huffman table:", err) return err } // Use our out buffer. - huff.Out = b.literalBuf[:0] - huff.MaxDecodedSize = litRegenSize if fourStreams { - literals, err = huff.Decompress4X(literals, litRegenSize) + literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) } else { - literals, err = huff.Decompress1X(literals) + literals, err = huff.Decoder().Decompress1X(b.literalBuf[:0:litRegenSize], literals) } if err != nil { println("decoding compressed literals:", err) return err } // Make sure we don't leak our literals buffer - huff.Out = nil if len(literals) != litRegenSize { return fmt.Errorf("literal output size mismatch want %d, got %d", litRegenSize, len(literals)) } @@ -631,15 +627,12 @@ func (b *blockDec) decodeCompressed(hist *history) error { var err error // Use our out buffer. huff = hist.huffTree - huff.Out = b.literalBuf[:0] - huff.MaxDecodedSize = litRegenSize if fourStreams { - literals, err = huff.Decompress4X(literals, litRegenSize) + literals, err = huff.Decoder().Decompress4X(b.literalBuf[:0:litRegenSize], literals) } else { - literals, err = huff.Decompress1X(literals) + literals, err = huff.Decoder().Decompress1X(b.literalBuf[:0:litRegenSize], literals) } // Make sure we don't leak our literals buffer - huff.Out = nil if err != nil { println("decompressing literals:", err) return err @@ -649,12 +642,13 @@ func (b *blockDec) decodeCompressed(hist *history) error { } } else { if hist.huffTree != nil && huff != nil { - huffDecoderPool.Put(hist.huffTree) + if hist.dict == nil || hist.dict.litDec != hist.huffTree { + huffDecoderPool.Put(hist.huffTree) + } hist.huffTree = nil } } if huff != nil { - huff.Out = nil hist.huffTree = huff } if debug { @@ -687,14 +681,20 @@ func (b *blockDec) decodeCompressed(hist *history) error { // If only recent offsets were not transferred, this would be an obvious win. // Also, if first 3 sequences don't reference recent offsets, all sequences can be decoded. - if err := seqs.initialize(br, hist, literals, b.dst); err != nil { - println("initializing sequences:", err) - return err - } hbytes := hist.b if len(hbytes) > hist.windowSize { hbytes = hbytes[len(hbytes)-hist.windowSize:] + // We do not need history any more. + if hist.dict != nil { + hist.dict.content = nil + } } + + if err := seqs.initialize(br, hist, literals, b.dst); err != nil { + println("initializing sequences:", err) + return err + } + err = seqs.decode(nSeqs, br, hbytes) if err != nil { return err diff --git a/vendor/github.com/klauspost/compress/zstd/bytereader.go b/vendor/github.com/klauspost/compress/zstd/bytereader.go index dc4378b64..f708df1c4 100644 --- a/vendor/github.com/klauspost/compress/zstd/bytereader.go +++ b/vendor/github.com/klauspost/compress/zstd/bytereader.go @@ -4,6 +4,8 @@ package zstd +import "encoding/binary" + // byteReader provides a byte reader that reads // little endian values from a byte stream. // The input stream is manually advanced. @@ -55,12 +57,7 @@ func (b byteReader) Uint32() uint32 { } return v } - b2 := b.b[b.off : b.off+4 : b.off+4] - v3 := uint32(b2[3]) - v2 := uint32(b2[2]) - v1 := uint32(b2[1]) - v0 := uint32(b2[0]) - return v0 | (v1 << 8) | (v2 << 16) | (v3 << 24) + return binary.LittleEndian.Uint32(b.b[b.off : b.off+4]) } // unread returns the unread portion of the input. diff --git a/vendor/github.com/klauspost/compress/zstd/decoder.go b/vendor/github.com/klauspost/compress/zstd/decoder.go index 324347623..8e34479ff 100644 --- a/vendor/github.com/klauspost/compress/zstd/decoder.go +++ b/vendor/github.com/klauspost/compress/zstd/decoder.go @@ -32,8 +32,9 @@ type Decoder struct { // Current read position used for Reader functionality. current decoderState - // Custom dictionaries - dicts map[uint32]struct{} + // Custom dictionaries. + // Always uses copies. + dicts map[uint32]dict // streamWg is the waitgroup for all streams streamWg sync.WaitGroup @@ -295,10 +296,18 @@ func (d *Decoder) DecodeAll(input, dst []byte) ([]byte, error) { frame.bBuf = input for { + frame.history.reset() err := frame.reset(&frame.bBuf) if err == io.EOF { return dst, nil } + if frame.DictionaryID != nil { + dict, ok := d.dicts[*frame.DictionaryID] + if !ok { + return nil, ErrUnknownDictionary + } + frame.history.setDict(&dict) + } if err != nil { return dst, err } @@ -393,6 +402,19 @@ func (d *Decoder) Close() { d.current.err = ErrDecoderClosed } +// RegisterDict will load a dictionary +func (d *Decoder) RegisterDict(b []byte) error { + dc, err := loadDict(b) + if err != nil { + return err + } + if d.dicts == nil { + d.dicts = make(map[uint32]dict, 1) + } + d.dicts[dc.id] = *dc + return nil +} + // IOReadCloser returns the decoder as an io.ReadCloser for convenience. // Any changes to the decoder will be reflected, so the returned ReadCloser // can be reused along with the decoder. @@ -466,6 +488,14 @@ func (d *Decoder) startStreamDecoder(inStream chan decodeStream) { if debug && err != nil { println("Frame decoder returned", err) } + if err == nil && frame.DictionaryID != nil { + dict, ok := d.dicts[*frame.DictionaryID] + if !ok { + err = ErrUnknownDictionary + } else { + frame.history.setDict(&dict) + } + } if err != nil { stream.output <- decodeOutput{ err: err, diff --git a/vendor/github.com/klauspost/compress/zstd/dict.go b/vendor/github.com/klauspost/compress/zstd/dict.go new file mode 100644 index 000000000..8eb6f6ba3 --- /dev/null +++ b/vendor/github.com/klauspost/compress/zstd/dict.go @@ -0,0 +1,104 @@ +package zstd + +import ( + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + + "github.com/klauspost/compress/huff0" +) + +type dict struct { + id uint32 + + litDec *huff0.Scratch + llDec, ofDec, mlDec sequenceDec + offsets [3]int + content []byte +} + +var dictMagic = [4]byte{0x37, 0xa4, 0x30, 0xec} + +// Load a dictionary as described in +// https://github.com/facebook/zstd/blob/master/doc/zstd_compression_format.md#dictionary-format +func loadDict(b []byte) (*dict, error) { + // Check static field size. + if len(b) <= 8+(3*4) { + return nil, io.ErrUnexpectedEOF + } + d := dict{ + llDec: sequenceDec{fse: &fseDecoder{}}, + ofDec: sequenceDec{fse: &fseDecoder{}}, + mlDec: sequenceDec{fse: &fseDecoder{}}, + } + if !bytes.Equal(b[:4], dictMagic[:]) { + return nil, ErrMagicMismatch + } + d.id = binary.LittleEndian.Uint32(b[4:8]) + if d.id == 0 { + return nil, errors.New("dictionaries cannot have ID 0") + } + + // Read literal table + var err error + d.litDec, b, err = huff0.ReadTable(b[8:], nil) + if err != nil { + return nil, err + } + + br := byteReader{ + b: b, + off: 0, + } + readDec := func(i tableIndex, dec *fseDecoder) error { + if err := dec.readNCount(&br, uint16(maxTableSymbol[i])); err != nil { + return err + } + if br.overread() { + return io.ErrUnexpectedEOF + } + err = dec.transform(symbolTableX[i]) + if err != nil { + println("Transform table error:", err) + return err + } + if debug { + println("Read table ok", "symbolLen:", dec.symbolLen) + } + // Set decoders as predefined so they aren't reused. + dec.preDefined = true + return nil + } + + if err := readDec(tableOffsets, d.ofDec.fse); err != nil { + return nil, err + } + if err := readDec(tableMatchLengths, d.mlDec.fse); err != nil { + return nil, err + } + if err := readDec(tableLiteralLengths, d.llDec.fse); err != nil { + return nil, err + } + if br.remain() < 12 { + return nil, io.ErrUnexpectedEOF + } + + d.offsets[0] = int(br.Uint32()) + br.advance(4) + d.offsets[1] = int(br.Uint32()) + br.advance(4) + d.offsets[2] = int(br.Uint32()) + br.advance(4) + if d.offsets[0] <= 0 || d.offsets[1] <= 0 || d.offsets[2] <= 0 { + return nil, errors.New("invalid offset in dictionary") + } + d.content = make([]byte, br.remain()) + copy(d.content, br.unread()) + if d.offsets[0] > len(d.content) || d.offsets[1] > len(d.content) || d.offsets[2] > len(d.content) { + return nil, fmt.Errorf("initial offset bigger than dictionary content size %d, offsets: %v", len(d.content), d.offsets) + } + + return &d, nil +} diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index 5ebead9dc..50276bcde 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -671,4 +671,8 @@ encodeLoop: println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } + // We do not store history, so we must offset e.cur to avoid false matches for next user. + if e.cur < bufferReset { + e.cur += int32(len(src)) + } } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_fast.go b/vendor/github.com/klauspost/compress/zstd/enc_fast.go index d1d3658e6..4104b456c 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_fast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_fast.go @@ -383,6 +383,7 @@ func (e *fastEncoder) EncodeNoHist(blk *blockEnc, src []byte) { panic("src too big") } } + // Protect against e.cur wraparound. if e.cur >= bufferReset { for i := range e.table[:] { @@ -516,6 +517,9 @@ encodeLoop: if debugAsserts && s-t > e.maxMatchOff { panic("s - t >e.maxMatchOff") } + if debugAsserts && t < 0 { + panic(fmt.Sprintf("t (%d) < 0, candidate.offset: %d, e.cur: %d, coffset0: %d, e.maxMatchOff: %d", t, candidate.offset, e.cur, coffset0, e.maxMatchOff)) + } break } @@ -548,6 +552,9 @@ encodeLoop: panic(fmt.Sprintf("s (%d) <= t (%d)", s, t)) } + if debugAsserts && t < 0 { + panic(fmt.Sprintf("t (%d) < 0 ", t)) + } // Extend the 4-byte match as long as possible. //l := e.matchlenNoHist(s+4, t+4, src) + 4 // l := int32(matchLen(src[s+4:], src[t+4:])) + 4 @@ -647,6 +654,10 @@ encodeLoop: if debug { println("returning, recent offsets:", blk.recentOffsets, "extra literals:", blk.extraLits) } + // We do not store history, so we must offset e.cur to avoid false matches for next user. + if e.cur < bufferReset { + e.cur += int32(len(src)) + } } func (e *fastBase) addBlock(src []byte) int32 { @@ -714,7 +725,7 @@ func (e *fastBase) matchlen(s, t int32, src []byte) int32 { } // Reset the encoding table. -func (e *fastBase) Reset() { +func (e *fastBase) Reset(singleBlock bool) { if e.blk == nil { e.blk = &blockEnc{} e.blk.init() @@ -727,7 +738,7 @@ func (e *fastBase) Reset() { } else { e.crc.Reset() } - if cap(e.hist) < int(e.maxMatchOff*2) { + if !singleBlock && cap(e.hist) < int(e.maxMatchOff*2) { l := e.maxMatchOff * 2 // Make it at least 1MB. if l < 1<<20 { diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go index af4f00b73..bf42bb1cf 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -35,7 +35,7 @@ type encoder interface { AppendCRC([]byte) []byte WindowSize(size int) int32 UseBlock(*blockEnc) - Reset() + Reset(singleBlock bool) } type encoderState struct { @@ -82,7 +82,10 @@ func (e *Encoder) initialize() { } e.encoders = make(chan encoder, e.o.concurrent) for i := 0; i < e.o.concurrent; i++ { - e.encoders <- e.o.encoder() + enc := e.o.encoder() + // If not single block, history will be allocated on first use. + enc.Reset(true) + e.encoders <- enc } } @@ -112,7 +115,7 @@ func (e *Encoder) Reset(w io.Writer) { s.filling = s.filling[:0] s.current = s.current[:0] s.previous = s.previous[:0] - s.encoder.Reset() + s.encoder.Reset(false) s.headerWritten = false s.eofWritten = false s.fullFrameWritten = false @@ -445,11 +448,10 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte { enc := <-e.encoders defer func() { // Release encoder reference to last block. - enc.Reset() + // If a non-single block is needed the encoder will reset again. + enc.Reset(true) e.encoders <- enc }() - enc.Reset() - blk := enc.Block() // Use single segments when above minimum window and below 1MB. single := len(src) < 1<<20 && len(src) > MinWindowSize if e.o.single != nil { @@ -472,12 +474,13 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte { panic(err) } - if len(src) <= e.o.blockSize && len(src) <= maxBlockSize { + // If we can do everything in one block, prefer that. + if len(src) <= maxCompressedBlockSize { // Slightly faster with no history and everything in one block. if e.o.crc { _, _ = enc.CRC().Write(src) } - blk.reset(nil) + blk := enc.Block() blk.last = true enc.EncodeNoHist(blk, src) @@ -504,6 +507,8 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte { } blk.output = oldout } else { + enc.Reset(false) + blk := enc.Block() for len(src) > 0 { todo := src if len(todo) > e.o.blockSize { diff --git a/vendor/github.com/klauspost/compress/zstd/framedec.go b/vendor/github.com/klauspost/compress/zstd/framedec.go index 780880ebe..fc4a566d3 100644 --- a/vendor/github.com/klauspost/compress/zstd/framedec.go +++ b/vendor/github.com/klauspost/compress/zstd/framedec.go @@ -40,7 +40,7 @@ type frameDec struct { FrameContentSize uint64 frameDone sync.WaitGroup - DictionaryID uint32 + DictionaryID *uint32 HasCheckSum bool SingleSegment bool @@ -142,7 +142,7 @@ func (d *frameDec) reset(br byteBuffer) error { // Read Dictionary_ID // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary_id - d.DictionaryID = 0 + d.DictionaryID = nil if size := fhd & 3; size != 0 { if size == 3 { size = 4 @@ -154,19 +154,22 @@ func (d *frameDec) reset(br byteBuffer) error { } return io.ErrUnexpectedEOF } + var id uint32 switch size { case 1: - d.DictionaryID = uint32(b[0]) + id = uint32(b[0]) case 2: - d.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) + id = uint32(b[0]) | (uint32(b[1]) << 8) case 4: - d.DictionaryID = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) + id = uint32(b[0]) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24) } if debug { - println("Dict size", size, "ID:", d.DictionaryID) + println("Dict size", size, "ID:", id) } - if d.DictionaryID != 0 { - return ErrUnknownDictionary + if id > 0 { + // ID 0 means "sorry, no dictionary anyway". + // https://github.com/facebook/zstd/blob/dev/doc/zstd_compression_format.md#dictionary-format + d.DictionaryID = &id } } @@ -351,8 +354,6 @@ func (d *frameDec) initAsync() { // When the frame has finished decoding the *bufio.Reader // containing the remaining input will be sent on frameDec.frameDone. func (d *frameDec) startDecoder(output chan decodeOutput) { - // TODO: Init to dictionary - d.history.reset() written := int64(0) defer func() { @@ -445,8 +446,6 @@ func (d *frameDec) startDecoder(output chan decodeOutput) { // runDecoder will create a sync decoder that will decode a block of data. func (d *frameDec) runDecoder(dst []byte, dec *blockDec) ([]byte, error) { - // TODO: Init to dictionary - d.history.reset() saved := d.history.b // We use the history for output to avoid copying it. diff --git a/vendor/github.com/klauspost/compress/zstd/fse_decoder.go b/vendor/github.com/klauspost/compress/zstd/fse_decoder.go index e002be98b..957cfeb79 100644 --- a/vendor/github.com/klauspost/compress/zstd/fse_decoder.go +++ b/vendor/github.com/klauspost/compress/zstd/fse_decoder.go @@ -19,7 +19,7 @@ const ( * Increasing memory usage improves compression ratio * Reduced memory usage can improve speed, due to cache effect * Recommended max value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ - maxMemoryUsage = 11 + maxMemoryUsage = tablelogAbsoluteMax + 2 maxTableLog = maxMemoryUsage - 2 maxTablesize = 1 << maxTableLog diff --git a/vendor/github.com/klauspost/compress/zstd/history.go b/vendor/github.com/klauspost/compress/zstd/history.go index e8c419bd5..f418f50fc 100644 --- a/vendor/github.com/klauspost/compress/zstd/history.go +++ b/vendor/github.com/klauspost/compress/zstd/history.go @@ -17,6 +17,7 @@ type history struct { windowSize int maxSize int error bool + dict *dict } // reset will reset the history to initial state of a frame. @@ -36,12 +37,27 @@ func (h *history) reset() { } h.decoders = sequenceDecs{} if h.huffTree != nil { - huffDecoderPool.Put(h.huffTree) + if h.dict == nil || h.dict.litDec != h.huffTree { + huffDecoderPool.Put(h.huffTree) + } } h.huffTree = nil + h.dict = nil //printf("history created: %+v (l: %d, c: %d)", *h, len(h.b), cap(h.b)) } +func (h *history) setDict(dict *dict) { + if dict == nil { + return + } + h.dict = dict + h.decoders.litLengths = dict.llDec + h.decoders.offsets = dict.ofDec + h.decoders.matchLengths = dict.mlDec + h.recentOffsets = dict.offsets + h.huffTree = dict.litDec +} + // append bytes to history. // This function will make sure there is space for it, // if the buffer has been allocated with enough extra space. diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec.go b/vendor/github.com/klauspost/compress/zstd/seqdec.go index 39238e16a..7ff870400 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec.go @@ -62,6 +62,7 @@ type sequenceDecs struct { matchLengths sequenceDec prevOffset [3]int hist []byte + dict []byte literals []byte out []byte windowSize int @@ -85,6 +86,10 @@ func (s *sequenceDecs) initialize(br *bitReader, hist *history, literals, out [] s.maxBits = s.litLengths.fse.maxBits + s.offsets.fse.maxBits + s.matchLengths.fse.maxBits s.windowSize = hist.windowSize s.out = out + s.dict = nil + if hist.dict != nil { + s.dict = hist.dict.content + } return nil } @@ -100,23 +105,78 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error { printf("reading sequence %d, exceeded available data\n", seqs-i) return io.ErrUnexpectedEOF } - var litLen, matchOff, matchLen int + var ll, mo, ml int if br.off > 4+((maxOffsetBits+16+16)>>3) { - litLen, matchOff, matchLen = s.nextFast(br, llState, mlState, ofState) + // inlined function: + // ll, mo, ml = s.nextFast(br, llState, mlState, ofState) + + // Final will not read from stream. + var llB, mlB, moB uint8 + ll, llB = llState.final() + ml, mlB = mlState.final() + mo, moB = ofState.final() + + // extra bits are stored in reverse order. + br.fillFast() + mo += br.getBits(moB) + if s.maxBits > 32 { + br.fillFast() + } + ml += br.getBits(mlB) + ll += br.getBits(llB) + + if moB > 1 { + s.prevOffset[2] = s.prevOffset[1] + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = mo + } else { + // mo = s.adjustOffset(mo, ll, moB) + // Inlined for rather big speedup + if ll == 0 { + // There is an exception though, when current sequence's literals_length = 0. + // In this case, repeated offsets are shifted by one, so an offset_value of 1 means Repeated_Offset2, + // an offset_value of 2 means Repeated_Offset3, and an offset_value of 3 means Repeated_Offset1 - 1_byte. + mo++ + } + + if mo == 0 { + mo = s.prevOffset[0] + } else { + var temp int + if mo == 3 { + temp = s.prevOffset[0] - 1 + } else { + temp = s.prevOffset[mo] + } + + if temp == 0 { + // 0 is not valid; input is corrupted; force offset to 1 + println("temp was 0") + temp = 1 + } + + if mo != 1 { + s.prevOffset[2] = s.prevOffset[1] + } + s.prevOffset[1] = s.prevOffset[0] + s.prevOffset[0] = temp + mo = temp + } + } br.fillFast() } else { - litLen, matchOff, matchLen = s.next(br, llState, mlState, ofState) + ll, mo, ml = s.next(br, llState, mlState, ofState) br.fill() } if debugSequences { - println("Seq", seqs-i-1, "Litlen:", litLen, "matchOff:", matchOff, "(abs) matchLen:", matchLen) + println("Seq", seqs-i-1, "Litlen:", ll, "mo:", mo, "(abs) ml:", ml) } - if litLen > len(s.literals) { - return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", litLen, len(s.literals)) + if ll > len(s.literals) { + return fmt.Errorf("unexpected literal count, want %d bytes, but only %d is available", ll, len(s.literals)) } - size := litLen + matchLen + len(s.out) + size := ll + ml + len(s.out) if size-startSize > maxBlockSize { return fmt.Errorf("output (%d) bigger than max block size", size) } @@ -127,52 +187,70 @@ func (s *sequenceDecs) decode(seqs int, br *bitReader, hist []byte) error { s.out = append(s.out, make([]byte, maxBlockSize)...) s.out = s.out[:len(s.out)-maxBlockSize] } - if matchLen > maxMatchLen { - return fmt.Errorf("match len (%d) bigger than max allowed length", matchLen) - } - if matchOff > len(s.out)+len(hist)+litLen { - return fmt.Errorf("match offset (%d) bigger than current history (%d)", matchOff, len(s.out)+len(hist)+litLen) - } - if matchOff > s.windowSize { - return fmt.Errorf("match offset (%d) bigger than window size (%d)", matchOff, s.windowSize) - } - if matchOff == 0 && matchLen > 0 { - return fmt.Errorf("zero matchoff and matchlen > 0") + if ml > maxMatchLen { + return fmt.Errorf("match len (%d) bigger than max allowed length", ml) } - s.out = append(s.out, s.literals[:litLen]...) - s.literals = s.literals[litLen:] + // Add literals + s.out = append(s.out, s.literals[:ll]...) + s.literals = s.literals[ll:] out := s.out + if mo > len(s.out)+len(hist) || mo > s.windowSize { + if len(s.dict) == 0 { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(s.out)+len(hist)) + } + + // we may be in dictionary. + dictO := len(s.dict) - (mo - (len(s.out) + len(hist))) + if dictO < 0 || dictO >= len(s.dict) { + return fmt.Errorf("match offset (%d) bigger than current history (%d)", mo, len(s.out)+len(hist)) + } + end := dictO + ml + if end > len(s.dict) { + out = append(out, s.dict[dictO:]...) + mo -= len(s.dict) - dictO + ml -= len(s.dict) - dictO + } else { + out = append(out, s.dict[dictO:end]...) + mo = 0 + ml = 0 + } + } + + if mo == 0 && ml > 0 { + return fmt.Errorf("zero matchoff and matchlen (%d) > 0", ml) + } + // Copy from history. // TODO: Blocks without history could be made to ignore this completely. - if v := matchOff - len(s.out); v > 0 { + if v := mo - len(s.out); v > 0 { // v is the start position in history from end. start := len(s.hist) - v - if matchLen > v { + if ml > v { // Some goes into current block. // Copy remainder of history out = append(out, s.hist[start:]...) - matchOff -= v - matchLen -= v + mo -= v + ml -= v } else { - out = append(out, s.hist[start:start+matchLen]...) - matchLen = 0 + out = append(out, s.hist[start:start+ml]...) + ml = 0 } } // We must be in current buffer now - if matchLen > 0 { - start := len(s.out) - matchOff - if matchLen <= len(s.out)-start { + if ml > 0 { + start := len(s.out) - mo + if ml <= len(s.out)-start { // No overlap - out = append(out, s.out[start:start+matchLen]...) + out = append(out, s.out[start:start+ml]...) } else { // Overlapping copy // Extend destination slice and copy one byte at the time. - out = out[:len(out)+matchLen] - src := out[start : start+matchLen] + out = out[:len(out)+ml] + src := out[start : start+ml] // Destination is the space we just added. - dst := out[len(out)-matchLen:] + dst := out[len(out)-ml:] dst = dst[:len(src)] for i := range src { dst[i] = src[i] diff --git a/vendor/github.com/seccomp/containers-golang/seccomp.json b/vendor/github.com/seccomp/containers-golang/seccomp.json index 4c84d981f..06b39024a 100644 --- a/vendor/github.com/seccomp/containers-golang/seccomp.json +++ b/vendor/github.com/seccomp/containers-golang/seccomp.json @@ -317,7 +317,6 @@ "signalfd", "signalfd4", "sigreturn", - "socket", "socketcall", "socketpair", "splice", @@ -769,6 +768,111 @@ ] }, "excludes": {} + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ERRNO", + "args": [ + { + "index": 0, + "value": 16, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + }, + { + "index": 2, + "value": 9, + "valueTwo": 0, + "op": "SCMP_CMP_EQ" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + }, + "errnoRet": 22 + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 2, + "value": 9, + "valueTwo": 0, + "op": "SCMP_CMP_NE" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 16, + "valueTwo": 0, + "op": "SCMP_CMP_NE" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 2, + "value": 9, + "valueTwo": 0, + "op": "SCMP_CMP_NE" + } + ], + "comment": "", + "includes": {}, + "excludes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + } + }, + { + "names": [ + "socket" + ], + "action": "SCMP_ACT_ALLOW", + "args": null, + "comment": "", + "includes": { + "caps": [ + "CAP_AUDIT_WRITE" + ] + }, + "excludes": {} } ] }
\ No newline at end of file diff --git a/vendor/github.com/seccomp/containers-golang/seccomp_default_linux.go b/vendor/github.com/seccomp/containers-golang/seccomp_default_linux.go index e137a5887..2e3e337ac 100644 --- a/vendor/github.com/seccomp/containers-golang/seccomp_default_linux.go +++ b/vendor/github.com/seccomp/containers-golang/seccomp_default_linux.go @@ -7,6 +7,8 @@ package seccomp // import "github.com/seccomp/containers-golang" import ( + "syscall" + "golang.org/x/sys/unix" ) @@ -45,6 +47,8 @@ func arches() []Architecture { // DefaultProfile defines the whitelist for the default seccomp profile. func DefaultProfile() *Seccomp { + einval := uint(syscall.EINVAL) + syscalls := []*Syscall{ { Names: []string{ @@ -313,7 +317,6 @@ func DefaultProfile() *Seccomp { "signalfd", "signalfd4", "sigreturn", - "socket", "socketcall", "socketpair", "splice", @@ -652,6 +655,85 @@ func DefaultProfile() *Seccomp { Caps: []string{"CAP_SYS_TTY_CONFIG"}, }, }, + { + Names: []string{ + "socket", + }, + Action: ActErrno, + ErrnoRet: &einval, + Args: []*Arg{ + { + Index: 0, + Value: syscall.AF_NETLINK, + Op: OpEqualTo, + }, + { + Index: 2, + Value: syscall.NETLINK_AUDIT, + Op: OpEqualTo, + }, + }, + Excludes: Filter{ + Caps: []string{"CAP_AUDIT_WRITE"}, + }, + }, + { + Names: []string{ + "socket", + }, + Action: ActAllow, + Args: []*Arg{ + { + Index: 2, + Value: syscall.NETLINK_AUDIT, + Op: OpNotEqual, + }, + }, + Excludes: Filter{ + Caps: []string{"CAP_AUDIT_WRITE"}, + }, + }, + { + Names: []string{ + "socket", + }, + Action: ActAllow, + Args: []*Arg{ + { + Index: 0, + Value: syscall.AF_NETLINK, + Op: OpNotEqual, + }, + }, + Excludes: Filter{ + Caps: []string{"CAP_AUDIT_WRITE"}, + }, + }, + { + Names: []string{ + "socket", + }, + Action: ActAllow, + Args: []*Arg{ + { + Index: 2, + Value: syscall.NETLINK_AUDIT, + Op: OpNotEqual, + }, + }, + Excludes: Filter{ + Caps: []string{"CAP_AUDIT_WRITE"}, + }, + }, + { + Names: []string{ + "socket", + }, + Action: ActAllow, + Includes: Filter{ + Caps: []string{"CAP_AUDIT_WRITE"}, + }, + }, } return &Seccomp{ diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go index b4c46042b..49370eb16 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_format.go @@ -6,7 +6,6 @@ package assert import ( - io "io" http "net/http" url "net/url" time "time" @@ -202,11 +201,11 @@ func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, arg // assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) bool { +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return HTTPBodyContains(t, handler, method, url, values, body, str, append([]interface{}{msg}, args...)...) + return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) } // HTTPBodyNotContainsf asserts that a specified handler returns a @@ -215,11 +214,11 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) bool { +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - return HTTPBodyNotContains(t, handler, method, url, values, body, str, append([]interface{}{msg}, args...)...) + return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) } // HTTPErrorf asserts that a specified handler returns an error status code. diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go index 9bea8d189..9db889427 100644 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ b/vendor/github.com/stretchr/testify/assert/assertion_forward.go @@ -6,7 +6,6 @@ package assert import ( - io "io" http "net/http" url "net/url" time "time" @@ -386,11 +385,11 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) bool { +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } - return HTTPBodyContains(a.t, handler, method, url, values, body, str, msgAndArgs...) + return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf asserts that a specified handler returns a @@ -399,11 +398,11 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) bool { +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } - return HTTPBodyContainsf(a.t, handler, method, url, values, body, str, msg, args...) + return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPBodyNotContains asserts that a specified handler returns a @@ -412,11 +411,11 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) bool { +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } - return HTTPBodyNotContains(a.t, handler, method, url, values, body, str, msgAndArgs...) + return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf asserts that a specified handler returns a @@ -425,11 +424,11 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) bool { +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { if h, ok := a.t.(tHelper); ok { h.Helper() } - return HTTPBodyNotContainsf(a.t, handler, method, url, values, body, str, msg, args...) + return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPError asserts that a specified handler returns an error status code. diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go index 30ef7cc06..4ed341dd2 100644 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ b/vendor/github.com/stretchr/testify/assert/http_assertions.go @@ -2,7 +2,6 @@ package assert import ( "fmt" - "io" "net/http" "net/http/httptest" "net/url" @@ -112,13 +111,9 @@ func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, va // HTTPBody is a helper that returns HTTP body of the response. It returns // empty string if building a new request fails. -func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values, body io.Reader) string { +func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { w := httptest.NewRecorder() - - if values != nil { - url = url + "?" + values.Encode() - } - req, err := http.NewRequest(method, url, body) + req, err := http.NewRequest(method, url+"?"+values.Encode(), nil) if err != nil { return "" } @@ -132,13 +127,13 @@ func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values, b // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) bool { +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - httpBody := HTTPBody(handler, method, url, values, body) + body := HTTPBody(handler, method, url, values) - contains := strings.Contains(httpBody, fmt.Sprint(str)) + contains := strings.Contains(body, fmt.Sprint(str)) if !contains { Fail(t, fmt.Sprintf("Expected response body for \"%s\" to contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) } @@ -152,13 +147,13 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) bool { +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { if h, ok := t.(tHelper); ok { h.Helper() } - httpBody := HTTPBody(handler, method, url, values, body) + body := HTTPBody(handler, method, url, values) - contains := strings.Contains(httpBody, fmt.Sprint(str)) + contains := strings.Contains(body, fmt.Sprint(str)) if contains { Fail(t, fmt.Sprintf("Expected response body for \"%s\" to NOT contain \"%s\" but found \"%s\"", url+"?"+values.Encode(), str, body)) } diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go index 693648f8a..ec4624b28 100644 --- a/vendor/github.com/stretchr/testify/require/require.go +++ b/vendor/github.com/stretchr/testify/require/require.go @@ -7,7 +7,6 @@ package require import ( assert "github.com/stretchr/testify/assert" - io "io" http "net/http" url "net/url" time "time" @@ -489,11 +488,11 @@ func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...in // assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) { +func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } - if assert.HTTPBodyContains(t, handler, method, url, values, body, str, msgAndArgs...) { + if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { return } t.FailNow() @@ -505,11 +504,11 @@ func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url s // assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) { +func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } - if assert.HTTPBodyContainsf(t, handler, method, url, values, body, str, msg, args...) { + if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { return } t.FailNow() @@ -521,11 +520,11 @@ func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url // assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) { +func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } - if assert.HTTPBodyNotContains(t, handler, method, url, values, body, str, msgAndArgs...) { + if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { return } t.FailNow() @@ -537,11 +536,11 @@ func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, ur // assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) { +func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := t.(tHelper); ok { h.Helper() } - if assert.HTTPBodyNotContainsf(t, handler, method, url, values, body, str, msg, args...) { + if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { return } t.FailNow() diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go index 84fc1c88d..103d7dcb6 100644 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ b/vendor/github.com/stretchr/testify/require/require_forward.go @@ -7,7 +7,6 @@ package require import ( assert "github.com/stretchr/testify/assert" - io "io" http "net/http" url "net/url" time "time" @@ -387,11 +386,11 @@ func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args . // a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) { +func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } - HTTPBodyContains(a.t, handler, method, url, values, body, str, msgAndArgs...) + HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf asserts that a specified handler returns a @@ -400,11 +399,11 @@ func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, u // a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) { +func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } - HTTPBodyContainsf(a.t, handler, method, url, values, body, str, msg, args...) + HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPBodyNotContains asserts that a specified handler returns a @@ -413,11 +412,11 @@ func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, // a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msgAndArgs ...interface{}) { +func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } - HTTPBodyNotContains(a.t, handler, method, url, values, body, str, msgAndArgs...) + HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf asserts that a specified handler returns a @@ -426,11 +425,11 @@ func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string // a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") // // Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, body io.Reader, str interface{}, msg string, args ...interface{}) { +func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { if h, ok := a.t.(tHelper); ok { h.Helper() } - HTTPBodyNotContainsf(a.t, handler, method, url, values, body, str, msg, args...) + HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) } // HTTPError asserts that a specified handler returns an error status code. diff --git a/vendor/golang.org/x/crypto/ssh/agent/client.go b/vendor/golang.org/x/crypto/ssh/agent/client.go new file mode 100644 index 000000000..b909471cc --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/agent/client.go @@ -0,0 +1,813 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package agent implements the ssh-agent protocol, and provides both +// a client and a server. The client can talk to a standard ssh-agent +// that uses UNIX sockets, and one could implement an alternative +// ssh-agent process using the sample server. +// +// References: +// [PROTOCOL.agent]: https://tools.ietf.org/html/draft-miller-ssh-agent-00 +package agent // import "golang.org/x/crypto/ssh/agent" + +import ( + "bytes" + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "encoding/base64" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" + "sync" + + "crypto" + "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh" +) + +// SignatureFlags represent additional flags that can be passed to the signature +// requests an defined in [PROTOCOL.agent] section 4.5.1. +type SignatureFlags uint32 + +// SignatureFlag values as defined in [PROTOCOL.agent] section 5.3. +const ( + SignatureFlagReserved SignatureFlags = 1 << iota + SignatureFlagRsaSha256 + SignatureFlagRsaSha512 +) + +// Agent represents the capabilities of an ssh-agent. +type Agent interface { + // List returns the identities known to the agent. + List() ([]*Key, error) + + // Sign has the agent sign the data using a protocol 2 key as defined + // in [PROTOCOL.agent] section 2.6.2. + Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) + + // Add adds a private key to the agent. + Add(key AddedKey) error + + // Remove removes all identities with the given public key. + Remove(key ssh.PublicKey) error + + // RemoveAll removes all identities. + RemoveAll() error + + // Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. + Lock(passphrase []byte) error + + // Unlock undoes the effect of Lock + Unlock(passphrase []byte) error + + // Signers returns signers for all the known keys. + Signers() ([]ssh.Signer, error) +} + +type ExtendedAgent interface { + Agent + + // SignWithFlags signs like Sign, but allows for additional flags to be sent/received + SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) + + // Extension processes a custom extension request. Standard-compliant agents are not + // required to support any extensions, but this method allows agents to implement + // vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7. + // If agent extensions are unsupported entirely this method MUST return an + // ErrExtensionUnsupported error. Similarly, if just the specific extensionType in + // the request is unsupported by the agent then ErrExtensionUnsupported MUST be + // returned. + // + // In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents + // of the response are unspecified (including the type of the message), the complete + // response will be returned as a []byte slice, including the "type" byte of the message. + Extension(extensionType string, contents []byte) ([]byte, error) +} + +// ConstraintExtension describes an optional constraint defined by users. +type ConstraintExtension struct { + // ExtensionName consist of a UTF-8 string suffixed by the + // implementation domain following the naming scheme defined + // in Section 4.2 of [RFC4251], e.g. "foo@example.com". + ExtensionName string + // ExtensionDetails contains the actual content of the extended + // constraint. + ExtensionDetails []byte +} + +// AddedKey describes an SSH key to be added to an Agent. +type AddedKey struct { + // PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey, + // ed25519.PrivateKey or *ecdsa.PrivateKey, which will be inserted into the + // agent. + PrivateKey interface{} + // Certificate, if not nil, is communicated to the agent and will be + // stored with the key. + Certificate *ssh.Certificate + // Comment is an optional, free-form string. + Comment string + // LifetimeSecs, if not zero, is the number of seconds that the + // agent will store the key for. + LifetimeSecs uint32 + // ConfirmBeforeUse, if true, requests that the agent confirm with the + // user before each use of this key. + ConfirmBeforeUse bool + // ConstraintExtensions are the experimental or private-use constraints + // defined by users. + ConstraintExtensions []ConstraintExtension +} + +// See [PROTOCOL.agent], section 3. +const ( + agentRequestV1Identities = 1 + agentRemoveAllV1Identities = 9 + + // 3.2 Requests from client to agent for protocol 2 key operations + agentAddIdentity = 17 + agentRemoveIdentity = 18 + agentRemoveAllIdentities = 19 + agentAddIDConstrained = 25 + + // 3.3 Key-type independent requests from client to agent + agentAddSmartcardKey = 20 + agentRemoveSmartcardKey = 21 + agentLock = 22 + agentUnlock = 23 + agentAddSmartcardKeyConstrained = 26 + + // 3.7 Key constraint identifiers + agentConstrainLifetime = 1 + agentConstrainConfirm = 2 + agentConstrainExtension = 3 +) + +// maxAgentResponseBytes is the maximum agent reply size that is accepted. This +// is a sanity check, not a limit in the spec. +const maxAgentResponseBytes = 16 << 20 + +// Agent messages: +// These structures mirror the wire format of the corresponding ssh agent +// messages found in [PROTOCOL.agent]. + +// 3.4 Generic replies from agent to client +const agentFailure = 5 + +type failureAgentMsg struct{} + +const agentSuccess = 6 + +type successAgentMsg struct{} + +// See [PROTOCOL.agent], section 2.5.2. +const agentRequestIdentities = 11 + +type requestIdentitiesAgentMsg struct{} + +// See [PROTOCOL.agent], section 2.5.2. +const agentIdentitiesAnswer = 12 + +type identitiesAnswerAgentMsg struct { + NumKeys uint32 `sshtype:"12"` + Keys []byte `ssh:"rest"` +} + +// See [PROTOCOL.agent], section 2.6.2. +const agentSignRequest = 13 + +type signRequestAgentMsg struct { + KeyBlob []byte `sshtype:"13"` + Data []byte + Flags uint32 +} + +// See [PROTOCOL.agent], section 2.6.2. + +// 3.6 Replies from agent to client for protocol 2 key operations +const agentSignResponse = 14 + +type signResponseAgentMsg struct { + SigBlob []byte `sshtype:"14"` +} + +type publicKey struct { + Format string + Rest []byte `ssh:"rest"` +} + +// 3.7 Key constraint identifiers +type constrainLifetimeAgentMsg struct { + LifetimeSecs uint32 `sshtype:"1"` +} + +type constrainExtensionAgentMsg struct { + ExtensionName string `sshtype:"3"` + ExtensionDetails []byte + + // Rest is a field used for parsing, not part of message + Rest []byte `ssh:"rest"` +} + +// See [PROTOCOL.agent], section 4.7 +const agentExtension = 27 +const agentExtensionFailure = 28 + +// ErrExtensionUnsupported indicates that an extension defined in +// [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this +// error indicates that the agent returned a standard SSH_AGENT_FAILURE message +// as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol +// specification (and therefore this error) does not distinguish between a +// specific extension being unsupported and extensions being unsupported entirely. +var ErrExtensionUnsupported = errors.New("agent: extension unsupported") + +type extensionAgentMsg struct { + ExtensionType string `sshtype:"27"` + Contents []byte +} + +// Key represents a protocol 2 public key as defined in +// [PROTOCOL.agent], section 2.5.2. +type Key struct { + Format string + Blob []byte + Comment string +} + +func clientErr(err error) error { + return fmt.Errorf("agent: client error: %v", err) +} + +// String returns the storage form of an agent key with the format, base64 +// encoded serialized key, and the comment if it is not empty. +func (k *Key) String() string { + s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob) + + if k.Comment != "" { + s += " " + k.Comment + } + + return s +} + +// Type returns the public key type. +func (k *Key) Type() string { + return k.Format +} + +// Marshal returns key blob to satisfy the ssh.PublicKey interface. +func (k *Key) Marshal() []byte { + return k.Blob +} + +// Verify satisfies the ssh.PublicKey interface. +func (k *Key) Verify(data []byte, sig *ssh.Signature) error { + pubKey, err := ssh.ParsePublicKey(k.Blob) + if err != nil { + return fmt.Errorf("agent: bad public key: %v", err) + } + return pubKey.Verify(data, sig) +} + +type wireKey struct { + Format string + Rest []byte `ssh:"rest"` +} + +func parseKey(in []byte) (out *Key, rest []byte, err error) { + var record struct { + Blob []byte + Comment string + Rest []byte `ssh:"rest"` + } + + if err := ssh.Unmarshal(in, &record); err != nil { + return nil, nil, err + } + + var wk wireKey + if err := ssh.Unmarshal(record.Blob, &wk); err != nil { + return nil, nil, err + } + + return &Key{ + Format: wk.Format, + Blob: record.Blob, + Comment: record.Comment, + }, record.Rest, nil +} + +// client is a client for an ssh-agent process. +type client struct { + // conn is typically a *net.UnixConn + conn io.ReadWriter + // mu is used to prevent concurrent access to the agent + mu sync.Mutex +} + +// NewClient returns an Agent that talks to an ssh-agent process over +// the given connection. +func NewClient(rw io.ReadWriter) ExtendedAgent { + return &client{conn: rw} +} + +// call sends an RPC to the agent. On success, the reply is +// unmarshaled into reply and replyType is set to the first byte of +// the reply, which contains the type of the message. +func (c *client) call(req []byte) (reply interface{}, err error) { + buf, err := c.callRaw(req) + if err != nil { + return nil, err + } + reply, err = unmarshal(buf) + if err != nil { + return nil, clientErr(err) + } + return reply, nil +} + +// callRaw sends an RPC to the agent. On success, the raw +// bytes of the response are returned; no unmarshalling is +// performed on the response. +func (c *client) callRaw(req []byte) (reply []byte, err error) { + c.mu.Lock() + defer c.mu.Unlock() + + msg := make([]byte, 4+len(req)) + binary.BigEndian.PutUint32(msg, uint32(len(req))) + copy(msg[4:], req) + if _, err = c.conn.Write(msg); err != nil { + return nil, clientErr(err) + } + + var respSizeBuf [4]byte + if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil { + return nil, clientErr(err) + } + respSize := binary.BigEndian.Uint32(respSizeBuf[:]) + if respSize > maxAgentResponseBytes { + return nil, clientErr(errors.New("response too large")) + } + + buf := make([]byte, respSize) + if _, err = io.ReadFull(c.conn, buf); err != nil { + return nil, clientErr(err) + } + return buf, nil +} + +func (c *client) simpleCall(req []byte) error { + resp, err := c.call(req) + if err != nil { + return err + } + if _, ok := resp.(*successAgentMsg); ok { + return nil + } + return errors.New("agent: failure") +} + +func (c *client) RemoveAll() error { + return c.simpleCall([]byte{agentRemoveAllIdentities}) +} + +func (c *client) Remove(key ssh.PublicKey) error { + req := ssh.Marshal(&agentRemoveIdentityMsg{ + KeyBlob: key.Marshal(), + }) + return c.simpleCall(req) +} + +func (c *client) Lock(passphrase []byte) error { + req := ssh.Marshal(&agentLockMsg{ + Passphrase: passphrase, + }) + return c.simpleCall(req) +} + +func (c *client) Unlock(passphrase []byte) error { + req := ssh.Marshal(&agentUnlockMsg{ + Passphrase: passphrase, + }) + return c.simpleCall(req) +} + +// List returns the identities known to the agent. +func (c *client) List() ([]*Key, error) { + // see [PROTOCOL.agent] section 2.5.2. + req := []byte{agentRequestIdentities} + + msg, err := c.call(req) + if err != nil { + return nil, err + } + + switch msg := msg.(type) { + case *identitiesAnswerAgentMsg: + if msg.NumKeys > maxAgentResponseBytes/8 { + return nil, errors.New("agent: too many keys in agent reply") + } + keys := make([]*Key, msg.NumKeys) + data := msg.Keys + for i := uint32(0); i < msg.NumKeys; i++ { + var key *Key + var err error + if key, data, err = parseKey(data); err != nil { + return nil, err + } + keys[i] = key + } + return keys, nil + case *failureAgentMsg: + return nil, errors.New("agent: failed to list keys") + } + panic("unreachable") +} + +// Sign has the agent sign the data using a protocol 2 key as defined +// in [PROTOCOL.agent] section 2.6.2. +func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { + return c.SignWithFlags(key, data, 0) +} + +func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) { + req := ssh.Marshal(signRequestAgentMsg{ + KeyBlob: key.Marshal(), + Data: data, + Flags: uint32(flags), + }) + + msg, err := c.call(req) + if err != nil { + return nil, err + } + + switch msg := msg.(type) { + case *signResponseAgentMsg: + var sig ssh.Signature + if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { + return nil, err + } + + return &sig, nil + case *failureAgentMsg: + return nil, errors.New("agent: failed to sign challenge") + } + panic("unreachable") +} + +// unmarshal parses an agent message in packet, returning the parsed +// form and the message type of packet. +func unmarshal(packet []byte) (interface{}, error) { + if len(packet) < 1 { + return nil, errors.New("agent: empty packet") + } + var msg interface{} + switch packet[0] { + case agentFailure: + return new(failureAgentMsg), nil + case agentSuccess: + return new(successAgentMsg), nil + case agentIdentitiesAnswer: + msg = new(identitiesAnswerAgentMsg) + case agentSignResponse: + msg = new(signResponseAgentMsg) + case agentV1IdentitiesAnswer: + msg = new(agentV1IdentityMsg) + default: + return nil, fmt.Errorf("agent: unknown type tag %d", packet[0]) + } + if err := ssh.Unmarshal(packet, msg); err != nil { + return nil, err + } + return msg, nil +} + +type rsaKeyMsg struct { + Type string `sshtype:"17|25"` + N *big.Int + E *big.Int + D *big.Int + Iqmp *big.Int // IQMP = Inverse Q Mod P + P *big.Int + Q *big.Int + Comments string + Constraints []byte `ssh:"rest"` +} + +type dsaKeyMsg struct { + Type string `sshtype:"17|25"` + P *big.Int + Q *big.Int + G *big.Int + Y *big.Int + X *big.Int + Comments string + Constraints []byte `ssh:"rest"` +} + +type ecdsaKeyMsg struct { + Type string `sshtype:"17|25"` + Curve string + KeyBytes []byte + D *big.Int + Comments string + Constraints []byte `ssh:"rest"` +} + +type ed25519KeyMsg struct { + Type string `sshtype:"17|25"` + Pub []byte + Priv []byte + Comments string + Constraints []byte `ssh:"rest"` +} + +// Insert adds a private key to the agent. +func (c *client) insertKey(s interface{}, comment string, constraints []byte) error { + var req []byte + switch k := s.(type) { + case *rsa.PrivateKey: + if len(k.Primes) != 2 { + return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) + } + k.Precompute() + req = ssh.Marshal(rsaKeyMsg{ + Type: ssh.KeyAlgoRSA, + N: k.N, + E: big.NewInt(int64(k.E)), + D: k.D, + Iqmp: k.Precomputed.Qinv, + P: k.Primes[0], + Q: k.Primes[1], + Comments: comment, + Constraints: constraints, + }) + case *dsa.PrivateKey: + req = ssh.Marshal(dsaKeyMsg{ + Type: ssh.KeyAlgoDSA, + P: k.P, + Q: k.Q, + G: k.G, + Y: k.Y, + X: k.X, + Comments: comment, + Constraints: constraints, + }) + case *ecdsa.PrivateKey: + nistID := fmt.Sprintf("nistp%d", k.Params().BitSize) + req = ssh.Marshal(ecdsaKeyMsg{ + Type: "ecdsa-sha2-" + nistID, + Curve: nistID, + KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y), + D: k.D, + Comments: comment, + Constraints: constraints, + }) + case ed25519.PrivateKey: + req = ssh.Marshal(ed25519KeyMsg{ + Type: ssh.KeyAlgoED25519, + Pub: []byte(k)[32:], + Priv: []byte(k), + Comments: comment, + Constraints: constraints, + }) + // This function originally supported only *ed25519.PrivateKey, however the + // general idiom is to pass ed25519.PrivateKey by value, not by pointer. + // We still support the pointer variant for backwards compatibility. + case *ed25519.PrivateKey: + req = ssh.Marshal(ed25519KeyMsg{ + Type: ssh.KeyAlgoED25519, + Pub: []byte(*k)[32:], + Priv: []byte(*k), + Comments: comment, + Constraints: constraints, + }) + default: + return fmt.Errorf("agent: unsupported key type %T", s) + } + + // if constraints are present then the message type needs to be changed. + if len(constraints) != 0 { + req[0] = agentAddIDConstrained + } + + resp, err := c.call(req) + if err != nil { + return err + } + if _, ok := resp.(*successAgentMsg); ok { + return nil + } + return errors.New("agent: failure") +} + +type rsaCertMsg struct { + Type string `sshtype:"17|25"` + CertBytes []byte + D *big.Int + Iqmp *big.Int // IQMP = Inverse Q Mod P + P *big.Int + Q *big.Int + Comments string + Constraints []byte `ssh:"rest"` +} + +type dsaCertMsg struct { + Type string `sshtype:"17|25"` + CertBytes []byte + X *big.Int + Comments string + Constraints []byte `ssh:"rest"` +} + +type ecdsaCertMsg struct { + Type string `sshtype:"17|25"` + CertBytes []byte + D *big.Int + Comments string + Constraints []byte `ssh:"rest"` +} + +type ed25519CertMsg struct { + Type string `sshtype:"17|25"` + CertBytes []byte + Pub []byte + Priv []byte + Comments string + Constraints []byte `ssh:"rest"` +} + +// Add adds a private key to the agent. If a certificate is given, +// that certificate is added instead as public key. +func (c *client) Add(key AddedKey) error { + var constraints []byte + + if secs := key.LifetimeSecs; secs != 0 { + constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...) + } + + if key.ConfirmBeforeUse { + constraints = append(constraints, agentConstrainConfirm) + } + + cert := key.Certificate + if cert == nil { + return c.insertKey(key.PrivateKey, key.Comment, constraints) + } + return c.insertCert(key.PrivateKey, cert, key.Comment, constraints) +} + +func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { + var req []byte + switch k := s.(type) { + case *rsa.PrivateKey: + if len(k.Primes) != 2 { + return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) + } + k.Precompute() + req = ssh.Marshal(rsaCertMsg{ + Type: cert.Type(), + CertBytes: cert.Marshal(), + D: k.D, + Iqmp: k.Precomputed.Qinv, + P: k.Primes[0], + Q: k.Primes[1], + Comments: comment, + Constraints: constraints, + }) + case *dsa.PrivateKey: + req = ssh.Marshal(dsaCertMsg{ + Type: cert.Type(), + CertBytes: cert.Marshal(), + X: k.X, + Comments: comment, + Constraints: constraints, + }) + case *ecdsa.PrivateKey: + req = ssh.Marshal(ecdsaCertMsg{ + Type: cert.Type(), + CertBytes: cert.Marshal(), + D: k.D, + Comments: comment, + Constraints: constraints, + }) + case ed25519.PrivateKey: + req = ssh.Marshal(ed25519CertMsg{ + Type: cert.Type(), + CertBytes: cert.Marshal(), + Pub: []byte(k)[32:], + Priv: []byte(k), + Comments: comment, + Constraints: constraints, + }) + // This function originally supported only *ed25519.PrivateKey, however the + // general idiom is to pass ed25519.PrivateKey by value, not by pointer. + // We still support the pointer variant for backwards compatibility. + case *ed25519.PrivateKey: + req = ssh.Marshal(ed25519CertMsg{ + Type: cert.Type(), + CertBytes: cert.Marshal(), + Pub: []byte(*k)[32:], + Priv: []byte(*k), + Comments: comment, + Constraints: constraints, + }) + default: + return fmt.Errorf("agent: unsupported key type %T", s) + } + + // if constraints are present then the message type needs to be changed. + if len(constraints) != 0 { + req[0] = agentAddIDConstrained + } + + signer, err := ssh.NewSignerFromKey(s) + if err != nil { + return err + } + if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { + return errors.New("agent: signer and cert have different public key") + } + + resp, err := c.call(req) + if err != nil { + return err + } + if _, ok := resp.(*successAgentMsg); ok { + return nil + } + return errors.New("agent: failure") +} + +// Signers provides a callback for client authentication. +func (c *client) Signers() ([]ssh.Signer, error) { + keys, err := c.List() + if err != nil { + return nil, err + } + + var result []ssh.Signer + for _, k := range keys { + result = append(result, &agentKeyringSigner{c, k}) + } + return result, nil +} + +type agentKeyringSigner struct { + agent *client + pub ssh.PublicKey +} + +func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { + return s.pub +} + +func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) { + // The agent has its own entropy source, so the rand argument is ignored. + return s.agent.Sign(s.pub, data) +} + +func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) { + var flags SignatureFlags + if opts != nil { + switch opts.HashFunc() { + case crypto.SHA256: + flags = SignatureFlagRsaSha256 + case crypto.SHA512: + flags = SignatureFlagRsaSha512 + } + } + return s.agent.SignWithFlags(s.pub, data, flags) +} + +// Calls an extension method. It is up to the agent implementation as to whether or not +// any particular extension is supported and may always return an error. Because the +// type of the response is up to the implementation, this returns the bytes of the +// response and does not attempt any type of unmarshalling. +func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) { + req := ssh.Marshal(extensionAgentMsg{ + ExtensionType: extensionType, + Contents: contents, + }) + buf, err := c.callRaw(req) + if err != nil { + return nil, err + } + if len(buf) == 0 { + return nil, errors.New("agent: failure; empty response") + } + // [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message + // represents an agent that does not support the extension + if buf[0] == agentFailure { + return nil, ErrExtensionUnsupported + } + if buf[0] == agentExtensionFailure { + return nil, errors.New("agent: generic extension failure") + } + + return buf, nil +} diff --git a/vendor/golang.org/x/crypto/ssh/agent/forward.go b/vendor/golang.org/x/crypto/ssh/agent/forward.go new file mode 100644 index 000000000..fd24ba900 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/agent/forward.go @@ -0,0 +1,103 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package agent + +import ( + "errors" + "io" + "net" + "sync" + + "golang.org/x/crypto/ssh" +) + +// RequestAgentForwarding sets up agent forwarding for the session. +// ForwardToAgent or ForwardToRemote should be called to route +// the authentication requests. +func RequestAgentForwarding(session *ssh.Session) error { + ok, err := session.SendRequest("auth-agent-req@openssh.com", true, nil) + if err != nil { + return err + } + if !ok { + return errors.New("forwarding request denied") + } + return nil +} + +// ForwardToAgent routes authentication requests to the given keyring. +func ForwardToAgent(client *ssh.Client, keyring Agent) error { + channels := client.HandleChannelOpen(channelType) + if channels == nil { + return errors.New("agent: already have handler for " + channelType) + } + + go func() { + for ch := range channels { + channel, reqs, err := ch.Accept() + if err != nil { + continue + } + go ssh.DiscardRequests(reqs) + go func() { + ServeAgent(keyring, channel) + channel.Close() + }() + } + }() + return nil +} + +const channelType = "auth-agent@openssh.com" + +// ForwardToRemote routes authentication requests to the ssh-agent +// process serving on the given unix socket. +func ForwardToRemote(client *ssh.Client, addr string) error { + channels := client.HandleChannelOpen(channelType) + if channels == nil { + return errors.New("agent: already have handler for " + channelType) + } + conn, err := net.Dial("unix", addr) + if err != nil { + return err + } + conn.Close() + + go func() { + for ch := range channels { + channel, reqs, err := ch.Accept() + if err != nil { + continue + } + go ssh.DiscardRequests(reqs) + go forwardUnixSocket(channel, addr) + } + }() + return nil +} + +func forwardUnixSocket(channel ssh.Channel, addr string) { + conn, err := net.Dial("unix", addr) + if err != nil { + return + } + + var wg sync.WaitGroup + wg.Add(2) + go func() { + io.Copy(conn, channel) + conn.(*net.UnixConn).CloseWrite() + wg.Done() + }() + go func() { + io.Copy(channel, conn) + channel.CloseWrite() + wg.Done() + }() + + wg.Wait() + conn.Close() + channel.Close() +} diff --git a/vendor/golang.org/x/crypto/ssh/agent/keyring.go b/vendor/golang.org/x/crypto/ssh/agent/keyring.go new file mode 100644 index 000000000..c9d979430 --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/agent/keyring.go @@ -0,0 +1,241 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package agent + +import ( + "bytes" + "crypto/rand" + "crypto/subtle" + "errors" + "fmt" + "sync" + "time" + + "golang.org/x/crypto/ssh" +) + +type privKey struct { + signer ssh.Signer + comment string + expire *time.Time +} + +type keyring struct { + mu sync.Mutex + keys []privKey + + locked bool + passphrase []byte +} + +var errLocked = errors.New("agent: locked") + +// NewKeyring returns an Agent that holds keys in memory. It is safe +// for concurrent use by multiple goroutines. +func NewKeyring() Agent { + return &keyring{} +} + +// RemoveAll removes all identities. +func (r *keyring) RemoveAll() error { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + return errLocked + } + + r.keys = nil + return nil +} + +// removeLocked does the actual key removal. The caller must already be holding the +// keyring mutex. +func (r *keyring) removeLocked(want []byte) error { + found := false + for i := 0; i < len(r.keys); { + if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) { + found = true + r.keys[i] = r.keys[len(r.keys)-1] + r.keys = r.keys[:len(r.keys)-1] + continue + } else { + i++ + } + } + + if !found { + return errors.New("agent: key not found") + } + return nil +} + +// Remove removes all identities with the given public key. +func (r *keyring) Remove(key ssh.PublicKey) error { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + return errLocked + } + + return r.removeLocked(key.Marshal()) +} + +// Lock locks the agent. Sign and Remove will fail, and List will return an empty list. +func (r *keyring) Lock(passphrase []byte) error { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + return errLocked + } + + r.locked = true + r.passphrase = passphrase + return nil +} + +// Unlock undoes the effect of Lock +func (r *keyring) Unlock(passphrase []byte) error { + r.mu.Lock() + defer r.mu.Unlock() + if !r.locked { + return errors.New("agent: not locked") + } + if 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) { + return fmt.Errorf("agent: incorrect passphrase") + } + + r.locked = false + r.passphrase = nil + return nil +} + +// expireKeysLocked removes expired keys from the keyring. If a key was added +// with a lifetimesecs contraint and seconds >= lifetimesecs seconds have +// ellapsed, it is removed. The caller *must* be holding the keyring mutex. +func (r *keyring) expireKeysLocked() { + for _, k := range r.keys { + if k.expire != nil && time.Now().After(*k.expire) { + r.removeLocked(k.signer.PublicKey().Marshal()) + } + } +} + +// List returns the identities known to the agent. +func (r *keyring) List() ([]*Key, error) { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + // section 2.7: locked agents return empty. + return nil, nil + } + + r.expireKeysLocked() + var ids []*Key + for _, k := range r.keys { + pub := k.signer.PublicKey() + ids = append(ids, &Key{ + Format: pub.Type(), + Blob: pub.Marshal(), + Comment: k.comment}) + } + return ids, nil +} + +// Insert adds a private key to the keyring. If a certificate +// is given, that certificate is added as public key. Note that +// any constraints given are ignored. +func (r *keyring) Add(key AddedKey) error { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + return errLocked + } + signer, err := ssh.NewSignerFromKey(key.PrivateKey) + + if err != nil { + return err + } + + if cert := key.Certificate; cert != nil { + signer, err = ssh.NewCertSigner(cert, signer) + if err != nil { + return err + } + } + + p := privKey{ + signer: signer, + comment: key.Comment, + } + + if key.LifetimeSecs > 0 { + t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second) + p.expire = &t + } + + r.keys = append(r.keys, p) + + return nil +} + +// Sign returns a signature for the data. +func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { + return r.SignWithFlags(key, data, 0) +} + +func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + return nil, errLocked + } + + r.expireKeysLocked() + wanted := key.Marshal() + for _, k := range r.keys { + if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { + if flags == 0 { + return k.signer.Sign(rand.Reader, data) + } else { + if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok { + return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer) + } else { + var algorithm string + switch flags { + case SignatureFlagRsaSha256: + algorithm = ssh.SigAlgoRSASHA2256 + case SignatureFlagRsaSha512: + algorithm = ssh.SigAlgoRSASHA2512 + default: + return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags) + } + return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm) + } + } + } + } + return nil, errors.New("not found") +} + +// Signers returns signers for all the known keys. +func (r *keyring) Signers() ([]ssh.Signer, error) { + r.mu.Lock() + defer r.mu.Unlock() + if r.locked { + return nil, errLocked + } + + r.expireKeysLocked() + s := make([]ssh.Signer, 0, len(r.keys)) + for _, k := range r.keys { + s = append(s, k.signer) + } + return s, nil +} + +// The keyring does not support any extensions +func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) { + return nil, ErrExtensionUnsupported +} diff --git a/vendor/golang.org/x/crypto/ssh/agent/server.go b/vendor/golang.org/x/crypto/ssh/agent/server.go new file mode 100644 index 000000000..6e7a1e02f --- /dev/null +++ b/vendor/golang.org/x/crypto/ssh/agent/server.go @@ -0,0 +1,570 @@ +// Copyright 2012 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package agent + +import ( + "crypto/dsa" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rsa" + "encoding/binary" + "errors" + "fmt" + "io" + "log" + "math/big" + + "golang.org/x/crypto/ed25519" + "golang.org/x/crypto/ssh" +) + +// Server wraps an Agent and uses it to implement the agent side of +// the SSH-agent, wire protocol. +type server struct { + agent Agent +} + +func (s *server) processRequestBytes(reqData []byte) []byte { + rep, err := s.processRequest(reqData) + if err != nil { + if err != errLocked { + // TODO(hanwen): provide better logging interface? + log.Printf("agent %d: %v", reqData[0], err) + } + return []byte{agentFailure} + } + + if err == nil && rep == nil { + return []byte{agentSuccess} + } + + return ssh.Marshal(rep) +} + +func marshalKey(k *Key) []byte { + var record struct { + Blob []byte + Comment string + } + record.Blob = k.Marshal() + record.Comment = k.Comment + + return ssh.Marshal(&record) +} + +// See [PROTOCOL.agent], section 2.5.1. +const agentV1IdentitiesAnswer = 2 + +type agentV1IdentityMsg struct { + Numkeys uint32 `sshtype:"2"` +} + +type agentRemoveIdentityMsg struct { + KeyBlob []byte `sshtype:"18"` +} + +type agentLockMsg struct { + Passphrase []byte `sshtype:"22"` +} + +type agentUnlockMsg struct { + Passphrase []byte `sshtype:"23"` +} + +func (s *server) processRequest(data []byte) (interface{}, error) { + switch data[0] { + case agentRequestV1Identities: + return &agentV1IdentityMsg{0}, nil + + case agentRemoveAllV1Identities: + return nil, nil + + case agentRemoveIdentity: + var req agentRemoveIdentityMsg + if err := ssh.Unmarshal(data, &req); err != nil { + return nil, err + } + + var wk wireKey + if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { + return nil, err + } + + return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) + + case agentRemoveAllIdentities: + return nil, s.agent.RemoveAll() + + case agentLock: + var req agentLockMsg + if err := ssh.Unmarshal(data, &req); err != nil { + return nil, err + } + + return nil, s.agent.Lock(req.Passphrase) + + case agentUnlock: + var req agentUnlockMsg + if err := ssh.Unmarshal(data, &req); err != nil { + return nil, err + } + return nil, s.agent.Unlock(req.Passphrase) + + case agentSignRequest: + var req signRequestAgentMsg + if err := ssh.Unmarshal(data, &req); err != nil { + return nil, err + } + + var wk wireKey + if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { + return nil, err + } + + k := &Key{ + Format: wk.Format, + Blob: req.KeyBlob, + } + + var sig *ssh.Signature + var err error + if extendedAgent, ok := s.agent.(ExtendedAgent); ok { + sig, err = extendedAgent.SignWithFlags(k, req.Data, SignatureFlags(req.Flags)) + } else { + sig, err = s.agent.Sign(k, req.Data) + } + + if err != nil { + return nil, err + } + return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil + + case agentRequestIdentities: + keys, err := s.agent.List() + if err != nil { + return nil, err + } + + rep := identitiesAnswerAgentMsg{ + NumKeys: uint32(len(keys)), + } + for _, k := range keys { + rep.Keys = append(rep.Keys, marshalKey(k)...) + } + return rep, nil + + case agentAddIDConstrained, agentAddIdentity: + return nil, s.insertIdentity(data) + + case agentExtension: + // Return a stub object where the whole contents of the response gets marshaled. + var responseStub struct { + Rest []byte `ssh:"rest"` + } + + if extendedAgent, ok := s.agent.(ExtendedAgent); !ok { + // If this agent doesn't implement extensions, [PROTOCOL.agent] section 4.7 + // requires that we return a standard SSH_AGENT_FAILURE message. + responseStub.Rest = []byte{agentFailure} + } else { + var req extensionAgentMsg + if err := ssh.Unmarshal(data, &req); err != nil { + return nil, err + } + res, err := extendedAgent.Extension(req.ExtensionType, req.Contents) + if err != nil { + // If agent extensions are unsupported, return a standard SSH_AGENT_FAILURE + // message as required by [PROTOCOL.agent] section 4.7. + if err == ErrExtensionUnsupported { + responseStub.Rest = []byte{agentFailure} + } else { + // As the result of any other error processing an extension request, + // [PROTOCOL.agent] section 4.7 requires that we return a + // SSH_AGENT_EXTENSION_FAILURE code. + responseStub.Rest = []byte{agentExtensionFailure} + } + } else { + if len(res) == 0 { + return nil, nil + } + responseStub.Rest = res + } + } + + return responseStub, nil + } + + return nil, fmt.Errorf("unknown opcode %d", data[0]) +} + +func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) { + for len(constraints) != 0 { + switch constraints[0] { + case agentConstrainLifetime: + lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5]) + constraints = constraints[5:] + case agentConstrainConfirm: + confirmBeforeUse = true + constraints = constraints[1:] + case agentConstrainExtension: + var msg constrainExtensionAgentMsg + if err = ssh.Unmarshal(constraints, &msg); err != nil { + return 0, false, nil, err + } + extensions = append(extensions, ConstraintExtension{ + ExtensionName: msg.ExtensionName, + ExtensionDetails: msg.ExtensionDetails, + }) + constraints = msg.Rest + default: + return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0]) + } + } + return +} + +func setConstraints(key *AddedKey, constraintBytes []byte) error { + lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes) + if err != nil { + return err + } + + key.LifetimeSecs = lifetimeSecs + key.ConfirmBeforeUse = confirmBeforeUse + key.ConstraintExtensions = constraintExtensions + return nil +} + +func parseRSAKey(req []byte) (*AddedKey, error) { + var k rsaKeyMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + if k.E.BitLen() > 30 { + return nil, errors.New("agent: RSA public exponent too large") + } + priv := &rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + E: int(k.E.Int64()), + N: k.N, + }, + D: k.D, + Primes: []*big.Int{k.P, k.Q}, + } + priv.Precompute() + + addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func parseEd25519Key(req []byte) (*AddedKey, error) { + var k ed25519KeyMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + priv := ed25519.PrivateKey(k.Priv) + + addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func parseDSAKey(req []byte) (*AddedKey, error) { + var k dsaKeyMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + priv := &dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: k.P, + Q: k.Q, + G: k.G, + }, + Y: k.Y, + }, + X: k.X, + } + + addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) { + priv = &ecdsa.PrivateKey{ + D: privScalar, + } + + switch curveName { + case "nistp256": + priv.Curve = elliptic.P256() + case "nistp384": + priv.Curve = elliptic.P384() + case "nistp521": + priv.Curve = elliptic.P521() + default: + return nil, fmt.Errorf("agent: unknown curve %q", curveName) + } + + priv.X, priv.Y = elliptic.Unmarshal(priv.Curve, keyBytes) + if priv.X == nil || priv.Y == nil { + return nil, errors.New("agent: point not on curve") + } + + return priv, nil +} + +func parseEd25519Cert(req []byte) (*AddedKey, error) { + var k ed25519CertMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + pubKey, err := ssh.ParsePublicKey(k.CertBytes) + if err != nil { + return nil, err + } + priv := ed25519.PrivateKey(k.Priv) + cert, ok := pubKey.(*ssh.Certificate) + if !ok { + return nil, errors.New("agent: bad ED25519 certificate") + } + + addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func parseECDSAKey(req []byte) (*AddedKey, error) { + var k ecdsaKeyMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + + priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D) + if err != nil { + return nil, err + } + + addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func parseRSACert(req []byte) (*AddedKey, error) { + var k rsaCertMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + + pubKey, err := ssh.ParsePublicKey(k.CertBytes) + if err != nil { + return nil, err + } + + cert, ok := pubKey.(*ssh.Certificate) + if !ok { + return nil, errors.New("agent: bad RSA certificate") + } + + // An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go + var rsaPub struct { + Name string + E *big.Int + N *big.Int + } + if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil { + return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) + } + + if rsaPub.E.BitLen() > 30 { + return nil, errors.New("agent: RSA public exponent too large") + } + + priv := rsa.PrivateKey{ + PublicKey: rsa.PublicKey{ + E: int(rsaPub.E.Int64()), + N: rsaPub.N, + }, + D: k.D, + Primes: []*big.Int{k.Q, k.P}, + } + priv.Precompute() + + addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func parseDSACert(req []byte) (*AddedKey, error) { + var k dsaCertMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + pubKey, err := ssh.ParsePublicKey(k.CertBytes) + if err != nil { + return nil, err + } + cert, ok := pubKey.(*ssh.Certificate) + if !ok { + return nil, errors.New("agent: bad DSA certificate") + } + + // A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go + var w struct { + Name string + P, Q, G, Y *big.Int + } + if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil { + return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err) + } + + priv := &dsa.PrivateKey{ + PublicKey: dsa.PublicKey{ + Parameters: dsa.Parameters{ + P: w.P, + Q: w.Q, + G: w.G, + }, + Y: w.Y, + }, + X: k.X, + } + + addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func parseECDSACert(req []byte) (*AddedKey, error) { + var k ecdsaCertMsg + if err := ssh.Unmarshal(req, &k); err != nil { + return nil, err + } + + pubKey, err := ssh.ParsePublicKey(k.CertBytes) + if err != nil { + return nil, err + } + cert, ok := pubKey.(*ssh.Certificate) + if !ok { + return nil, errors.New("agent: bad ECDSA certificate") + } + + // An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go + var ecdsaPub struct { + Name string + ID string + Key []byte + } + if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil { + return nil, err + } + + priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D) + if err != nil { + return nil, err + } + + addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments} + if err := setConstraints(addedKey, k.Constraints); err != nil { + return nil, err + } + return addedKey, nil +} + +func (s *server) insertIdentity(req []byte) error { + var record struct { + Type string `sshtype:"17|25"` + Rest []byte `ssh:"rest"` + } + + if err := ssh.Unmarshal(req, &record); err != nil { + return err + } + + var addedKey *AddedKey + var err error + + switch record.Type { + case ssh.KeyAlgoRSA: + addedKey, err = parseRSAKey(req) + case ssh.KeyAlgoDSA: + addedKey, err = parseDSAKey(req) + case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521: + addedKey, err = parseECDSAKey(req) + case ssh.KeyAlgoED25519: + addedKey, err = parseEd25519Key(req) + case ssh.CertAlgoRSAv01: + addedKey, err = parseRSACert(req) + case ssh.CertAlgoDSAv01: + addedKey, err = parseDSACert(req) + case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01: + addedKey, err = parseECDSACert(req) + case ssh.CertAlgoED25519v01: + addedKey, err = parseEd25519Cert(req) + default: + return fmt.Errorf("agent: not implemented: %q", record.Type) + } + + if err != nil { + return err + } + return s.agent.Add(*addedKey) +} + +// ServeAgent serves the agent protocol on the given connection. It +// returns when an I/O error occurs. +func ServeAgent(agent Agent, c io.ReadWriter) error { + s := &server{agent} + + var length [4]byte + for { + if _, err := io.ReadFull(c, length[:]); err != nil { + return err + } + l := binary.BigEndian.Uint32(length[:]) + if l == 0 { + return fmt.Errorf("agent: request size is 0") + } + if l > maxAgentResponseBytes { + // We also cap requests. + return fmt.Errorf("agent: request too large: %d", l) + } + + req := make([]byte, l) + if _, err := io.ReadFull(c, req); err != nil { + return err + } + + repData := s.processRequestBytes(req) + if len(repData) > maxAgentResponseBytes { + return fmt.Errorf("agent: reply too large: %d bytes", len(repData)) + } + + binary.BigEndian.PutUint32(length[:], uint32(len(repData))) + if _, err := c.Write(length[:]); err != nil { + return err + } + if _, err := c.Write(repData); err != nil { + return err + } + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index e2e83df9b..a44d0e88d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -84,14 +84,14 @@ github.com/containers/buildah/pkg/secrets github.com/containers/buildah/pkg/supplemented github.com/containers/buildah/pkg/umask github.com/containers/buildah/util -# github.com/containers/common v0.12.0 +# github.com/containers/common v0.13.0 github.com/containers/common/pkg/apparmor github.com/containers/common/pkg/auth github.com/containers/common/pkg/capabilities github.com/containers/common/pkg/cgroupv2 github.com/containers/common/pkg/config github.com/containers/common/pkg/sysinfo -# github.com/containers/conmon v2.0.16+incompatible +# github.com/containers/conmon v2.0.17+incompatible github.com/containers/conmon/runner/config # github.com/containers/image/v5 v5.4.5-0.20200529084758-46b2ee6aebb0 github.com/containers/image/v5/copy @@ -154,7 +154,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.20.1 +# github.com/containers/storage v1.20.2 github.com/containers/storage github.com/containers/storage/drivers github.com/containers/storage/drivers/aufs @@ -322,9 +322,9 @@ github.com/imdario/mergo github.com/inconshreveable/mousetrap # github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07 github.com/ishidawataru/sctp -# github.com/json-iterator/go v1.1.9 +# github.com/json-iterator/go v1.1.10 github.com/json-iterator/go -# github.com/klauspost/compress v1.10.6 +# github.com/klauspost/compress v1.10.7 github.com/klauspost/compress/flate github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 @@ -480,7 +480,7 @@ github.com/rootless-containers/rootlesskit/pkg/port/builtin/parent/udp/udpproxy github.com/rootless-containers/rootlesskit/pkg/port/portutil # github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 github.com/safchain/ethtool -# github.com/seccomp/containers-golang v0.4.1 +# github.com/seccomp/containers-golang v0.5.0 github.com/seccomp/containers-golang # github.com/seccomp/libseccomp-golang v0.9.1 github.com/seccomp/libseccomp-golang @@ -491,7 +491,7 @@ github.com/sirupsen/logrus/hooks/syslog github.com/spf13/cobra # github.com/spf13/pflag v1.0.5 github.com/spf13/pflag -# github.com/stretchr/testify v1.6.0 +# github.com/stretchr/testify v1.6.1 github.com/stretchr/testify/assert github.com/stretchr/testify/require # github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 @@ -575,6 +575,7 @@ golang.org/x/crypto/openpgp/s2k golang.org/x/crypto/pbkdf2 golang.org/x/crypto/poly1305 golang.org/x/crypto/ssh +golang.org/x/crypto/ssh/agent golang.org/x/crypto/ssh/internal/bcrypt_pbkdf golang.org/x/crypto/ssh/terminal # golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 |