github.com/cloudfoundry-attic/garden-linux@v0.333.2-candidate/integration/lifecycle/nesting_test.go (about) 1 package lifecycle_test 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 11 . "github.com/onsi/ginkgo" 12 . "github.com/onsi/gomega" 13 "github.com/onsi/gomega/gbytes" 14 "github.com/onsi/gomega/gexec" 15 16 "io/ioutil" 17 18 "github.com/cloudfoundry-incubator/garden" 19 "github.com/cloudfoundry-incubator/garden-linux/integration/runner" 20 gclient "github.com/cloudfoundry-incubator/garden/client" 21 gconn "github.com/cloudfoundry-incubator/garden/client/connection" 22 ) 23 24 var _ = Describe("When nested", func() { 25 nestedRootfsPath := os.Getenv("GARDEN_NESTABLE_TEST_ROOTFS") 26 27 BeforeEach(func() { 28 if nestedRootfsPath == "" { 29 Skip("GARDEN_NESTABLE_TEST_ROOTFS undefined") 30 } 31 32 client = startGarden() 33 }) 34 35 startNestedGarden := func() (garden.Container, string) { 36 absoluteBinPath, err := filepath.Abs(runner.BinPath) 37 Expect(err).ToNot(HaveOccurred()) 38 39 absoluteGardenPath, err := filepath.Abs(runner.GardenBin) 40 Expect(err).ToNot(HaveOccurred()) 41 42 Expect(absoluteBinPath).To(BeADirectory()) 43 Expect(filepath.Join(absoluteBinPath, "..", "skeleton")).To(BeADirectory()) 44 45 container, err := client.Create(garden.ContainerSpec{ 46 RootFSPath: nestedRootfsPath, 47 // only privileged containers support nesting 48 Privileged: true, 49 BindMounts: []garden.BindMount{ 50 { 51 SrcPath: filepath.Dir(absoluteGardenPath), 52 DstPath: "/root/bin/", 53 Mode: garden.BindMountModeRO, 54 }, 55 { 56 SrcPath: absoluteBinPath, 57 DstPath: "/root/binpath/bin", 58 Mode: garden.BindMountModeRO, 59 }, 60 { 61 SrcPath: filepath.Join(absoluteBinPath, "..", "skeleton"), 62 DstPath: "/root/binpath/skeleton", 63 Mode: garden.BindMountModeRO, 64 }, 65 { 66 SrcPath: runner.RootFSPath, 67 DstPath: "/root/rootfs", 68 Mode: garden.BindMountModeRO, 69 }, 70 }, 71 }) 72 Expect(err).ToNot(HaveOccurred()) 73 74 nestedServerOutput := gbytes.NewBuffer() 75 76 // start nested garden, again need to be root 77 _, err = container.Run(garden.ProcessSpec{ 78 Path: "sh", 79 User: "root", 80 Dir: "/root", 81 Args: []string{ 82 "-c", 83 fmt.Sprintf(` 84 set -e 85 86 tmpdir=/tmp/dir 87 rm -fr $tmpdir 88 mkdir $tmpdir 89 mount -t tmpfs none $tmpdir 90 91 mkdir $tmpdir/depot 92 mkdir $tmpdir/snapshots 93 mkdir $tmpdir/state 94 mkdir $tmpdir/graph 95 96 ./bin/garden-linux \ 97 -bin /root/binpath/bin \ 98 -rootfs /root/rootfs \ 99 -depot $tmpdir/depot \ 100 -snapshots $tmpdir/snapshots \ 101 -stateDir $tmpdir/state \ 102 -graph $tmpdir/graph \ 103 -tag n \ 104 -listenNetwork tcp \ 105 -listenAddr 0.0.0.0:7778 106 `), 107 }, 108 }, garden.ProcessIO{ 109 Stdout: io.MultiWriter(nestedServerOutput, gexec.NewPrefixedWriter("\x1b[32m[o]\x1b[34m[nested-garden-linux]\x1b[0m ", GinkgoWriter)), 110 Stderr: gexec.NewPrefixedWriter("\x1b[91m[e]\x1b[34m[nested-garden-linux]\x1b[0m ", GinkgoWriter), 111 }) 112 113 info, err := container.Info() 114 Expect(err).ToNot(HaveOccurred()) 115 116 nestedGardenAddress := fmt.Sprintf("%s:7778", info.ContainerIP) 117 Eventually(nestedServerOutput, "60s").Should(gbytes.Say("garden-linux.started")) 118 119 return container, nestedGardenAddress 120 } 121 122 It("can start a nested garden-linux and run a container inside it", func() { 123 container, nestedGardenAddress := startNestedGarden() 124 defer func() { 125 Expect(client.Destroy(container.Handle())).To(Succeed()) 126 }() 127 128 nestedClient := gclient.New(gconn.New("tcp", nestedGardenAddress)) 129 nestedContainer, err := nestedClient.Create(garden.ContainerSpec{}) 130 Expect(err).ToNot(HaveOccurred()) 131 132 nestedOutput := gbytes.NewBuffer() 133 _, err = nestedContainer.Run(garden.ProcessSpec{ 134 User: "root", 135 Path: "/bin/echo", 136 Args: []string{ 137 "I am nested!", 138 }, 139 }, garden.ProcessIO{Stdout: nestedOutput, Stderr: nestedOutput}) 140 Expect(err).ToNot(HaveOccurred()) 141 142 Eventually(nestedOutput, "60s").Should(gbytes.Say("I am nested!")) 143 }) 144 145 Context("when cgroup limits are applied to the parent garden process", func() { 146 devicesCgroupNode := func() string { 147 contents, err := ioutil.ReadFile("/proc/self/cgroup") 148 Expect(err).ToNot(HaveOccurred()) 149 for _, line := range strings.Split(string(contents), "\n") { 150 if strings.Contains(line, "devices:") { 151 lineParts := strings.Split(line, ":") 152 Expect(lineParts).To(HaveLen(3)) 153 return lineParts[2] 154 } 155 } 156 Fail("could not find devices cgroup node") 157 return "" 158 } 159 160 It("passes on these limits to the child container", func() { 161 // When this test is run in garden (e.g. in Concourse), we cannot create more permissive device cgroups 162 // than are allowed in the outermost container. So we apply this rule to the outermost container's cgroup 163 cmd := exec.Command( 164 "sh", 165 "-c", 166 fmt.Sprintf("echo 'b 7:200 r' > /tmp/garden-%d/cgroup/devices%s/devices.allow", GinkgoParallelNode(), devicesCgroupNode()), 167 ) 168 cmd.Stdout = GinkgoWriter 169 cmd.Stderr = GinkgoWriter 170 Expect(cmd.Run()).To(Succeed()) 171 172 gardenInContainer, nestedGardenAddress := startNestedGarden() 173 defer client.Destroy(gardenInContainer.Handle()) 174 175 postProc, err := gardenInContainer.Run(garden.ProcessSpec{ 176 Path: "bash", 177 User: "root", 178 Args: []string{"-c", 179 ` 180 cgroup_path_segment=$(cat /proc/self/cgroup | grep devices: | cut -d ':' -f 3) 181 echo "b 7:200 r" > /tmp/garden-n/cgroup/devices${cgroup_path_segment}/devices.allow 182 `}, 183 }, garden.ProcessIO{ 184 Stdout: GinkgoWriter, 185 Stderr: GinkgoWriter, 186 }) 187 Expect(err).ToNot(HaveOccurred()) 188 Expect(postProc.Wait()).To(Equal(0)) 189 190 nestedClient := gclient.New(gconn.New("tcp", nestedGardenAddress)) 191 nestedContainer, err := nestedClient.Create(garden.ContainerSpec{ 192 Privileged: true, 193 }) 194 Expect(err).ToNot(HaveOccurred()) 195 196 nestedProcess, err := nestedContainer.Run(garden.ProcessSpec{ 197 User: "root", 198 Path: "sh", 199 Args: []string{"-c", ` 200 mknod ./foo b 7 200 201 cat foo > /dev/null 202 `}, 203 }, garden.ProcessIO{ 204 Stdout: GinkgoWriter, 205 Stderr: GinkgoWriter, 206 }) 207 Expect(err).ToNot(HaveOccurred()) 208 209 Expect(nestedProcess.Wait()).To(Equal(0)) 210 }) 211 }) 212 })