Skip to content

Commit 3a041e9

Browse files
authored
Merge pull request #3081 from kolyshkin/carry-3065
Make cgroup freezer only care about current control group (carry #3065)
2 parents 9493bb8 + 3e5c199 commit 3a041e9

File tree

2 files changed

+160
-1
lines changed

2 files changed

+160
-1
lines changed

libcontainer/cgroups/fs/freezer.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,25 @@ func (s *FreezerGroup) GetState(path string) (configs.FreezerState, error) {
129129
case "THAWED":
130130
return configs.Thawed, nil
131131
case "FROZEN":
132-
return configs.Frozen, nil
132+
// Find out whether the cgroup is frozen directly,
133+
// or indirectly via an ancestor.
134+
self, err := cgroups.ReadFile(path, "freezer.self_freezing")
135+
if err != nil {
136+
// If the kernel is too old, then we just treat
137+
// it as being frozen.
138+
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.ENODEV) {
139+
err = nil
140+
}
141+
return configs.Frozen, err
142+
}
143+
switch self {
144+
case "0\n":
145+
return configs.Thawed, nil
146+
case "1\n":
147+
return configs.Frozen, nil
148+
default:
149+
return configs.Undefined, fmt.Errorf(`unknown "freezer.self_freezing" state: %q`, self)
150+
}
133151
case "FREEZING":
134152
// Make sure we get a stable freezer state, so retry if the cgroup
135153
// is still undergoing freezing. This should be a temporary delay.

libcontainer/cgroups/systemd/systemd_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package systemd
22

33
import (
4+
"bufio"
45
"bytes"
56
"os"
67
"os/exec"
@@ -193,3 +194,143 @@ func TestUnitExistsIgnored(t *testing.T) {
193194
}
194195
}
195196
}
197+
198+
func TestFreezePodCgroup(t *testing.T) {
199+
if !IsRunningSystemd() {
200+
t.Skip("Test requires systemd.")
201+
}
202+
if os.Geteuid() != 0 {
203+
t.Skip("Test requires root.")
204+
}
205+
206+
podConfig := &configs.Cgroup{
207+
Parent: "system.slice",
208+
Name: "system-runc_test_pod.slice",
209+
Resources: &configs.Resources{
210+
SkipDevices: true,
211+
Freezer: configs.Frozen,
212+
},
213+
}
214+
// Create a "pod" cgroup (a systemd slice to hold containers),
215+
// which is frozen initially.
216+
pm := newManager(podConfig)
217+
defer pm.Destroy() //nolint:errcheck
218+
if err := pm.Apply(-1); err != nil {
219+
t.Fatal(err)
220+
}
221+
222+
if err := pm.Freeze(configs.Frozen); err != nil {
223+
t.Fatal(err)
224+
}
225+
if err := pm.Set(podConfig.Resources); err != nil {
226+
t.Fatal(err)
227+
}
228+
229+
// Check the pod is frozen.
230+
pf, err := pm.GetFreezerState()
231+
if err != nil {
232+
t.Fatal(err)
233+
}
234+
if pf != configs.Frozen {
235+
t.Fatalf("expected pod to be frozen, got %v", pf)
236+
}
237+
238+
// Create a "container" within the "pod" cgroup.
239+
// This is not a real container, just a process in the cgroup.
240+
containerConfig := &configs.Cgroup{
241+
Parent: "system-runc_test_pod.slice",
242+
ScopePrefix: "test",
243+
Name: "inner-contianer",
244+
Resources: &configs.Resources{},
245+
}
246+
247+
cmd := exec.Command("bash", "-c", "while read; do echo $REPLY; done")
248+
cmd.Env = append(os.Environ(), "LANG=C")
249+
250+
// Setup stdin.
251+
stdinR, stdinW, err := os.Pipe()
252+
if err != nil {
253+
t.Fatal(err)
254+
}
255+
cmd.Stdin = stdinR
256+
257+
// Setup stdout.
258+
stdoutR, stdoutW, err := os.Pipe()
259+
if err != nil {
260+
t.Fatal(err)
261+
}
262+
cmd.Stdout = stdoutW
263+
rdr := bufio.NewReader(stdoutR)
264+
265+
// Setup stderr.
266+
var stderr bytes.Buffer
267+
cmd.Stderr = &stderr
268+
269+
err = cmd.Start()
270+
stdinR.Close()
271+
stdoutW.Close()
272+
defer func() {
273+
_ = stdinW.Close()
274+
_ = stdoutR.Close()
275+
}()
276+
if err != nil {
277+
t.Fatal(err)
278+
}
279+
// Make sure to not leave a zombie.
280+
defer func() {
281+
// These may fail, we don't care.
282+
_ = cmd.Process.Kill()
283+
_ = cmd.Wait()
284+
}()
285+
286+
// Put the process into a cgroup.
287+
cm := newManager(containerConfig)
288+
defer cm.Destroy() //nolint:errcheck
289+
290+
if err := cm.Apply(cmd.Process.Pid); err != nil {
291+
t.Fatal(err)
292+
}
293+
if err := cm.Set(containerConfig.Resources); err != nil {
294+
t.Fatal(err)
295+
}
296+
// Check that we put the "container" into the "pod" cgroup.
297+
if !strings.HasPrefix(cm.Path("freezer"), pm.Path("freezer")) {
298+
t.Fatalf("expected container cgroup path %q to be under pod cgroup path %q",
299+
cm.Path("freezer"), pm.Path("freezer"))
300+
}
301+
// Check the container is not reported as frozen despite the frozen parent.
302+
cf, err := cm.GetFreezerState()
303+
if err != nil {
304+
t.Fatal(err)
305+
}
306+
if cf != configs.Thawed {
307+
t.Fatalf("expected container to be thawed, got %v", cf)
308+
}
309+
310+
// Unfreeze the pod.
311+
if err := pm.Freeze(configs.Thawed); err != nil {
312+
t.Fatal(err)
313+
}
314+
315+
cf, err = cm.GetFreezerState()
316+
if err != nil {
317+
t.Fatal(err)
318+
}
319+
if cf != configs.Thawed {
320+
t.Fatalf("expected container to be thawed, got %v", cf)
321+
}
322+
323+
// Check the "container" works.
324+
marker := "one two\n"
325+
_, err = stdinW.WriteString(marker)
326+
if err != nil {
327+
t.Fatal(err)
328+
}
329+
reply, err := rdr.ReadString('\n')
330+
if err != nil {
331+
t.Fatalf("reading from container: %v", err)
332+
}
333+
if reply != marker {
334+
t.Fatalf("expected %q, got %q", marker, reply)
335+
}
336+
}

0 commit comments

Comments
 (0)