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