From f51e0d0597757da72fd7a10085998052684e396b Mon Sep 17 00:00:00 2001
From: Jhon Honce <jhonce@redhat.com>
Date: Wed, 20 May 2020 14:24:18 -0700
Subject: V2 enable remote logs and testing

* wire up bindings and handler for obtaining logs remotely
* enable debug logging from podman in e2e test using DEBUG and
  DEBUG_SERVICE env variables
* Fix error in streaming log frames
* enable remote logs test

Signed-off-by: Jhon Honce <jhonce@redhat.com>
---
 cmd/podman/containers/logs.go         |  24 ++++---
 pkg/api/handlers/compat/containers.go |  27 ++++----
 pkg/bindings/containers/containers.go |   7 +-
 pkg/bindings/containers/logs.go       |  77 +++++++---------------
 pkg/bindings/test/containers_test.go  |   4 +-
 pkg/domain/infra/tunnel/containers.go |  35 +++++++++-
 test/e2e/libpod_suite_remote_test.go  |   9 ++-
 test/e2e/logs_test.go                 | 117 +++++++++++++++++-----------------
 8 files changed, 156 insertions(+), 144 deletions(-)

diff --git a/cmd/podman/containers/logs.go b/cmd/podman/containers/logs.go
index 2b8c3ed5f..de5234044 100644
--- a/cmd/podman/containers/logs.go
+++ b/cmd/podman/containers/logs.go
@@ -29,7 +29,18 @@ var (
 		Use:   "logs [flags] CONTAINER [CONTAINER...]",
 		Short: "Fetch the logs of one or more containers",
 		Long:  logsDescription,
-		RunE:  logs,
+		Args: func(cmd *cobra.Command, args []string) error {
+			switch {
+			case registry.IsRemote() && len(args) > 1:
+				return errors.New(cmd.Name() + " does not support multiple containers when run remotely")
+			case logsOptions.Latest && len(args) > 0:
+				return errors.New("no containers can be specified when using 'latest'")
+			case !logsOptions.Latest && len(args) < 1:
+				return errors.New("specify at least one container name or ID to log")
+			}
+			return nil
+		},
+		RunE: logs,
 		Example: `podman logs ctrID
   podman logs --names ctrID1 ctrID2
   podman logs --tail 2 mywebserver
@@ -41,6 +52,7 @@ var (
 		Use:   logsCommand.Use,
 		Short: logsCommand.Short,
 		Long:  logsCommand.Long,
+		Args:  logsCommand.Args,
 		RunE:  logsCommand.RunE,
 		Example: `podman container logs ctrID
 		podman container logs --names ctrID1 ctrID2
@@ -53,7 +65,7 @@ var (
 func init() {
 	// logs
 	registry.Commands = append(registry.Commands, registry.CliCommand{
-		Mode:    []entities.EngineMode{entities.ABIMode},
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
 		Command: logsCommand,
 	})
 
@@ -62,7 +74,7 @@ func init() {
 
 	// container logs
 	registry.Commands = append(registry.Commands, registry.CliCommand{
-		Mode:    []entities.EngineMode{entities.ABIMode},
+		Mode:    []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
 		Command: containerLogsCommand,
 		Parent:  containerCmd,
 	})
@@ -84,12 +96,6 @@ func logsFlags(flags *pflag.FlagSet) {
 }
 
 func logs(cmd *cobra.Command, args []string) error {
-	if len(args) > 0 && logsOptions.Latest {
-		return errors.New("no containers can be specified when using 'latest'")
-	}
-	if !logsOptions.Latest && len(args) < 1 {
-		return errors.New("specify at least one container name or ID to log")
-	}
 	if logsOptions.SinceRaw != "" {
 		// parse time, error out if something is wrong
 		since, err := util.ParseInputTime(logsOptions.SinceRaw)
diff --git a/pkg/api/handlers/compat/containers.go b/pkg/api/handlers/compat/containers.go
index 239e41af4..cea4bd0f6 100644
--- a/pkg/api/handlers/compat/containers.go
+++ b/pkg/api/handlers/compat/containers.go
@@ -4,6 +4,7 @@ import (
 	"encoding/binary"
 	"encoding/json"
 	"fmt"
+	"io"
 	"net/http"
 	"strconv"
 	"strings"
@@ -295,7 +296,9 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
 	}()
 
 	w.WriteHeader(http.StatusOK)
-	var builder strings.Builder
+
+	var frame strings.Builder
+	header := make([]byte, 8)
 	for ok := true; ok; ok = query.Follow {
 		for line := range logChannel {
 			if _, found := r.URL.Query()["until"]; found {
@@ -304,10 +307,8 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
 				}
 			}
 
-			// Reset variables we're ready to loop again
-			builder.Reset()
-			header := [8]byte{}
-
+			// Reset buffer we're ready to loop again
+			frame.Reset()
 			switch line.Device {
 			case "stdout":
 				if !query.Stdout {
@@ -327,17 +328,17 @@ func LogsFromContainer(w http.ResponseWriter, r *http.Request) {
 			}
 
 			if query.Timestamps {
-				builder.WriteString(line.Time.Format(time.RFC3339))
-				builder.WriteRune(' ')
+				frame.WriteString(line.Time.Format(time.RFC3339))
+				frame.WriteString(" ")
 			}
-			builder.WriteString(line.Msg)
-			// Build header and output entry
-			binary.BigEndian.PutUint32(header[4:], uint32(len(header)+builder.Len()))
-			if _, err := w.Write(header[:]); err != nil {
+			frame.WriteString(line.Msg)
+
+			binary.BigEndian.PutUint32(header[4:], uint32(frame.Len()))
+			if _, err := w.Write(header[0:8]); err != nil {
 				log.Errorf("unable to write log output header: %q", err)
 			}
-			if _, err := fmt.Fprint(w, builder.String()); err != nil {
-				log.Errorf("unable to write builder string: %q", err)
+			if _, err := io.WriteString(w, frame.String()); err != nil {
+				log.Errorf("unable to write frame string: %q", err)
 			}
 			if flusher, ok := w.(http.Flusher); ok {
 				flusher.Flush()
diff --git a/pkg/bindings/containers/containers.go b/pkg/bindings/containers/containers.go
index 1ed4919e0..39a077f36 100644
--- a/pkg/bindings/containers/containers.go
+++ b/pkg/bindings/containers/containers.go
@@ -23,7 +23,7 @@ import (
 )
 
 var (
-	ErrLostSync = errors.New("lost synchronization with attach multiplexed result")
+	ErrLostSync = errors.New("lost synchronization with multiplexed stream")
 )
 
 // List obtains a list of containers in local storage.  All parameters to this method are optional.
@@ -485,7 +485,7 @@ func Attach(ctx context.Context, nameOrId string, detachKeys *string, logs, stre
 					return err
 				}
 			case fd == 3:
-				return fmt.Errorf("error from daemon in stream: %s", frame)
+				return errors.New("error from service in stream: " + string(frame))
 			default:
 				return fmt.Errorf("unrecognized input header: %d", fd)
 			}
@@ -507,7 +507,7 @@ func DemuxHeader(r io.Reader, buffer []byte) (fd, sz int, err error) {
 
 	fd = int(buffer[0])
 	if fd < 0 || fd > 3 {
-		err = ErrLostSync
+		err = errors.Wrapf(ErrLostSync, fmt.Sprintf(`channel "%d" found, 0-3 supported`, fd))
 		return
 	}
 
@@ -528,7 +528,6 @@ func DemuxFrame(r io.Reader, buffer []byte, length int) (frame []byte, err error
 		err = io.ErrUnexpectedEOF
 		return
 	}
-
 	return buffer[0:length], nil
 }
 
diff --git a/pkg/bindings/containers/logs.go b/pkg/bindings/containers/logs.go
index b7ecb3c7e..20c8b4292 100644
--- a/pkg/bindings/containers/logs.go
+++ b/pkg/bindings/containers/logs.go
@@ -1,8 +1,9 @@
 package containers
 
 import (
+	"bytes"
 	"context"
-	"encoding/binary"
+	"fmt"
 	"io"
 	"net/http"
 	"net/url"
@@ -49,68 +50,34 @@ func Logs(ctx context.Context, nameOrID string, opts LogOptions, stdoutChan, std
 	if err != nil {
 		return err
 	}
+	defer response.Body.Close()
 
-	// read 8 bytes
-	// first byte determines stderr=2|stdout=1
-	// bytes 4-7 len(msg) in uint32
+	buffer := make([]byte, 1024)
 	for {
-		stream, msgSize, err := readHeader(response.Body)
+		fd, l, err := DemuxHeader(response.Body, buffer)
 		if err != nil {
-			// In case the server side closes up shop because !follow
-			if err == io.EOF {
-				break
+			if errors.Is(err, io.EOF) {
+				return nil
 			}
-			return errors.Wrap(err, "unable to read log header")
+			return err
 		}
-		msg, err := readMsg(response.Body, msgSize)
+		frame, err := DemuxFrame(response.Body, buffer, l)
 		if err != nil {
-			return errors.Wrap(err, "unable to read log message")
+			return err
 		}
-		if stream == 1 {
-			stdoutChan <- msg
-		} else {
-			stderrChan <- msg
-		}
-	}
-	return nil
-}
+		frame = bytes.Replace(frame[0:l], []byte{13}, []byte{10}, -1)
 
-func readMsg(r io.Reader, msgSize int) (string, error) {
-	var msg []byte
-	size := msgSize
-	for {
-		b := make([]byte, size)
-		_, err := r.Read(b)
-		if err != nil {
-			return "", err
-		}
-		msg = append(msg, b...)
-		if len(msg) == msgSize {
-			break
-		}
-		size = msgSize - len(msg)
-	}
-	return string(msg), nil
-}
-
-func readHeader(r io.Reader) (byte, int, error) {
-	var (
-		header []byte
-		size   = 8
-	)
-	for {
-		b := make([]byte, size)
-		_, err := r.Read(b)
-		if err != nil {
-			return 0, 0, err
-		}
-		header = append(header, b...)
-		if len(header) == 8 {
-			break
+		switch fd {
+		case 0:
+			stdoutChan <- string(frame)
+		case 1:
+			stdoutChan <- string(frame)
+		case 2:
+			stderrChan <- string(frame)
+		case 3:
+			return errors.New("error from service in stream: " + string(frame))
+		default:
+			return fmt.Errorf("unrecognized input header: %d", fd)
 		}
-		size = 8 - len(header)
 	}
-	stream := header[0]
-	msgSize := int(binary.BigEndian.Uint32(header[4:]) - 8)
-	return stream, msgSize, nil
 }
diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go
index f725d1cf2..3b94b10eb 100644
--- a/pkg/bindings/test/containers_test.go
+++ b/pkg/bindings/test/containers_test.go
@@ -378,9 +378,9 @@ var _ = Describe("Podman containers ", func() {
 			containers.Logs(bt.conn, r.ID, opts, stdoutChan, nil)
 		}()
 		o := <-stdoutChan
-		o = strings.ReplaceAll(o, "\r", "")
+		o = strings.TrimSpace(o)
 		_, err = time.Parse(time.RFC1123Z, o)
-		Expect(err).To(BeNil())
+		Expect(err).ShouldNot(HaveOccurred())
 	})
 
 	It("podman top", func() {
diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go
index 30c4a8359..beba55c2b 100644
--- a/pkg/domain/infra/tunnel/containers.go
+++ b/pkg/domain/infra/tunnel/containers.go
@@ -4,7 +4,9 @@ import (
 	"context"
 	"io"
 	"os"
+	"strconv"
 	"strings"
+	"time"
 
 	"github.com/containers/common/pkg/config"
 	"github.com/containers/image/v5/docker/reference"
@@ -336,9 +338,36 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG
 	return &entities.ContainerCreateReport{Id: response.ID}, nil
 }
 
-func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error {
-	// The endpoint is not ready yet and requires some more work.
-	return errors.New("not implemented yet")
+func (ic *ContainerEngine) ContainerLogs(_ context.Context, nameOrIds []string, options entities.ContainerLogsOptions) error {
+	since := options.Since.Format(time.RFC3339)
+	tail := strconv.FormatInt(options.Tail, 10)
+	stdout := options.Writer != nil
+	opts := containers.LogOptions{
+		Follow:     &options.Follow,
+		Since:      &since,
+		Stderr:     &stdout,
+		Stdout:     &stdout,
+		Tail:       &tail,
+		Timestamps: &options.Timestamps,
+		Until:      nil,
+	}
+
+	var err error
+	outCh := make(chan string)
+	ctx, cancel := context.WithCancel(context.Background())
+	go func() {
+		err = containers.Logs(ic.ClientCxt, nameOrIds[0], opts, outCh, outCh)
+		cancel()
+	}()
+
+	for {
+		select {
+		case <-ctx.Done():
+			return err
+		case line := <-outCh:
+			_, _ = io.WriteString(options.Writer, line)
+		}
+	}
 }
 
 func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error {
diff --git a/test/e2e/libpod_suite_remote_test.go b/test/e2e/libpod_suite_remote_test.go
index 79d18115c..e8cdf91ee 100644
--- a/test/e2e/libpod_suite_remote_test.go
+++ b/test/e2e/libpod_suite_remote_test.go
@@ -89,10 +89,17 @@ func (p *PodmanTestIntegration) StartRemoteService() {
 	if os.Geteuid() == 0 {
 		os.MkdirAll("/run/podman", 0755)
 	}
+
+	args := []string{}
+	if _, found := os.LookupEnv("DEBUG_SERVICE"); found {
+		args = append(args, "--log-level", "debug")
+	}
 	remoteSocket := p.RemoteSocket
-	args := []string{"system", "service", "--timeout", "0", remoteSocket}
+	args = append(args, "system", "service", "--timeout", "0", remoteSocket)
 	podmanOptions := getRemoteOptions(p, args)
 	command := exec.Command(p.PodmanBinary, podmanOptions...)
+	command.Stdout = os.Stdout
+	command.Stderr = os.Stderr
 	fmt.Printf("Running: %s %s\n", p.PodmanBinary, strings.Join(podmanOptions, " "))
 	command.Start()
 	command.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
diff --git a/test/e2e/logs_test.go b/test/e2e/logs_test.go
index 8924db670..f36163ccc 100644
--- a/test/e2e/logs_test.go
+++ b/test/e2e/logs_test.go
@@ -9,6 +9,7 @@ import (
 	. "github.com/containers/libpod/test/utils"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	. "github.com/onsi/gomega/gexec"
 )
 
 var _ = Describe("Podman logs", func() {
@@ -19,7 +20,6 @@ var _ = Describe("Podman logs", func() {
 	)
 
 	BeforeEach(func() {
-		SkipIfRemote() // v2remotefail
 		tempdir, err = CreateTempDirInTempDir()
 		if err != nil {
 			os.Exit(1)
@@ -36,97 +36,99 @@ var _ = Describe("Podman logs", func() {
 
 	})
 
-	It("podman logs for container", func() {
+	It("all lines", func() {
 		logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
 		logc.WaitWithDefaultTimeout()
-		Expect(logc.ExitCode()).To(Equal(0))
-		cid := logc.OutputToString()
+		Expect(logc).To(Exit(0))
 
+		cid := logc.OutputToString()
 		results := podmanTest.Podman([]string{"logs", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman logs tail two lines", func() {
+	It("tail two lines", func() {
 		logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
 		logc.WaitWithDefaultTimeout()
-		Expect(logc.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--tail", "2", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(2))
 	})
 
-	It("podman logs tail zero lines", func() {
+	It("tail zero lines", func() {
 		logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
 		logc.WaitWithDefaultTimeout()
-		Expect(logc.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--tail", "0", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(0))
 	})
 
-	It("podman logs tail 99 lines", func() {
+	It("tail 99 lines", func() {
 		logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
 		logc.WaitWithDefaultTimeout()
-		Expect(logc.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--tail", "99", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman logs tail 2 lines with timestamps", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(2))
 	})
 
-	It("podman logs latest with since time", func() {
+	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.ExitCode()).To(Equal(0))
+		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.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman logs latest with since duration", func() {
+	It("since duration 10m", func() {
 		logc := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
 		logc.WaitWithDefaultTimeout()
-		Expect(logc.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--since", "10m", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman logs latest and container name should fail", func() {
+	It("latest and container name should fail", func() {
+		SkipIfRemote() // -l not supported
 		results := podmanTest.Podman([]string{"logs", "-l", "foobar"})
 		results.WaitWithDefaultTimeout()
 		Expect(results).To(ExitWithError())
 	})
 
-	It("podman logs two containers and should display short container IDs", func() {
+	It("two containers showing short container IDs", func() {
+		SkipIfRemote() // remote does not support multiple containers
 		log1 := podmanTest.Podman([]string{"run", "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
 		log1.WaitWithDefaultTimeout()
 		Expect(log1.ExitCode()).To(Equal(0))
@@ -139,7 +141,7 @@ var _ = Describe("Podman logs", func() {
 
 		results := podmanTest.Podman([]string{"logs", cid1, cid2})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).Should(Exit(0))
 
 		output := results.OutputToStringArray()
 		Expect(len(output)).To(Equal(6))
@@ -149,23 +151,24 @@ var _ = Describe("Podman logs", func() {
 	It("podman logs on a created container should result in 0 exit code", func() {
 		session := podmanTest.Podman([]string{"create", "-dt", "--name", "log", ALPINE})
 		session.WaitWithDefaultTimeout()
-		Expect(session.ExitCode()).To(BeZero())
+		Expect(session).To(Exit(0))
 
 		results := podmanTest.Podman([]string{"logs", "log"})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(BeZero())
+		Expect(results).To(Exit(0))
 	})
 
-	It("podman journald logs for container with container tag", func() {
+	It("using journald for container with container tag", func() {
+		SkipIfRemote()
 		Skip("need to verify images have correct packages for journald")
 		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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		wait := podmanTest.Podman([]string{"wait", "-l"})
 		wait.WaitWithDefaultTimeout()
-		Expect(wait.ExitCode()).To(BeZero())
+		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))
 		out, err := cmd.CombinedOutput()
@@ -173,17 +176,18 @@ var _ = Describe("Podman logs", func() {
 		Expect(string(out)).To(ContainSubstring("alpine"))
 	})
 
-	It("podman journald logs for container name", func() {
+	It("using journald for container name", func() {
 		Skip("need to verify images have correct packages for journald")
+		SkipIfRemote()
 		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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		wait := podmanTest.Podman([]string{"wait", "-l"})
 		wait.WaitWithDefaultTimeout()
-		Expect(wait.ExitCode()).To(BeZero())
+		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))
 		out, err := cmd.CombinedOutput()
@@ -191,98 +195,97 @@ var _ = Describe("Podman logs", func() {
 		Expect(string(out)).To(ContainSubstring(containerName))
 	})
 
-	It("podman journald logs for container", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman journald logs tail two lines", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
-
 		results := podmanTest.Podman([]string{"logs", "--tail", "2", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(2))
 	})
 
-	It("podman journald logs tail 99 lines", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--tail", "99", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman journald logs tail 2 lines with timestamps", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(2))
 	})
 
-	It("podman journald logs latest with since time", func() {
+	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.ExitCode()).To(Equal(0))
+		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.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman journald logs latest with since duration", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 		cid := logc.OutputToString()
 
 		results := podmanTest.Podman([]string{"logs", "--since", "10m", cid})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 		Expect(len(results.OutputToStringArray())).To(Equal(3))
 	})
 
-	It("podman logs -f two lines", func() {
+	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.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 
 		results := podmanTest.Podman([]string{"logs", "-f", containerName})
 		results.WaitWithDefaultTimeout()
-		Expect(results.ExitCode()).To(Equal(0))
+		Expect(results).To(Exit(0))
 
 		// Verify that the cleanup process worked correctly and we can recreate a container with the same name
 		logc = podmanTest.Podman([]string{"run", "--rm", "--name", containerName, "-dt", ALPINE, "true"})
 		logc.WaitWithDefaultTimeout()
-		Expect(logc.ExitCode()).To(Equal(0))
+		Expect(logc).To(Exit(0))
 	})
 })
-- 
cgit v1.2.3-54-g00ecf