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 }