fn: fixup temp dir read/write permissions if tmp fs size is not set. (#1024)

When TmpFsSize is not set in a route, docker fails to create a /tmp
mount that is writable. Forcing docker to explicitly to this if
read-only root directory is enabled (default).
This commit is contained in:
Tolga Ceylan
2018-06-01 09:25:51 -07:00
committed by Chad Arimura
parent 316940285d
commit f97b63f878
2 changed files with 107 additions and 8 deletions

View File

@@ -632,10 +632,106 @@ func TestGetCallReturnsResourceImpossibility(t *testing.T) {
}
}
func TestTmpFsSize(t *testing.T) {
// TODO it may be a good idea to mock out the http server and use a real
// response writer with sync, and also test that this works with async + log
//
// Tmp directory should be RW by default.
//
func TestTmpFsRW(t *testing.T) {
appName := "myapp"
path := "/hello"
url := "http://127.0.0.1:8080/r/" + appName + path
app := &models.App{Name: appName}
app.SetDefaults()
// we need to load in app & route so that FromRequest works
ds := datastore.NewMockInit(
[]*models.App{app},
[]*models.Route{
{
Path: path,
AppID: app.ID,
Image: "fnproject/fn-test-utils",
Type: "sync",
Format: "http", // this _is_ the test
Timeout: 5,
IdleTimeout: 10,
Memory: 64,
},
},
)
a := New(NewDirectDataAccess(ds, ds, new(mqs.Mock)))
defer checkClose(t, a)
// Here we tell fn-test-utils to read file /proc/mounts and create a /tmp/salsa of 4MB
bodOne := `{"readFile":"/proc/mounts", "createFile":"/tmp/salsa", "createFileSize": 4194304, "isDebug": true}`
req, err := http.NewRequest("GET", url, &dummyReader{Reader: strings.NewReader(bodOne)})
if err != nil {
t.Fatal("unexpected error building request", err)
}
var out bytes.Buffer
callI, err := a.GetCall(FromRequest(a, app, path, req), WithWriter(&out))
if err != nil {
t.Fatal(err)
}
err = a.Submit(callI)
if err != nil {
t.Error("submit should not error:", err)
}
// we're using http format so this will have written a whole http request
res, err := http.ReadResponse(bufio.NewReader(&out), nil)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
// Let's fetch read output and write results. See fn-test-utils AppResponse struct (data field)
var resp struct {
R struct {
MountsRead string `json:"/proc/mounts.read_output"`
CreateFile string `json:"/tmp/salsa.create_error"`
} `json:"data"`
}
json.NewDecoder(res.Body).Decode(&resp)
// Let's check what mounts are on...
mounts := strings.Split(resp.R.MountsRead, "\n")
isFound := false
isRootFound := false
for _, mnt := range mounts {
tokens := strings.Split(mnt, " ")
if len(tokens) < 3 {
continue
}
point := tokens[1]
opts := tokens[3]
// tmp dir with RW and no other options (size, inodes, etc.)
if point == "/tmp" && opts == "rw,nosuid,nodev,noexec,relatime" {
// good
isFound = true
} else if point == "/" && strings.HasPrefix(opts, "ro,") {
// Read-only root, good...
isRootFound = true
}
}
if !isFound || !isRootFound {
t.Fatal(`didn't get proper mounts for /tmp or /, got /proc/mounts content of:\n`, resp.R.MountsRead)
}
// write file should not have failed...
if resp.R.CreateFile != "" {
t.Fatal(`limited tmpfs should generate fs full error, but got output: `, resp.R.CreateFile)
}
}
func TestTmpFsSize(t *testing.T) {
appName := "myapp"
path := "/hello"
url := "http://127.0.0.1:8080/r/" + appName + path
@@ -719,6 +815,7 @@ func TestTmpFsSize(t *testing.T) {
point := tokens[1]
opts := tokens[3]
// rw tmp dir with size and inode limits applied.
if point == "/tmp" && opts == "rw,nosuid,nodev,noexec,relatime,size=1024k,nr_inodes=1024" {
// good
isFound = true

View File

@@ -219,7 +219,7 @@ func (drv *DockerDriver) configureFs(log logrus.FieldLogger, container *docker.C
}
func (drv *DockerDriver) configureTmpFs(log logrus.FieldLogger, container *docker.CreateContainerOptions, task drivers.ContainerTask) {
if task.TmpFsSize() == 0 {
if task.TmpFsSize() == 0 && !drv.conf.EnableReadOnlyRootFs {
return
}
@@ -228,11 +228,13 @@ func (drv *DockerDriver) configureTmpFs(log logrus.FieldLogger, container *docke
}
var tmpFsOption string
if task.TmpFsSize() != 0 {
if drv.conf.MaxTmpFsInodes != 0 {
tmpFsOption = fmt.Sprintf("size=%dm,nr_inodes=%d", task.TmpFsSize(), drv.conf.MaxTmpFsInodes)
} else {
tmpFsOption = fmt.Sprintf("size=%dm", task.TmpFsSize())
}
}
target := "/tmp"
log.WithFields(logrus.Fields{"target": target, "options": tmpFsOption, "call_id": task.Id()}).Debug("setting tmpfs")