overlayfs_linux_test.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package container
  2. import (
  3. "io"
  4. "strings"
  5. "testing"
  6. "github.com/docker/docker/api/types"
  7. "github.com/docker/docker/integration/internal/container"
  8. "github.com/docker/docker/pkg/archive"
  9. "github.com/docker/docker/pkg/dmesg"
  10. "gotest.tools/v3/assert"
  11. "gotest.tools/v3/skip"
  12. )
  13. func TestNoOverlayfsWarningsAboutUndefinedBehaviors(t *testing.T) {
  14. skip.If(t, testEnv.DaemonInfo.OSType != "linux", "overlayfs is only available on linux")
  15. skip.If(t, testEnv.IsRemoteDaemon(), "local daemon is needed for kernel log access")
  16. skip.If(t, testEnv.IsRootless(), "root is needed for reading kernel log")
  17. ctx := setupTest(t)
  18. client := testEnv.APIClient()
  19. cID := container.Run(ctx, t, client, container.WithCmd("sh", "-c", `while true; do echo $RANDOM >>/file; sleep 0.1; done`))
  20. testCases := []struct {
  21. name string
  22. operation func(t *testing.T) error
  23. }{
  24. {name: "diff", operation: func(*testing.T) error {
  25. _, err := client.ContainerDiff(ctx, cID)
  26. return err
  27. }},
  28. {name: "export", operation: func(*testing.T) error {
  29. rc, err := client.ContainerExport(ctx, cID)
  30. if err == nil {
  31. defer rc.Close()
  32. _, err = io.Copy(io.Discard, rc)
  33. }
  34. return err
  35. }},
  36. {name: "cp to container", operation: func(t *testing.T) error {
  37. archive, err := archive.Generate("new-file", "hello-world")
  38. assert.NilError(t, err, "failed to create a temporary archive")
  39. return client.CopyToContainer(ctx, cID, "/", archive, types.CopyToContainerOptions{})
  40. }},
  41. {name: "cp from container", operation: func(*testing.T) error {
  42. rc, _, err := client.CopyFromContainer(ctx, cID, "/file")
  43. if err == nil {
  44. defer rc.Close()
  45. _, err = io.Copy(io.Discard, rc)
  46. }
  47. return err
  48. }},
  49. }
  50. for _, tc := range testCases {
  51. tc := tc
  52. t.Run(tc.name, func(t *testing.T) {
  53. prev := dmesgLines(256)
  54. err := tc.operation(t)
  55. assert.NilError(t, err)
  56. after := dmesgLines(2048)
  57. diff := diffDmesg(prev, after)
  58. for _, line := range diff {
  59. overlayfs := strings.Contains(line, "overlayfs: ")
  60. lowerDirInUse := strings.Contains(line, "lowerdir is in-use as ")
  61. upperDirInUse := strings.Contains(line, "upperdir is in-use as ")
  62. workDirInuse := strings.Contains(line, "workdir is in-use as ")
  63. undefinedBehavior := strings.Contains(line, "will result in undefined behavior")
  64. if overlayfs && (lowerDirInUse || upperDirInUse || workDirInuse) && undefinedBehavior {
  65. t.Errorf("%s caused overlayfs kernel warning: %s", tc.name, line)
  66. }
  67. }
  68. })
  69. }
  70. }
  71. func dmesgLines(bytes int) []string {
  72. data := dmesg.Dmesg(bytes)
  73. return strings.Split(strings.TrimSpace(string(data)), "\n")
  74. }
  75. func diffDmesg(prev, next []string) []string {
  76. // All lines have a timestamp, so just take the last one from the previous
  77. // log and find it in the new log.
  78. lastPrev := prev[len(prev)-1]
  79. for idx := len(next) - 1; idx >= 0; idx-- {
  80. line := next[idx]
  81. if line == lastPrev {
  82. nextIdx := idx + 1
  83. if nextIdx < len(next) {
  84. return next[nextIdx:]
  85. } else {
  86. // Found at the last position, log is the same.
  87. return nil
  88. }
  89. }
  90. }
  91. return next
  92. }