diff options
-rw-r--r-- | .cirrus.yml | 2 | ||||
-rw-r--r-- | .papr.sh | 3 | ||||
-rwxr-xr-x | contrib/cirrus/optional_system_test.sh | 24 | ||||
-rw-r--r-- | contrib/python/podman/test/test_pods_ctnrs.py | 3 | ||||
-rw-r--r-- | docs/podman-build.1.md | 2 | ||||
-rw-r--r-- | docs/podman-create.1.md | 12 | ||||
-rw-r--r-- | docs/podman-run.1.md | 13 | ||||
-rw-r--r-- | libpod/container_api.go | 23 | ||||
-rw-r--r-- | libpod/container_internal.go | 106 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 41 | ||||
-rw-r--r-- | libpod/oci.go | 61 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 2 | ||||
-rw-r--r-- | libpod/runtime_pod_linux.go | 19 | ||||
-rw-r--r-- | pkg/lookup/lookup.go | 8 | ||||
-rw-r--r-- | test/e2e/pause_test.go | 4 | ||||
-rw-r--r-- | test/e2e/run_dns_test.go | 9 |
16 files changed, 241 insertions, 91 deletions
diff --git a/.cirrus.yml b/.cirrus.yml index c5d35141e..f78205a49 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -63,6 +63,8 @@ full_vm_testing_task: integration_test_script: $SCRIPT_BASE/integration_test.sh + optional_system_test_script: $SCRIPT_BASE/optional_system_test.sh + success_script: $SCRIPT_BASE/success.sh @@ -139,6 +139,3 @@ if [ $integrationtest -eq 1 ]; then fi make ginkgo GOPATH=/go $INTEGRATION_TEST_ENVS fi - - -exit 0 diff --git a/contrib/cirrus/optional_system_test.sh b/contrib/cirrus/optional_system_test.sh new file mode 100755 index 000000000..705dda5ad --- /dev/null +++ b/contrib/cirrus/optional_system_test.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e +source $(dirname $0)/lib.sh + +MAGIC_RE='\*\*\*\s*CIRRUS:\s*SYSTEM\s*TEST\s*\*\*\*' +if ! echo "$CIRRUS_CHANGE_MESSAGE" | egrep -q "$MAGIC_RE" +then + echo "Skipping system-testing because PR title or description" + echo "does not match regular expression: $MAGIC_RE" + exit 0 +fi + +req_env_var " +GOSRC $GOSRC +OS_RELEASE_ID $OS_RELEASE_ID +OS_RELEASE_VER $OS_RELEASE_VER +" + +show_env_vars + +set -x +cd "$GOSRC" +make localsystem diff --git a/contrib/python/podman/test/test_pods_ctnrs.py b/contrib/python/podman/test/test_pods_ctnrs.py index 14ce95c8a..009e30720 100644 --- a/contrib/python/podman/test/test_pods_ctnrs.py +++ b/contrib/python/podman/test/test_pods_ctnrs.py @@ -52,7 +52,8 @@ class TestPodsCtnrs(PodmanTestCase): status = FoldedString(pod.containersinfo[0]['status']) self.assertIn(status, ('stopped', 'exited', 'running')) - killed = pod.kill() + # Pod kill is broken, so use stop for now + killed = pod.stop() self.assertEqual(pod, killed) def test_999_remove(self): diff --git a/docs/podman-build.1.md b/docs/podman-build.1.md index 0cbce15c0..f887d68cd 100644 --- a/docs/podman-build.1.md +++ b/docs/podman-build.1.md @@ -171,7 +171,7 @@ value can be entered. The password is entered without echo. **--disable-content-trust** This is a Docker specific option to disable image verification to a Docker -registry and is not supported by Buildah. This flag is a NOOP and provided +registry and is not supported by Podman. This flag is a NOOP and provided soley for scripting compatibility. **--file, -f** *Dockerfile* diff --git a/docs/podman-create.1.md b/docs/podman-create.1.md index 5a4d7fb5a..68c00685b 100644 --- a/docs/podman-create.1.md +++ b/docs/podman-create.1.md @@ -66,7 +66,7 @@ Write the container ID to the file **--conmon-pidfile**="" -Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers. +Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers. **--cpu-count**=*0* @@ -321,13 +321,13 @@ Not implemented **--log-driver**="*json-file*" -Logging driver for the container. Default is defined by daemon `--log-driver` flag. -**Warning**: the `podman logs` command works only for the `json-file` and -`journald` logging drivers. +Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility. **--log-opt**=[] -Logging driver specific options. +Logging driver specific options. Used to set the path to the container log file. For example: + +`--log-opt path=/var/log/container/mycontainer.json` **--mac-address**="" @@ -414,7 +414,7 @@ UUID short identifier (“f78375b1c487”) Name (“jonah”) podman generates a UUID for each container, and if a name is not assigned -to the container with **--name** then the daemon will also generate a random +to the container with **--name** then it will generate a random string name. The name is useful any place you need to identify a container. This works for both background and foreground containers. diff --git a/docs/podman-run.1.md b/docs/podman-run.1.md index b708e3407..912026a55 100644 --- a/docs/podman-run.1.md +++ b/docs/podman-run.1.md @@ -78,7 +78,7 @@ Write the container ID to the file **--conmon-pidfile**="" -Write the pid of the `conmon` process to a file. `conmon` daemonizes separate from Podman, so this is necessary when using systemd to restart Podman containers. +Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers. **--cpu-period**=*0* @@ -333,16 +333,13 @@ Not implemented **--log-driver**="*json-file*" -Logging driver for the container. Default is defined by daemon `--log-driver` flag. - -**Warning**: the `podman logs` command works only for the `json-file` and -`journald` logging drivers. +Logging driver for the container. Currently not supported. This flag is a NOOP provided soley for scripting compatibility. **--log-opt**=[] -Logging driver specific options. +Logging driver specific options. Used to set the path to the container log file. For example: -`path=/var/log/container/mycontainer.json`: Set the path to the container log file. +`--log-opt path=/var/log/container/mycontainer.json` **--mac-address**="" @@ -399,7 +396,7 @@ The operator can identify a container in three ways: - Name (“jonah”) podman generates a UUID for each container, and if a name is not assigned -to the container with **--name** then the daemon will also generate a random +to the container with **--name** then it will generate a random string name. The name is useful any place you need to identify a container. This works for both background and foreground containers. diff --git a/libpod/container_api.go b/libpod/container_api.go index 30c67eb2a..d99aec5b4 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -46,9 +46,6 @@ func (c *Container) Init(ctx context.Context) (err error) { return errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString) } - if err := c.prepare(); err != nil { - return err - } defer func() { if err != nil { if err2 := c.cleanup(ctx); err2 != nil { @@ -57,6 +54,10 @@ func (c *Container) Init(ctx context.Context) (err error) { } }() + if err := c.prepare(); err != nil { + return err + } + if c.state.State == ContainerStateStopped { // Reinitialize the container return c.reinit(ctx) @@ -99,9 +100,6 @@ func (c *Container) Start(ctx context.Context) (err error) { return errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString) } - if err := c.prepare(); err != nil { - return err - } defer func() { if err != nil { if err2 := c.cleanup(ctx); err2 != nil { @@ -110,6 +108,10 @@ func (c *Container) Start(ctx context.Context) (err error) { } }() + if err := c.prepare(); err != nil { + return err + } + if c.state.State == ContainerStateStopped { // Reinitialize the container if we need to if err := c.reinit(ctx); err != nil { @@ -164,9 +166,6 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams, return nil, errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString) } - if err := c.prepare(); err != nil { - return nil, err - } defer func() { if err != nil { if err2 := c.cleanup(ctx); err2 != nil { @@ -175,6 +174,10 @@ func (c *Container) StartAndAttach(ctx context.Context, streams *AttachStreams, } }() + if err := c.prepare(); err != nil { + return nil, err + } + if c.state.State == ContainerStateStopped { // Reinitialize the container if we need to if err := c.reinit(ctx); err != nil { @@ -685,7 +688,7 @@ func (c *Container) Sync() error { (c.state.State != ContainerStateConfigured) { oldState := c.state.State // TODO: optionally replace this with a stat for the exit file - if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil { + if err := c.runtime.ociRuntime.updateContainerStatus(c, true); err != nil { return err } // Only save back to DB if state changed diff --git a/libpod/container_internal.go b/libpod/container_internal.go index d928c4aed..d2f48d661 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "fmt" - "github.com/opencontainers/runc/libcontainer/user" "io" "io/ioutil" "os" @@ -13,8 +12,10 @@ import ( "strconv" "strings" "syscall" + "time" "github.com/containers/buildah/imagebuildah" + "github.com/containers/libpod/pkg/ctime" "github.com/containers/libpod/pkg/hooks" "github.com/containers/libpod/pkg/hooks/exec" "github.com/containers/libpod/pkg/lookup" @@ -25,12 +26,14 @@ import ( "github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/chrootarchive" "github.com/containers/storage/pkg/mount" + "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" "golang.org/x/text/language" + kwait "k8s.io/apimachinery/pkg/util/wait" ) const ( @@ -147,6 +150,77 @@ func (c *Container) execPidPath(sessionID string) string { return filepath.Join(c.state.RunDir, "exec_pid_"+sessionID) } +// exitFilePath gets the path to the container's exit file +func (c *Container) exitFilePath() string { + return filepath.Join(c.runtime.ociRuntime.exitsDir, c.ID()) +} + +// Wait for the container's exit file to appear. +// When it does, update our state based on it. +func (c *Container) waitForExitFileAndSync() error { + exitFile := c.exitFilePath() + + err := kwait.ExponentialBackoff( + kwait.Backoff{ + Duration: 500 * time.Millisecond, + Factor: 1.2, + Steps: 6, + }, + func() (bool, error) { + _, err := os.Stat(exitFile) + if err != nil { + // wait longer + return false, nil + } + return true, nil + }) + if err != nil { + // Exit file did not appear + // Reset our state + c.state.ExitCode = -1 + c.state.FinishedTime = time.Now() + c.state.State = ContainerStateStopped + + if err2 := c.save(); err2 != nil { + logrus.Errorf("Error saving container %s state: %v", c.ID(), err2) + } + + return err + } + + if err := c.runtime.ociRuntime.updateContainerStatus(c, false); err != nil { + return err + } + + return c.save() +} + +// Handle the container exit file. +// The exit file is used to supply container exit time and exit code. +// This assumes the exit file already exists. +func (c *Container) handleExitFile(exitFile string, fi os.FileInfo) error { + c.state.FinishedTime = ctime.Created(fi) + statusCodeStr, err := ioutil.ReadFile(exitFile) + if err != nil { + return errors.Wrapf(err, "failed to read exit file for container %s", c.ID()) + } + statusCode, err := strconv.Atoi(string(statusCodeStr)) + if err != nil { + return errors.Wrapf(err, "error converting exit status code (%q) for container %s to int", + c.ID(), statusCodeStr) + } + c.state.ExitCode = int32(statusCode) + + oomFilePath := filepath.Join(c.bundlePath(), "oom") + if _, err = os.Stat(oomFilePath); err == nil { + c.state.OOMKilled = true + } + + c.state.Exited = true + + return nil +} + // Sync this container with on-disk state and runtime status // Should only be called with container lock held // This function should suffice to ensure a container's state is accurate and @@ -162,7 +236,7 @@ func (c *Container) syncContainer() error { (c.state.State != ContainerStateExited) { oldState := c.state.State // TODO: optionally replace this with a stat for the exit file - if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil { + if err := c.runtime.ociRuntime.updateContainerStatus(c, false); err != nil { return err } // Only save back to DB if state changed @@ -623,9 +697,6 @@ func (c *Container) initAndStart(ctx context.Context) (err error) { return errors.Wrapf(ErrCtrStateInvalid, "cannot start paused container %s", c.ID()) } - if err := c.prepare(); err != nil { - return err - } defer func() { if err != nil { if err2 := c.cleanup(ctx); err2 != nil { @@ -634,6 +705,10 @@ func (c *Container) initAndStart(ctx context.Context) (err error) { } }() + if err := c.prepare(); err != nil { + return err + } + // If we are ContainerStateStopped we need to remove from runtime // And reset to ContainerStateConfigured if c.state.State == ContainerStateStopped { @@ -673,13 +748,8 @@ func (c *Container) stop(timeout uint) error { return err } - // Sync the container's state to pick up return code - if err := c.runtime.ociRuntime.updateContainerStatus(c); err != nil { - return err - } - - // Container should clean itself up - return nil + // Wait until we have an exit file, and sync once we do + return c.waitForExitFileAndSync() } // Internal, non-locking function to pause a container @@ -719,9 +789,6 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e return err } } - if err := c.prepare(); err != nil { - return err - } defer func() { if err != nil { if err2 := c.cleanup(ctx); err2 != nil { @@ -729,6 +796,9 @@ func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err e } } }() + if err := c.prepare(); err != nil { + return err + } if c.state.State == ContainerStateStopped { // Reinitialize the container if we need to @@ -1069,7 +1139,7 @@ func (c *Container) generatePasswd() (string, error) { } originPasswdFile := filepath.Join(c.state.Mountpoint, "/etc/passwd") orig, err := ioutil.ReadFile(originPasswdFile) - if err != nil { + if err != nil && !os.IsNotExist(err) { return "", errors.Wrapf(err, "unable to read passwd file %s", originPasswdFile) } @@ -1157,6 +1227,10 @@ func (c *Container) generateHosts() (string, error) { hosts += fmt.Sprintf("%s %s\n", fields[1], fields[0]) } } + if len(c.state.NetworkStatus) > 0 && len(c.state.NetworkStatus[0].IPs) > 0 { + ipAddress := strings.Split(c.state.NetworkStatus[0].IPs[0].Address.String(), "/")[0] + hosts += fmt.Sprintf("%s\t%s\n", ipAddress, c.Hostname()) + } return c.writeStringToRundir("hosts", hosts) } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 7bf2c71ca..163cd75e7 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -360,19 +360,31 @@ func (c *Container) setupSystemd(mounts []spec.Mount, g generate.Generator) erro g.AddMount(tmpfsMnt) } - cgroupPath, err := c.CGroupPath() - if err != nil { - return err - } - sourcePath := filepath.Join("/sys/fs/cgroup/systemd", cgroupPath) + // rootless containers have no write access to /sys/fs/cgroup, so don't + // add any mount into the container. + if !rootless.IsRootless() { + cgroupPath, err := c.CGroupPath() + if err != nil { + return err + } + sourcePath := filepath.Join("/sys/fs/cgroup/systemd", cgroupPath) - systemdMnt := spec.Mount{ - Destination: "/sys/fs/cgroup/systemd", - Type: "bind", - Source: sourcePath, - Options: []string{"bind", "private"}, + systemdMnt := spec.Mount{ + Destination: "/sys/fs/cgroup/systemd", + Type: "bind", + Source: sourcePath, + Options: []string{"bind", "private"}, + } + g.AddMount(systemdMnt) + } else { + systemdMnt := spec.Mount{ + Destination: "/sys/fs/cgroup/systemd", + Type: "bind", + Source: "/sys/fs/cgroup/systemd", + Options: []string{"bind", "nodev", "noexec", "nosuid"}, + } + g.AddMount(systemdMnt) } - g.AddMount(systemdMnt) return nil } @@ -484,9 +496,6 @@ func (c *Container) restore(ctx context.Context, keep bool) (err error) { } } - if err := c.prepare(); err != nil { - return err - } defer func() { if err != nil { if err2 := c.cleanup(ctx); err2 != nil { @@ -495,6 +504,10 @@ func (c *Container) restore(ctx context.Context, keep bool) (err error) { } }() + if err := c.prepare(); err != nil { + return err + } + // TODO: use existing way to request static IPs, once it is merged in ocicni // https://github.com/cri-o/ocicni/pull/23/ diff --git a/libpod/oci.go b/libpod/oci.go index ca8f967c4..233bacfbb 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -11,12 +11,10 @@ import ( "os/exec" "path/filepath" "runtime" - "strconv" "strings" "syscall" "time" - "github.com/containers/libpod/pkg/ctime" "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" "github.com/coreos/go-systemd/activation" @@ -443,6 +441,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res } return errors.Wrapf(ErrInternal, "container create failed") } + ctr.state.PID = ss.si.Pid case <-time.After(ContainerCreateTimeout): return errors.Wrapf(ErrInternal, "container creation timeout") } @@ -451,17 +450,47 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res // updateContainerStatus retrieves the current status of the container from the // runtime. It updates the container's state but does not save it. -func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { - state := new(spec.State) +// If useRunc is false, we will not directly hit runc to see the container's +// status, but will instead only check for the existence of the conmon exit file +// and update state to stopped if it exists. +func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRunc bool) error { + exitFile := ctr.exitFilePath() runtimeDir, err := util.GetRootlessRuntimeDir() if err != nil { return err } + // If not using runc, we don't need to do most of this. + if !useRunc { + // If the container's not running, nothing to do. + if ctr.state.State != ContainerStateRunning { + return nil + } + + // Check for the exit file conmon makes + info, err := os.Stat(exitFile) + if err != nil { + if os.IsNotExist(err) { + // Container is still running, no error + return nil + } + + return errors.Wrapf(err, "error running stat on container %s exit file", ctr.ID()) + } + + // Alright, it exists. Transition to Stopped state. + ctr.state.State = ContainerStateStopped + + // Read the exit file to get our stopped time and exit code. + return ctr.handleExitFile(exitFile, info) + } + // Store old state so we know if we were already stopped oldState := ctr.state.State + state := new(spec.State) + cmd := exec.Command(r.path, "state", ctr.ID()) cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) outPipe, err := cmd.StdoutPipe() @@ -480,6 +509,8 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { } if strings.Contains(string(out), "does not exist") { ctr.removeConmonFiles() + ctr.state.ExitCode = -1 + ctr.state.FinishedTime = time.Now() ctr.state.State = ContainerStateExited return nil } @@ -514,7 +545,6 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { // Only grab exit status if we were not already stopped // If we were, it should already be in the database if ctr.state.State == ContainerStateStopped && oldState != ContainerStateStopped { - exitFile := filepath.Join(r.exitsDir, ctr.ID()) var fi os.FileInfo err = kwait.ExponentialBackoff( kwait.Backoff{ @@ -538,24 +568,7 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container) error { return nil } - ctr.state.FinishedTime = ctime.Created(fi) - statusCodeStr, err := ioutil.ReadFile(exitFile) - if err != nil { - return errors.Wrapf(err, "failed to read exit file for container %s", ctr.ID()) - } - statusCode, err := strconv.Atoi(string(statusCodeStr)) - if err != nil { - return errors.Wrapf(err, "error converting exit status code for container %s to int", - ctr.ID()) - } - ctr.state.ExitCode = int32(statusCode) - - oomFilePath := filepath.Join(ctr.bundlePath(), "oom") - if _, err = os.Stat(oomFilePath); err == nil { - ctr.state.OOMKilled = true - } - - ctr.state.Exited = true + return ctr.handleExitFile(exitFile, fi) } return nil @@ -601,6 +614,8 @@ func (r *OCIRuntime) killContainer(ctr *Container, signal uint) error { // Does not set finished time for container, assumes you will run updateStatus // after to pull the exit code func (r *OCIRuntime) stopContainer(ctr *Container, timeout uint) error { + logrus.Debugf("Stopping container %s (PID %d)", ctr.ID(), ctr.state.PID) + // Ping the container to see if it's alive // If it's not, it's already stopped, return err := unix.Kill(ctr.state.PID, 0) diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index b63726f29..09dc7c48b 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -256,7 +256,7 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force bool) } // Need to update container state to make sure we know it's stopped - if err := c.syncContainer(); err != nil { + if err := c.waitForExitFileAndSync(); err != nil { return err } } else if !(c.state.State == ContainerStateConfigured || diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go index eb3d471dd..3d6fad52f 100644 --- a/libpod/runtime_pod_linux.go +++ b/libpod/runtime_pod_linux.go @@ -265,15 +265,26 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool) } case CgroupfsCgroupsManager: // Delete the cgroupfs cgroup + // Make sure the conmon cgroup is deleted first + // Since the pod is almost gone, don't bother failing + // hard - instead, just log errors. v1CGroups := GetV1CGroups(getExcludedCGroups()) + conmonCgroupPath := filepath.Join(p.state.CgroupPath, "conmon") + conmonCgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(conmonCgroupPath)) + if err != nil && err != cgroups.ErrCgroupDeleted { + return err + } + if err == nil { + if err := conmonCgroup.Delete(); err != nil { + logrus.Errorf("Error deleting pod %s conmon cgroup %s: %v", p.ID(), conmonCgroupPath, err) + } + } cgroup, err := cgroups.Load(v1CGroups, cgroups.StaticPath(p.state.CgroupPath)) if err != nil && err != cgroups.ErrCgroupDeleted { return err - } else if err == nil { + } + if err == nil { if err := cgroup.Delete(); err != nil { - // The pod is already almost gone. - // No point in hard-failing if we fail - // this bit of cleanup. logrus.Errorf("Error deleting pod %s cgroup %s: %v", p.ID(), p.state.CgroupPath, err) } } diff --git a/pkg/lookup/lookup.go b/pkg/lookup/lookup.go index b27e2a724..a9d975b4b 100644 --- a/pkg/lookup/lookup.go +++ b/pkg/lookup/lookup.go @@ -1,10 +1,12 @@ package lookup import ( + "os" + "strconv" + "github.com/cyphar/filepath-securejoin" "github.com/opencontainers/runc/libcontainer/user" "github.com/sirupsen/logrus" - "strconv" ) const ( @@ -116,7 +118,7 @@ func GetUser(containerMount, userIDorName string) (*user.User, error) { } return u.Uid == uid }) - if err != nil { + if err != nil && !os.IsNotExist(err) { return nil, err } if len(users) > 0 { @@ -146,7 +148,7 @@ func GetGroup(containerMount, groupIDorName string) (*user.Group, error) { } return g.Gid == gid }) - if err != nil { + if err != nil && !os.IsNotExist(err) { return nil, err } if len(groups) > 0 { diff --git a/test/e2e/pause_test.go b/test/e2e/pause_test.go index 24876b6d6..1a2eb1a09 100644 --- a/test/e2e/pause_test.go +++ b/test/e2e/pause_test.go @@ -250,6 +250,10 @@ var _ = Describe("Podman pause", func() { running.WaitWithDefaultTimeout() Expect(running.ExitCode()).To(Equal(0)) Expect(len(running.OutputToStringArray())).To(Equal(0)) + + unpause := podmanTest.Podman([]string{"unpause", "--all"}) + unpause.WaitWithDefaultTimeout() + Expect(unpause.ExitCode()).To(Equal(0)) }) It("Unpause a bunch of running containers", func() { diff --git a/test/e2e/run_dns_test.go b/test/e2e/run_dns_test.go index c5a02c776..a617035a1 100644 --- a/test/e2e/run_dns_test.go +++ b/test/e2e/run_dns_test.go @@ -1,9 +1,9 @@ package integration import ( + "fmt" "os" - "fmt" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -83,4 +83,11 @@ var _ = Describe("Podman run dns", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("foobar")) }) + + It("podman run add hostname sets /etc/hosts", func() { + session := podmanTest.Podman([]string{"run", "-t", "-i", "--hostname=foobar", ALPINE, "cat", "/etc/hosts"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(session.LineInOutputContains("foobar")).To(BeTrue()) + }) }) |