github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/integration/container/overlayfs_linux_test.go (about)

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