github.com/containers/podman/v5@v5.1.0-rc1/test/e2e/logs_test.go (about)

     1  package integration
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"os/exec"
     7  	"time"
     8  
     9  	. "github.com/containers/podman/v5/test/utils"
    10  	"github.com/containers/storage/pkg/stringid"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	"github.com/onsi/gomega/format"
    14  	. "github.com/onsi/gomega/gexec"
    15  )
    16  
    17  func isEventBackendJournald(podmanTest *PodmanTestIntegration) bool {
    18  	if !podmanTest.RemoteTest {
    19  		// If not remote test, '--events-backend' is set to 'file' or 'none'
    20  		return false
    21  	}
    22  	info := podmanTest.Podman([]string{"info", "--format", "{{.Host.EventLogger}}"})
    23  	info.WaitWithDefaultTimeout()
    24  	return info.OutputToString() == "journald"
    25  }
    26  
    27  var _ = Describe("Podman logs", func() {
    28  
    29  	It("podman logs on not existent container", func() {
    30  		results := podmanTest.Podman([]string{"logs", "notexist"})
    31  		results.WaitWithDefaultTimeout()
    32  		Expect(results).To(ExitWithError(125, `no container with name or ID "notexist" found: no such container`))
    33  	})
    34  
    35  	for _, log := range []string{"k8s-file", "journald", "json-file"} {
    36  		// This is important to move the 'log' var to the correct scope under Ginkgo flow.
    37  		log := log
    38  
    39  		// Flake prevention: journalctl makes no timeliness guarantees
    40  		logTimeout := time.Millisecond
    41  		if log == "journald" {
    42  			logTimeout = time.Second
    43  		}
    44  
    45  		skipIfJournaldInContainer := func() {
    46  			if log == "journald" {
    47  				SkipIfJournaldUnavailable()
    48  			}
    49  		}
    50  
    51  		It("all lines: "+log, func() {
    52  			skipIfJournaldInContainer()
    53  
    54  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
    55  			logc.WaitWithDefaultTimeout()
    56  			Expect(logc).To(ExitCleanly())
    57  			cid := logc.OutputToString()
    58  
    59  			results := podmanTest.Podman([]string{"wait", cid})
    60  			results.WaitWithDefaultTimeout()
    61  			Expect(results).To(ExitCleanly())
    62  
    63  			Eventually(func(g Gomega) {
    64  				results = podmanTest.Podman([]string{"logs", cid})
    65  				results.WaitWithDefaultTimeout()
    66  				g.Expect(results).To(ExitCleanly())
    67  				g.Expect(results.OutputToStringArray()).To(HaveLen(3))
    68  				g.Expect(results.OutputToString()).To(Equal("podman podman podman"))
    69  			}).WithTimeout(logTimeout).Should(Succeed())
    70  		})
    71  
    72  		It("tail two lines: "+log, func() {
    73  			skipIfJournaldInContainer()
    74  
    75  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
    76  			logc.WaitWithDefaultTimeout()
    77  			Expect(logc).To(ExitCleanly())
    78  			cid := logc.OutputToString()
    79  
    80  			wait := podmanTest.Podman([]string{"wait", cid})
    81  			wait.WaitWithDefaultTimeout()
    82  			Expect(wait).To(ExitCleanly())
    83  
    84  			Eventually(func(g Gomega) {
    85  				results := podmanTest.Podman([]string{"logs", "--tail", "2", cid})
    86  				results.WaitWithDefaultTimeout()
    87  				g.Expect(results).To(ExitCleanly())
    88  				g.Expect(results.OutputToStringArray()).To(HaveLen(2))
    89  				g.Expect(results.OutputToString()).To(Equal("podman podman"))
    90  			}).WithTimeout(logTimeout).Should(Succeed())
    91  		})
    92  
    93  		It("tail zero lines: "+log, func() {
    94  			skipIfJournaldInContainer()
    95  
    96  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
    97  			logc.WaitWithDefaultTimeout()
    98  			Expect(logc).To(ExitCleanly())
    99  			cid := logc.OutputToString()
   100  
   101  			wait := podmanTest.Podman([]string{"wait", cid})
   102  			wait.WaitWithDefaultTimeout()
   103  			Expect(wait).To(ExitCleanly())
   104  
   105  			time.Sleep(logTimeout)
   106  			results := podmanTest.Podman([]string{"logs", "--tail", "0", cid})
   107  			results.WaitWithDefaultTimeout()
   108  			Expect(results).To(ExitCleanly())
   109  			Expect(results.OutputToStringArray()).To(BeEmpty())
   110  		})
   111  
   112  		It("tail 99 lines: "+log, func() {
   113  			skipIfJournaldInContainer()
   114  
   115  			name := "test1"
   116  			logc := podmanTest.Podman([]string{"run", "--name", name, "--log-driver", log, ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   117  			logc.WaitWithDefaultTimeout()
   118  			Expect(logc).To(ExitCleanly())
   119  
   120  			wait := podmanTest.Podman([]string{"wait", name})
   121  			wait.WaitWithDefaultTimeout()
   122  			Expect(wait).To(ExitCleanly())
   123  
   124  			Eventually(func(g Gomega) {
   125  				results := podmanTest.Podman([]string{"logs", "--tail", "99", name})
   126  				results.WaitWithDefaultTimeout()
   127  				g.Expect(results).To(ExitCleanly())
   128  				g.Expect(results.OutputToStringArray()).To(HaveLen(3))
   129  			}).WithTimeout(logTimeout).Should(Succeed())
   130  		})
   131  
   132  		It("tail 800 lines: "+log, func() {
   133  			skipIfJournaldInContainer()
   134  
   135  			// we match 800 line array here, make sure to print all lines when assertion fails.
   136  			// There is something weird going on (https://github.com/containers/podman/issues/18501)
   137  			// and only the normal output log does not seem to be enough to figure out why it flakes.
   138  			oldLength := format.MaxLength
   139  			// unlimited matcher output
   140  			format.MaxLength = 0
   141  			defer func() {
   142  				format.MaxLength = oldLength
   143  			}()
   144  
   145  			// this uses -d so that we do not have 1000 unnecessary lines printed in every test log
   146  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-d", ALPINE, "sh", "-c", "i=1; while [ \"$i\" -ne 1000 ]; do echo \"line $i\"; i=$((i + 1)); done"})
   147  			logc.WaitWithDefaultTimeout()
   148  			Expect(logc).To(ExitCleanly())
   149  			cid := logc.OutputToString()
   150  
   151  			// make sure we wait for the container to finish writing its output
   152  			wait := podmanTest.Podman([]string{"wait", cid})
   153  			wait.WaitWithDefaultTimeout()
   154  			Expect(wait).To(ExitCleanly())
   155  
   156  			Eventually(func(g Gomega) {
   157  				results := podmanTest.Podman([]string{"logs", "--tail", "800", cid})
   158  				results.WaitWithDefaultTimeout()
   159  				g.Expect(results).To(ExitCleanly())
   160  				g.Expect(results.OutputToStringArray()).To(HaveLen(800))
   161  			}).WithTimeout(logTimeout).Should(Succeed())
   162  		})
   163  
   164  		It("tail 2 lines with timestamps: "+log, func() {
   165  			skipIfJournaldInContainer()
   166  
   167  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   168  			logc.WaitWithDefaultTimeout()
   169  			Expect(logc).To(ExitCleanly())
   170  			cid := logc.OutputToString()
   171  
   172  			wait := podmanTest.Podman([]string{"wait", cid})
   173  			wait.WaitWithDefaultTimeout()
   174  			Expect(wait).To(ExitCleanly())
   175  
   176  			Eventually(func(g Gomega) {
   177  				results := podmanTest.Podman([]string{"logs", "--tail", "2", "-t", cid})
   178  				results.WaitWithDefaultTimeout()
   179  				g.Expect(results).To(ExitCleanly())
   180  				g.Expect(results.OutputToStringArray()).To(HaveLen(2))
   181  			}).WithTimeout(logTimeout).Should(Succeed())
   182  		})
   183  
   184  		It("since time 2017-08-07: "+log, func() {
   185  			skipIfJournaldInContainer()
   186  
   187  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   188  			logc.WaitWithDefaultTimeout()
   189  			Expect(logc).To(ExitCleanly())
   190  			cid := logc.OutputToString()
   191  
   192  			wait := podmanTest.Podman([]string{"wait", cid})
   193  			wait.WaitWithDefaultTimeout()
   194  			Expect(wait).To(ExitCleanly())
   195  
   196  			Eventually(func(g Gomega) {
   197  				results := podmanTest.Podman([]string{"logs", "--since", "2017-08-07T10:10:09.056611202-04:00", cid})
   198  				results.WaitWithDefaultTimeout()
   199  				g.Expect(results).To(ExitCleanly())
   200  				g.Expect(results.OutputToStringArray()).To(HaveLen(3))
   201  			}).WithTimeout(logTimeout).Should(Succeed())
   202  		})
   203  
   204  		It("since duration 10m: "+log, func() {
   205  			skipIfJournaldInContainer()
   206  
   207  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   208  			logc.WaitWithDefaultTimeout()
   209  			Expect(logc).To(ExitCleanly())
   210  			cid := logc.OutputToString()
   211  
   212  			wait := podmanTest.Podman([]string{"wait", cid})
   213  			wait.WaitWithDefaultTimeout()
   214  			Expect(wait).To(ExitCleanly())
   215  
   216  			Eventually(func(g Gomega) {
   217  				results := podmanTest.Podman([]string{"logs", "--since", "10m", cid})
   218  				results.WaitWithDefaultTimeout()
   219  				g.Expect(results).To(ExitCleanly())
   220  				g.Expect(results.OutputToStringArray()).To(HaveLen(3))
   221  			}).WithTimeout(logTimeout).Should(Succeed())
   222  		})
   223  
   224  		It("until duration 10m: "+log, func() {
   225  			skipIfJournaldInContainer()
   226  
   227  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   228  			logc.WaitWithDefaultTimeout()
   229  			Expect(logc).To(ExitCleanly())
   230  			cid := logc.OutputToString()
   231  
   232  			wait := podmanTest.Podman([]string{"wait", cid})
   233  			wait.WaitWithDefaultTimeout()
   234  			Expect(wait).To(ExitCleanly())
   235  
   236  			Eventually(func(g Gomega) {
   237  				results := podmanTest.Podman([]string{"logs", "--until", "10m", cid})
   238  				results.WaitWithDefaultTimeout()
   239  				g.Expect(results).To(ExitCleanly())
   240  				g.Expect(results.OutputToStringArray()).To(HaveLen(3))
   241  			}).WithTimeout(logTimeout).Should(Succeed())
   242  		})
   243  
   244  		It("until time NOW: "+log, func() {
   245  			skipIfJournaldInContainer()
   246  
   247  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   248  			logc.WaitWithDefaultTimeout()
   249  			Expect(logc).To(ExitCleanly())
   250  			cid := logc.OutputToString()
   251  
   252  			wait := podmanTest.Podman([]string{"wait", cid})
   253  			wait.WaitWithDefaultTimeout()
   254  			Expect(wait).To(ExitCleanly())
   255  
   256  			Eventually(func(g Gomega) {
   257  				now := time.Now()
   258  				now = now.Add(time.Minute * 1)
   259  				nowS := now.Format(time.RFC3339)
   260  				results := podmanTest.Podman([]string{"logs", "--until", nowS, cid})
   261  				results.WaitWithDefaultTimeout()
   262  				g.Expect(results).To(ExitCleanly())
   263  				g.Expect(results.OutputToStringArray()).To(HaveLen(3))
   264  			}).WithTimeout(logTimeout).Should(Succeed())
   265  		})
   266  
   267  		It("latest and container name should fail: "+log, func() {
   268  			skipIfJournaldInContainer()
   269  
   270  			results := podmanTest.Podman([]string{"logs", "-l", "foobar"})
   271  			results.WaitWithDefaultTimeout()
   272  			if IsRemote() {
   273  				Expect(results).To(ExitWithError(125, "unknown shorthand flag: 'l' in -l"))
   274  			} else {
   275  				Expect(results).To(ExitWithError(125, "--latest and containers cannot be used together"))
   276  			}
   277  		})
   278  
   279  		It("two containers showing short container IDs: "+log, func() {
   280  			skipIfJournaldInContainer()
   281  			SkipIfRemote("podman-remote logs does not support showing two containers at the same time")
   282  
   283  			log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   284  			log1.WaitWithDefaultTimeout()
   285  			Expect(log1).Should(ExitCleanly())
   286  			cid1 := log1.OutputToString()
   287  
   288  			log2 := podmanTest.Podman([]string{"run", "--log-driver", log, "-dt", ALPINE, "sh", "-c", "echo podman; echo podman; echo podman"})
   289  			log2.WaitWithDefaultTimeout()
   290  			Expect(log2).Should(ExitCleanly())
   291  			cid2 := log2.OutputToString()
   292  
   293  			wait := podmanTest.Podman([]string{"wait", cid1, cid2})
   294  			wait.WaitWithDefaultTimeout()
   295  			Expect(wait).To(ExitCleanly())
   296  
   297  			results := podmanTest.Podman([]string{"logs", cid1, cid2})
   298  			results.WaitWithDefaultTimeout()
   299  			Expect(results).Should(ExitCleanly())
   300  
   301  			output := results.OutputToStringArray()
   302  			Expect(output).To(HaveLen(6))
   303  			Expect(output[0]).To(Or(ContainSubstring(cid1[:12]), ContainSubstring(cid2[:12])))
   304  		})
   305  
   306  		It("podman logs on a created container should result in 0 exit code: "+log, func() {
   307  			skipIfJournaldInContainer()
   308  
   309  			session := podmanTest.Podman([]string{"create", "--log-driver", log, "--name", "log", ALPINE})
   310  			session.WaitWithDefaultTimeout()
   311  			Expect(session).To(ExitCleanly())
   312  
   313  			results := podmanTest.Podman([]string{"logs", "log"})
   314  			results.WaitWithDefaultTimeout()
   315  			Expect(results).To(ExitCleanly())
   316  		})
   317  
   318  		It("streaming output: "+log, func() {
   319  			skipIfJournaldInContainer()
   320  
   321  			containerName := "logs-f"
   322  
   323  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, "-dt", ALPINE, "sh", "-c", "echo podman-1; sleep 1; echo podman-2"})
   324  			logc.WaitWithDefaultTimeout()
   325  			Expect(logc).To(ExitCleanly())
   326  
   327  			results := podmanTest.Podman([]string{"logs", "-f", containerName})
   328  			results.WaitWithDefaultTimeout()
   329  
   330  			if log == "journald" && !isEventBackendJournald(podmanTest) {
   331  				// --follow + journald log-driver is only supported with journald events-backend(PR #10431)
   332  				Expect(results).To(ExitWithError(125, "using --follow with the journald --log-driver but without the journald --events-backend"))
   333  				return
   334  			}
   335  
   336  			Expect(results).To(ExitCleanly())
   337  
   338  			Expect(results.OutputToString()).To(ContainSubstring("podman-1"))
   339  			Expect(results.OutputToString()).To(ContainSubstring("podman-2"))
   340  
   341  			// Container should now be terminatING or terminatED, but we
   342  			// have no guarantee of which: 'logs -f' does not necessarily
   343  			// wait for cleanup. Run 'inspect' and accept either state.
   344  			inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.State.Status}}", containerName})
   345  			inspect.WaitWithDefaultTimeout()
   346  			if inspect.ExitCode() == 0 {
   347  				Expect(inspect.OutputToString()).To(Equal("exited"))
   348  				// TODO: add 2-second wait loop to confirm cleanup
   349  			} else {
   350  				Expect(inspect.ErrorToString()).To(ContainSubstring("no such container"))
   351  			}
   352  
   353  			results = podmanTest.Podman([]string{"rm", "--time", "0", "-f", containerName})
   354  			results.WaitWithDefaultTimeout()
   355  			Expect(results).To(ExitCleanly())
   356  		})
   357  
   358  		It("follow output stopped container: "+log, func() {
   359  			skipIfJournaldInContainer()
   360  
   361  			containerName := "logs-f"
   362  
   363  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName, ALPINE, "true"})
   364  			logc.WaitWithDefaultTimeout()
   365  			Expect(logc).To(ExitCleanly())
   366  
   367  			results := podmanTest.Podman([]string{"logs", "-f", containerName})
   368  			results.WaitWithDefaultTimeout()
   369  			if log == "journald" && !isEventBackendJournald(podmanTest) {
   370  				// --follow + journald log-driver is only supported with journald events-backend(PR #10431)
   371  				Expect(results).To(ExitWithError(125, "using --follow with the journald --log-driver but without the journald --events-backend"))
   372  				return
   373  			}
   374  			Expect(results).To(ExitCleanly())
   375  		})
   376  
   377  		It("using container with container log-size: "+log, func() {
   378  			skipIfJournaldInContainer()
   379  
   380  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--log-opt=max-size=10k", "-d", ALPINE, "echo", "podman podman podman"})
   381  			logc.WaitWithDefaultTimeout()
   382  			Expect(logc).To(ExitCleanly())
   383  			cid := logc.OutputToString()
   384  
   385  			wait := podmanTest.Podman([]string{"wait", cid})
   386  			wait.WaitWithDefaultTimeout()
   387  			Expect(wait).To(ExitCleanly())
   388  
   389  			inspect := podmanTest.Podman([]string{"container", "inspect", "--format", "{{.HostConfig.LogConfig.Size}}", cid})
   390  			inspect.WaitWithDefaultTimeout()
   391  			Expect(inspect).To(ExitCleanly())
   392  			Expect(inspect.OutputToString()).To(Equal("10kB"))
   393  
   394  			Eventually(func(g Gomega) {
   395  				results := podmanTest.Podman([]string{"logs", cid})
   396  				results.WaitWithDefaultTimeout()
   397  				g.Expect(results).To(ExitCleanly())
   398  				g.Expect(results.OutputToString()).To(Equal("podman podman podman"))
   399  			}).WithTimeout(logTimeout).Should(Succeed())
   400  		})
   401  
   402  		It("Make sure logs match expected length: "+log, func() {
   403  			skipIfJournaldInContainer()
   404  
   405  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", "test", ALPINE, "sh", "-c", "echo 1; echo 2"})
   406  			logc.WaitWithDefaultTimeout()
   407  			Expect(logc).To(ExitCleanly())
   408  
   409  			wait := podmanTest.Podman([]string{"wait", "test"})
   410  			wait.WaitWithDefaultTimeout()
   411  			Expect(wait).To(ExitCleanly())
   412  
   413  			Eventually(func(g Gomega) {
   414  				results := podmanTest.Podman([]string{"logs", "test"})
   415  				results.WaitWithDefaultTimeout()
   416  				g.Expect(results).To(ExitCleanly())
   417  				outlines := results.OutputToStringArray()
   418  				g.Expect(outlines).To(HaveLen(2))
   419  				g.Expect(outlines[0]).To(Equal("1"))
   420  				g.Expect(outlines[1]).To(Equal("2"))
   421  			}).WithTimeout(logTimeout).Should(Succeed())
   422  		})
   423  
   424  		It("podman logs test stdout and stderr: "+log, func() {
   425  			skipIfJournaldInContainer()
   426  
   427  			cname := "log-test"
   428  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "sh", "-c", "echo stdout; echo stderr >&2"})
   429  			logc.WaitWithDefaultTimeout()
   430  			Expect(logc).To(Exit(0))
   431  
   432  			wait := podmanTest.Podman([]string{"wait", cname})
   433  			wait.WaitWithDefaultTimeout()
   434  			Expect(wait).To(ExitCleanly())
   435  
   436  			Eventually(func(g Gomega) {
   437  				results := podmanTest.Podman([]string{"logs", cname})
   438  				results.WaitWithDefaultTimeout()
   439  				g.Expect(results).To(Exit(0))
   440  				g.Expect(results.OutputToString()).To(Equal("stdout"))
   441  				g.Expect(results.ErrorToString()).To(Equal("stderr"))
   442  			}).WithTimeout(logTimeout).Should(Succeed())
   443  		})
   444  
   445  		It("podman logs partial log lines: "+log, func() {
   446  			skipIfJournaldInContainer()
   447  
   448  			cname := "log-test"
   449  			content := stringid.GenerateRandomID()
   450  			// use printf to print no extra newline
   451  			logc := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", cname, ALPINE, "printf", content})
   452  			logc.WaitWithDefaultTimeout()
   453  			Expect(logc).To(ExitCleanly())
   454  			// Important: do not use OutputToString(), this will remove the trailing newline from the output.
   455  			// However this test must make sure that there is no such extra newline.
   456  			Expect(string(logc.Out.Contents())).To(Equal(content))
   457  
   458  			Eventually(func(g Gomega) {
   459  				logs := podmanTest.Podman([]string{"logs", cname})
   460  				logs.WaitWithDefaultTimeout()
   461  				g.Expect(logs).To(ExitCleanly())
   462  				// see comment above
   463  				g.Expect(string(logs.Out.Contents())).To(Equal(content))
   464  			}).WithTimeout(logTimeout).Should(Succeed())
   465  		})
   466  
   467  		It("podman pod logs -l with newer container created: "+log, func() {
   468  			skipIfJournaldInContainer()
   469  			SkipIfRemote("no -l in remote")
   470  
   471  			podName := "testPod"
   472  			containerName1 := "container1"
   473  			containerName2 := "container2"
   474  			containerName3 := "container3"
   475  
   476  			testPod := podmanTest.Podman([]string{"pod", "create", fmt.Sprintf("--name=%s", podName)})
   477  			testPod.WaitWithDefaultTimeout()
   478  			Expect(testPod).To(ExitCleanly())
   479  
   480  			log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName1, "--pod", podName, BB, "echo", "log1"})
   481  			log1.WaitWithDefaultTimeout()
   482  			Expect(log1).To(ExitCleanly())
   483  
   484  			log2 := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName2, "--pod", podName, BB, "echo", "log2"})
   485  			log2.WaitWithDefaultTimeout()
   486  			Expect(log2).To(ExitCleanly())
   487  
   488  			ctr := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName3, BB, "date"})
   489  			ctr.WaitWithDefaultTimeout()
   490  			Expect(ctr).To(ExitCleanly())
   491  
   492  			Eventually(func(g Gomega) {
   493  				results := podmanTest.Podman([]string{"pod", "logs", "-l"})
   494  				results.WaitWithDefaultTimeout()
   495  				g.Expect(results).To(ExitCleanly())
   496  				podOutput := results.OutputToString()
   497  
   498  				results = podmanTest.Podman([]string{"logs", "-l"})
   499  				results.WaitWithDefaultTimeout()
   500  				g.Expect(results).To(ExitCleanly())
   501  				ctrOutput := results.OutputToString()
   502  
   503  				g.Expect(podOutput).ToNot(Equal(ctrOutput))
   504  			}).WithTimeout(logTimeout).Should(Succeed())
   505  		})
   506  
   507  		It("podman pod logs -l: "+log, func() {
   508  			skipIfJournaldInContainer()
   509  			SkipIfRemote("no -l in remote")
   510  
   511  			podName := "testPod"
   512  			containerName1 := "container1"
   513  			containerName2 := "container2"
   514  
   515  			testPod := podmanTest.Podman([]string{"pod", "create", fmt.Sprintf("--name=%s", podName)})
   516  			testPod.WaitWithDefaultTimeout()
   517  			Expect(testPod).To(ExitCleanly())
   518  
   519  			log1 := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName1, "--pod", podName, BB, "echo", "log1"})
   520  			log1.WaitWithDefaultTimeout()
   521  			Expect(log1).To(ExitCleanly())
   522  
   523  			log2 := podmanTest.Podman([]string{"run", "--log-driver", log, "--name", containerName2, "--pod", podName, BB, "echo", "log2"})
   524  			log2.WaitWithDefaultTimeout()
   525  			Expect(log2).To(ExitCleanly())
   526  
   527  			Eventually(func(g Gomega) {
   528  				results := podmanTest.Podman([]string{"pod", "logs", "-l"})
   529  				results.WaitWithDefaultTimeout()
   530  				g.Expect(results).To(ExitCleanly())
   531  				output := results.OutputToString()
   532  				g.Expect(output).To(ContainSubstring("log1"))
   533  				g.Expect(output).To(ContainSubstring("log2"))
   534  			}).WithTimeout(logTimeout).Should(Succeed())
   535  		})
   536  	}
   537  
   538  	It("using journald for container with container tag", func() {
   539  		SkipIfJournaldUnavailable()
   540  		logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "--log-opt=tag={{.ImageName}},withcomma", "-d", ALPINE, "sh", "-c", "echo podman; sleep 0.1; echo podman; sleep 0.1; echo podman"})
   541  		logc.WaitWithDefaultTimeout()
   542  		Expect(logc).To(ExitCleanly())
   543  		cid := logc.OutputToString()
   544  
   545  		wait := podmanTest.Podman([]string{"wait", cid})
   546  		wait.WaitWithDefaultTimeout()
   547  		Expect(wait).To(ExitCleanly())
   548  
   549  		Eventually(func(g Gomega) {
   550  			cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_TAG", fmt.Sprintf("CONTAINER_ID_FULL=%s", cid))
   551  			out, err := cmd.CombinedOutput()
   552  			g.Expect(err).ToNot(HaveOccurred())
   553  			g.Expect(string(out)).To(ContainSubstring(ALPINE + ",withcomma"))
   554  		}).Should(Succeed())
   555  	})
   556  
   557  	It("using journald container name", func() {
   558  		SkipIfJournaldUnavailable()
   559  		containerName := "inside-journal"
   560  		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"})
   561  		logc.WaitWithDefaultTimeout()
   562  		Expect(logc).To(ExitCleanly())
   563  		cid := logc.OutputToString()
   564  
   565  		wait := podmanTest.Podman([]string{"wait", cid})
   566  		wait.WaitWithDefaultTimeout()
   567  		Expect(wait).To(ExitCleanly())
   568  
   569  		Eventually(func(g Gomega) {
   570  			cmd := exec.Command("journalctl", "--no-pager", "-o", "json", "--output-fields=CONTAINER_NAME", fmt.Sprintf("CONTAINER_ID_FULL=%s", cid))
   571  			out, err := cmd.CombinedOutput()
   572  			g.Expect(err).ToNot(HaveOccurred())
   573  			g.Expect(string(out)).To(ContainSubstring(containerName))
   574  		}).Should(Succeed())
   575  	})
   576  
   577  	It("podman logs with log-driver=none errors", func() {
   578  		ctrName := "logsctr"
   579  		logc := podmanTest.Podman([]string{"run", "--name", ctrName, "-d", "--log-driver", "none", ALPINE, "top"})
   580  		logc.WaitWithDefaultTimeout()
   581  		Expect(logc).To(ExitCleanly())
   582  
   583  		logs := podmanTest.Podman([]string{"logs", "-f", ctrName})
   584  		logs.WaitWithDefaultTimeout()
   585  		Expect(logs).To(ExitWithError(125, "this container is using the 'none' log driver, cannot read logs: this container is not logging output"))
   586  	})
   587  
   588  	It("podman logs with non ASCII log tag fails without correct LANG", func() {
   589  		SkipIfJournaldUnavailable()
   590  		// need to set the LANG to something that does not support german umlaute to trigger the failure case
   591  		cleanup := setLangEnv("C")
   592  		if IsRemote() {
   593  			podmanTest.RestartRemoteService()
   594  		}
   595  		defer cleanup()
   596  		logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "--log-opt", "tag=äöüß", ALPINE, "echo", "podman"})
   597  		logc.WaitWithDefaultTimeout()
   598  		Expect(logc).To(ExitWithError(126, "conmon failed: exit status 1"))
   599  		if !IsRemote() {
   600  			Expect(logc.ErrorToString()).To(ContainSubstring("conmon: option parsing failed: Invalid byte sequence in conversion input"))
   601  		}
   602  	})
   603  
   604  	It("podman logs with non ASCII log tag succeeds with proper env", func() {
   605  		SkipIfJournaldUnavailable()
   606  		cleanup := setLangEnv("en_US.UTF-8")
   607  		if IsRemote() {
   608  			podmanTest.RestartRemoteService()
   609  		}
   610  		defer cleanup()
   611  		logc := podmanTest.Podman([]string{"run", "--log-driver", "journald", "--log-opt", "tag=äöüß", ALPINE, "echo", "podman"})
   612  		logc.WaitWithDefaultTimeout()
   613  		Expect(logc).To(ExitCleanly())
   614  		Expect(logc.OutputToString()).To(Equal("podman"))
   615  	})
   616  
   617  	It("podman pod logs with container names", func() {
   618  		SkipIfRemote("Remote can only process one container at a time")
   619  		podName := "testPod"
   620  		containerName1 := "container1"
   621  		containerName2 := "container2"
   622  
   623  		testPod := podmanTest.Podman([]string{"pod", "create", fmt.Sprintf("--name=%s", podName)})
   624  		testPod.WaitWithDefaultTimeout()
   625  		Expect(testPod).To(ExitCleanly())
   626  
   627  		log1 := podmanTest.Podman([]string{"run", "--name", containerName1, "--pod", podName, BB, "echo", "log1"})
   628  		log1.WaitWithDefaultTimeout()
   629  		Expect(log1).To(ExitCleanly())
   630  
   631  		log2 := podmanTest.Podman([]string{"run", "--name", containerName2, "--pod", podName, BB, "echo", "log2"})
   632  		log2.WaitWithDefaultTimeout()
   633  		Expect(log2).To(ExitCleanly())
   634  
   635  		Eventually(func(g Gomega) {
   636  			results := podmanTest.Podman([]string{"pod", "logs", "--names", podName})
   637  			results.WaitWithDefaultTimeout()
   638  			g.Expect(results).To(ExitCleanly())
   639  
   640  			output := results.OutputToStringArray()
   641  			g.Expect(output).To(HaveLen(2))
   642  			g.Expect(output).To(ContainElement(ContainSubstring(containerName1)))
   643  			g.Expect(output).To(ContainElement(ContainSubstring(containerName2)))
   644  		}).Should(Succeed())
   645  	})
   646  	It("podman pod logs with different colors", func() {
   647  		SkipIfRemote("Remote can only process one container at a time")
   648  		podName := "testPod"
   649  		containerName1 := "container1"
   650  		containerName2 := "container2"
   651  		testPod := podmanTest.Podman([]string{"pod", "create", fmt.Sprintf("--name=%s", podName)})
   652  		testPod.WaitWithDefaultTimeout()
   653  		Expect(testPod).To(ExitCleanly())
   654  		log1 := podmanTest.Podman([]string{"run", "--name", containerName1, "--pod", podName, BB, "echo", "log1"})
   655  		log1.WaitWithDefaultTimeout()
   656  		Expect(log1).To(ExitCleanly())
   657  		log2 := podmanTest.Podman([]string{"run", "--name", containerName2, "--pod", podName, BB, "echo", "log2"})
   658  		log2.WaitWithDefaultTimeout()
   659  		Expect(log2).To(ExitCleanly())
   660  
   661  		Eventually(func(g Gomega) {
   662  			results := podmanTest.Podman([]string{"pod", "logs", "--color", podName})
   663  			results.WaitWithDefaultTimeout()
   664  			g.Expect(results).To(ExitCleanly())
   665  			output := results.OutputToStringArray()
   666  			g.Expect(output).To(HaveLen(2))
   667  			g.Expect(output[0]).To(MatchRegexp(`\x1b\[3[0-9a-z ]+\x1b\[0m`))
   668  			g.Expect(output[1]).To(MatchRegexp(`\x1b\[3[0-9a-z ]+\x1b\[0m`))
   669  		}).Should(Succeed())
   670  	})
   671  })
   672  
   673  func setLangEnv(lang string) func() {
   674  	oldLang, okLang := os.LookupEnv("LANG")
   675  	oldLcAll, okLc := os.LookupEnv("LC_ALL")
   676  	err := os.Setenv("LANG", lang)
   677  	Expect(err).ToNot(HaveOccurred())
   678  	err = os.Setenv("LC_ALL", "")
   679  	Expect(err).ToNot(HaveOccurred())
   680  
   681  	return func() {
   682  		if okLang {
   683  			os.Setenv("LANG", oldLang)
   684  		} else {
   685  			os.Unsetenv("LANG")
   686  		}
   687  		if okLc {
   688  			os.Setenv("LC_ALL", oldLcAll)
   689  		} else {
   690  			os.Unsetenv("LC_ALL")
   691  		}
   692  	}
   693  }