github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/integration/lifecycle/flags_test.go (about) 1 package lifecycle_test 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "os" 9 "os/exec" 10 "path" 11 12 "code.cloudfoundry.org/garden" 13 "code.cloudfoundry.org/garden-linux/integration/runner" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 "github.com/onsi/gomega/gbytes" 17 ) 18 19 var _ = Describe("Garden startup flags", func() { 20 21 var debugAddr string 22 23 BeforeEach(func() { 24 debugAddr = fmt.Sprintf("0.0.0.0:%d", 15000+GinkgoParallelNode()) 25 }) 26 27 Context("when starting without the --debugAddr flag", func() { 28 BeforeEach(func() { 29 client = startGarden() 30 }) 31 32 It("does not expose the pprof debug endpoint", func() { 33 _, err := http.Get(fmt.Sprintf("http://%s/debug/pprof/?debug=1", debugAddr)) 34 Expect(err).To(HaveOccurred()) 35 }) 36 37 It("does not expose the log level adjustment endpoint", func() { 38 _, err := http.Get(fmt.Sprintf("http://%s/log-level -X PUT -d debug", debugAddr)) 39 Expect(err).To(HaveOccurred()) 40 }) 41 }) 42 43 Context("when started with the --maxContainers flag", func() { 44 Context("when maxContainers is lower than the subnet pool capacity", func() { 45 BeforeEach(func() { 46 client = startGarden("--maxContainers", "1") 47 }) 48 49 Context("when getting the capacity", func() { 50 It("returns the maxContainers flag value", func() { 51 capacity, err := client.Capacity() 52 Expect(err).ToNot(HaveOccurred()) 53 Expect(capacity.MaxContainers).To(Equal(uint64(1))) 54 }) 55 }) 56 }) 57 58 Context("when maxContainers is higher than the subnet pool capacity", func() { 59 BeforeEach(func() { 60 client = startGarden("--maxContainers", "1000") 61 }) 62 63 Context("when getting the capacity", func() { 64 It("returns the capacity of the subnet pool", func() { 65 capacity, err := client.Capacity() 66 Expect(err).ToNot(HaveOccurred()) 67 Expect(capacity.MaxContainers).To(Equal(uint64(64))) 68 }) 69 }) 70 }) 71 }) 72 73 Context("when starting with the --debugAddr flag", func() { 74 BeforeEach(func() { 75 client = startGarden("--debugAddr", debugAddr) 76 }) 77 78 It("exposes the pprof debug endpoint", func() { 79 _, err := http.Get(fmt.Sprintf("http://%s/debug/pprof/?debug=1", debugAddr)) 80 Expect(err).ToNot(HaveOccurred()) 81 }) 82 83 It("exposes the log level adjustment endpoint", func() { 84 _, err := http.Get(fmt.Sprintf("http://%s/log-level -X PUT -d debug", debugAddr)) 85 Expect(err).ToNot(HaveOccurred()) 86 87 _, err = http.Get(fmt.Sprintf("http://%s/log-level -X PUT -d info", debugAddr)) 88 Expect(err).ToNot(HaveOccurred()) 89 90 _, err = http.Get(fmt.Sprintf("http://%s/log-level -X PUT -d error", debugAddr)) 91 Expect(err).ToNot(HaveOccurred()) 92 93 _, err = http.Get(fmt.Sprintf("http://%s/log-level -X PUT -d fatal", debugAddr)) 94 Expect(err).ToNot(HaveOccurred()) 95 }) 96 97 Describe("vars", func() { 98 var ( 99 diskLimits garden.DiskLimits 100 container garden.Container 101 vars map[string]interface{} 102 ) 103 104 BeforeEach(func() { 105 diskLimits = garden.DiskLimits{ 106 ByteHard: 10 * 1024 * 1024, 107 Scope: garden.DiskLimitScopeExclusive, 108 } 109 }) 110 111 JustBeforeEach(func() { 112 var err error 113 114 container, err = client.Create(garden.ContainerSpec{ 115 Limits: garden.Limits{ 116 Disk: diskLimits, 117 }, 118 RootFSPath: "docker:///busybox", 119 }) 120 Expect(err).NotTo(HaveOccurred()) 121 122 response, err := http.Get(fmt.Sprintf("http://%s/debug/vars", debugAddr)) 123 Expect(err).ToNot(HaveOccurred()) 124 125 contents, err := ioutil.ReadAll(response.Body) 126 Expect(err).ToNot(HaveOccurred()) 127 128 vars = make(map[string]interface{}) 129 Expect(json.Unmarshal(contents, &vars)).To(Succeed()) 130 }) 131 132 AfterEach(func() { 133 Expect(client.Destroy(container.Handle())).To(Succeed()) 134 }) 135 136 It("exposes the number of loop devices", func() { 137 Expect(vars["loopDevices"]).To(BeNumerically(">=", float64(1))) 138 }) 139 140 It("exposes the number of depot directories", func() { 141 Expect(vars["depotDirs"]).To(Equal(float64(1))) 142 }) 143 144 It("exposes the number of backing stores", func() { 145 Expect(vars["backingStores"]).To(Equal(float64(1))) 146 }) 147 148 Context("when the container does not have a limit", func() { 149 BeforeEach(func() { 150 diskLimits = garden.DiskLimits{} 151 }) 152 153 It("should not have any backing stores", func() { 154 Expect(vars["depotDirs"]).To(Equal(float64(1))) 155 Expect(vars["backingStores"]).To(Equal(float64(0))) 156 }) 157 }) 158 }) 159 }) 160 161 Describe("graph cleanup flags", func() { 162 var ( 163 layersPath string 164 diffPath string 165 mntPath string 166 nonDefaultRootfsPath string 167 args []string 168 persistentImages []string 169 ) 170 171 numLayersInGraph := func() int { 172 layerFiles, err := ioutil.ReadDir(layersPath) 173 Expect(err).ToNot(HaveOccurred()) 174 diffFiles, err := ioutil.ReadDir(diffPath) 175 Expect(err).ToNot(HaveOccurred()) 176 mntFiles, err := ioutil.ReadDir(mntPath) 177 Expect(err).ToNot(HaveOccurred()) 178 179 numLayerFiles := len(layerFiles) 180 Expect(numLayerFiles).To(Equal(len(diffFiles))) 181 Expect(numLayerFiles).To(Equal(len(mntFiles))) 182 return numLayerFiles 183 } 184 185 expectLayerCountAfterGraphCleanupToBe := func(layerCount int) { 186 nonPersistantRootfsContainer, err := client.Create(garden.ContainerSpec{ 187 RootFSPath: nonDefaultRootfsPath, 188 }) 189 Expect(err).ToNot(HaveOccurred()) 190 Expect(client.Destroy(nonPersistantRootfsContainer.Handle())).To(Succeed()) 191 Expect(numLayersInGraph()).To(Equal(layerCount + 2)) // +2 for the layers created for the nondefaultrootfs container 192 } 193 194 BeforeEach(func() { 195 var err error 196 nonDefaultRootfsPath, err = ioutil.TempDir("", "tmpRootfs") 197 Expect(err).ToNot(HaveOccurred()) 198 }) 199 200 JustBeforeEach(func() { 201 for _, image := range persistentImages { 202 args = append(args, "--persistentImage", image) 203 } 204 client = startGarden(args...) 205 206 layersPath = path.Join(client.GraphPath, "aufs", "layers") 207 diffPath = path.Join(client.GraphPath, "aufs", "diff") 208 mntPath = path.Join(client.GraphPath, "aufs", "mnt") 209 }) 210 211 AfterEach(func() { 212 Expect(os.RemoveAll(nonDefaultRootfsPath)).To(Succeed()) 213 }) 214 215 Describe("--graphCleanupThresholdMB", func() { 216 JustBeforeEach(func() { 217 container, err := client.Create(garden.ContainerSpec{ 218 RootFSPath: "docker:///busybox", 219 }) 220 Expect(err).ToNot(HaveOccurred()) 221 Expect(client.Destroy(container.Handle())).To(Succeed()) 222 }) 223 224 Context("when the graph cleanup threshold is set to -1", func() { 225 BeforeEach(func() { 226 args = []string{"--graphCleanupThresholdMB", "-1"} 227 }) 228 229 It("does NOT clean up the graph directory on create", func() { 230 initialNumberOfLayers := numLayersInGraph() 231 anotherContainer, err := client.Create(garden.ContainerSpec{}) 232 Expect(err).ToNot(HaveOccurred()) 233 234 Expect(numLayersInGraph()).To(BeNumerically(">", initialNumberOfLayers), "after creation, should NOT have deleted anything") 235 Expect(client.Destroy(anotherContainer.Handle())).To(Succeed()) 236 }) 237 }) 238 239 Context("when the graph cleanup threshold is exceeded", func() { 240 BeforeEach(func() { 241 args = []string{"--graphCleanupThresholdMB", "0"} 242 }) 243 244 Context("when there are other rootfs layers in the graph dir", func() { 245 BeforeEach(func() { 246 args = append(args, "--persistentImage", "docker:///busybox") 247 }) 248 249 It("cleans up the graph directory on container creation (and not on destruction)", func() { 250 restartGarden("--graphCleanupThresholdMB=1") // restart with persistent image list empty 251 Expect(numLayersInGraph()).To(BeNumerically(">", 0)) 252 253 anotherContainer, err := client.Create(garden.ContainerSpec{}) 254 Expect(err).ToNot(HaveOccurred()) 255 256 Expect(numLayersInGraph()).To(Equal(3), "after creation, should have deleted everything other than the default rootfs, uid translation layer and container layer") 257 Expect(client.Destroy(anotherContainer.Handle())).To(Succeed()) 258 Expect(numLayersInGraph()).To(Equal(2), "should not garbage collect parent layers on destroy") 259 }) 260 }) 261 }) 262 263 Context("when the graph cleanup threshold is not exceeded", func() { 264 BeforeEach(func() { 265 args = []string{"--graphCleanupThresholdMB", "1024"} 266 }) 267 268 It("does not cleanup", func() { 269 // threshold is not yet exceeded 270 Expect(numLayersInGraph()).To(Equal(3)) 271 272 anotherContainer, err := client.Create(garden.ContainerSpec{}) 273 Expect(err).ToNot(HaveOccurred()) 274 275 Expect(numLayersInGraph()).To(Equal(6)) 276 Expect(client.Destroy(anotherContainer.Handle())).To(Succeed()) 277 }) 278 }) 279 }) 280 281 Describe("--persistentImage", func() { 282 BeforeEach(func() { 283 args = []string{"--graphCleanupThresholdMB", "0"} 284 }) 285 286 Context("when set", func() { 287 JustBeforeEach(func() { 288 Eventually(client, "30s").Should(gbytes.Say("retain.retained")) 289 }) 290 291 Context("and local images are used", func() { 292 BeforeEach(func() { 293 persistentImages = []string{runner.RootFSPath} 294 }) 295 296 Describe("graph cleanup for a rootfs on the whitelist", func() { 297 It("keeps the rootfs in the graph", func() { 298 container, err := client.Create(garden.ContainerSpec{ 299 RootFSPath: persistentImages[0], 300 }) 301 Expect(err).ToNot(HaveOccurred()) 302 Expect(client.Destroy(container.Handle())).To(Succeed()) 303 304 expectLayerCountAfterGraphCleanupToBe(2) 305 }) 306 307 Context("which is a symlink", func() { 308 BeforeEach(func() { 309 Expect(os.MkdirAll("/var/vcap/packages", 0755)).To(Succeed()) 310 err := exec.Command("ln", "-s", runner.RootFSPath, "/var/vcap/packages/busybox").Run() 311 Expect(err).ToNot(HaveOccurred()) 312 313 persistentImages = []string{"/var/vcap/packages/busybox"} 314 }) 315 316 It("keeps the rootfs in the graph", func() { 317 container, err := client.Create(garden.ContainerSpec{ 318 RootFSPath: persistentImages[0], 319 }) 320 Expect(err).ToNot(HaveOccurred()) 321 Expect(client.Destroy(container.Handle())).To(Succeed()) 322 323 expectLayerCountAfterGraphCleanupToBe(2) 324 }) 325 }) 326 }) 327 328 Describe("graph cleanup for a rootfs not on the whitelist", func() { 329 It("cleans up all unused images from the graph", func() { 330 container, err := client.Create(garden.ContainerSpec{ 331 RootFSPath: nonDefaultRootfsPath, 332 }) 333 Expect(err).ToNot(HaveOccurred()) 334 Expect(client.Destroy(container.Handle())).To(Succeed()) 335 336 expectLayerCountAfterGraphCleanupToBe(0) 337 }) 338 }) 339 }) 340 341 Context("and docker images are used", func() { 342 BeforeEach(func() { 343 persistentImages = []string{ 344 "docker:///busybox", 345 "docker:///ubuntu", 346 "docker://banana/bananatest", 347 } 348 }) 349 350 Describe("graph cleanup for a rootfs on the whitelist", func() { 351 It("keeps the rootfs in the graph", func() { 352 numLayersBeforeDockerPull := numLayersInGraph() 353 container, err := client.Create(garden.ContainerSpec{ 354 RootFSPath: persistentImages[0], 355 }) 356 Expect(err).ToNot(HaveOccurred()) 357 Expect(client.Destroy(container.Handle())).To(Succeed()) 358 numLayersInImage := numLayersInGraph() - numLayersBeforeDockerPull 359 360 expectLayerCountAfterGraphCleanupToBe(numLayersInImage) 361 }) 362 }) 363 364 Describe("graph cleanup for a rootfs not on the whitelist", func() { 365 It("cleans up all unused images from the graph", func() { 366 container, err := client.Create(garden.ContainerSpec{ 367 RootFSPath: "docker:///cfgarden/garden-busybox", 368 }) 369 Expect(err).ToNot(HaveOccurred()) 370 Expect(client.Destroy(container.Handle())).To(Succeed()) 371 372 expectLayerCountAfterGraphCleanupToBe(0) 373 }) 374 }) 375 }) 376 }) 377 378 Context("when it is not set", func() { 379 BeforeEach(func() { 380 persistentImages = []string{} 381 }) 382 383 It("cleans up all unused images from the graph", func() { 384 defaultRootfsContainer, err := client.Create(garden.ContainerSpec{}) 385 Expect(err).ToNot(HaveOccurred()) 386 387 nonDefaultRootfsContainer, err := client.Create(garden.ContainerSpec{ 388 RootFSPath: nonDefaultRootfsPath, 389 }) 390 Expect(err).ToNot(HaveOccurred()) 391 392 Expect(client.Destroy(defaultRootfsContainer.Handle())).To(Succeed()) 393 Expect(client.Destroy(nonDefaultRootfsContainer.Handle())).To(Succeed()) 394 395 expectLayerCountAfterGraphCleanupToBe(0) 396 }) 397 }) 398 }) 399 }) 400 })