github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/integration/container/mounts_linux_test.go (about)

     1  package container // import "github.com/docker/docker/integration/container"
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"path/filepath"
     7  	"testing"
     8  
     9  	"github.com/docker/docker/api/types"
    10  	"github.com/docker/docker/api/types/container"
    11  	"github.com/docker/docker/api/types/mount"
    12  	"github.com/docker/docker/api/types/network"
    13  	"github.com/docker/docker/client"
    14  	"github.com/docker/docker/pkg/system"
    15  	"gotest.tools/assert"
    16  	is "gotest.tools/assert/cmp"
    17  	"gotest.tools/fs"
    18  	"gotest.tools/skip"
    19  )
    20  
    21  func TestContainerNetworkMountsNoChown(t *testing.T) {
    22  	// chown only applies to Linux bind mounted volumes; must be same host to verify
    23  	skip.If(t, testEnv.IsRemoteDaemon)
    24  
    25  	defer setupTest(t)()
    26  
    27  	ctx := context.Background()
    28  
    29  	tmpDir := fs.NewDir(t, "network-file-mounts", fs.WithMode(0755), fs.WithFile("nwfile", "network file bind mount", fs.WithMode(0644)))
    30  	defer tmpDir.Remove()
    31  
    32  	tmpNWFileMount := tmpDir.Join("nwfile")
    33  
    34  	config := container.Config{
    35  		Image: "busybox",
    36  	}
    37  	hostConfig := container.HostConfig{
    38  		Mounts: []mount.Mount{
    39  			{
    40  				Type:   "bind",
    41  				Source: tmpNWFileMount,
    42  				Target: "/etc/resolv.conf",
    43  			},
    44  			{
    45  				Type:   "bind",
    46  				Source: tmpNWFileMount,
    47  				Target: "/etc/hostname",
    48  			},
    49  			{
    50  				Type:   "bind",
    51  				Source: tmpNWFileMount,
    52  				Target: "/etc/hosts",
    53  			},
    54  		},
    55  	}
    56  
    57  	cli, err := client.NewClientWithOpts(client.FromEnv)
    58  	assert.NilError(t, err)
    59  	defer cli.Close()
    60  
    61  	ctrCreate, err := cli.ContainerCreate(ctx, &config, &hostConfig, &network.NetworkingConfig{}, "")
    62  	assert.NilError(t, err)
    63  	// container will exit immediately because of no tty, but we only need the start sequence to test the condition
    64  	err = cli.ContainerStart(ctx, ctrCreate.ID, types.ContainerStartOptions{})
    65  	assert.NilError(t, err)
    66  
    67  	// Check that host-located bind mount network file did not change ownership when the container was started
    68  	// Note: If the user specifies a mountpath from the host, we should not be
    69  	// attempting to chown files outside the daemon's metadata directory
    70  	// (represented by `daemon.repository` at init time).
    71  	// This forces users who want to use user namespaces to handle the
    72  	// ownership needs of any external files mounted as network files
    73  	// (/etc/resolv.conf, /etc/hosts, /etc/hostname) separately from the
    74  	// daemon. In all other volume/bind mount situations we have taken this
    75  	// same line--we don't chown host file content.
    76  	// See GitHub PR 34224 for details.
    77  	statT, err := system.Stat(tmpNWFileMount)
    78  	assert.NilError(t, err)
    79  	assert.Check(t, is.Equal(uint32(0), statT.UID()), "bind mounted network file should not change ownership from root")
    80  }
    81  
    82  func TestMountDaemonRoot(t *testing.T) {
    83  	skip.If(t, testEnv.IsRemoteDaemon)
    84  
    85  	defer setupTest(t)()
    86  	client := testEnv.APIClient()
    87  	ctx := context.Background()
    88  	info, err := client.Info(ctx)
    89  	if err != nil {
    90  		t.Fatal(err)
    91  	}
    92  
    93  	for _, test := range []struct {
    94  		desc        string
    95  		propagation mount.Propagation
    96  		expected    mount.Propagation
    97  	}{
    98  		{
    99  			desc:        "default",
   100  			propagation: "",
   101  			expected:    mount.PropagationRSlave,
   102  		},
   103  		{
   104  			desc:        "private",
   105  			propagation: mount.PropagationPrivate,
   106  		},
   107  		{
   108  			desc:        "rprivate",
   109  			propagation: mount.PropagationRPrivate,
   110  		},
   111  		{
   112  			desc:        "slave",
   113  			propagation: mount.PropagationSlave,
   114  		},
   115  		{
   116  			desc:        "rslave",
   117  			propagation: mount.PropagationRSlave,
   118  			expected:    mount.PropagationRSlave,
   119  		},
   120  		{
   121  			desc:        "shared",
   122  			propagation: mount.PropagationShared,
   123  		},
   124  		{
   125  			desc:        "rshared",
   126  			propagation: mount.PropagationRShared,
   127  			expected:    mount.PropagationRShared,
   128  		},
   129  	} {
   130  		t.Run(test.desc, func(t *testing.T) {
   131  			test := test
   132  			t.Parallel()
   133  
   134  			propagationSpec := fmt.Sprintf(":%s", test.propagation)
   135  			if test.propagation == "" {
   136  				propagationSpec = ""
   137  			}
   138  			bindSpecRoot := info.DockerRootDir + ":" + "/foo" + propagationSpec
   139  			bindSpecSub := filepath.Join(info.DockerRootDir, "containers") + ":/foo" + propagationSpec
   140  
   141  			for name, hc := range map[string]*container.HostConfig{
   142  				"bind root":    {Binds: []string{bindSpecRoot}},
   143  				"bind subpath": {Binds: []string{bindSpecSub}},
   144  				"mount root": {
   145  					Mounts: []mount.Mount{
   146  						{
   147  							Type:        mount.TypeBind,
   148  							Source:      info.DockerRootDir,
   149  							Target:      "/foo",
   150  							BindOptions: &mount.BindOptions{Propagation: test.propagation},
   151  						},
   152  					},
   153  				},
   154  				"mount subpath": {
   155  					Mounts: []mount.Mount{
   156  						{
   157  							Type:        mount.TypeBind,
   158  							Source:      filepath.Join(info.DockerRootDir, "containers"),
   159  							Target:      "/foo",
   160  							BindOptions: &mount.BindOptions{Propagation: test.propagation},
   161  						},
   162  					},
   163  				},
   164  			} {
   165  				t.Run(name, func(t *testing.T) {
   166  					hc := hc
   167  					t.Parallel()
   168  
   169  					c, err := client.ContainerCreate(ctx, &container.Config{
   170  						Image: "busybox",
   171  						Cmd:   []string{"true"},
   172  					}, hc, nil, "")
   173  
   174  					if err != nil {
   175  						if test.expected != "" {
   176  							t.Fatal(err)
   177  						}
   178  						// expected an error, so this is ok and should not continue
   179  						return
   180  					}
   181  					if test.expected == "" {
   182  						t.Fatal("expected create to fail")
   183  					}
   184  
   185  					defer func() {
   186  						if err := client.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{Force: true}); err != nil {
   187  							panic(err)
   188  						}
   189  					}()
   190  
   191  					inspect, err := client.ContainerInspect(ctx, c.ID)
   192  					if err != nil {
   193  						t.Fatal(err)
   194  					}
   195  					if len(inspect.Mounts) != 1 {
   196  						t.Fatalf("unexpected number of mounts: %+v", inspect.Mounts)
   197  					}
   198  
   199  					m := inspect.Mounts[0]
   200  					if m.Propagation != test.expected {
   201  						t.Fatalf("got unexpected propagation mode, expected %q, got: %v", test.expected, m.Propagation)
   202  					}
   203  				})
   204  			}
   205  		})
   206  	}
   207  }