mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
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:
committed by
Chad Arimura
parent
316940285d
commit
f97b63f878
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user