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