diff options
-rw-r--r-- | cmd/podman/create.go | 11 | ||||
-rw-r--r-- | cmd/podman/exec.go | 29 | ||||
-rw-r--r-- | cmd/podman/images.go | 27 | ||||
-rw-r--r-- | cmd/podman/ps.go | 4 | ||||
-rw-r--r-- | libpod/runtime.go | 9 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 2 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.go | 1 | ||||
-rw-r--r-- | test/README.md | 4 | ||||
-rw-r--r-- | test/e2e/images_test.go | 12 | ||||
-rw-r--r-- | test/e2e/load_test.go | 6 | ||||
-rw-r--r-- | test/system/TODO.md | 105 |
11 files changed, 179 insertions, 31 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go index fa8b9f57b..8a5d0cf73 100644 --- a/cmd/podman/create.go +++ b/cmd/podman/create.go @@ -893,7 +893,16 @@ 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), 0) + + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return false, -1, errors.Wrapf(err, "cannot parse PID %q", data) + } + return rootless.JoinDirectUserAndMountNS(uint(conmonPid)) } } return rootless.BecomeRootInUserNS() diff --git a/cmd/podman/exec.go b/cmd/podman/exec.go index c3bcec2ec..e4cea1f5e 100644 --- a/cmd/podman/exec.go +++ b/cmd/podman/exec.go @@ -106,16 +106,25 @@ func execCmd(c *cliconfig.ExecValues) error { } - pid, err := ctr.PID() - if err != nil { - return err - } - became, ret, err := rootless.JoinNS(uint(pid), c.PreserveFDs) - if err != nil { - return err - } - if became { - os.Exit(ret) + if os.Geteuid() != 0 { + var became bool + var ret int + + data, err := ioutil.ReadFile(ctr.Config().ConmonPidFile) + if err != nil { + return errors.Wrapf(err, "cannot read conmon PID file %q", ctr.Config().ConmonPidFile) + } + conmonPid, err := strconv.Atoi(string(data)) + if err != nil { + return errors.Wrapf(err, "cannot parse PID %q", data) + } + became, ret, err = rootless.JoinDirectUserAndMountNS(uint(conmonPid)) + if err != nil { + return err + } + if became { + os.Exit(ret) + } } // ENVIRONMENT VARIABLES diff --git a/cmd/podman/images.go b/cmd/podman/images.go index 78dc87ad5..f92e5d44d 100644 --- a/cmd/podman/images.go +++ b/cmd/podman/images.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "reflect" "sort" "strings" @@ -128,7 +129,7 @@ func init() { func imagesCmd(c *cliconfig.ImagesValues) error { var ( filterFuncs []imagefilters.ResultFilter - newImage *adapter.ContainerImage + image string ) runtime, err := adapter.GetRuntime(&c.PodmanCommand) @@ -137,23 +138,23 @@ func imagesCmd(c *cliconfig.ImagesValues) error { } defer runtime.Shutdown(false) if len(c.InputArgs) == 1 { - newImage, err = runtime.NewImageFromLocal(c.InputArgs[0]) - if err != nil { - return err - } + image = c.InputArgs[0] } - if len(c.InputArgs) > 1 { return errors.New("'podman images' requires at most 1 argument") } - + if len(c.Filter) > 0 && image != "" { + return errors.New("can not specify an image and a filter") + } ctx := getContext() - if len(c.Filter) > 0 || newImage != nil { - filterFuncs, err = CreateFilterFuncs(ctx, runtime, c.Filter, newImage) - if err != nil { - return err - } + if len(c.Filter) > 0 { + filterFuncs, err = CreateFilterFuncs(ctx, runtime, c.Filter, nil) + } else { + filterFuncs, err = CreateFilterFuncs(ctx, runtime, []string{fmt.Sprintf("reference=%s", image)}, nil) + } + if err != nil { + return err } opts := imagesOptions{ @@ -174,7 +175,7 @@ func imagesCmd(c *cliconfig.ImagesValues) error { var filteredImages []*adapter.ContainerImage //filter the images - if len(c.Filter) > 0 || newImage != nil { + if len(c.Filter) > 0 || len(c.InputArgs) == 1 { filteredImages = imagefilters.FilterImages(images, filterFuncs) } else { filteredImages = images diff --git a/cmd/podman/ps.go b/cmd/podman/ps.go index 9793d67f8..6caac2406 100644 --- a/cmd/podman/ps.go +++ b/cmd/podman/ps.go @@ -419,7 +419,7 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru return false }, nil case "status": - if !util.StringInSlice(filterValue, []string{"created", "restarting", "running", "paused", "exited", "unknown"}) { + if !util.StringInSlice(filterValue, []string{"created", "running", "paused", "exited", "unknown"}) { return nil, errors.Errorf("%s is not a valid status", filterValue) } return func(c *libpod.Container) bool { @@ -430,6 +430,8 @@ func generateContainerFilterFuncs(filter, filterValue string, runtime *libpod.Ru state := status.String() if status == libpod.ContainerStateConfigured { state = "created" + } else if status == libpod.ContainerStateStopped { + state = "exited" } return state == filterValue }, nil diff --git a/libpod/runtime.go b/libpod/runtime.go index 482cd9d73..9667abfe6 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -971,9 +971,12 @@ func (r *Runtime) refreshRootless() error { // Take advantage of a command that requires a new userns // so that we are running as the root user and able to use refresh() cmd := exec.Command(os.Args[0], "info") - err := cmd.Run() - if err != nil { - return errors.Wrapf(err, "Error running %s info while refreshing state", os.Args[0]) + + if output, err := cmd.CombinedOutput(); err != nil { + if _, ok := err.(*exec.ExitError); !ok { + return errors.Wrapf(err, "Error waiting for info while refreshing state: %s", os.Args[0]) + } + return errors.Wrapf(err, "Error running %s info while refreshing state: %s", os.Args[0], output) } return nil } diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 41acd3475..ff39e9e77 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -277,6 +277,8 @@ reexec_in_user_namespace (int ready) _exit (EXIT_FAILURE); } close (ready); + if (b != '1') + _exit (EXIT_FAILURE); if (syscall_setresgid (0, 0, 0) < 0) { diff --git a/pkg/rootless/rootless_linux.go b/pkg/rootless/rootless_linux.go index 55fba900e..933cfa2c2 100644 --- a/pkg/rootless/rootless_linux.go +++ b/pkg/rootless/rootless_linux.go @@ -229,6 +229,7 @@ func BecomeRootInUserNS() (bool, int, error) { } defer r.Close() defer w.Close() + defer w.Write([]byte("0")) pidC := C.reexec_in_user_namespace(C.int(r.Fd())) pid := int(pidC) diff --git a/test/README.md b/test/README.md index ef3bfbcf9..5e5a7da61 100644 --- a/test/README.md +++ b/test/README.md @@ -105,3 +105,7 @@ You can run the test with following command: ``` make localsystem ``` + +## Contributing to system tests + +Please see [the TODO list of needed workflows/tests](system/TODO.md). diff --git a/test/e2e/images_test.go b/test/e2e/images_test.go index 4cf58e5bf..4018bf355 100644 --- a/test/e2e/images_test.go +++ b/test/e2e/images_test.go @@ -112,6 +112,18 @@ var _ = Describe("Podman images", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(0)) Expect(len(session.OutputToStringArray())).To(Equal(1)) + + session = podmanTest.Podman([]string{"tag", ALPINE, "foo:a"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + session = podmanTest.Podman([]string{"tag", BB, "foo:b"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"images", "-q", "foo"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + Expect(len(session.OutputToStringArray())).To(Equal(2)) }) It("podman images filter reference", func() { diff --git a/test/e2e/load_test.go b/test/e2e/load_test.go index 571754347..75c8e4850 100644 --- a/test/e2e/load_test.go +++ b/test/e2e/load_test.go @@ -190,7 +190,7 @@ var _ = Describe("Podman load", func() { load.WaitWithDefaultTimeout() Expect(load.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"images", "-f", "label", "hello:world"}) + result := podmanTest.Podman([]string{"images", "hello:world"}) result.WaitWithDefaultTimeout() Expect(result.LineInOutputContains("docker")).To(Not(BeTrue())) Expect(result.LineInOutputContains("localhost")).To(BeTrue()) @@ -216,7 +216,7 @@ var _ = Describe("Podman load", func() { load.WaitWithDefaultTimeout() Expect(load.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"images", "-f", "label", "hello:latest"}) + result := podmanTest.Podman([]string{"images", "hello:latest"}) result.WaitWithDefaultTimeout() Expect(result.LineInOutputContains("docker")).To(Not(BeTrue())) Expect(result.LineInOutputContains("localhost")).To(BeTrue()) @@ -241,7 +241,7 @@ var _ = Describe("Podman load", func() { load.WaitWithDefaultTimeout() Expect(load.ExitCode()).To(Equal(0)) - result := podmanTest.Podman([]string{"images", "-f", "label", "load:latest"}) + result := podmanTest.Podman([]string{"images", "load:latest"}) result.WaitWithDefaultTimeout() Expect(result.LineInOutputContains("docker")).To(Not(BeTrue())) Expect(result.LineInOutputContains("localhost")).To(BeTrue()) diff --git a/test/system/TODO.md b/test/system/TODO.md new file mode 100644 index 000000000..f6110d2e9 --- /dev/null +++ b/test/system/TODO.md @@ -0,0 +1,105 @@ +![PODMAN logo](../../logo/podman-logo-source.svg) + +# Overview + +System tests exercise Podman in the context of a complete, composed environment from +distribution packages. It should match as closely as possible to how an end-user +would experience a fresh-install. Dependencies on external configuration and resources +must be kept minimal, and the tests must be generic and vendor-neutral. + +The system-tests must execute cleanly on all tested platforms. They may optionally +be executed during continuous-integration testing of code-changes, after all other +testing completes successfully. For a list of tested platforms, please see [the +CI configuration file.](../../.cirrus.yml) + + +# Execution + +When working from a clone of [the libpod repository](https://github.com/containers/libpod), +the main entry-point for humans and automation is `make localsystem`. When operating +from a packaged version of the system-tests, the entry-point may vary as appropriate. +Running the packaged system-tests assumes the version of Podman matches the test +version, and all standard dependencies are installed. + + +# Test Design and overview + +System-tests should be high-level and user work-flow oriented. For example, consider +how multiple Podman invocations would be used together by an end-user. The set of +related commands should be considered a single test. If one or more intermediate +commands fail, the test could still pass if the end-result is still achieved. + + +# *TODO*: List of needed System-tests + +***Note***: Common operations (like `rm` and `rmi` for cleanup/reset) +have been omitted as they are verified by repeated implied use. + +- [ ] pull, build, run, attach, commit, diff, inspect + + - Pull existing image from registry + - Build new image FROM explicitly pulled image + - Run built container in detached mode + - Attach to running container, execute command to modify storage. + - Commit running container to new image w/ changed ENV VAR + - Verify attach + commit using diff + - verify changed ENV VAR with inspect + +- [ ] Implied pull, create, start, exec, log, stop, wait, rm + + - Create non-existing local image + - start stopped container + - exec simple command in running container + - verify exec result with log + - wait on running container + - stop running container with 2 second timeout + - verify wait in 4 seconds or less + - verify stopped by rm **without** --force + +- [ ] Implied pull, build, export, modify, import, tag, run, kill + + - Build from Dockerfile FROM non-existing local image + - Export built container as tarball + - Modify tarball contents + - Import tarball + - Tag imported image + - Run imported image to confirm tarball modification, block on non-special signal + - Kill can send non-TERM/KILL signal to container to exit + - Confirm exit within timeout + +- [ ] Container runlabel, exists, checkpoint, exists, restore, stop, prune + + - Using pre-existing remote image, start it with 'podman container runlabel --pull' + - Run a named container that exits immediatly + - Confirm 'container exists' zero exit (both containers) + - Checkpoint the running container + - Confirm 'container exists' non-zero exit (runlabel container) + - Confirm 'container exists' zero exit (named container) + - Run 'container restore' + - Confirm 'container exists' zero exit (both containers) + - Stop container + - Run 'container prune' + - Confirm `podman ps -a` lists no containers + + +# TODO: List of commands to be combined into additional workflows above. + +- podman-remote (workflow TBD) +- history +- image +- load +- mount +- pause +- pod +- port +- login, push, & logout (difficult, save for last) +- restart +- save +- search +- stats +- top +- umount, unmount +- unpause +- volume +- `--namespace` +- `--storage-driver` |