github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/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 "github.com/cloudfoundry-incubator/garden" 13 "github.com/cloudfoundry-incubator/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("--enableGraphCleanup", func() { 216 217 JustBeforeEach(func() { 218 container, err := client.Create(garden.ContainerSpec{ 219 RootFSPath: "docker:///busybox", 220 }) 221 Expect(err).ToNot(HaveOccurred()) 222 Expect(client.Destroy(container.Handle())).To(Succeed()) 223 }) 224 225 Context("when starting without the flag", func() { 226 BeforeEach(func() { 227 args = []string{"-enableGraphCleanup=false"} 228 }) 229 230 It("does NOT clean up the graph directory on create", func() { 231 initialNumberOfLayers := numLayersInGraph() 232 anotherContainer, err := client.Create(garden.ContainerSpec{}) 233 Expect(err).ToNot(HaveOccurred()) 234 235 Expect(numLayersInGraph()).To(BeNumerically(">", initialNumberOfLayers), "after creation, should NOT have deleted anything") 236 Expect(client.Destroy(anotherContainer.Handle())).To(Succeed()) 237 }) 238 }) 239 240 Context("when starting with the flag", func() { 241 BeforeEach(func() { 242 args = []string{"-enableGraphCleanup=true"} 243 }) 244 245 Context("when there are other rootfs layers in the graph dir", func() { 246 BeforeEach(func() { 247 args = append(args, "-persistentImage", "docker:///busybox") 248 }) 249 250 It("cleans up the graph directory on container creation (and not on destruction)", func() { 251 restartGarden("-enableGraphCleanup=true") // restart with persistent image list empty 252 Expect(numLayersInGraph()).To(BeNumerically(">", 0)) 253 254 anotherContainer, err := client.Create(garden.ContainerSpec{}) 255 Expect(err).ToNot(HaveOccurred()) 256 257 Expect(numLayersInGraph()).To(Equal(3), "after creation, should have deleted everything other than the default rootfs, uid translation layer and container layer") 258 Expect(client.Destroy(anotherContainer.Handle())).To(Succeed()) 259 Expect(numLayersInGraph()).To(Equal(2), "should not garbage collect parent layers on destroy") 260 }) 261 }) 262 }) 263 }) 264 265 Describe("--persistentImage", func() { 266 BeforeEach(func() { 267 args = []string{"-enableGraphCleanup=true"} 268 }) 269 270 Context("when set", func() { 271 JustBeforeEach(func() { 272 Eventually(client, "30s").Should(gbytes.Say("retain.retained")) 273 }) 274 275 Context("and local images are used", func() { 276 BeforeEach(func() { 277 persistentImages = []string{runner.RootFSPath} 278 }) 279 280 Describe("graph cleanup for a rootfs on the whitelist", func() { 281 It("keeps the rootfs in the graph", func() { 282 container, err := client.Create(garden.ContainerSpec{ 283 RootFSPath: persistentImages[0], 284 }) 285 Expect(err).ToNot(HaveOccurred()) 286 Expect(client.Destroy(container.Handle())).To(Succeed()) 287 288 expectLayerCountAfterGraphCleanupToBe(2) 289 }) 290 291 Context("which is a symlink", func() { 292 BeforeEach(func() { 293 Expect(os.MkdirAll("/var/vcap/packages", 0755)).To(Succeed()) 294 err := exec.Command("ln", "-s", runner.RootFSPath, "/var/vcap/packages/busybox").Run() 295 Expect(err).ToNot(HaveOccurred()) 296 297 persistentImages = []string{"/var/vcap/packages/busybox"} 298 }) 299 300 It("keeps the rootfs in the graph", func() { 301 container, err := client.Create(garden.ContainerSpec{ 302 RootFSPath: persistentImages[0], 303 }) 304 Expect(err).ToNot(HaveOccurred()) 305 Expect(client.Destroy(container.Handle())).To(Succeed()) 306 307 expectLayerCountAfterGraphCleanupToBe(2) 308 }) 309 }) 310 }) 311 312 Describe("graph cleanup for a rootfs not on the whitelist", func() { 313 It("cleans up all unused images from the graph", func() { 314 container, err := client.Create(garden.ContainerSpec{ 315 RootFSPath: nonDefaultRootfsPath, 316 }) 317 Expect(err).ToNot(HaveOccurred()) 318 Expect(client.Destroy(container.Handle())).To(Succeed()) 319 320 expectLayerCountAfterGraphCleanupToBe(0) 321 }) 322 }) 323 }) 324 325 Context("and docker images are used", func() { 326 BeforeEach(func() { 327 persistentImages = []string{ 328 "docker:///busybox", 329 "docker:///ubuntu", 330 "docker://banana/bananatest", 331 } 332 }) 333 334 Describe("graph cleanup for a rootfs on the whitelist", func() { 335 It("keeps the rootfs in the graph", func() { 336 numLayersBeforeDockerPull := numLayersInGraph() 337 container, err := client.Create(garden.ContainerSpec{ 338 RootFSPath: persistentImages[0], 339 }) 340 Expect(err).ToNot(HaveOccurred()) 341 Expect(client.Destroy(container.Handle())).To(Succeed()) 342 numLayersInImage := numLayersInGraph() - numLayersBeforeDockerPull 343 344 expectLayerCountAfterGraphCleanupToBe(numLayersInImage) 345 }) 346 }) 347 348 Describe("graph cleanup for a rootfs not on the whitelist", func() { 349 It("cleans up all unused images from the graph", func() { 350 container, err := client.Create(garden.ContainerSpec{ 351 RootFSPath: "docker:///cfgarden/garden-busybox", 352 }) 353 Expect(err).ToNot(HaveOccurred()) 354 Expect(client.Destroy(container.Handle())).To(Succeed()) 355 356 expectLayerCountAfterGraphCleanupToBe(0) 357 }) 358 }) 359 }) 360 }) 361 362 Context("when it is not set", func() { 363 BeforeEach(func() { 364 persistentImages = []string{} 365 }) 366 367 It("cleans up all unused images from the graph", func() { 368 defaultRootfsContainer, err := client.Create(garden.ContainerSpec{}) 369 Expect(err).ToNot(HaveOccurred()) 370 371 nonDefaultRootfsContainer, err := client.Create(garden.ContainerSpec{ 372 RootFSPath: nonDefaultRootfsPath, 373 }) 374 Expect(err).ToNot(HaveOccurred()) 375 376 Expect(client.Destroy(defaultRootfsContainer.Handle())).To(Succeed()) 377 Expect(client.Destroy(nonDefaultRootfsContainer.Handle())).To(Succeed()) 378 379 expectLayerCountAfterGraphCleanupToBe(0) 380 }) 381 }) 382 }) 383 }) 384 })