diff options
-rw-r--r-- | cmd/podman/common/specgen.go | 11 | ||||
-rw-r--r-- | cmd/podman/containers/cp.go | 4 | ||||
-rw-r--r-- | libpod/container_log_linux.go | 78 | ||||
-rw-r--r-- | pkg/api/server/register_containers.go | 22 | ||||
-rw-r--r-- | pkg/domain/infra/abi/archive.go | 7 | ||||
-rw-r--r-- | pkg/rootless/rootless_linux.c | 6 | ||||
-rw-r--r-- | test/e2e/logs_test.go | 622 | ||||
-rw-r--r-- | test/e2e/run_memory_test.go | 24 | ||||
-rw-r--r-- | test/system/065-cp.bats | 26 | ||||
-rw-r--r-- | test/utils/utils.go | 10 |
10 files changed, 470 insertions, 340 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go index 975c76fd9..eff8b43aa 100644 --- a/cmd/podman/common/specgen.go +++ b/cmd/podman/common/specgen.go @@ -148,17 +148,16 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.Linu } if m := c.MemorySwap; len(m) > 0 { var ms int64 - if m == "-1" { - ms = int64(-1) - s.ResourceLimits.Memory.Swap = &ms - } else { + // only set memory swap if it was set + // -1 indicates unlimited + if m != "-1" { ms, err = units.RAMInBytes(m) + memory.Swap = &ms if err != nil { return nil, errors.Wrapf(err, "invalid value for memory") } + hasLimits = true } - memory.Swap = &ms - hasLimits = true } if m := c.KernelMemory; len(m) > 0 { mk, err := units.RAMInBytes(m) diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go index 69b61a06c..5db05719c 100644 --- a/cmd/podman/containers/cp.go +++ b/cmd/podman/containers/cp.go @@ -311,8 +311,8 @@ func copyToContainer(container string, containerPath string, hostPath string) er } getOptions := buildahCopiah.GetOptions{ - // Unless the specified path ends with ".", we want to copy the base directory. - KeepDirectoryNames: !strings.HasSuffix(hostPath, "."), + // Unless the specified points to ".", we want to copy the base directory. + KeepDirectoryNames: hostInfo.IsDir && filepath.Base(hostPath) != ".", } if !hostInfo.IsDir && (!containerInfo.IsDir || containerInfoErr != nil) { // If we're having a file-to-file copy, make sure to diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go index d895171cf..b1f601a4c 100644 --- a/libpod/container_log_linux.go +++ b/libpod/container_log_linux.go @@ -8,11 +8,12 @@ import ( "fmt" "io" "math" - "strings" "time" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/libpod/logs" journal "github.com/coreos/go-systemd/v22/sdjournal" + "github.com/hpcloud/tail/watch" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -34,10 +35,16 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption var config journal.JournalReaderConfig if options.Tail < 0 { config.NumFromTail = 0 + } else if options.Tail == 0 { + config.NumFromTail = math.MaxUint64 } else { config.NumFromTail = uint64(options.Tail) } - config.Formatter = journalFormatter + if options.Multi { + config.Formatter = journalFormatterWithID + } else { + config.Formatter = journalFormatter + } defaultTime := time.Time{} if options.Since != defaultTime { // coreos/go-systemd/sdjournal doesn't correctly handle requests for data in the future @@ -45,7 +52,7 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption if time.Now().Before(options.Since) { return nil } - config.Since = time.Since(options.Since) + config.Since = -time.Since(options.Since) } config.Matches = append(config.Matches, journal.Match{ Field: "CONTAINER_ID_FULL", @@ -63,8 +70,12 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption if options.Tail == math.MaxInt64 { r.Rewind() } + state, err := c.State() + if err != nil { + return err + } - if options.Follow { + if options.Follow && state == define.ContainerStateRunning { go func() { done := make(chan bool) until := make(chan time.Time) @@ -76,6 +87,21 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption // nothing to do anymore } }() + go func() { + for { + state, err := c.State() + if err != nil { + until <- time.Time{} + logrus.Error(err) + break + } + time.Sleep(watch.POLL_DURATION) + if state != define.ContainerStateRunning && state != define.ContainerStatePaused { + until <- time.Time{} + break + } + } + }() follower := FollowBuffer{logChannel} err := r.Follow(until, follower) if err != nil { @@ -114,7 +140,44 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption return nil } +func journalFormatterWithID(entry *journal.JournalEntry) (string, error) { + output, err := formatterPrefix(entry) + if err != nil { + return "", err + } + + id, ok := entry.Fields["CONTAINER_ID_FULL"] + if !ok { + return "", fmt.Errorf("no CONTAINER_ID_FULL field present in journal entry") + } + if len(id) > 12 { + id = id[:12] + } + output += fmt.Sprintf("%s ", id) + // Append message + msg, err := formatterMessage(entry) + if err != nil { + return "", err + } + output += msg + return output, nil +} + func journalFormatter(entry *journal.JournalEntry) (string, error) { + output, err := formatterPrefix(entry) + if err != nil { + return "", err + } + // Append message + msg, err := formatterMessage(entry) + if err != nil { + return "", err + } + output += msg + return output, nil +} + +func formatterPrefix(entry *journal.JournalEntry) (string, error) { usec := entry.RealtimeTimestamp tsString := time.Unix(0, int64(usec)*int64(time.Microsecond)).Format(logs.LogTimeFormat) output := fmt.Sprintf("%s ", tsString) @@ -137,13 +200,16 @@ func journalFormatter(entry *journal.JournalEntry) (string, error) { output += fmt.Sprintf("%s ", logs.FullLogType) } + return output, nil +} + +func formatterMessage(entry *journal.JournalEntry) (string, error) { // Finally, append the message msg, ok := entry.Fields["MESSAGE"] if !ok { return "", fmt.Errorf("no MESSAGE field present in journal entry") } - output += strings.TrimSpace(msg) - return output, nil + return msg, nil } type FollowBuffer struct { diff --git a/pkg/api/server/register_containers.go b/pkg/api/server/register_containers.go index ff1781d1e..2b8330d4c 100644 --- a/pkg/api/server/register_containers.go +++ b/pkg/api/server/register_containers.go @@ -1176,15 +1176,19 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error { // description: the name or ID of the container // - in: query // name: condition - // type: string - // description: | - // wait until container is to a given condition. default is stopped. valid conditions are: - // - configured - // - created - // - exited - // - paused - // - running - // - stopped + // type: array + // items: + // type: string + // enum: + // - configured + // - created + // - running + // - stopped + // - paused + // - exited + // - removing + // - stopping + // description: "Conditions to wait for. If no condition provided the 'exited' condition is assumed." // produces: // - application/json // responses: diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go index c64dfb02a..f38f5c132 100644 --- a/pkg/domain/infra/abi/archive.go +++ b/pkg/domain/infra/abi/archive.go @@ -3,6 +3,7 @@ package abi import ( "context" "io" + "path/filepath" "strings" buildahCopiah "github.com/containers/buildah/copier" @@ -93,7 +94,7 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID containerPath = "/." } - _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, containerPath) + statInfo, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, containerPath) if err != nil { unmount() return nil, err @@ -110,8 +111,8 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID return func() error { defer container.Unmount(false) getOptions := buildahCopiah.GetOptions{ - // Unless the specified path ends with ".", we want to copy the base directory. - KeepDirectoryNames: !strings.HasSuffix(resolvedContainerPath, "."), + // Unless the specified points to ".", we want to copy the base directory. + KeepDirectoryNames: statInfo.IsDir && filepath.Base(containerPath) != ".", UIDMap: idMappings.UIDMap, GIDMap: idMappings.GIDMap, ChownDirs: idPair, diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c index 2e1fddc48..d588d848b 100644 --- a/pkg/rootless/rootless_linux.c +++ b/pkg/rootless/rootless_linux.c @@ -196,7 +196,11 @@ can_use_shortcut () return false; if (strstr (argv[0], "podman") == NULL) - return false; + { + free (argv[0]); + free (argv); + return false; + } for (argc = 0; argv[argc]; argc++) { diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go index b370aeec1..1b9d26c49 100644 --- a/test/e2e/logs_test.go +++ b/test/e2e/logs_test.go @@ -36,345 +36,341 @@ var _ = Describe("Podman logs", func() { }) - It("all lines", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - - cid := logc.OutputToString() - results := podmanTest.Podman([]string{"logs", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - }) - - It("tail two lines", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--tail", "2", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(2)) - }) - - It("tail zero lines", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--tail", "0", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(0)) - }) - - It("tail 800 lines", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "i=1; while [ \"$i\" -ne 1000 ]; do echo \"line $i\"; i=$((i + 1)); done"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--tail", "800", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(800)) - }) - - It("tail 2 lines with timestamps", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(2)) - }) - - It("since time 2017-08-07", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - }) - - It("since duration 10m", func() { - logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--since", "10m", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - }) - - It("latest and container name should fail", func() { - results := podmanTest.Podman([]string{"logs", "-l", "foobar"}) - results.WaitWithDefaultTimeout() - Expect(results).To(ExitWithError()) - }) - - It("two containers showing short container IDs", func() { - SkipIfRemote("FIXME: podman-remote logs does not support showing two containers at the same time") - log1 := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - log1.WaitWithDefaultTimeout() - Expect(log1.ExitCode()).To(Equal(0)) - cid1 := log1.OutputToString() - - log2 := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - log2.WaitWithDefaultTimeout() - Expect(log2.ExitCode()).To(Equal(0)) - cid2 := log2.OutputToString() - - results := podmanTest.Podman([]string{"logs", cid1, cid2}) - results.WaitWithDefaultTimeout() - Expect(results).Should(Exit(0)) - - output := results.OutputToStringArray() - Expect(len(output)).To(Equal(6)) - Expect(strings.Contains(output[0], cid1[:12]) || strings.Contains(output[0], cid2[:12])).To(BeTrue()) - }) - - It("podman logs on a created container should result in 0 exit code", func() { - session := podmanTest.Podman([]string{"create", "-t", "--name", "log", ALPINE}) - session.WaitWithDefaultTimeout() - Expect(session).To(Exit(0)) - - results := podmanTest.Podman([]string{"logs", "log"}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - }) + for _, log := range []string{"k8s-file", "journald", "json-file"} { + It("all lines: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + cid := logc.OutputToString() + results := podmanTest.Podman([]string{"logs", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("tail two lines: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--tail", "2", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(2)) + }) + + It("tail zero lines: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--tail", "0", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(0)) + }) + + It("tail 800 lines: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "i=1; while [ \"$i\" -ne 1000 ]; do echo \"line $i\"; i=$((i + 1)); done"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--tail", "800", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(800)) + }) + + It("tail 2 lines with timestamps: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(2)) + }) + + It("since time 2017-08-07: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("since duration 10m: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--since", "10m", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("latest and container name should fail: "+log, func() { + results := podmanTest.Podman([]string{"logs", "-l", "foobar"}) + results.WaitWithDefaultTimeout() + Expect(results).To(ExitWithError()) + }) + + It("two containers showing short container IDs: "+log, func() { + SkipIfRemote("FIXME: podman-remote logs does not support showing two containers at the same time") + log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + log1.WaitWithDefaultTimeout() + Expect(log1.ExitCode()).To(Equal(0)) + cid1 := log1.OutputToString() + + log2 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + log2.WaitWithDefaultTimeout() + Expect(log2.ExitCode()).To(Equal(0)) + cid2 := log2.OutputToString() + + results := podmanTest.Podman([]string{"logs", cid1, cid2}) + results.WaitWithDefaultTimeout() + Expect(results).Should(Exit(0)) + + output := results.OutputToStringArray() + Expect(len(output)).To(Equal(6)) + Expect(strings.Contains(output[0], cid1[:12]) || strings.Contains(output[0], cid2[:12])).To(BeTrue()) + }) + + It("podman logs on a created container should result in 0 exit code: "+log, func() { + session := podmanTest.Podman([]string{"create", "-t", "--name", "log", ALPINE}) + session.WaitWithDefaultTimeout() + Expect(session).To(Exit(0)) + + results := podmanTest.Podman([]string{"logs", "log"}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + }) + + It("for container: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + Expect(results.OutputToString()).To(Equal("podman podman podman")) + }) + + It("tail two lines: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + results := podmanTest.Podman([]string{"logs", "--tail", "2", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(2)) + }) + + It("tail 99 lines: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--tail", "99", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("tail 2 lines with timestamps: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(2)) + }) + + It("since time 2017-08-07: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("with duration 10m: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + results := podmanTest.Podman([]string{"logs", "--since", "10m", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(len(results.OutputToStringArray())).To(Equal(3)) + }) + + It("streaming output: "+log, func() { + containerName := "logs-f-rm" + + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--rm", "--name", containerName, "-dt", ALPINE, "sh", "-c", "echo podman; sleep 1; echo podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + results := podmanTest.Podman([]string{"logs", "-f", containerName}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + + // TODO: we should actually check for two podman lines, + // but as of 2020-06-17 there's a race condition in which + // 'logs -f' may not catch all output from a container + Expect(results.OutputToString()).To(ContainSubstring("podman")) + + // Container should now be terminatING or terminatED, but we + // have no guarantee of which: 'logs -f' does not necessarily + // wait for cleanup. Run 'inspect' and accept either state. + inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Status}}", containerName}) + inspect.WaitWithDefaultTimeout() + if inspect.ExitCode() == 0 { + Expect(inspect.OutputToString()).To(Equal("exited")) + // TODO: add 2-second wait loop to confirm cleanup + } else { + Expect(inspect.ErrorToString()).To(ContainSubstring("no such container")) + } + }) + + It("podman logs with log-driver=none errors: "+log, func() { + ctrName := "logsctr" + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", ctrName, "-d", "--log-driver", "none", ALPINE, "top"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + logs := podmanTest.Podman([]string{"logs", "-f", ctrName}) + logs.WaitWithDefaultTimeout() + Expect(logs).To(Not(Exit(0))) + }) + + It("follow output stopped container: "+log, func() { + containerName := "logs-f" + + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-d", ALPINE, "true"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + results := podmanTest.Podman([]string{"logs", "-f", containerName}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + }) + + It("using container with container log-size: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--log-opt=max-size=10k", "-d", ALPINE, "sh", "-c", "echo podman podman podman"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + cid := logc.OutputToString() + + wait := podmanTest.Podman([]string{"wait", cid}) + wait.WaitWithDefaultTimeout() + Expect(wait).To(Exit(0)) + + inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid}) + inspect.WaitWithDefaultTimeout() + Expect(inspect).To(Exit(0)) + Expect(inspect.OutputToString()).To(Equal("10kB")) + + results := podmanTest.Podman([]string{"logs", cid}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(results.OutputToString()).To(Equal("podman podman podman")) + }) + + It("Make sure logs match expected length: "+log, func() { + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-t", "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + wait := podmanTest.Podman([]string{"wait", "test"}) + wait.WaitWithDefaultTimeout() + Expect(wait).To(Exit(0)) + + results := podmanTest.Podman([]string{"logs", "test"}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + outlines := results.OutputToStringArray() + Expect(len(outlines)).To(Equal(2)) + Expect(outlines[0]).To(Equal("1\r")) + Expect(outlines[1]).To(Equal("2\r")) + }) + + It("podman logs test stdout and stderr: "+log, func() { + cname := "log-test" + logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"}) + logc.WaitWithDefaultTimeout() + Expect(logc).To(Exit(0)) + + wait := podmanTest.Podman([]string{"wait", cname}) + wait.WaitWithDefaultTimeout() + Expect(wait).To(Exit(0)) + + results := podmanTest.Podman([]string{"logs", cname}) + results.WaitWithDefaultTimeout() + Expect(results).To(Exit(0)) + Expect(results.OutputToString()).To(Equal("stdout")) + Expect(results.ErrorToString()).To(Equal("stderr")) + }) + } It("using journald for container with container tag", func() { - Skip("need to verify images have correct packages for journald") + SkipIfInContainer("journalctl inside a container doesn't work correctly") logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "--log-opt=tag={{.ImageName}}", "-d", ALPINE, "sh", "-c", "echo podman; sleep 0.1; echo podman; sleep 0.1; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) cid := logc.OutputToString() - wait := podmanTest.Podman([]string{"wait", "-l"}) + wait := podmanTest.Podman([]string{"wait", cid}) wait.WaitWithDefaultTimeout() Expect(wait).To(Exit(0)) - cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_TAG", "-u", fmt.Sprintf("libpod-conmon-%s.scope", cid)) + cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_TAG", fmt.Sprintf("CONTAINER_ID_FULL=%s", cid)) out, err := cmd.CombinedOutput() Expect(err).To(BeNil()) Expect(string(out)).To(ContainSubstring("alpine")) }) - It("using journald for container name", func() { - Skip("need to verify images have correct packages for journald") + It("using journald container name", func() { + SkipIfInContainer("journalctl inside a container doesn't work correctly") containerName := "inside-journal" logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-d", "--name", containerName, ALPINE, "sh", "-c", "echo podman; sleep 0.1; echo podman; sleep 0.1; echo podman"}) logc.WaitWithDefaultTimeout() Expect(logc).To(Exit(0)) cid := logc.OutputToString() - wait := podmanTest.Podman([]string{"wait", "-l"}) + wait := podmanTest.Podman([]string{"wait", cid}) wait.WaitWithDefaultTimeout() Expect(wait).To(Exit(0)) - cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_NAME", "-u", fmt.Sprintf("libpod-conmon-%s.scope", cid)) + cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_NAME", fmt.Sprintf("CONTAINER_ID_FULL=%s", cid)) out, err := cmd.CombinedOutput() Expect(err).To(BeNil()) Expect(string(out)).To(ContainSubstring(containerName)) }) - - It("using journald for container", func() { - Skip("need to verify images have correct packages for journald") - logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - Expect(results.OutputToString()).To(Equal("podman podman podman")) - }) - - It("using journald tail two lines", func() { - Skip("need to verify images have correct packages for journald") - logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - results := podmanTest.Podman([]string{"logs", "--tail", "2", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(2)) - }) - - It("using journald tail 99 lines", func() { - Skip("need to verify images have correct packages for journald") - logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--tail", "99", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - }) - - It("using journald tail 2 lines with timestamps", func() { - Skip("need to verify images have correct packages for journald") - logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(2)) - }) - - It("using journald since time 2017-08-07", func() { - Skip("need to verify images have correct packages for journald") - logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - }) - - It("using journald with duration 10m", func() { - Skip("need to verify images have correct packages for journald") - logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - results := podmanTest.Podman([]string{"logs", "--since", "10m", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(len(results.OutputToStringArray())).To(Equal(3)) - }) - - It("streaming output", func() { - containerName := "logs-f-rm" - - logc := podmanTest.Podman([]string{"run", "--rm", "--name", containerName, "-dt", ALPINE, "sh", "-c", "echo podman; sleep 1; echo podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - - results := podmanTest.Podman([]string{"logs", "-f", containerName}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - - // TODO: we should actually check for two podman lines, - // but as of 2020-06-17 there's a race condition in which - // 'logs -f' may not catch all output from a container - Expect(results.OutputToString()).To(ContainSubstring("podman")) - - // Container should now be terminatING or terminatED, but we - // have no guarantee of which: 'logs -f' does not necessarily - // wait for cleanup. Run 'inspect' and accept either state. - inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Status}}", containerName}) - inspect.WaitWithDefaultTimeout() - if inspect.ExitCode() == 0 { - Expect(inspect.OutputToString()).To(Equal("exited")) - // TODO: add 2-second wait loop to confirm cleanup - } else { - Expect(inspect.ErrorToString()).To(ContainSubstring("no such container")) - } - }) - - It("podman logs with log-driver=none errors", func() { - ctrName := "logsctr" - logc := podmanTest.Podman([]string{"run", "--name", ctrName, "-d", "--log-driver", "none", ALPINE, "top"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - - logs := podmanTest.Podman([]string{"logs", "-f", ctrName}) - logs.WaitWithDefaultTimeout() - Expect(logs).To(Not(Exit(0))) - }) - - It("follow output stopped container", func() { - containerName := "logs-f" - - logc := podmanTest.Podman([]string{"run", "--name", containerName, "-d", ALPINE, "true"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - - results := podmanTest.Podman([]string{"logs", "-f", containerName}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - }) - - It("using container with container log-size", func() { - logc := podmanTest.Podman([]string{"run", "--log-opt=max-size=10k", "-d", ALPINE, "sh", "-c", "echo podman podman podman"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - cid := logc.OutputToString() - - wait := podmanTest.Podman([]string{"wait", cid}) - wait.WaitWithDefaultTimeout() - Expect(wait).To(Exit(0)) - - inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid}) - inspect.WaitWithDefaultTimeout() - Expect(inspect).To(Exit(0)) - Expect(inspect.OutputToString()).To(Equal("10kB")) - - results := podmanTest.Podman([]string{"logs", cid}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(results.OutputToString()).To(Equal("podman podman podman")) - }) - - It("Make sure logs match expected length", func() { - logc := podmanTest.Podman([]string{"run", "-t", "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - - wait := podmanTest.Podman([]string{"wait", "test"}) - wait.WaitWithDefaultTimeout() - Expect(wait).To(Exit(0)) - - results := podmanTest.Podman([]string{"logs", "test"}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - outlines := results.OutputToStringArray() - Expect(len(outlines)).To(Equal(2)) - Expect(outlines[0]).To(Equal("1\r")) - Expect(outlines[1]).To(Equal("2\r")) - }) - - It("podman logs test stdout and stderr", func() { - cname := "log-test" - logc := podmanTest.Podman([]string{"run", "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"}) - logc.WaitWithDefaultTimeout() - Expect(logc).To(Exit(0)) - - wait := podmanTest.Podman([]string{"wait", cname}) - wait.WaitWithDefaultTimeout() - Expect(wait).To(Exit(0)) - - results := podmanTest.Podman([]string{"logs", cname}) - results.WaitWithDefaultTimeout() - Expect(results).To(Exit(0)) - Expect(results.OutputToString()).To(Equal("stdout")) - Expect(results.ErrorToString()).To(Equal("stderr")) - }) }) diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go index ad3a2b54f..8371d3cae 100644 --- a/test/e2e/run_memory_test.go +++ b/test/e2e/run_memory_test.go @@ -2,6 +2,7 @@ package integration import ( "os" + "strconv" . "github.com/containers/podman/v2/test/utils" . "github.com/onsi/ginkgo" @@ -90,4 +91,27 @@ var _ = Describe("Podman run memory", func() { Expect(session.ExitCode()).To(Equal(0)) Expect(session.OutputToString()).To(Equal("41943040")) }) + + It("podman run kernel-memory test", func() { + if podmanTest.Host.Distribution == "ubuntu" { + Skip("Unable to perform test on Ubuntu distributions due to memory management") + } + var session *PodmanSessionIntegration + if CGROUPSV2 { + session = podmanTest.Podman([]string{"run", "--memory", "256m", "--memory-swap", "-1", ALPINE, "cat", "/sys/fs/cgroup/memory.swap.max"}) + } else { + session = podmanTest.Podman([]string{"run", "--cgroupns=private", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"}) + } + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(BeZero()) + output := session.OutputToString() + Expect(err).To(BeNil()) + if CGROUPSV2 { + Expect(output).To(Equal("max")) + } else { + crazyHighNumber, err := strconv.ParseInt(output, 10, 64) + Expect(err).To(BeZero()) + Expect(crazyHighNumber).To(BeNumerically(">", 936854771712)) + } + }) }) diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats index d3cf1c274..0fcc437d4 100644 --- a/test/system/065-cp.bats +++ b/test/system/065-cp.bats @@ -18,6 +18,8 @@ load helpers echo "${randomcontent[0]}" > $srcdir/hostfile0 echo "${randomcontent[1]}" > $srcdir/hostfile1 echo "${randomcontent[2]}" > $srcdir/hostfile2 + mkdir -p $srcdir/subdir + echo "${randomcontent[2]}" > $srcdir/subdir/dotfile. run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity run_podman exec cpcontainer mkdir /srv/subdir @@ -50,6 +52,11 @@ load helpers is "$output" "${randomcontent[$id]}" "$description (cp -> ctr:$dest)" done < <(parse_table "$tests") + # Dots are special for dirs not files. + run_podman cp $srcdir/subdir/dotfile. cpcontainer:/tmp + run_podman exec cpcontainer cat /tmp/dotfile. + is "$output" "${randomcontent[2]}" "$description (cp -> ctr:$dest)" + # Host path does not exist. run_podman 125 cp $srcdir/IdoNotExist cpcontainer:/tmp is "$output" 'Error: ".*/IdoNotExist" could not be found on the host' \ @@ -76,12 +83,14 @@ load helpers ) run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile" + run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/dotfile." run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1" run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2" # format is: <id> | <source arg to cp> | <destination arg (appended to $srcdir) to cp> | <full dest path (appended to $srcdir)> | <test name> tests=" 0 | /tmp/containerfile | | /containerfile | copy to srcdir/ +0 | /tmp/dotfile. | | /dotfile. | copy to srcdir/ 0 | /tmp/containerfile | / | /containerfile | copy to srcdir/ 0 | /tmp/containerfile | /. | /containerfile | copy to srcdir/. 0 | /tmp/containerfile | /newfile | /newfile | copy to srcdir/newfile @@ -117,12 +126,18 @@ load helpers echo "${randomcontent[0]}" > $srcdir/hostfile0 echo "${randomcontent[1]}" > $srcdir/hostfile1 + # "." and "dir/." will copy the contents, so make sure that a dir ending + # with dot is treated correctly. + mkdir -p $srcdir. + cp $srcdir/* $srcdir./ + run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity run_podman exec cpcontainer mkdir /srv/subdir # format is: <source arg to cp (appended to srcdir)> | <destination arg to cp> | <full dest path> | <test name> tests=" | / | /dir-test | copy to root + . | / | /dir-test. | copy dotdir to root / | /tmp | /tmp/dir-test | copy to tmp /. | /usr/ | /usr/ | copy contents of dir to usr/ | . | /srv/dir-test | copy to workdir (rel path) @@ -153,6 +168,9 @@ load helpers run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity run_podman exec cpcontainer sh -c 'mkdir /srv/subdir; echo "This first file is on the container" > /srv/subdir/containerfile1' run_podman exec cpcontainer sh -c 'echo "This second file is on the container as well" > /srv/subdir/containerfile2' + # "." and "dir/." will copy the contents, so make sure that a dir ending + # with dot is treated correctly. + run_podman exec cpcontainer sh -c 'mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./' run_podman cp cpcontainer:/srv $srcdir run cat $srcdir/srv/subdir/containerfile1 @@ -174,6 +192,14 @@ load helpers is "$output" "This first file is on the container" run cat $srcdir/containerfile2 is "$output" "This second file is on the container as well" + rm -rf $srcdir/subdir + + run_podman cp cpcontainer:/tmp/subdir. $srcdir + run cat $srcdir/subdir./containerfile1 + is "$output" "This first file is on the container" + run cat $srcdir/subdir./containerfile2 + is "$output" "This second file is on the container as well" + rm -rf $srcdir/subdir. run_podman rm -f cpcontainer } diff --git a/test/utils/utils.go b/test/utils/utils.go index 6790f31cd..80af7fb7c 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -482,3 +482,13 @@ func RandomString(n int) string { } return string(b) } + +//SkipIfInContainer skips a test if the test is run inside a container +func SkipIfInContainer(reason string) { + if len(reason) < 5 { + panic("SkipIfInContainer must specify a reason to skip") + } + if os.Getenv("TEST_ENVIRON") == "container" { + Skip("[container]: " + reason) + } +} |