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  })