import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/opencontainers/runtime-spec/specs-go" "code.cloudfoundry.org/garden" "code.cloudfoundry.org/guardian/gardener" "code.cloudfoundry.org/guardian/rundmc/bundlerules" "code.cloudfoundry.org/guardian/rundmc/goci" ) var _ = Describe("BindMountsRule", func() { var newBndl goci.Bndl BeforeEach(func() { newBndl = bundlerules.BindMounts{}.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ BindMounts: []garden.BindMount{ { SrcPath: "/path/to/ro/src", DstPath: "/path/to/ro/dest", Mode: garden.BindMountModeRO, }, { SrcPath: "/path/to/rw/src", DstPath: "/path/to/rw/dest", Mode: garden.BindMountModeRW, }, }, }) })
package goci_test import ( "code.cloudfoundry.org/guardian/rundmc/goci" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/opencontainers/runtime-spec/specs-go" ) var _ = Describe("Bundle", func() { var initialBundle goci.Bndl var returnedBundle goci.Bndl BeforeEach(func() { initialBundle = goci.Bundle() }) It("specifies the correct version", func() { Expect(initialBundle.Spec.Version).To(Equal("0.2.0")) }) Describe("WithHostname", func() { It("sets the Hostname in the bundle", func() { returnedBundle := initialBundle.WithHostname("hostname") Expect(returnedBundle.Hostname()).To(Equal("hostname")) }) }) Describe("WithCapabilities", func() { It("adds capabilities to the bundle", func() { returnedBundle := initialBundle.WithCapabilities("growtulips", "waterspuds")
Context("when removing bundle from depot fails", func() { BeforeEach(func() { fakeDepot.DestroyReturns(errors.New("destroy failed")) }) It("returns an error", func() { Expect(containerizer.RemoveBundle(logger, "some-handle")).To(MatchError(ContainSubstring("destroy failed"))) }) }) }) Describe("Info", func() { BeforeEach(func() { fakeBundleLoader.LoadStub = func(bundlePath string) (goci.Bndl, error) { if bundlePath != "/path/to/some-handle" { return goci.Bundle(), errors.New("cannot find bundle") } var limit uint64 = 10 var shares uint64 = 20 return goci.Bndl{ Spec: specs.Spec{ Linux: &specs.Linux{ Resources: &specs.LinuxResources{ Memory: &specs.LinuxMemory{ Limit: &limit, }, CPU: &specs.LinuxCPU{ Shares: &shares, }, },
func (cmd *GuardianCommand) wireContainerizer(log lager.Logger, depotPath, dadooPath, runcPath, nstarPath, tarPath, defaultRootFSPath, appArmorProfile string, properties gardener.PropertyManager) *rundmc.Containerizer { depot := depot.New(depotPath) commandRunner := linux_command_runner.New() chrootMkdir := bundlerules.ChrootMkdir{ Command: preparerootfs.Command, CommandRunner: commandRunner, } pidFileReader := &dadoo.PidFileReader{ Clock: clock.NewClock(), Timeout: 10 * time.Second, SleepInterval: time.Millisecond * 100, } runcrunner := runrunc.New( commandRunner, runrunc.NewLogRunner(commandRunner, runrunc.LogDir(os.TempDir()).GenerateLogFile), goci.RuncBinary(runcPath), dadooPath, runcPath, runrunc.NewExecPreparer(&goci.BndlLoader{}, runrunc.LookupFunc(runrunc.LookupUser), chrootMkdir, NonRootMaxCaps), dadoo.NewExecRunner( dadooPath, runcPath, cmd.wireUidGenerator(), pidFileReader, linux_command_runner.New()), ) mounts := []specs.Mount{ {Type: "sysfs", Source: "sysfs", Destination: "/sys", Options: []string{"nosuid", "noexec", "nodev", "ro"}}, {Type: "tmpfs", Source: "tmpfs", Destination: "/dev/shm"}, {Type: "devpts", Source: "devpts", Destination: "/dev/pts", Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620"}}, {Type: "bind", Source: cmd.Bin.Init.Path(), Destination: "/tmp/garden-init", Options: []string{"bind"}}, } privilegedMounts := append(mounts, specs.Mount{Type: "proc", Source: "proc", Destination: "/proc", Options: []string{"nosuid", "noexec", "nodev"}}, ) unprivilegedMounts := append(mounts, specs.Mount{Type: "proc", Source: "proc", Destination: "/proc", Options: []string{"nosuid", "noexec", "nodev"}}, ) rwm := "rwm" character := "c" var majorMinor = func(i int64) *int64 { return &i } var worldReadWrite os.FileMode = 0666 fuseDevice := specs.LinuxDevice{ Path: "/dev/fuse", Type: "c", Major: 10, Minor: 229, FileMode: &worldReadWrite, } denyAll := specs.LinuxDeviceCgroup{Allow: false, Access: &rwm} allowedDevices := []specs.LinuxDeviceCgroup{ {Access: &rwm, Type: &character, Major: majorMinor(1), Minor: majorMinor(3), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(5), Minor: majorMinor(0), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(1), Minor: majorMinor(8), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(1), Minor: majorMinor(9), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(1), Minor: majorMinor(5), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(1), Minor: majorMinor(7), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(1), Minor: majorMinor(7), Allow: true}, {Access: &rwm, Type: &character, Major: majorMinor(fuseDevice.Major), Minor: majorMinor(fuseDevice.Minor), Allow: true}, } baseProcess := specs.Process{ Capabilities: UnprivilegedMaxCaps, Args: []string{"/tmp/garden-init"}, Cwd: "/", } baseBundle := goci.Bundle(). WithNamespaces(PrivilegedContainerNamespaces...). WithResources(&specs.LinuxResources{Devices: append([]specs.LinuxDeviceCgroup{denyAll}, allowedDevices...)}). WithRootFS(defaultRootFSPath). WithDevices(fuseDevice). WithProcess(baseProcess) unprivilegedBundle := baseBundle. WithNamespace(goci.UserNamespace). WithUIDMappings(idMappings...). WithGIDMappings(idMappings...). WithMounts(unprivilegedMounts...). WithMaskedPaths(defaultMaskedPaths()) unprivilegedBundle.Spec.Linux.Seccomp = seccomp if appArmorProfile != "" { unprivilegedBundle.Spec.Process.ApparmorProfile = appArmorProfile } privilegedBundle := baseBundle. WithMounts(privilegedMounts...). WithCapabilities(PrivilegedMaxCaps...) template := &rundmc.BundleTemplate{ Rules: []rundmc.BundlerRule{ bundlerules.Base{ PrivilegedBase: privilegedBundle, UnprivilegedBase: unprivilegedBundle, }, bundlerules.RootFS{ ContainerRootUID: idMappings.Map(0), ContainerRootGID: idMappings.Map(0), MkdirChown: chrootMkdir, }, bundlerules.Limits{}, bundlerules.BindMounts{}, bundlerules.Env{}, bundlerules.Hostname{}, }, } log.Info("base-bundles", lager.Data{ "privileged": privilegedBundle, "unprivileged": unprivilegedBundle, }) eventStore := rundmc.NewEventStore(properties) stateStore := rundmc.NewStateStore(properties) nstar := rundmc.NewNstarRunner(nstarPath, tarPath, linux_command_runner.New()) stopper := stopper.New(stopper.NewRuncStateCgroupPathResolver("/run/runc"), nil, retrier.New(retrier.ConstantBackoff(10, 1*time.Second), nil)) return rundmc.New(depot, template, runcrunner, &goci.BndlLoader{}, nstar, stopper, eventStore, stateStore) }
Command: func(rootfsPath string, uid, gid int, mode os.FileMode, recreate bool, paths ...string) *exec.Cmd { return exec.Command("reexeced-thing", append( []string{ "-rootfsPath", rootfsPath, "-uid", strconv.Itoa(uid), "-gid", strconv.Itoa(gid), "-recreate", fmt.Sprintf("%t", recreate), "-perm", strconv.FormatUint(uint64(mode.Perm()), 8), }, paths...)...) }, CommandRunner: commandRunner, }, } returnedBundle = rule.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ RootFSPath: rootfsPath, Privileged: privileged, }) }) AfterEach(func() { Expect(os.RemoveAll(rootfsPath)).To(Succeed()) }) It("applies the rootfs to the passed bundle", func() { Expect(returnedBundle.Spec.Root.Path).To(Equal(rootfsPath)) }) Describe("creating needed directories", func() { Context("when the container is privileged", func() {
package bundlerules_test import ( "strings" "code.cloudfoundry.org/guardian/gardener" "code.cloudfoundry.org/guardian/rundmc/bundlerules" "code.cloudfoundry.org/guardian/rundmc/goci" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Hostname", func() { It("sets the correct hostname in the bundle", func() { newBndl := bundlerules.Hostname{}.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ Hostname: "banana", }) Expect(newBndl.Hostname()).To(Equal("banana")) }) Context("when the hostname is longer than 49 characters", func() { It("should use the last 49 characters of it", func() { newBndl := bundlerules.Hostname{}.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ Hostname: strings.Repeat("banana", 9), }) Expect(newBndl.Hostname()).To(Equal("a" + strings.Repeat("banana", 8))) }) }) })
preparer runrunc.ExecPreparer ) BeforeEach(func() { logger = lagertest.NewTestLogger("test") bundleLoader = new(fakes.FakeBundleLoader) users = new(fakes.FakeUserLookupper) mkdirer = new(fakes.FakeMkdirer) var err error bundlePath, err = ioutil.TempDir("", "bundle") Expect(err).NotTo(HaveOccurred()) bundleLoader.LoadStub = func(path string) (goci.Bndl, error) { bndl := goci.Bundle() return bndl, nil } users.LookupReturns(&user.ExecUser{}, nil) Expect(ioutil.WriteFile(filepath.Join(bundlePath, "pidfile"), []byte("999"), 0644)).To(Succeed()) preparer = runrunc.NewExecPreparer(bundleLoader, users, mkdirer, []string{"foo", "bar", "brains"}) }) It("passes a process.json with the correct path and args", func() { spec, err := preparer.Prepare(logger, bundlePath, garden.ProcessSpec{Path: "to enlightenment", Args: []string{"infinity", "and beyond"}}) Expect(err).NotTo(HaveOccurred()) Expect(spec.Args).To(Equal([]string{"to enlightenment", "infinity", "and beyond"})) })
package bundlerules_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/opencontainers/runtime-spec/specs-go" "code.cloudfoundry.org/garden" "code.cloudfoundry.org/guardian/gardener" "code.cloudfoundry.org/guardian/rundmc/bundlerules" "code.cloudfoundry.org/guardian/rundmc/goci" ) var _ = Describe("LimitsRule", func() { It("sets the correct memory limit in bundle resources", func() { newBndl := bundlerules.Limits{}.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ Limits: garden.Limits{ Memory: garden.MemoryLimits{LimitInBytes: 4096}, }, }) Expect(*(newBndl.Resources().Memory.Limit)).To(BeNumerically("==", 4096)) Expect(*(newBndl.Resources().Memory.Swap)).To(BeNumerically("==", 4096)) }) It("sets the correct CPU limit in bundle resources", func() { newBndl := bundlerules.Limits{}.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ Limits: garden.Limits{ CPU: garden.CPULimits{LimitInShares: 1}, }, })
import ( "code.cloudfoundry.org/guardian/gardener" "code.cloudfoundry.org/guardian/rundmc/bundlerules" "code.cloudfoundry.org/guardian/rundmc/goci" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Env Rule", func() { var ( newBndl goci.Bndl rule bundlerules.Env ) JustBeforeEach(func() { rule = bundlerules.Env{} newBndl = rule.Apply(goci.Bundle(), gardener.DesiredContainerSpec{ Env: []string{ "TEST=banana", "CONTAINER_NAME=hello", }, }) }) It("sets the environment onto the bundle process", func() { Expect(newBndl.Spec.Process.Env).To(Equal([]string{ "TEST=banana", "CONTAINER_NAME=hello", })) }) })