diff options
37 files changed, 367 insertions, 75 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 08d5b7ac4..71fa68d45 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -368,6 +368,7 @@ testing_task: df_script: '$SCRIPT_BASE/logcollector.sh df' audit_log_script: '$SCRIPT_BASE/logcollector.sh audit' journal_script: '$SCRIPT_BASE/logcollector.sh journal' + varlink_script: '$SCRIPT_BASE/logcollector.sh varlink' # This task executes tests under unique environments/conditions @@ -1591,7 +1591,7 @@ subgidname [?string](#?string) sysctl [?[]string](#?[]string) -systemd [?bool](#?bool) +systemd [?string](#?string) tmpfs [?[]string](#?[]string) @@ -89,6 +89,9 @@ 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 GOPATH not specified, use one in the local directory ifeq ($(GOPATH),) export GOPATH := $(CURDIR)/_output @@ -274,7 +277,7 @@ remotesystem: if timeout -v 1 true; then \ SOCK_FILE=$(shell mktemp --dry-run --tmpdir io.podman.XXXXXX);\ export PODMAN_VARLINK_ADDRESS=unix:$$SOCK_FILE; \ - ./bin/podman varlink --timeout=0 $$PODMAN_VARLINK_ADDRESS &>/dev/null & \ + ./bin/podman varlink --timeout=0 $$PODMAN_VARLINK_ADDRESS &> $(if $(VARLINK_LOG),$(VARLINK_LOG),/dev/null) & \ retry=5;\ while [[ $$retry -ge 0 ]]; do\ echo Waiting for varlink server...;\ @@ -435,7 +438,10 @@ install.systemd: install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${SYSTEMDDIR}/io.podman.socket install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.socket ${DESTDIR}${USERSYSTEMDDIR}/io.podman.socket install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.service ${DESTDIR}${SYSTEMDDIR}/io.podman.service - install ${SELINUXOPT} -m 644 contrib/varlink/io.podman.service ${DESTDIR}${USERSYSTEMDDIR}/io.podman.service + install ${SELINUXOPT} -d ${DESTDIR}${USERSYSTEMDDIR} + # User units are ordered differently, we can't make the *system* multi-user.target depend on a user unit. + # For user units the default.target that's the default is fine. + sed -e 's,^WantedBy=.*,WantedBy=default.target,' < contrib/varlink/io.podman.service > ${DESTDIR}${USERSYSTEMDDIR}/io.podman.service install ${SELINUXOPT} -m 644 contrib/varlink/podman.conf ${DESTDIR}${TMPFILESDIR}/podman.conf uninstall: diff --git a/cmd/podman/cliconfig/defaults.go b/cmd/podman/cliconfig/defaults.go index d5dae0874..ce695d153 100644 --- a/cmd/podman/cliconfig/defaults.go +++ b/cmd/podman/cliconfig/defaults.go @@ -1,10 +1,5 @@ package cliconfig -const ( - // DefaultSystemD value - DefaultSystemD bool = true -) - var ( // DefaultHealthCheckInterval default value DefaultHealthCheckInterval = "30s" diff --git a/cmd/podman/common.go b/cmd/podman/common.go index 2a3f8f3ad..e93586b62 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -455,9 +455,9 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "sysctl", []string{}, "Sysctl options (default [])", ) - createFlags.Bool( - "systemd", cliconfig.DefaultSystemD, - "Run container in systemd mode if the command executable is systemd or init", + createFlags.String( + "systemd", "true", + `Run container in systemd mode ("true"|"false"|"always" (default "true")`, ) createFlags.StringArray( "tmpfs", []string{}, diff --git a/cmd/podman/main_local.go b/cmd/podman/main_local.go index 917096e17..bdffb6b1e 100644 --- a/cmd/podman/main_local.go +++ b/cmd/podman/main_local.go @@ -200,17 +200,12 @@ func setupRootless(cmd *cobra.Command, args []string) error { return errors.Wrapf(err, "could not get pause process pid file path") } - if _, err := os.Stat(pausePidPath); err == nil { - became, ret, err := rootless.TryJoinFromFilePaths("", false, []string{pausePidPath}) - if err != nil { - logrus.Errorf("cannot join pause process. You may need to remove %s and stop all containers", pausePidPath) - logrus.Errorf("you can use `%s system migrate` to recreate the pause process and restart the containers", os.Args[0]) - logrus.Errorf(err.Error()) - os.Exit(1) - } - if became { - os.Exit(ret) - } + became, ret, err := rootless.TryJoinPauseProcess(pausePidPath) + if err != nil { + return err + } + if became { + os.Exit(ret) } // if there is no pid file, try to join existing containers, and create a pause process. @@ -225,7 +220,7 @@ func setupRootless(cmd *cobra.Command, args []string) error { paths = append(paths, ctr.Config().ConmonPidFile) } - became, ret, err := rootless.TryJoinFromFilePaths(pausePidPath, true, paths) + became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths) if err := movePauseProcessToScope(); err != nil { conf, err := runtime.GetConfig() if err != nil { diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 9020613c5..7c56db8db 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -662,9 +662,17 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, errors.Errorf("invalid image-volume type %q. Pick one of bind, tmpfs, or ignore", c.String("image-volume")) } - var systemd bool - if command != nil && c.Bool("systemd") && ((filepath.Base(command[0]) == "init") || (filepath.Base(command[0]) == "systemd")) { - systemd = true + systemd := c.String("systemd") == "always" + if !systemd && command != nil { + x, err := strconv.ParseBool(c.String("systemd")) + if err != nil { + return nil, errors.Wrapf(err, "cannot parse bool %s", c.String("systemd")) + } + if x && (command[0] == "/usr/sbin/init" || (filepath.Base(command[0]) == "systemd")) { + systemd = true + } + } + if systemd { if signalString == "" { stopSignal, err = signal.ParseSignal("RTMIN+3") if err != nil { diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index cccdd1bea..0f71dc087 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -449,7 +449,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["subgidname"] = newCRString(c, "subgidname") m["subuidname"] = newCRString(c, "subuidname") m["sysctl"] = newCRStringSlice(c, "sysctl") - m["systemd"] = newCRBool(c, "systemd") + m["systemd"] = newCRString(c, "systemd") m["tmpfs"] = newCRStringArray(c, "tmpfs") m["tty"] = newCRBool(c, "tty") m["uidmap"] = newCRStringSlice(c, "uidmap") diff --git a/cmd/podman/shared/intermediate_varlink.go b/cmd/podman/shared/intermediate_varlink.go index 9dbf83950..c95470a72 100644 --- a/cmd/podman/shared/intermediate_varlink.go +++ b/cmd/podman/shared/intermediate_varlink.go @@ -152,7 +152,7 @@ func (g GenericCLIResults) MakeVarlink() iopodman.Create { Subuidname: StringToPtr(g.Find("subuidname")), Subgidname: StringToPtr(g.Find("subgidname")), Sysctl: StringSliceToPtr(g.Find("sysctl")), - Systemd: BoolToPtr(g.Find("systemd")), + Systemd: StringToPtr(g.Find("systemd")), Tmpfs: StringSliceToPtr(g.Find("tmpfs")), Tty: BoolToPtr(g.Find("tty")), Uidmap: StringSliceToPtr(g.Find("uidmap")), @@ -321,6 +321,7 @@ func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults { var memSwapDefault int64 = -1 netModeDefault := "bridge" + systemdDefault := "true" if rootless.IsRootless() { netModeDefault = "slirp4netns" } @@ -409,7 +410,7 @@ func VarlinkCreateToGeneric(opts iopodman.Create) GenericCLIResults { m["subgidname"] = stringFromVarlink(opts.Subgidname, "subgidname", nil) m["subuidname"] = stringFromVarlink(opts.Subuidname, "subuidname", nil) m["sysctl"] = stringSliceFromVarlink(opts.Sysctl, "sysctl", nil) - m["systemd"] = boolFromVarlink(opts.Systemd, "systemd", cliconfig.DefaultSystemD) + m["systemd"] = stringFromVarlink(opts.Systemd, "systemd", &systemdDefault) m["tmpfs"] = stringSliceFromVarlink(opts.Tmpfs, "tmpfs", nil) m["tty"] = boolFromVarlink(opts.Tty, "tty", false) m["uidmap"] = stringSliceFromVarlink(opts.Uidmap, "uidmap", nil) diff --git a/cmd/podman/start.go b/cmd/podman/start.go index 737a6d9f1..2d2cf74d2 100644 --- a/cmd/podman/start.go +++ b/cmd/podman/start.go @@ -60,6 +60,9 @@ func startCmd(c *cliconfig.StartValues) error { } sigProxy := c.SigProxy || attach + if c.Flag("sig-proxy").Changed { + sigProxy = c.SigProxy + } if sigProxy && !attach { return errors.Wrapf(define.ErrInvalidArg, "you cannot use sig-proxy without --attach") diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index 2408dc80c..13e8394fb 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -363,7 +363,7 @@ type Create ( subuidname: ?string, subgidname: ?string, sysctl: ?[]string, - systemd: ?bool, + systemd: ?string, tmpfs: ?[]string, tty: ?bool, uidmap: ?[]string, diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh index 110066ea7..e8f6c50d9 100755 --- a/contrib/cirrus/integration_test.sh +++ b/contrib/cirrus/integration_test.sh @@ -4,7 +4,7 @@ set -e source $(dirname $0)/lib.sh -req_env_var GOSRC SCRIPT_BASE OS_RELEASE_ID OS_RELEASE_VER CONTAINER_RUNTIME +req_env_var GOSRC SCRIPT_BASE OS_RELEASE_ID OS_RELEASE_VER CONTAINER_RUNTIME VARLINK_LOG # 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' @@ -59,7 +59,7 @@ case "$SPECIALMODE" in make test-binaries if [[ "$TEST_REMOTE_CLIENT" == "true" ]] then - make remote${TESTSUITE} + make remote${TESTSUITE} VARLINK_LOG=$VARLINK_LOG else make local${TESTSUITE} fi diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index 94a94f70d..8a7d3c1a3 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -35,6 +35,9 @@ PACKER_BASE=${PACKER_BASE:-./contrib/cirrus/packer} 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" +# Log remote-client system test varlink output here +export VARLINK_LOG=/var/tmp/varlink.log + cd $GOSRC if type -P git &> /dev/null && [[ -d "$GOSRC/.git" ]] then diff --git a/contrib/cirrus/logcollector.sh b/contrib/cirrus/logcollector.sh index b0a644f8c..17f5eb099 100755 --- a/contrib/cirrus/logcollector.sh +++ b/contrib/cirrus/logcollector.sh @@ -4,7 +4,7 @@ set -e source $(dirname $0)/lib.sh -req_env_var CIRRUS_WORKING_DIR OS_RELEASE_ID +req_env_var CIRRUS_WORKING_DIR OS_RELEASE_ID TEST_REMOTE_CLIENT # Assume there are other log collection commands to follow - Don't # let one break another that may be useful, but also keep any @@ -32,6 +32,15 @@ case $1 in df) showrun df -lhTx tmpfs ;; ginkgo) showrun cat $CIRRUS_WORKING_DIR/test/e2e/ginkgo-node-*.log ;; journal) showrun journalctl -b ;; + varlink) + if [[ "$TEST_REMOTE_CLIENT" == "true" ]] + then + echo "(Trailing 100 lines of $VARLINK_LOG)" + showrun tail -100 $VARLINK_LOG + else + die 0 "\$TEST_REMOTE_CLIENT is not 'true': $TEST_REMOTE_CLIENT" + fi + ;; packages) # These names are common to Fedora and Ubuntu PKG_NAMES=(\ diff --git a/contrib/varlink/io.podman.socket b/contrib/varlink/io.podman.socket index 629a5dd20..f6a3ddc49 100644 --- a/contrib/varlink/io.podman.socket +++ b/contrib/varlink/io.podman.socket @@ -8,4 +8,3 @@ SocketMode=0600 [Install] WantedBy=sockets.target -Also=multi-user.target diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 46fa4fcd4..35602f97b 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -710,12 +710,18 @@ Network Namespace - current sysctls allowed: Note: if you use the --network=host option these sysctls will not be allowed. -**--systemd**=*true|false* +**--systemd**=*true|false|always* Run container in systemd mode. The default is *true*. -If the command you running inside of the container is systemd or init, podman -will setup tmpfs mount points in the following directories: +The value *always* enforces the systemd mode is enforced without +looking at the executable name. Otherwise, if set to true and the +command you are running inside the container is systemd or +/usr/sbin/init. + +If the command you are running inside of the container is systemd or +/usr/sbin/init, Podman will setup tmpfs mount points in the following +directories: /run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal diff --git a/docs/podman-derivative-api b/docs/podman-derivative-api new file mode 100644 index 000000000..1b6153df5 --- /dev/null +++ b/docs/podman-derivative-api @@ -0,0 +1,64 @@ +.TH How to use libpod for custom/derivative projects +.PP +libpod today is a Golang library and a CLI. The choice of interface you make has advantages and disadvantages. + +.SH Running as a subprocess +.PP +Advantages: + +.RS +.IP \(bu 2 +Many commands output JSON +.IP \(bu 2 +Works with languages other than Golang +.IP \(bu 2 +Easy to get started + +.RE + +.PP +Disadvantages: + +.RS +.IP \(bu 2 +Error handling is harder +.IP \(bu 2 +May be slower +.IP \(bu 2 +Can't hook into or control low\-level things like how images are pulled + +.RE + +.SH Vendoring into a Go project +.PP +Advantages: + +.RS +.IP \(bu 2 +Significant power and control + +.RE + +.PP +Disadvantages: + +.RS +.IP \(bu 2 +You are now on the hook for container runtime security updates (partially, \fB\fCrunc\fR/\fB\fCcrun\fR are separate) +.IP \(bu 2 +Binary size +.IP \(bu 2 +Potential skew between multiple libpod versions operating on the same storage can cause problems + +.RE + +.SH Varlink +.PP +Some code exists for this; splits the difference. Future uncertain. + +.SH Making the choice +.PP +A good question to ask first is: Do you want users to be able to use \fB\fCpodman\fR to manipulate the containers created by your project? +If so, that makes it more likely that you want to run \fB\fCpodman\fR as a subprocess. If you want a separate image store and a fundamentally +different experience; if what you're doing with containers is quite different from those created by the \fB\fCpodman\fR CLI, +that may drive you towards vendoring. diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index dfc634288..e255119d7 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -747,12 +747,18 @@ Network Namespace - current sysctls allowed: Note: if you use the `--network=host` option these sysctls will not be allowed. -**--systemd**=*true|false* +**--systemd**=*true|false|always* Run container in systemd mode. The default is *true*. -If the command you are running inside of the container is systemd or init, Podman -will setup tmpfs mount points in the following directories: +The value *always* enforces the systemd mode is enforced without +looking at the executable name. Otherwise, if set to true and the +command you are running inside the container is systemd or +/usr/sbin/init. + +If the command you are running inside of the container is systemd or +/usr/sbin/init, Podman will setup tmpfs mount points in the following +directories: /run, /run/lock, /tmp, /sys/fs/cgroup/systemd, /var/lib/journal @@ -14,9 +14,8 @@ require ( github.com/containernetworking/cni v0.7.1 github.com/containernetworking/plugins v0.8.2 github.com/containers/buildah v1.11.3 - github.com/containers/image v3.0.2+incompatible // indirect github.com/containers/image/v4 v4.0.1 - github.com/containers/psgo v1.3.1 + github.com/containers/psgo v1.3.2 github.com/containers/storage v1.13.4 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect @@ -69,6 +69,20 @@ github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDpl github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/psgo v1.3.1 h1:1kE+jJ9Ou5f9zQT/M2IdeSclsKWsXrSFlOcnqc+F2TA= github.com/containers/psgo v1.3.1/go.mod h1:LLiRMmxZ6FWP4bB/fOUu6kDT+4okk/ZCeeykqh0O5Ns= +github.com/containers/psgo v1.3.2 h1:jYfppPih3S/j2Yi5O14AXjd8GfCx1ph9L3YsoK3adko= +github.com/containers/psgo v1.3.2/go.mod h1:ENXXLQ5E1At4K0EUsGogXBJi/C28gwqkONWeLPI9fJ8= +github.com/containers/storage v1.12.10-0.20190627120555-8eed0c36d1e3 h1:kO/YA36sGuPDFvVIzZxJp7xmwa+/wCVADxDSuFzsZwM= +github.com/containers/storage v1.12.10-0.20190627120555-8eed0c36d1e3/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= +github.com/containers/storage v1.12.11 h1:r35VsROen9Kw3+LN/v4O4g7cT5zQPX06vkcjqScJ2z8= +github.com/containers/storage v1.12.11/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= +github.com/containers/storage v1.12.12 h1:gao0GNzjmSX4Ai/StOHtUVIrBguC0OKyvx/ZMwBdyuY= +github.com/containers/storage v1.12.12/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= +github.com/containers/storage v1.12.13 h1:GtaLCY8p1Drlk1Oew581jGvB137UaO+kpz0HII67T0A= +github.com/containers/storage v1.12.13/go.mod h1:+RirK6VQAqskQlaTBrOG6ulDvn4si2QjFE1NZCn06MM= +github.com/containers/storage v1.12.16 h1:zePYS1GiG8CuRqLCeA0ufx4X27K06HcJLV50DdojL+Y= +github.com/containers/storage v1.12.16/go.mod h1:QsZp4XMJjyPNNbQHZeyNW3OmhwsWviI+7S6iOcu6a4c= +github.com/containers/storage v1.13.1 h1:rjVirLS9fCGkUFlLDZEoGDDUugtIf46DufWvJu08wxQ= +github.com/containers/storage v1.13.1/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= github.com/containers/storage v1.13.2 h1:UXZ0Ckmk6+6+4vj2M2ywruVtH97pnRoAhTG8ctd+yQI= github.com/containers/storage v1.13.2/go.mod h1:6D8nK2sU9V7nEmAraINRs88ZEscM5C5DK+8Npp27GeA= github.com/containers/storage v1.13.4 h1:j0bBaJDKbUHtAW1MXPFnwXJtqcH+foWeuXK1YaBV5GA= diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go index e43d54eee..0bb1df7b8 100644 --- a/libpod/boltdb_state.go +++ b/libpod/boltdb_state.go @@ -2,6 +2,7 @@ package libpod import ( "bytes" + "os" "strings" "sync" @@ -658,9 +659,13 @@ func (s *BoltState) UpdateContainer(ctr *Container) error { return err } - // Handle network namespace - if err := replaceNetNS(netNSPath, ctr, newState); err != nil { - return err + // Handle network namespace. + if os.Geteuid() == 0 { + // Do it only when root, either on the host or as root in the + // user namespace. + if err := replaceNetNS(netNSPath, ctr, newState); err != nil { + return err + } } // New state compiled successfully, swap it into the current state diff --git a/libpod/container_internal.go b/libpod/container_internal.go index a4dcd23be..ac921d737 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -939,6 +939,9 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error { // With the spec complete, do an OCI create if err := c.ociRuntime.createContainer(c, nil); err != nil { + if strings.Contains(err.Error(), "this version of runc doesn't work on cgroups v2") { + logrus.Errorf("oci runtime %q does not support CGroups V2: use system migrate to mitigate", c.ociRuntime.name) + } return err } diff --git a/libpod/info.go b/libpod/info.go index 297086ebb..6caa87038 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -69,6 +69,18 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) { program["Package"] = packageVersion(path) info["slirp4netns"] = program } + uidmappings, err := rootless.ReadMappingsProc("/proc/self/uid_map") + if err != nil { + return nil, errors.Wrapf(err, "error reading uid mappings") + } + gidmappings, err := rootless.ReadMappingsProc("/proc/self/gid_map") + if err != nil { + return nil, errors.Wrapf(err, "error reading gid mappings") + } + idmappings := make(map[string]interface{}) + idmappings["uidmap"] = uidmappings + idmappings["gidmap"] = gidmappings + info["IDMappings"] = idmappings } info["OCIRuntime"] = map[string]interface{}{ "path": r.defaultOCIRuntime.path, @@ -128,6 +140,7 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) { } info["hostname"] = host info["eventlogger"] = r.eventer.String() + return info, nil } diff --git a/pkg/rootless/rootless.go b/pkg/rootless/rootless.go new file mode 100644 index 000000000..7e9fe9db6 --- /dev/null +++ b/pkg/rootless/rootless.go @@ -0,0 +1,45 @@ +package rootless + +import ( + "os" + + "github.com/containers/storage" + "github.com/pkg/errors" +) + +func TryJoinPauseProcess(pausePidPath string) (bool, int, error) { + if _, err := os.Stat(pausePidPath); err != nil { + return false, -1, nil + } + + became, ret, err := TryJoinFromFilePaths("", false, []string{pausePidPath}) + if err == nil { + return became, ret, err + } + + // It could not join the pause process, let's lock the file before trying to delete it. + pidFileLock, err := storage.GetLockfile(pausePidPath) + if err != nil { + // The file was deleted by another process. + if os.IsNotExist(err) { + return false, -1, nil + } + return false, -1, errors.Wrapf(err, "error acquiring lock on %s", pausePidPath) + } + + pidFileLock.Lock() + defer func() { + if pidFileLock.Locked() { + pidFileLock.Unlock() + } + }() + + // Now the pause PID file is locked. Try to join once again in case it changed while it was not locked. + became, ret, err = TryJoinFromFilePaths("", false, []string{pausePidPath}) + if err != nil { + // It is still failing. We can safely remove it. + os.Remove(pausePidPath) + return false, -1, nil + } + return became, ret, err +} diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 6f6239e5f..99307e8c4 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -431,12 +431,14 @@ func becomeRootInUserNS(pausePid, fileToRead string, fileOutput *os.File) (bool, if err != nil { return false, -1, errors.Wrapf(err, "cannot write setgroups file") } + logrus.Debugf("write setgroups file exited with 0") uidMap := fmt.Sprintf("/proc/%d/uid_map", pid) err = ioutil.WriteFile(uidMap, []byte(fmt.Sprintf("%d %d 1\n", 0, os.Geteuid())), 0666) if err != nil { return false, -1, errors.Wrapf(err, "cannot write uid_map") } + logrus.Debugf("write uid_map exited with 0") } gidsMapped := false @@ -566,10 +568,10 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st r, w := os.NewFile(uintptr(fds[0]), "read file"), os.NewFile(uintptr(fds[1]), "write file") - defer errorhandling.CloseQuiet(w) defer errorhandling.CloseQuiet(r) if _, _, err := becomeRootInUserNS("", path, w); err != nil { + w.Close() lastErr = err continue } @@ -578,7 +580,6 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st return false, 0, err } defer func() { - errorhandling.CloseQuiet(r) C.reexec_in_user_namespace_wait(-1, 0) }() @@ -603,7 +604,7 @@ func TryJoinFromFilePaths(pausePidPath string, needNewNamespace bool, paths []st return joinUserAndMountNS(uint(pausePid), pausePidPath) } -func readMappingsProc(path string) ([]idtools.IDMap, error) { +func ReadMappingsProc(path string) ([]idtools.IDMap, error) { file, err := os.Open(path) if err != nil { return nil, errors.Wrapf(err, "cannot open %s", path) @@ -669,7 +670,7 @@ func ConfigurationMatches() (bool, error) { return false, err } - currentUIDs, err := readMappingsProc("/proc/self/uid_map") + currentUIDs, err := ReadMappingsProc("/proc/self/uid_map") if err != nil { return false, err } @@ -678,7 +679,7 @@ func ConfigurationMatches() (bool, error) { return false, err } - currentGIDs, err := readMappingsProc("/proc/self/gid_map") + currentGIDs, err := ReadMappingsProc("/proc/self/gid_map") if err != nil { return false, err } diff --git a/pkg/rootless/rootless_unsupported.go b/pkg/rootless/rootless_unsupported.go index ddd9182b0..ce488f364 100644 --- a/pkg/rootless/rootless_unsupported.go +++ b/pkg/rootless/rootless_unsupported.go @@ -65,3 +65,8 @@ func ConfigurationMatches() (bool, error) { func GetConfiguredMappings() ([]idtools.IDMap, []idtools.IDMap, error) { return nil, nil, errors.New("this function is not supported on this os") } + +// ReadMappingsProc returns the uid_map and gid_map +func ReadMappingsProc(path string) ([]idtools.IDMap, error) { + return nil, nil +} diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go index a65263b7d..3685450f0 100644 --- a/pkg/spec/createconfig.go +++ b/pkg/spec/createconfig.go @@ -195,8 +195,7 @@ func (c *CreateConfig) getContainerCreateOptions(runtime *libpod.Runtime, pod *l if c.Interactive { options = append(options, libpod.WithStdin()) } - if c.Systemd && (strings.HasSuffix(c.Command[0], "init") || - strings.HasSuffix(c.Command[0], "systemd")) { + if c.Systemd { options = append(options, libpod.WithSystemd()) } if c.Name != "" { diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go index 06ab6aacd..13f14183b 100644 --- a/test/e2e/start_test.go +++ b/test/e2e/start_test.go @@ -110,7 +110,7 @@ var _ = Describe("Podman start", func() { start.WaitWithDefaultTimeout() Expect(start.ExitCode()).Should(BeNumerically(">", 0)) - Eventually(podmanTest.NumberOfContainers(), defaultWaitTimeout).Should(BeZero()) + Eventually(podmanTest.NumberOfContainers(), defaultWaitTimeout, 3.0).Should(BeZero()) }) It("podman failed to start without --rm should NOT delete the container", func() { @@ -122,7 +122,7 @@ var _ = Describe("Podman start", func() { start.WaitWithDefaultTimeout() Expect(start.ExitCode()).Should(BeNumerically(">", 0)) - Eventually(podmanTest.NumberOfContainers(), defaultWaitTimeout).Should(Equal(1)) + Eventually(podmanTest.NumberOfContainers(), defaultWaitTimeout, 3.0).Should(Equal(1)) }) It("podman start --sig-proxy should not work without --attach", func() { diff --git a/test/e2e/systemd_test.go b/test/e2e/systemd_test.go index 02778d493..9ec48ba00 100644 --- a/test/e2e/systemd_test.go +++ b/test/e2e/systemd_test.go @@ -94,7 +94,7 @@ WantedBy=multi-user.target Expect(pull.ExitCode()).To(Equal(0)) ctrName := "testSystemd" - run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", systemdImage, "init"}) + run := podmanTest.Podman([]string{"run", "--name", ctrName, "-t", "-i", "-d", systemdImage, "/usr/sbin/init"}) run.WaitWithDefaultTimeout() Expect(run.ExitCode()).To(Equal(0)) ctrID := run.OutputToString() diff --git a/troubleshooting.md b/troubleshooting.md index 89c850356..6fed719f7 100644 --- a/troubleshooting.md +++ b/troubleshooting.md @@ -142,7 +142,7 @@ If you are using a useradd command within a Dockerfile with a large UID/GID, it #### Solution -If the entry in the Dockerfile looked like: RUN useradd -u 99999000 -g users newuser then add the `--log-no-init` parameter to change it to: `RUN useradd --log-no-init -u 99999000 -g users newuser`. This option tells useradd to stop creating the lastlog file. +If the entry in the Dockerfile looked like: RUN useradd -u 99999000 -g users newuser then add the `--no-log-init` parameter to change it to: `RUN useradd --no-log-init -u 99999000 -g users newuser`. This option tells useradd to stop creating the lastlog file. ### 7) Permission denied when running Podman commands diff --git a/vendor/github.com/containers/psgo/.travis.yml b/vendor/github.com/containers/psgo/.travis.yml index 1399d24d3..c07bb140b 100644 --- a/vendor/github.com/containers/psgo/.travis.yml +++ b/vendor/github.com/containers/psgo/.travis.yml @@ -14,6 +14,6 @@ before_install: - sudo apt-get install -qq bats script: - - make validate || travis_terminate 1 - - make build || travis_terminate 1 - - make test || travis_terminate 1 + - make validate + - make build + - make test diff --git a/vendor/github.com/containers/psgo/Makefile b/vendor/github.com/containers/psgo/Makefile index 6050b9d5b..361820784 100644 --- a/vendor/github.com/containers/psgo/Makefile +++ b/vendor/github.com/containers/psgo/Makefile @@ -1,4 +1,5 @@ export GO111MODULE=off +export GOPROXY=https://proxy.golang.org SHELL= /bin/bash GO ?= go @@ -9,11 +10,17 @@ PROJECT := github.com/containers/psgo BATS_TESTS := *.bats GO_SRC=$(shell find . -name \*.go) +GO_BUILD=$(GO) build +# Go module support: set `-mod=vendor` to use the vendored sources +ifeq ($(shell go help mod >/dev/null 2>&1 && echo true), true) + GO_BUILD=GO111MODULE=on $(GO) build -mod=vendor +endif + all: validate build .PHONY: build build: $(GO_SRC) - $(GO) build -buildmode=pie -o $(BUILD_DIR)/$(NAME) $(PROJECT)/sample + $(GO_BUILD) -buildmode=pie -o $(BUILD_DIR)/$(NAME) $(PROJECT)/sample .PHONY: clean clean: diff --git a/vendor/github.com/containers/psgo/go.mod b/vendor/github.com/containers/psgo/go.mod index a194ec196..d9d54c5f7 100644 --- a/vendor/github.com/containers/psgo/go.mod +++ b/vendor/github.com/containers/psgo/go.mod @@ -6,6 +6,6 @@ require ( github.com/opencontainers/runc v0.0.0-20190425234816-dae70e8efea4 github.com/pkg/errors v0.0.0-20190227000051-27936f6d90f9 github.com/sirupsen/logrus v0.0.0-20190403091019-9b3cdde74fbe - github.com/stretchr/testify v1.3.0 + github.com/stretchr/testify v1.4.0 golang.org/x/sys v0.0.0-20190425145619-16072639606e ) diff --git a/vendor/github.com/containers/psgo/go.sum b/vendor/github.com/containers/psgo/go.sum index da6c750db..bbdd99730 100644 --- a/vendor/github.com/containers/psgo/go.sum +++ b/vendor/github.com/containers/psgo/go.sum @@ -16,8 +16,12 @@ 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 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190425145619-16072639606e h1:4ktJgTV34+N3qOZUc5fAaG3Pb11qzMm3PkAoTAgUZ2I= golang.org/x/sys v0.0.0-20190425145619-16072639606e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/containers/psgo/internal/cgroups/cgroups.go b/vendor/github.com/containers/psgo/internal/cgroups/cgroups.go new file mode 100644 index 000000000..eecaf87cb --- /dev/null +++ b/vendor/github.com/containers/psgo/internal/cgroups/cgroups.go @@ -0,0 +1,44 @@ +// Copyright 2019 psgo authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cgroups + +import ( + "sync" + "syscall" +) + +const ( + CgroupRoot = "/sys/fs/cgroup" + cgroup2SuperMagic = 0x63677270 +) + +var ( + isUnifiedOnce sync.Once + isUnified bool + isUnifiedErr error +) + +// IsCgroup2UnifiedMode returns whether we are running in cgroup or cgroupv2 mode. +func IsCgroup2UnifiedMode() (bool, error) { + isUnifiedOnce.Do(func() { + var st syscall.Statfs_t + if err := syscall.Statfs(CgroupRoot, &st); err != nil { + isUnified, isUnifiedErr = false, err + } else { + isUnified, isUnifiedErr = st.Type == cgroup2SuperMagic, nil + } + }) + return isUnified, isUnifiedErr +} diff --git a/vendor/github.com/containers/psgo/internal/proc/pids.go b/vendor/github.com/containers/psgo/internal/proc/pids.go index ff4887364..69e8befc1 100644 --- a/vendor/github.com/containers/psgo/internal/proc/pids.go +++ b/vendor/github.com/containers/psgo/internal/proc/pids.go @@ -1,4 +1,4 @@ -// Copyright 2018 psgo authors +// Copyright 2018-2019 psgo authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,8 +18,11 @@ import ( "bufio" "fmt" "os" + "path/filepath" "strconv" "strings" + + "github.com/containers/psgo/internal/cgroups" ) // GetPIDs extracts and returns all PIDs from /proc. @@ -49,45 +52,98 @@ func GetPIDs() ([]string, error) { return pids, nil } -// pidCgroupPath returns the path to the pid's pids cgroup. -func pidCgroupPath(pid string) (string, error) { +// GetPIDsFromCgroup returns a strings slice of all pids listesd in pid's pids +// cgroup. It automatically detects if we're running in unified mode or not. +func GetPIDsFromCgroup(pid string) ([]string, error) { + unified, err := cgroups.IsCgroup2UnifiedMode() + if err != nil { + return nil, err + } + if unified { + return getPIDsFromCgroupV2(pid) + } + return getPIDsFromCgroupV1(pid) +} + +// getPIDsFromCgroupV1 returns a strings slice of all pids listesd in pid's pids +// cgroup. +func getPIDsFromCgroupV1(pid string) ([]string, error) { + // First, find the corresponding path to the PID cgroup. f, err := os.Open(fmt.Sprintf("/proc/%s/cgroup", pid)) if err != nil { - return "", err + return nil, err } defer f.Close() scanner := bufio.NewScanner(f) + cgroupPath := "" for scanner.Scan() { fields := strings.Split(scanner.Text(), ":") if len(fields) != 3 { continue } if fields[1] == "pids" { - return fmt.Sprintf("/sys/fs/cgroup/pids/%s/cgroup.procs", fields[2]), nil + cgroupPath = fmt.Sprintf("/sys/fs/cgroup/pids/%s/cgroup.procs", fields[2]) } } - return "", fmt.Errorf("couldn't find pids group for PID %s", pid) + + if cgroupPath == "" { + return nil, fmt.Errorf("couldn't find v1 pids group for PID %s", pid) + } + + // Second, extract the PIDs inside the cgroup. + f, err = os.Open(cgroupPath) + if err != nil { + return nil, err + } + defer f.Close() + + pids := []string{} + scanner = bufio.NewScanner(f) + for scanner.Scan() { + pids = append(pids, scanner.Text()) + } + + return pids, nil } -// GetPIDsFromCgroup returns a strings slice of all pids listesd in pid's pids +// getPIDsFromCgroupV2 returns a strings slice of all pids listesd in pid's pids // cgroup. -func GetPIDsFromCgroup(pid string) ([]string, error) { - cgroupPath, err := pidCgroupPath(pid) +func getPIDsFromCgroupV2(pid string) ([]string, error) { + // First, find the corresponding path to the PID cgroup. + f, err := os.Open(fmt.Sprintf("/proc/%s/cgroup", pid)) if err != nil { return nil, err } + defer f.Close() + + scanner := bufio.NewScanner(f) + cgroupSlice := "" + for scanner.Scan() { + fields := strings.Split(scanner.Text(), ":") + if len(fields) != 3 { + continue + } + cgroupSlice = fields[2] + break + } + + if cgroupSlice == "" { + return nil, fmt.Errorf("couldn't find v2 pids group for PID %s", pid) + } - f, err := os.Open(cgroupPath) + // Second, extract the PIDs inside the cgroup. + f, err = os.Open(filepath.Join(cgroups.CgroupRoot, cgroupSlice, "cgroup.procs")) if err != nil { return nil, err } defer f.Close() pids := []string{} - scanner := bufio.NewScanner(f) + scanner = bufio.NewScanner(f) for scanner.Scan() { pids = append(pids, scanner.Text()) } + return pids, nil } diff --git a/vendor/modules.txt b/vendor/modules.txt index c01409444..c422598fb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -108,12 +108,13 @@ github.com/containers/image/v4/pkg/blobinfocache/memory github.com/containers/image/v4/pkg/blobinfocache/internal/prioritize # github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b github.com/containers/libtrust -# github.com/containers/psgo v1.3.1 +# github.com/containers/psgo v1.3.2 github.com/containers/psgo github.com/containers/psgo/internal/capabilities github.com/containers/psgo/internal/dev github.com/containers/psgo/internal/proc github.com/containers/psgo/internal/process +github.com/containers/psgo/internal/cgroups github.com/containers/psgo/internal/host # github.com/containers/storage v1.13.4 github.com/containers/storage |