raw_exec: don't use cgroups when no_cgroup is set (#9328)

When raw_exec is configured with [`no_cgroups`](https://www.nomadproject.io/docs/drivers/raw_exec#no_cgroups), raw_exec shouldn't attempt to create a cgroup.

Prior to this change, we accidentally always required freezer cgroup to do stats PID tracking. We already have the proper fallback in place for metrics, so only need to ensure that we don't create a cgroup for the task.

Fixes https://github.com/hashicorp/nomad/issues/8565
This commit is contained in:
Mahmood Ali 2020-11-11 16:20:34 -05:00 committed by GitHub
parent f1d53584bc
commit a89da9982d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 2 deletions

View File

@ -55,6 +55,7 @@ BUG FIXES:
* csi: Fixed a bug where multi-writer volumes were allowed only 1 write claim. [[GH-9040](https://github.com/hashicorp/nomad/issues/9040)]
* csi: Fixed a bug where `nomad volume detach` would not accept prefixes for the node ID parameter. [[GH-9041](https://github.com/hashicorp/nomad/issues/9041)]
* driver/docker: Fixed a bug where the default `image_delay` configuration was ignored if the `gc` configuration was not set. [[GH-9101](https://github.com/hashicorp/nomad/issues/9101)]
* driver/rawexec: Fixed a bug where raw_exec attempts to create a freezer cgroups for the tasks even when `no_cgroups` is set. [[GH-9328](https://github.com/hashicorp/nomad/issues/9328)]
## 0.12.8 (November 10, 2020)

View File

@ -338,3 +338,62 @@ func TestRawExec_ExecTaskStreaming(t *testing.T) {
dtestutil.ExecTaskStreamingConformanceTests(t, harness, task.ID)
}
func TestRawExecDriver_NoCgroup(t *testing.T) {
t.Parallel()
if runtime.GOOS != "linux" {
t.Skip("Linux only test")
}
expectedBytes, err := ioutil.ReadFile("/proc/self/cgroup")
require.NoError(t, err)
expected := strings.TrimSpace(string(expectedBytes))
d := newEnabledRawExecDriver(t)
d.config.NoCgroups = true
harness := dtestutil.NewDriverHarness(t, d)
task := &drivers.TaskConfig{
ID: uuid.Generate(),
Name: "nocgroup",
}
cleanup := harness.MkAllocDir(task, true)
defer cleanup()
tc := &TaskConfig{
Command: "/bin/cat",
Args: []string{"/proc/self/cgroup"},
}
require.NoError(t, task.EncodeConcreteDriverConfig(&tc))
testtask.SetTaskConfigEnv(task)
_, _, err = harness.StartTask(task)
require.NoError(t, err)
// Task should terminate quickly
waitCh, err := harness.WaitTask(context.Background(), task.ID)
require.NoError(t, err)
select {
case res := <-waitCh:
require.True(t, res.Successful())
require.Zero(t, res.ExitCode)
case <-time.After(time.Duration(testutil.TestMultiplier()*6) * time.Second):
require.Fail(t, "WaitTask timeout")
}
// Check the log file to see it exited because of the signal
outputFile := filepath.Join(task.TaskDir().LogDir, "nocgroup.stdout.0")
testutil.WaitForResult(func() (bool, error) {
act, err := ioutil.ReadFile(outputFile)
if err != nil {
return false, fmt.Errorf("Couldn't read expected output: %v", err)
}
if strings.TrimSpace(string(act)) != expected {
t.Logf("Read from %v", outputFile)
return false, fmt.Errorf("Command outputted\n%v; want\n%v", string(act), expected)
}
return true, nil
}, func(err error) { require.NoError(t, err) })
}

View File

@ -280,8 +280,10 @@ func (e *UniversalExecutor) Launch(command *ExecCommand) (*ProcessState, error)
}
// Setup cgroups on linux
if err := e.configureResourceContainer(os.Getpid()); err != nil {
return nil, err
if e.commandCfg.ResourceLimits || e.commandCfg.BasicProcessCgroup {
if err := e.configureResourceContainer(os.Getpid()); err != nil {
return nil, err
}
}
stdout, err := e.commandCfg.Stdout()

View File

@ -571,3 +571,46 @@ func TestExecutor_cmdMounts(t *testing.T) {
require.EqualValues(t, expected, cmdMounts(input))
}
// TestUniversalExecutor_NoCgroup asserts that commands are executed in the
// same cgroup as parent process
func TestUniversalExecutor_NoCgroup(t *testing.T) {
t.Parallel()
testutil.ExecCompatible(t)
expectedBytes, err := ioutil.ReadFile("/proc/self/cgroup")
require.NoError(t, err)
expected := strings.TrimSpace(string(expectedBytes))
testExecCmd := testExecutorCommand(t)
execCmd, allocDir := testExecCmd.command, testExecCmd.allocDir
execCmd.Cmd = "/bin/cat"
execCmd.Args = []string{"/proc/self/cgroup"}
defer allocDir.Destroy()
execCmd.BasicProcessCgroup = false
execCmd.ResourceLimits = false
executor := NewExecutor(testlog.HCLogger(t))
defer executor.Shutdown("SIGKILL", 0)
_, err = executor.Launch(execCmd)
require.NoError(t, err)
_, err = executor.Wait(context.Background())
require.NoError(t, err)
tu.WaitForResult(func() (bool, error) {
act := strings.TrimSpace(string(testExecCmd.stdout.String()))
if expected != act {
return false, fmt.Errorf("expected:\n%s actual:\n%s", expected, act)
}
return true, nil
}, func(err error) {
stderr := strings.TrimSpace(string(testExecCmd.stderr.String()))
t.Logf("stderr: %v", stderr)
require.NoError(t, err)
})
}