github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/integration/bind_mount/bind_mount_test.go (about) 1 package bind_mount_test 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "time" 11 12 "github.com/cloudfoundry-incubator/garden" 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 "github.com/onsi/gomega/gbytes" 16 "github.com/onsi/gomega/gexec" 17 ) 18 19 var _ = Describe("A container", func() { 20 var ( 21 container garden.Container 22 23 // container create parms 24 privilegedContainer bool 25 srcPath string // bm: source 26 dstPath string // bm: destination 27 bindMountMode garden.BindMountMode // bm: RO or RW 28 bindMountOrigin garden.BindMountOrigin // bm: Container or Host 29 30 // pre-existing file for permissions testing 31 testFileName string 32 ) 33 34 allBridges := func() []byte { 35 stdout := gbytes.NewBuffer() 36 cmd, err := gexec.Start(exec.Command("ip", "a"), stdout, GinkgoWriter) 37 Expect(err).ToNot(HaveOccurred()) 38 cmd.Wait(time.Second * 5) 39 40 return stdout.Contents() 41 } 42 43 BeforeEach(func() { 44 privilegedContainer = false 45 container = nil 46 srcPath = "" 47 dstPath = "" 48 bindMountMode = garden.BindMountModeRO 49 bindMountOrigin = garden.BindMountOriginHost 50 testFileName = "" 51 }) 52 53 JustBeforeEach(func() { 54 client = startGarden() 55 56 var err error 57 container, err = client.Create( 58 garden.ContainerSpec{ 59 Privileged: privilegedContainer, 60 BindMounts: []garden.BindMount{garden.BindMount{ 61 SrcPath: srcPath, 62 DstPath: dstPath, 63 Mode: bindMountMode, 64 Origin: bindMountOrigin, 65 }}, 66 Network: fmt.Sprintf("10.0.%d.0/24", GinkgoParallelNode()), 67 }) 68 Expect(err).NotTo(HaveOccurred()) 69 }) 70 71 AfterEach(func() { 72 if container != nil { 73 err := client.Destroy(container.Handle()) 74 Expect(err).ToNot(HaveOccurred()) 75 } 76 77 // sanity check that bridges were cleaned up 78 bridgePrefix := fmt.Sprintf("w%db-", GinkgoParallelNode()) 79 Expect(allBridges()).ToNot(ContainSubstring(bridgePrefix)) 80 }) 81 82 Context("with a host origin bind-mount", func() { 83 BeforeEach(func() { 84 srcPath, testFileName = createTestHostDirAndTestFile() 85 bindMountOrigin = garden.BindMountOriginHost 86 }) 87 88 AfterEach(func() { 89 err := os.RemoveAll(srcPath) 90 Expect(err).ToNot(HaveOccurred()) 91 }) 92 93 Context("which is read-only", func() { 94 BeforeEach(func() { 95 bindMountMode = garden.BindMountModeRO 96 dstPath = "/home/alice/readonly" 97 }) 98 99 Context("and with privileged=true", func() { 100 BeforeEach(func() { 101 privilegedContainer = true 102 }) 103 104 It("is successfully created with correct privileges for non-root in container", func() { 105 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 106 }) 107 108 It("is successfully created with correct privileges for root in container", func() { 109 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 110 }) 111 }) 112 113 Context("and with privileged=false", func() { 114 BeforeEach(func() { 115 privilegedContainer = false 116 }) 117 118 It("is successfully created with correct privileges for non-root in container", func() { 119 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 120 }) 121 122 It("is successfully created with correct privileges for root in container", func() { 123 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 124 }) 125 126 Context("and the parents of the dstPath don't yet exist", func() { 127 BeforeEach(func() { 128 dstPath = "/home/alice/has/a/restaurant/readonly" 129 }) 130 131 It("successfully creates the parents of the dstPath with correct ownership for root in the container", func() { 132 out := gbytes.NewBuffer() 133 proc, err := container.Run(garden.ProcessSpec{ 134 User: "root", 135 Path: "ls", 136 Args: []string{"-l", "/home/alice/has"}, 137 }, garden.ProcessIO{ 138 Stdout: io.MultiWriter(out, GinkgoWriter), 139 Stderr: GinkgoWriter, 140 }) 141 Expect(err).NotTo(HaveOccurred()) 142 Expect(proc.Wait()).To(Equal(0)) 143 Expect(out).To(gbytes.Say(`root`)) 144 }) 145 }) 146 }) 147 }) 148 149 Context("which is read-write", func() { 150 BeforeEach(func() { 151 bindMountMode = garden.BindMountModeRW 152 dstPath = "/home/alice/readwrite" 153 }) 154 155 Context("and with privileged=true", func() { 156 BeforeEach(func() { 157 privilegedContainer = true 158 }) 159 160 It("is successfully created with correct privileges for non-root in container", func() { 161 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 162 }) 163 164 It("is successfully created with correct privileges for root in container", func() { 165 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 166 }) 167 }) 168 169 Context("and with privileged=false", func() { 170 BeforeEach(func() { 171 privilegedContainer = false 172 }) 173 174 It("is successfully created with correct privileges for non-root in container", func() { 175 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 176 }) 177 178 It("is successfully created with correct privileges for root in container", func() { 179 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 180 }) 181 }) 182 }) 183 }) 184 185 Context("with a container origin bind-mount", func() { 186 BeforeEach(func() { 187 srcPath = "/home/alice" 188 bindMountOrigin = garden.BindMountOriginContainer 189 }) 190 191 JustBeforeEach(func() { 192 testFileName = createContainerTestFileIn(container, srcPath) 193 }) 194 195 Context("which is read-only", func() { 196 BeforeEach(func() { 197 bindMountMode = garden.BindMountModeRO 198 dstPath = "/home/alice/readonly" 199 }) 200 201 Context("and with privileged=true", func() { 202 BeforeEach(func() { 203 privilegedContainer = true 204 }) 205 206 It("is successfully created with correct privileges for non-root in container", func() { 207 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 208 }) 209 210 It("is successfully created with correct privileges for root in container", func() { 211 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 212 }) 213 }) 214 215 Context("and with privileged=false", func() { 216 BeforeEach(func() { 217 privilegedContainer = false 218 }) 219 220 It("is successfully created with correct privileges for non-root in container", func() { 221 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 222 }) 223 224 It("is successfully created with correct privileges for root in container", func() { 225 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 226 }) 227 }) 228 229 }) 230 231 Context("which is read-write", func() { 232 BeforeEach(func() { 233 bindMountMode = garden.BindMountModeRW 234 dstPath = "/home/alice/readwrite" 235 }) 236 237 Context("and with privileged=true", func() { 238 BeforeEach(func() { 239 privilegedContainer = true 240 }) 241 242 It("is successfully created with correct privileges for non-root in container", func() { 243 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 244 }) 245 246 It("is successfully created with correct privileges for root in container", func() { 247 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 248 }) 249 }) 250 251 Context("and with privileged=false", func() { 252 BeforeEach(func() { 253 privilegedContainer = false 254 }) 255 256 It("is successfully created with correct privileges for non-root in container", func() { 257 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, false) 258 }) 259 260 It("is successfully created with correct privileges for root in container", func() { 261 checkFileAccess(container, bindMountMode, bindMountOrigin, dstPath, testFileName, privilegedContainer, true) 262 }) 263 }) 264 }) 265 }) 266 }) 267 268 func createTestHostDirAndTestFile() (string, string) { 269 tstHostDir, err := ioutil.TempDir("", "bind-mount-test-dir") 270 Expect(err).ToNot(HaveOccurred()) 271 err = os.Chown(tstHostDir, 0, 0) 272 Expect(err).ToNot(HaveOccurred()) 273 err = os.Chmod(tstHostDir, 0755) 274 Expect(err).ToNot(HaveOccurred()) 275 276 fileName := fmt.Sprintf("bind-mount-%d-test-file", GinkgoParallelNode()) 277 file, err := os.OpenFile(filepath.Join(tstHostDir, fileName), os.O_CREATE|os.O_RDWR, 0777) 278 Expect(err).ToNot(HaveOccurred()) 279 Expect(file.Close()).ToNot(HaveOccurred()) 280 281 return tstHostDir, fileName 282 } 283 284 func createContainerTestFileIn(container garden.Container, dir string) string { 285 fileName := "bind-mount-test-file" 286 filePath := filepath.Join(dir, fileName) 287 288 process, err := container.Run(garden.ProcessSpec{ 289 Path: "touch", 290 Args: []string{filePath}, 291 User: "root", 292 }, garden.ProcessIO{nil, os.Stdout, os.Stderr}) 293 Expect(err).ToNot(HaveOccurred()) 294 Expect(process.Wait()).To(Equal(0)) 295 296 process, err = container.Run(garden.ProcessSpec{ 297 Path: "chmod", 298 Args: []string{"0777", filePath}, 299 User: "root", 300 }, garden.ProcessIO{nil, os.Stdout, os.Stderr}) 301 Expect(err).ToNot(HaveOccurred()) 302 Expect(process.Wait()).To(Equal(0)) 303 304 return fileName 305 } 306 307 func checkFileAccess(container garden.Container, bindMountMode garden.BindMountMode, bindMountOrigin garden.BindMountOrigin, dstPath string, fileName string, privCtr, privReq bool) { 308 readOnly := (garden.BindMountModeRO == bindMountMode) 309 ctrOrigin := (garden.BindMountOriginContainer == bindMountOrigin) 310 realRoot := (privReq && privCtr) 311 312 // can we read a file? 313 filePath := filepath.Join(dstPath, fileName) 314 315 var user string 316 if privReq { 317 user = "root" 318 } else { 319 user = "alice" 320 } 321 322 process, err := container.Run(garden.ProcessSpec{ 323 Path: "cat", 324 Args: []string{filePath}, 325 User: user, 326 }, garden.ProcessIO{}) 327 Expect(err).ToNot(HaveOccurred()) 328 329 Expect(process.Wait()).To(Equal(0)) 330 331 // try to write a new file 332 filePath = filepath.Join(dstPath, "checkFileAccess-file") 333 334 process, err = container.Run(garden.ProcessSpec{ 335 Path: "touch", 336 Args: []string{filePath}, 337 User: user, 338 }, garden.ProcessIO{ 339 Stderr: GinkgoWriter, 340 Stdout: GinkgoWriter, 341 }) 342 Expect(err).ToNot(HaveOccurred()) 343 344 if readOnly || (!realRoot && !ctrOrigin) { 345 Expect(process.Wait()).ToNot(Equal(0)) 346 } else { 347 Expect(process.Wait()).To(Equal(0)) 348 } 349 350 // try to delete an existing file 351 filePath = filepath.Join(dstPath, fileName) 352 353 process, err = container.Run(garden.ProcessSpec{ 354 Path: "rm", 355 Args: []string{filePath}, 356 User: user, 357 }, garden.ProcessIO{}) 358 Expect(err).ToNot(HaveOccurred()) 359 if readOnly || (!realRoot && !ctrOrigin) { 360 Expect(process.Wait()).ToNot(Equal(0)) 361 } else { 362 Expect(process.Wait()).To(Equal(0)) 363 } 364 }