Skip to content
This repository was archived by the owner on Mar 9, 2022. It is now read-only.

Commit fa28558

Browse files
authored
Merge pull request #67 from Random-Liu/add-sandbox-dev-shm
Add sandbox dev shm
2 parents e9a930b + 9d5990f commit fa28558

File tree

7 files changed

+265
-24
lines changed

7 files changed

+265
-24
lines changed

pkg/os/os.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import (
2121
"io/ioutil"
2222
"os"
2323

24-
"golang.org/x/net/context"
25-
2624
"github.com/tonistiigi/fifo"
25+
"golang.org/x/net/context"
26+
"golang.org/x/sys/unix"
2727
)
2828

2929
// OS collects system level operations that need to be mocked out
@@ -35,6 +35,8 @@ type OS interface {
3535
Stat(name string) (os.FileInfo, error)
3636
CopyFile(src, dest string, perm os.FileMode) error
3737
WriteFile(filename string, data []byte, perm os.FileMode) error
38+
Mount(source string, target string, fstype string, flags uintptr, data string) error
39+
Unmount(target string, flags int) error
3840
}
3941

4042
// RealOS is used to dispatch the real system level operations.
@@ -82,3 +84,13 @@ func (RealOS) CopyFile(src, dest string, perm os.FileMode) error {
8284
func (RealOS) WriteFile(filename string, data []byte, perm os.FileMode) error {
8385
return ioutil.WriteFile(filename, data, perm)
8486
}
87+
88+
// Mount will call unix.Mount to mount the file.
89+
func (RealOS) Mount(source string, target string, fstype string, flags uintptr, data string) error {
90+
return unix.Mount(source, target, fstype, flags, data)
91+
}
92+
93+
// Unmount will call unix.Unmount to unmount the file.
94+
func (RealOS) Unmount(target string, flags int) error {
95+
return unix.Unmount(target, flags)
96+
}

pkg/os/testing/fake_os.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ import (
2626
osInterface "github.com/kubernetes-incubator/cri-containerd/pkg/os"
2727
)
2828

29+
// CalledDetail is the struct contains called function name and arguments.
30+
type CalledDetail struct {
31+
// Name of the function called.
32+
Name string
33+
// Arguments of the function called.
34+
Arguments []interface{}
35+
}
36+
2937
// FakeOS mocks out certain OS calls to avoid perturbing the filesystem
3038
// If a member of the form `*Fn` is set, that function will be called in place
3139
// of the real call.
@@ -37,6 +45,9 @@ type FakeOS struct {
3745
StatFn func(string) (os.FileInfo, error)
3846
CopyFileFn func(string, string, os.FileMode) error
3947
WriteFileFn func(string, []byte, os.FileMode) error
48+
MountFn func(source string, target string, fstype string, flags uintptr, data string) error
49+
UnmountFn func(target string, flags int) error
50+
calls []CalledDetail
4051
errors map[string]error
4152
}
4253

@@ -77,6 +88,19 @@ func (f *FakeOS) ClearErrors() {
7788
f.errors = make(map[string]error)
7889
}
7990

91+
func (f *FakeOS) appendCalls(name string, args ...interface{}) {
92+
f.Lock()
93+
defer f.Unlock()
94+
f.calls = append(f.calls, CalledDetail{Name: name, Arguments: args})
95+
}
96+
97+
// GetCalls get detail of calls.
98+
func (f *FakeOS) GetCalls() []CalledDetail {
99+
f.Lock()
100+
defer f.Unlock()
101+
return append([]CalledDetail{}, f.calls...)
102+
}
103+
80104
// NewFakeOS creates a FakeOS.
81105
func NewFakeOS() *FakeOS {
82106
return &FakeOS{
@@ -86,6 +110,7 @@ func NewFakeOS() *FakeOS {
86110

87111
// MkdirAll is a fake call that invokes MkdirAllFn or just returns nil.
88112
func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error {
113+
f.appendCalls("MkdirAll", path, perm)
89114
if err := f.getError("MkdirAll"); err != nil {
90115
return err
91116
}
@@ -98,6 +123,7 @@ func (f *FakeOS) MkdirAll(path string, perm os.FileMode) error {
98123

99124
// RemoveAll is a fake call that invokes RemoveAllFn or just returns nil.
100125
func (f *FakeOS) RemoveAll(path string) error {
126+
f.appendCalls("RemoveAll", path)
101127
if err := f.getError("RemoveAll"); err != nil {
102128
return err
103129
}
@@ -110,6 +136,7 @@ func (f *FakeOS) RemoveAll(path string) error {
110136

111137
// OpenFifo is a fake call that invokes OpenFifoFn or just returns nil.
112138
func (f *FakeOS) OpenFifo(ctx context.Context, fn string, flag int, perm os.FileMode) (io.ReadWriteCloser, error) {
139+
f.appendCalls("OpenFifo", ctx, fn, flag, perm)
113140
if err := f.getError("OpenFifo"); err != nil {
114141
return nil, err
115142
}
@@ -122,6 +149,7 @@ func (f *FakeOS) OpenFifo(ctx context.Context, fn string, flag int, perm os.File
122149

123150
// Stat is a fake call that invokes StatFn or just return nil.
124151
func (f *FakeOS) Stat(name string) (os.FileInfo, error) {
152+
f.appendCalls("Stat", name)
125153
if err := f.getError("Stat"); err != nil {
126154
return nil, err
127155
}
@@ -134,6 +162,7 @@ func (f *FakeOS) Stat(name string) (os.FileInfo, error) {
134162

135163
// CopyFile is a fake call that invokes CopyFileFn or just return nil.
136164
func (f *FakeOS) CopyFile(src, dest string, perm os.FileMode) error {
165+
f.appendCalls("CopyFile", src, dest, perm)
137166
if err := f.getError("CopyFile"); err != nil {
138167
return err
139168
}
@@ -146,6 +175,7 @@ func (f *FakeOS) CopyFile(src, dest string, perm os.FileMode) error {
146175

147176
// WriteFile is a fake call that invokes WriteFileFn or just return nil.
148177
func (f *FakeOS) WriteFile(filename string, data []byte, perm os.FileMode) error {
178+
f.appendCalls("WriteFile", filename, data, perm)
149179
if err := f.getError("WriteFile"); err != nil {
150180
return err
151181
}
@@ -155,3 +185,29 @@ func (f *FakeOS) WriteFile(filename string, data []byte, perm os.FileMode) error
155185
}
156186
return nil
157187
}
188+
189+
// Mount is a fake call that invokes MountFn or just return nil.
190+
func (f *FakeOS) Mount(source string, target string, fstype string, flags uintptr, data string) error {
191+
f.appendCalls("Mount", source, target, fstype, flags, data)
192+
if err := f.getError("Mount"); err != nil {
193+
return err
194+
}
195+
196+
if f.MountFn != nil {
197+
return f.MountFn(source, target, fstype, flags, data)
198+
}
199+
return nil
200+
}
201+
202+
// Unmount is a fake call that invokes UnmountFn or just return nil.
203+
func (f *FakeOS) Unmount(target string, flags int) error {
204+
f.appendCalls("Unmount", target, flags)
205+
if err := f.getError("Unmount"); err != nil {
206+
return err
207+
}
208+
209+
if f.UnmountFn != nil {
210+
return f.UnmountFn(target, flags)
211+
}
212+
return nil
213+
}

pkg/server/container_start.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,15 @@ func (c *criContainerdService) generateContainerMounts(sandboxRootDir string, co
315315
Readonly: securityContext.GetReadonlyRootfs(),
316316
})
317317

318-
// TODO(random-liu): [P0] Mount sandbox /dev/shm.
318+
sandboxDevShm := getSandboxDevShm(sandboxRootDir)
319+
if securityContext.GetNamespaceOptions().GetHostIpc() {
320+
sandboxDevShm = devShm
321+
}
322+
mounts = append(mounts, &runtime.Mount{
323+
ContainerPath: devShm,
324+
HostPath: sandboxDevShm,
325+
Readonly: false,
326+
})
319327
return mounts
320328
}
321329

@@ -416,6 +424,8 @@ func addOCIDevices(g *generate.Generator, devs []*runtime.Device, privileged boo
416424
}
417425

418426
// addOCIBindMounts adds bind mounts.
427+
// TODO(random-liu): Figure out whether we need to change all CRI mounts to readonly when
428+
// rootfs is readonly. (https://github.com/moby/moby/blob/master/daemon/oci_linux.go)
419429
func addOCIBindMounts(g *generate.Generator, mounts []*runtime.Mount, privileged bool) {
420430
for _, mount := range mounts {
421431
dst := mount.GetContainerPath()

pkg/server/container_start_test.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,11 @@ func TestGenerateContainerMounts(t *testing.T) {
324324
HostPath: testSandboxRootDir + "/resolv.conf",
325325
Readonly: true,
326326
},
327+
{
328+
ContainerPath: "/dev/shm",
329+
HostPath: testSandboxRootDir + "/shm",
330+
Readonly: false,
331+
},
327332
},
328333
},
329334
"should setup rw mount when rootfs is read-write": {
@@ -336,7 +341,34 @@ func TestGenerateContainerMounts(t *testing.T) {
336341
},
337342
{
338343
ContainerPath: resolvConfPath,
339-
HostPath: getResolvPath(testSandboxRootDir),
344+
HostPath: testSandboxRootDir + "/resolv.conf",
345+
Readonly: false,
346+
},
347+
{
348+
ContainerPath: "/dev/shm",
349+
HostPath: testSandboxRootDir + "/shm",
350+
Readonly: false,
351+
},
352+
},
353+
},
354+
"should use host /dev/shm when host ipc is set": {
355+
securityContext: &runtime.LinuxContainerSecurityContext{
356+
NamespaceOptions: &runtime.NamespaceOption{HostIpc: true},
357+
},
358+
expectedMounts: []*runtime.Mount{
359+
{
360+
ContainerPath: "/etc/hosts",
361+
HostPath: testSandboxRootDir + "/hosts",
362+
Readonly: false,
363+
},
364+
{
365+
ContainerPath: resolvConfPath,
366+
HostPath: testSandboxRootDir + "/resolv.conf",
367+
Readonly: false,
368+
},
369+
{
370+
ContainerPath: "/dev/shm",
371+
HostPath: "/dev/shm",
340372
Readonly: false,
341373
},
342374
},

pkg/server/helpers.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ const (
5858
// defaultSandboxImage is the image used by sandbox container.
5959
// TODO(random-liu): [P1] Build schema 2 pause image and use it here.
6060
defaultSandboxImage = "gcr.io/google.com/noogler-kubernetes/pause-amd64:3.0"
61+
// defaultShmSize is the default size of the sandbox shm.
62+
defaultShmSize = int64(1024 * 1024 * 64)
6163
// relativeRootfsPath is the rootfs path relative to bundle path.
6264
relativeRootfsPath = "rootfs"
6365
// defaultRuntime is the runtime to use in containerd. We may support
@@ -88,6 +90,8 @@ const (
8890
utsNSFormat = "/proc/%v/ns/uts"
8991
// pidNSFormat is the format of pid namespace of a process.
9092
pidNSFormat = "/proc/%v/ns/pid"
93+
// devShm is the default path of /dev/shm.
94+
devShm = "/dev/shm"
9195
// etcHosts is the default path of /etc/hosts file.
9296
etcHosts = "/etc/hosts"
9397
// resolvConfPath is the abs path of resolv.conf on host or container.
@@ -159,6 +163,11 @@ func getResolvPath(sandboxRoot string) string {
159163
return filepath.Join(sandboxRoot, "resolv.conf")
160164
}
161165

166+
// getSandboxDevShm returns the shm file path inside the sandbox root directory.
167+
func getSandboxDevShm(sandboxRootDir string) string {
168+
return filepath.Join(sandboxRootDir, "shm")
169+
}
170+
162171
// prepareStreamingPipes prepares stream named pipe for container. returns nil
163172
// streaming handler if corresponding stream path is empty.
164173
func (c *criContainerdService) prepareStreamingPipes(ctx context.Context, stdin, stdout, stderr string) (

pkg/server/sandbox_run.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package server
1919
import (
2020
"encoding/json"
2121
"fmt"
22+
"os"
2223
"strings"
2324
"time"
2425

@@ -31,6 +32,7 @@ import (
3132
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
3233
"github.com/opencontainers/runtime-tools/generate"
3334
"golang.org/x/net/context"
35+
"golang.org/x/sys/unix"
3436
runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/v1alpha1"
3537

3638
"github.com/kubernetes-incubator/cri-containerd/pkg/metadata"
@@ -137,8 +139,11 @@ func (c *criContainerdService) RunPodSandbox(ctx context.Context, r *runtime.Run
137139
if err = c.setupSandboxFiles(sandboxRootDir, config); err != nil {
138140
return nil, fmt.Errorf("failed to setup sandbox files: %v", err)
139141
}
140-
// No need to cleanup on error, because the whole sandbox root directory will be removed
141-
// on error.
142+
defer func() {
143+
if retErr != nil {
144+
c.cleanupSandboxFiles(sandboxRootDir, config)
145+
}
146+
}()
142147

143148
// Start sandbox container.
144149
spec, err := c.generateSandboxContainerSpec(id, config, imageMeta.Config)
@@ -301,7 +306,7 @@ func (c *criContainerdService) generateSandboxContainerSpec(id string, config *r
301306
func (c *criContainerdService) setupSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) error {
302307
// TODO(random-liu): Consider whether we should maintain /etc/hosts and /etc/resolv.conf in kubelet.
303308
sandboxEtcHosts := getSandboxHosts(rootDir)
304-
if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0666); err != nil {
309+
if err := c.os.CopyFile(etcHosts, sandboxEtcHosts, 0644); err != nil {
305310
return fmt.Errorf("failed to generate sandbox hosts file %q: %v", sandboxEtcHosts, err)
306311
}
307312

@@ -328,8 +333,22 @@ func (c *criContainerdService) setupSandboxFiles(rootDir string, config *runtime
328333
}
329334
}
330335

331-
// TODO(random-liu): [P0] Deal with /dev/shm. Use host for HostIpc, and create and mount for
332-
// non-HostIpc. What about mqueue?
336+
// Setup sandbox /dev/shm.
337+
if config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() {
338+
if _, err := c.os.Stat(devShm); err != nil {
339+
return fmt.Errorf("host %q is not available for host ipc: %v", devShm, err)
340+
}
341+
} else {
342+
sandboxDevShm := getSandboxDevShm(rootDir)
343+
if err := c.os.MkdirAll(sandboxDevShm, 0700); err != nil {
344+
return fmt.Errorf("failed to create sandbox shm: %v", err)
345+
}
346+
shmproperty := fmt.Sprintf("mode=1777,size=%d", defaultShmSize)
347+
if err := c.os.Mount("shm", sandboxDevShm, "tmpfs", uintptr(unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV), shmproperty); err != nil {
348+
return fmt.Errorf("failed to mount sandbox shm: %v", err)
349+
}
350+
}
351+
333352
return nil
334353
}
335354

@@ -356,3 +375,13 @@ func parseDNSOptions(servers, searches, options []string) (string, error) {
356375

357376
return resolvContent, nil
358377
}
378+
379+
// cleanupSandboxFiles only unmount files, we rely on the removal of sandbox root directory to remove files.
380+
// Each cleanup task should log error instead of returning, so as to keep on cleanup on error.
381+
func (c *criContainerdService) cleanupSandboxFiles(rootDir string, config *runtime.PodSandboxConfig) {
382+
if !config.GetLinux().GetSecurityContext().GetNamespaceOptions().GetHostIpc() {
383+
if err := c.os.Unmount(getSandboxDevShm(rootDir), unix.MNT_DETACH); err != nil && os.IsNotExist(err) {
384+
glog.Errorf("failed to unmount sandbox shm: %v", err)
385+
}
386+
}
387+
}

0 commit comments

Comments
 (0)