diff options
-rw-r--r-- | .cirrus.yml | 16 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | RELEASE_NOTES.md | 11 | ||||
-rw-r--r-- | changelog.txt | 17 | ||||
-rw-r--r-- | cmd/podman/cliconfig/config.go | 1 | ||||
-rw-r--r-- | cmd/podman/create.go | 2 | ||||
-rw-r--r-- | cmd/podman/exec.go | 30 | ||||
-rw-r--r-- | cmd/podman/rm.go | 3 | ||||
-rw-r--r-- | cmd/podman/top.go | 2 | ||||
-rwxr-xr-x | contrib/cirrus/integration_test.sh | 2 | ||||
-rw-r--r-- | contrib/cirrus/lib.sh | 15 | ||||
-rw-r--r-- | contrib/cirrus/packer/centos_setup.sh | 1 | ||||
-rw-r--r-- | contrib/cirrus/packer/fedora_setup.sh | 1 | ||||
-rw-r--r-- | contrib/cirrus/packer/rhel_setup.sh | 1 | ||||
-rw-r--r-- | contrib/cirrus/packer/ubuntu_setup.sh | 1 | ||||
-rwxr-xr-x | contrib/cirrus/setup_environment.sh | 4 | ||||
-rw-r--r-- | docs/podman-exec.1.md | 4 | ||||
-rw-r--r-- | libpod/container_api.go | 4 | ||||
-rw-r--r-- | libpod/oci.go | 13 | ||||
-rw-r--r-- | libpod/runtime.go | 25 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 23 | ||||
-rw-r--r-- | pkg/logs/logs.go | 22 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 9 | ||||
-rw-r--r-- | test/e2e/stop_test.go | 28 |
24 files changed, 183 insertions, 56 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index 4521866d1..b473980ea 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -27,11 +27,11 @@ env: #### #### Cache-image names to test with ### - FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-7f4cd1f7" - PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-7f4cd1f7" - UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-84514d8b" + FEDORA_CACHE_IMAGE_NAME: "fedora-29-libpod-d6d53e40" + PRIOR_FEDORA_CACHE_IMAGE_NAME: "fedora-28-libpod-d6d53e40" + UBUNTU_CACHE_IMAGE_NAME: "ubuntu-18-libpod-d6d53e40" # RHEL_CACHE_IMAGE_NAME: "rhel-8-notready" - PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-7f4cd1f7" + PRIOR_RHEL_CACHE_IMAGE_NAME: "rhel-7-libpod-d6d53e40" # CENTOS_CACHE_IMAGE_NAME: "centos-7-notready" #### @@ -169,9 +169,9 @@ testing_task: image_name: "${FEDORA_CACHE_IMAGE_NAME}" image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" image_name: "${UBUNTU_CACHE_IMAGE_NAME}" - image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}" - # TODO: tests fail + # TODO: Make these work (also optional_testing_task below) + # image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}" # image_name: "${RHEL_CACHE_IMAGE_NAME}" # image_name: "${CENTOS_CACHE_IMAGE_NAME}" @@ -206,9 +206,9 @@ optional_testing_task: image_name: "${FEDORA_CACHE_IMAGE_NAME}" image_name: "${PRIOR_FEDORA_CACHE_IMAGE_NAME}" image_name: "${UBUNTU_CACHE_IMAGE_NAME}" - image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}" - # TODO: Make these work (also build_images_task below) + # TODO: Make these work (also testing_task above) # image_name: "${RHEL_CACHE_IMAGE_NAME}" + # image_name: "${PRIOR_RHEL_CACHE_IMAGE_NAME}" # image_name: "${CENTOS_CACHE_IMAGE_NAME}" timeout_in: 60m @@ -1,6 +1,6 @@ GO ?= go DESTDIR ?= / -EPOCH_TEST_COMMIT ?= cabfc9b6fb16c5b55d70140182496bbe129f6a4b +EPOCH_TEST_COMMIT ?= 4b80517b6a638ff06f8ad432f0f0f5839283d058 HEAD ?= HEAD CHANGELOG_BASE ?= HEAD~ CHANGELOG_TARGET ?= HEAD @@ -44,7 +44,7 @@ LIBSECCOMP_COMMIT := release-2.3 # Rarely if ever should integration tests take more than 50min, # caller may override in special circumstances if needed. -GINKGOTIMEOUT ?= -timeout=50m +GINKGOTIMEOUT ?= -timeout=90m # If GOPATH not specified, use one in the local directory ifeq ($(GOPATH),) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 67975730e..65a3f5eea 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,16 @@ # Release Notes +## 1.1.2 +### Bugfixes +- Fixed a bug where the `podman image list`, `podman image rm`, and `podman container list` had broken global storage options +- Fixed a bug where the `--label` option to `podman create` and `podman run` was missing the `-l` alias +- Fixed a bug where running Podman with the `--config` flag would not set an appropriate default value for `tmp_dir` ([#2408](https://github.com/containers/libpod/issues/2408)) +- Fixed a bug where the `podman logs` command with the `--timestamps` flag produced unreadable output ([#2500](https://github.com/containers/libpod/issues/2500)) +- Fixed a bug where the `podman cp` command would automatically extract `.tar` files copied into the container ([#2509](https://github.com/containers/libpod/issues/2509)) + +### Misc +- The `podman container stop` command is now usable with the Podman remote client + ## 1.1.1 ### Bugfixes - Fixed a bug where `podman container restore` was erroneously available as `podman restore` ([#2191](https://github.com/containers/libpod/issues/2191)) diff --git a/changelog.txt b/changelog.txt index 9dd0ae2d1..7db579f3a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,20 @@ +- Changelog for v1.1.2 (2019-03-04) + * Fix #2521 + * Update release notes for v1.1.2 + * Change timestamp format for podman logs + * Don't extract tar file in podman cp + * runtime: fill a proper default tmpdir when --config is used + * Add additional defense against 0-length log segfaults + * When logging with timestamps, append only until newline + * Ensure that each log line is newline-terminated + * A few more usage-message tweaks + * Add missing short flag -l for run/create + * Fix aliased commands to actually work + * Support podman-remote stop container(s) + * Add tests to make sure podman container and podman image commands work + * Bump gitvalidation epoch + * Bump to v1.2.0-dev + - Changelog for v1.1.1 (2019-03-01) * Update release notes for v1.1.1 * Pull image for runlabel if not local diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 702e20040..ea99aafc2 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -100,6 +100,7 @@ type ExecValues struct { User string Latest bool Workdir string + PreserveFDs int } type ImageExistsValues struct { diff --git a/cmd/podman/create.go b/cmd/podman/create.go index 95cb732d9..129c886b2 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -869,7 +869,7 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l } return false, -1, errors.Errorf("dependency container %s is not running", ctr.ID()) } - return rootless.JoinNS(uint(pid)) + return rootless.JoinNS(uint(pid), 0) } } return rootless.BecomeRootInUserNS() diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index 4917fb606..32a6e4bb5 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -4,7 +4,9 @@ import ( "fmt" "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/spf13/cobra" + "io/ioutil" "os" + "strconv" "github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/libpod" @@ -47,6 +49,7 @@ func init() { flags.BoolVarP(&execCommand.Tty, "tty", "t", false, "Allocate a pseudo-TTY. The default is false") flags.StringVarP(&execCommand.User, "user", "u", "", "Sets the username or UID used and optionally the groupname or GID for the specified command") + flags.IntVar(&execCommand.PreserveFDs, "preserve-fds", 0, "Pass N additional file descriptors to the container") flags.StringVarP(&execCommand.Workdir, "workdir", "w", "", "Working directory inside the container") markFlagHiddenForRemoteClient("latest", flags) } @@ -82,11 +85,34 @@ func execCmd(c *cliconfig.ExecValues) error { return errors.Wrapf(err, "unable to exec into %s", args[0]) } + if c.PreserveFDs > 0 { + entries, err := ioutil.ReadDir("/proc/self/fd") + if err != nil { + return errors.Wrapf(err, "unable to read /proc/self/fd") + } + m := make(map[int]bool) + for _, e := range entries { + i, err := strconv.Atoi(e.Name()) + if err != nil { + if err != nil { + return errors.Wrapf(err, "cannot parse %s in /proc/self/fd", e.Name()) + } + } + m[i] = true + } + for i := 3; i < 3+c.PreserveFDs; i++ { + if _, found := m[i]; !found { + return errors.New("invalid --preserve-fds=N specified. Not enough FDs available") + } + } + + } + pid, err := ctr.PID() if err != nil { return err } - became, ret, err := rootless.JoinNS(uint(pid)) + became, ret, err := rootless.JoinNS(uint(pid), c.PreserveFDs) if err != nil { return err } @@ -113,5 +139,5 @@ func execCmd(c *cliconfig.ExecValues) error { streams.AttachError = true streams.AttachInput = true - return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams) + return ctr.Exec(c.Tty, c.Privileged, envs, cmd, c.User, c.Workdir, streams, c.PreserveFDs) } diff --git a/cmd/podman/rm.go b/cmd/podman/rm.go index 61b049840..d23f8228c 100644 --- a/cmd/podman/rm.go +++ b/cmd/podman/rm.go @@ -80,6 +80,9 @@ func rmCmd(c *cliconfig.RmValues) error { return err } if err != nil { + if errors.Cause(err) == libpod.ErrNoSuchCtr { + exitCode = 1 + } fmt.Println(err.Error()) } } diff --git a/cmd/podman/top.go b/cmd/podman/top.go index cdf270fa7..d96402f1a 100644 --- a/cmd/podman/top.go +++ b/cmd/podman/top.go @@ -108,7 +108,7 @@ func topCmd(c *cliconfig.TopValues) error { if err != nil { return err } - became, ret, err := rootless.JoinNS(uint(pid)) + became, ret, err := rootless.JoinNS(uint(pid), 0) if err != nil { return err } diff --git a/contrib/cirrus/integration_test.sh b/contrib/cirrus/integration_test.sh index 0fd86dfdc..58c8af289 100755 --- a/contrib/cirrus/integration_test.sh +++ b/contrib/cirrus/integration_test.sh @@ -19,7 +19,7 @@ case "${OS_RELEASE_ID}-${OS_RELEASE_VER}" in ubuntu-18) make install PREFIX=/usr ETCDIR=/etc make test-binaries - SKIP_USERNS=1 make localintegration GINKGOTIMEOUT=90m + SKIP_USERNS=1 make localintegration ;; fedora-29) ;& # Continue to the next item fedora-28) ;& diff --git a/contrib/cirrus/lib.sh b/contrib/cirrus/lib.sh index 8be696933..acd2447c0 100644 --- a/contrib/cirrus/lib.sh +++ b/contrib/cirrus/lib.sh @@ -300,21 +300,6 @@ EOF fi } -# Runs in testing VM, not image building -install_testing_dependencies() { - echo "Installing ginkgo, gomega, and easyjson into \$GOPATH=$GOPATH" - req_env_var " - GOPATH $GOPATH - GOSRC $GOSRC - " - cd "$GOSRC" - ooe.sh go get -u github.com/onsi/ginkgo/ginkgo - ooe.sh install -D -m 755 "$GOPATH"/bin/ginkgo /usr/bin/ - ooe.sh go get github.com/onsi/gomega/... - ooe.sh go get -u github.com/mailru/easyjson/... - sudo install -D -m 755 "$GOPATH"/bin/easyjson /usr/bin/ -} - install_packer_copied_files(){ # Install cni config, policy and registry config sudo install -D -m 755 /tmp/libpod/cni/87-podman-bridge.conflist \ diff --git a/contrib/cirrus/packer/centos_setup.sh b/contrib/cirrus/packer/centos_setup.sh index 923f2563b..d947a1d7f 100644 --- a/contrib/cirrus/packer/centos_setup.sh +++ b/contrib/cirrus/packer/centos_setup.sh @@ -27,6 +27,7 @@ ooe.sh sudo yum -y install centos-release-scl epel-release ooe.sh sudo yum -y install \ PyYAML \ atomic-registries \ + bats \ btrfs-progs-devel \ bzip2 \ device-mapper-devel \ diff --git a/contrib/cirrus/packer/fedora_setup.sh b/contrib/cirrus/packer/fedora_setup.sh index de7ad4506..84aee7667 100644 --- a/contrib/cirrus/packer/fedora_setup.sh +++ b/contrib/cirrus/packer/fedora_setup.sh @@ -26,6 +26,7 @@ ooe.sh sudo dnf update -y ooe.sh sudo dnf install -y \ atomic-registries \ + bats \ btrfs-progs-devel \ bzip2 \ device-mapper-devel \ diff --git a/contrib/cirrus/packer/rhel_setup.sh b/contrib/cirrus/packer/rhel_setup.sh index ac6866a57..20be97f9b 100644 --- a/contrib/cirrus/packer/rhel_setup.sh +++ b/contrib/cirrus/packer/rhel_setup.sh @@ -33,6 +33,7 @@ ooe.sh sudo yum -y update ooe.sh sudo yum -y install \ PyYAML \ atomic-registries \ + bats \ btrfs-progs-devel \ bzip2 \ device-mapper-devel \ diff --git a/contrib/cirrus/packer/ubuntu_setup.sh b/contrib/cirrus/packer/ubuntu_setup.sh index 5b7e1d714..24f1cce21 100644 --- a/contrib/cirrus/packer/ubuntu_setup.sh +++ b/contrib/cirrus/packer/ubuntu_setup.sh @@ -38,6 +38,7 @@ ooe.sh sudo -E apt-get -qq install \ apparmor \ autoconf \ automake \ + bats \ bison \ btrfs-tools \ build-essential \ diff --git a/contrib/cirrus/setup_environment.sh b/contrib/cirrus/setup_environment.sh index 77c20d9bd..c3276bb6f 100755 --- a/contrib/cirrus/setup_environment.sh +++ b/contrib/cirrus/setup_environment.sh @@ -78,10 +78,6 @@ then cd "${GOSRC}/" source "$SCRIPT_BASE/lib.sh" - - # Only testing-VMs need deps installed, not image-builder VM - echo "$CIRRUS_TASK_NAME" | grep -q 'image' || \ - install_testing_dependencies # must exist in $GOPATH fi record_timestamp "env. setup end" diff --git a/docs/podman-exec.1.md b/docs/podman-exec.1.md index 14088b468..b74713b0b 100644 --- a/docs/podman-exec.1.md +++ b/docs/podman-exec.1.md @@ -26,6 +26,10 @@ to run containers such as CRI-O, the last started container could be from eithe The latest option is not supported on the remote client. +**--preserve-fds=N** + +Pass down to the process N additional file descriptors (in addition to 0, 1, 2). The total FDs will be 3+N. + **--privileged** Give the process extended Linux capabilities when running the command in container. diff --git a/libpod/container_api.go b/libpod/container_api.go index 6bef3c47d..4a76e1434 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -204,7 +204,7 @@ func (c *Container) Kill(signal uint) error { // Exec starts a new process inside the container // TODO investigate allowing exec without attaching -func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string, streams *AttachStreams) error { +func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir string, streams *AttachStreams, preserveFDs int) error { var capList []string locked := false @@ -266,7 +266,7 @@ func (c *Container) Exec(tty, privileged bool, env, cmd []string, user, workDir logrus.Debugf("Creating new exec session in container %s with session id %s", c.ID(), sessionID) - execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID, streams) + execCmd, err := c.runtime.ociRuntime.execContainer(c, cmd, capList, env, tty, workDir, hostUser, sessionID, streams, preserveFDs) if err != nil { return errors.Wrapf(err, "error exec %s", c.ID()) } diff --git a/libpod/oci.go b/libpod/oci.go index 2b3cc5db5..2cbf25699 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -733,7 +733,7 @@ func (r *OCIRuntime) unpauseContainer(ctr *Container) error { // TODO: Add --detach support // TODO: Convert to use conmon // TODO: add --pid-file and use that to generate exec session tracking -func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string, streams *AttachStreams) (*exec.Cmd, error) { +func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty bool, cwd, user, sessionID string, streams *AttachStreams, preserveFDs int) (*exec.Cmd, error) { if len(cmd) == 0 { return nil, errors.Wrapf(ErrInvalidArg, "must provide a command to execute") } @@ -770,6 +770,9 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty args = append(args, "--user", user) } + if preserveFDs > 0 { + args = append(args, fmt.Sprintf("--preserve-fds=%d", preserveFDs)) + } if c.config.Spec.Process.NoNewPrivileges { args = append(args, "--no-new-privs") } @@ -806,6 +809,14 @@ func (r *OCIRuntime) execContainer(c *Container, cmd, capAdd, env []string, tty return nil, errors.Wrapf(err, "cannot start container %s", c.ID()) } + if preserveFDs > 0 { + for fd := 3; fd < 3+preserveFDs; fd++ { + // These fds were passed down to the runtime. Close them + // and not interfere + os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close() + } + } + return execCmd, nil } diff --git a/libpod/runtime.go b/libpod/runtime.go index 112b6820a..482cd9d73 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -333,7 +333,7 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { storageConf, err := util.GetDefaultStoreOptions() if err != nil { - return nil, errors.Wrapf(err, "error retrieving rootless storage config") + return nil, errors.Wrapf(err, "error retrieving storage config") } runtime.config.StorageConfig = storageConf runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod") @@ -365,8 +365,7 @@ func NewRuntime(options ...RuntimeOption) (runtime *Runtime, err error) { // containers/image uses XDG_RUNTIME_DIR to locate the auth file. // So make sure the env variable is set. - err = SetXdgRuntimeDir(runtimeDir) - if err != nil { + if err := SetXdgRuntimeDir(runtimeDir); err != nil { return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") } @@ -475,13 +474,31 @@ func NewRuntimeFromConfig(configPath string, options ...RuntimeOption) (runtime // Set three fields not in the TOML config runtime.config.StateType = defaultRuntimeConfig.StateType runtime.config.OCIRuntime = defaultRuntimeConfig.OCIRuntime - runtime.config.StorageConfig = storage.StoreOptions{} + + storageConf, err := util.GetDefaultStoreOptions() + if err != nil { + return nil, errors.Wrapf(err, "error retrieving storage config") + } + runtime.config.StorageConfig = storageConf + runtime.config.StaticDir = filepath.Join(storageConf.GraphRoot, "libpod") + runtime.config.VolumePath = filepath.Join(storageConf.GraphRoot, "volumes") tmpDir, err := getDefaultTmpDir() if err != nil { return nil, err } runtime.config.TmpDir = tmpDir + if rootless.IsRootless() { + runtimeDir, err := util.GetRootlessRuntimeDir() + if err != nil { + return nil, err + } + // containers/image uses XDG_RUNTIME_DIR to locate the auth file. + // So make sure the env variable is set. + if err := SetXdgRuntimeDir(runtimeDir); err != nil { + return nil, errors.Wrapf(err, "cannot set XDG_RUNTIME_DIR") + } + } // Check to see if the given configuration file exists if _, err := os.Stat(configPath); err != nil { diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index 4975bea9c..fcce9bb86 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -10,6 +10,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/adapter/shortcuts" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) // GetLatestContainer gets the latest Container and wraps it in an adapter Container @@ -46,9 +47,10 @@ func (r *LocalRuntime) LookupContainer(idOrName string) (*Container, error) { // StopContainers stops container(s) based on CLI inputs. // Returns list of successful id(s), map of failed id(s) + error, or error not from container func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopValues) ([]string, map[string]error, error) { - timeout := uint(0) - if cli.Flags().Changed("timeout") { - timeout = uint(cli.Timeout) + var timeout *uint + if cli.Flags().Changed("timeout") || cli.Flags().Changed("time") { + t := uint(cli.Timeout) + timeout = &t } var ( @@ -62,11 +64,18 @@ func (r *LocalRuntime) StopContainers(ctx context.Context, cli *cliconfig.StopVa } for _, c := range ctrs { - err := c.StopWithTimeout(timeout) - if err != nil && errors.Cause(err) != libpod.ErrCtrStopped { - failures[c.ID()] = err - } else { + if timeout == nil { + t := c.StopTimeout() + timeout = &t + logrus.Debugf("Set timeout to container %s default (%d)", c.ID(), *timeout) + } + if err := c.StopWithTimeout(*timeout); err == nil { ok = append(ok, c.ID()) + } else if errors.Cause(err) == libpod.ErrCtrStopped { + ok = append(ok, c.ID()) + logrus.Debugf("Container %s is already stopped", c.ID()) + } else { + failures[c.ID()] = err } } return ok, failures, nil diff --git a/pkg/logs/logs.go b/pkg/logs/logs.go index b104c592b..7fb5c7ea8 100644 --- a/pkg/logs/logs.go +++ b/pkg/logs/logs.go @@ -35,7 +35,10 @@ import ( const ( // timeFormat is the time format used in the log. - timeFormat = time.RFC3339Nano + // It is a modified version of RFC3339Nano that guarantees trailing + // zeroes are not trimmed, taken from + // https://github.com/golang/go/issues/19635 + timeFormat = "2006-01-02T15:04:05.000000000Z07:00" ) // LogStreamType is the type of the stream in CRI container log. @@ -277,10 +280,11 @@ func readLog(reader *bufio.Reader, opts *LogOptions) []string { // logWriter controls the writing into the stream based on the log options. type logWriter struct { - stdout io.Writer - stderr io.Writer - opts *LogOptions - remain int64 + stdout io.Writer + stderr io.Writer + opts *LogOptions + remain int64 + doAppend bool } // errMaximumWrite is returned when all bytes have been written. @@ -309,9 +313,15 @@ func (w *logWriter) write(msg *logMessage) error { return nil } line := msg.log - if w.opts.Timestamps { + if w.opts.Timestamps && !w.doAppend { prefix := append([]byte(msg.timestamp.Format(timeFormat)), delimiter[0]) line = append(prefix, line...) + if len(line) > 0 && line[len(line)-1] != '\n' { + w.doAppend = true + } + } + if w.doAppend && len(line) > 0 && line[len(line)-1] == '\n' { + w.doAppend = false } // If the line is longer than the remaining bytes, cut it. if int64(len(line)) > w.remain { diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 98692707f..55fba900e 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -102,7 +102,7 @@ func tryMappingTool(tool string, pid int, hostID int, mappings []idtools.IDMap) // JoinNS re-exec podman in a new userNS and join the user namespace of the specified // PID. -func JoinNS(pid uint) (bool, int, error) { +func JoinNS(pid uint, preserveFDs int) (bool, int, error) { if os.Geteuid() == 0 || os.Getenv("_LIBPOD_USERNS_CONFIGURED") != "" { return false, -1, nil } @@ -117,6 +117,13 @@ func JoinNS(pid uint) (bool, int, error) { if int(pidC) < 0 { return false, -1, errors.Errorf("cannot re-exec process") } + if preserveFDs > 0 { + for fd := 3; fd < 3+preserveFDs; fd++ { + // These fds were passed down to the runtime. Close them + // and not interfere + os.NewFile(uintptr(fd), fmt.Sprintf("fd-%d", fd)).Close() + } + } ret := C.reexec_in_user_namespace_wait(pidC) if ret < 0 { diff --git a/test/e2e/stop_test.go b/test/e2e/stop_test.go index eb680d2a1..cd0d804ee 100644 --- a/test/e2e/stop_test.go +++ b/test/e2e/stop_test.go @@ -82,7 +82,7 @@ var _ = Describe("Podman stop", func() { Expect(session3.ExitCode()).To(Equal(0)) }) - It("podman stop all containers", func() { + It("podman stop all containers -t", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) @@ -107,6 +107,32 @@ var _ = Describe("Podman stop", func() { Expect(output).To(ContainSubstring(cid3)) }) + It("podman stop container --time", func() { + session := podmanTest.RunTopContainer("test4") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"stop", "--time", "1", "test4"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + }) + + It("podman stop container --timeout", func() { + session := podmanTest.RunTopContainer("test5") + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + cid1 := session.OutputToString() + + session = podmanTest.Podman([]string{"stop", "--timeout", "1", "test5"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + output := session.OutputToString() + Expect(output).To(ContainSubstring(cid1)) + }) + It("podman stop latest containers", func() { session := podmanTest.RunTopContainer("test1") session.WaitWithDefaultTimeout() |