2014-10-23 07:53:28 +08:00
|
|
|
// +build linux
|
|
|
|
|
|
|
|
package libcontainer
|
|
|
|
|
|
|
|
import (
|
2015-02-12 06:45:07 +08:00
|
|
|
"fmt"
|
2017-11-27 16:43:10 +08:00
|
|
|
"io/ioutil"
|
2015-02-12 06:45:07 +08:00
|
|
|
"os"
|
2014-10-23 07:53:28 +08:00
|
|
|
"testing"
|
|
|
|
|
2015-06-22 10:29:59 +08:00
|
|
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
|
|
|
"github.com/opencontainers/runc/libcontainer/configs"
|
2017-08-30 19:35:09 +08:00
|
|
|
"github.com/opencontainers/runc/libcontainer/intelrdt"
|
2017-08-15 14:30:58 +08:00
|
|
|
"github.com/opencontainers/runc/libcontainer/system"
|
2014-10-23 07:53:28 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
type mockCgroupManager struct {
|
Simplify cgroup path handing in v2 via unified API
This unties the Gordian Knot of using GetPaths in cgroupv2 code.
The problem is, the current code uses GetPaths for three kinds of things:
1. Get all the paths to cgroup v1 controllers to save its state (see
(*linuxContainer).currentState(), (*LinuxFactory).loadState()
methods).
2. Get all the paths to cgroup v1 controllers to have the setns process
enter the proper cgroups in `(*setnsProcess).start()`.
3. Get the path to a specific controller (for example,
`m.GetPaths()["devices"]`).
Now, for cgroup v2 instead of a set of per-controller paths, we have only
one single unified path, and a dedicated function `GetUnifiedPath()` to get it.
This discrepancy between v1 and v2 cgroupManager API leads to the
following problems with the code:
- multiple if/else code blocks that have to treat v1 and v2 separately;
- backward-compatible GetPaths() methods in v2 controllers;
- - repeated writing of the PID into the same cgroup for v2;
Overall, it's hard to write the right code with all this, and the code
that is written is kinda hard to follow.
The solution is to slightly change the API to do the 3 things outlined
above in the same manner for v1 and v2:
1. Use `GetPaths()` for state saving and setns process cgroups entering.
2. Introduce and use Path(subsys string) to obtain a path to a
subsystem. For v2, the argument is ignored and the unified path is
returned.
This commit converts all the controllers to the new API, and modifies
all the users to use it.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2020-05-07 08:36:28 +08:00
|
|
|
pids []int
|
|
|
|
allPids []int
|
|
|
|
stats *cgroups.Stats
|
|
|
|
paths map[string]string
|
2014-10-23 07:53:28 +08:00
|
|
|
}
|
|
|
|
|
2017-08-30 19:35:09 +08:00
|
|
|
type mockIntelRdtManager struct {
|
|
|
|
stats *intelrdt.Stats
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
2014-12-06 09:02:49 +08:00
|
|
|
func (m *mockCgroupManager) GetPids() ([]int, error) {
|
2014-10-23 07:53:28 +08:00
|
|
|
return m.pids, nil
|
|
|
|
}
|
|
|
|
|
2016-01-09 03:37:18 +08:00
|
|
|
func (m *mockCgroupManager) GetAllPids() ([]int, error) {
|
|
|
|
return m.allPids, nil
|
|
|
|
}
|
|
|
|
|
2014-12-06 09:02:49 +08:00
|
|
|
func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) {
|
2014-10-23 07:53:28 +08:00
|
|
|
return m.stats, nil
|
|
|
|
}
|
|
|
|
|
2015-01-13 05:54:00 +08:00
|
|
|
func (m *mockCgroupManager) Apply(pid int) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-02-25 17:20:01 +08:00
|
|
|
func (m *mockCgroupManager) Set(container *configs.Config) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-01-14 23:23:42 +08:00
|
|
|
func (m *mockCgroupManager) Destroy() error {
|
2015-01-13 05:54:00 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-23 21:33:47 +08:00
|
|
|
func (m *mockCgroupManager) Exists() bool {
|
2020-06-16 09:21:01 +08:00
|
|
|
_, err := os.Lstat(m.Path("devices"))
|
|
|
|
return err == nil
|
2020-04-23 21:33:47 +08:00
|
|
|
}
|
|
|
|
|
2015-01-13 05:54:00 +08:00
|
|
|
func (m *mockCgroupManager) GetPaths() map[string]string {
|
2015-02-12 06:45:07 +08:00
|
|
|
return m.paths
|
2015-01-13 05:54:00 +08:00
|
|
|
}
|
|
|
|
|
Simplify cgroup path handing in v2 via unified API
This unties the Gordian Knot of using GetPaths in cgroupv2 code.
The problem is, the current code uses GetPaths for three kinds of things:
1. Get all the paths to cgroup v1 controllers to save its state (see
(*linuxContainer).currentState(), (*LinuxFactory).loadState()
methods).
2. Get all the paths to cgroup v1 controllers to have the setns process
enter the proper cgroups in `(*setnsProcess).start()`.
3. Get the path to a specific controller (for example,
`m.GetPaths()["devices"]`).
Now, for cgroup v2 instead of a set of per-controller paths, we have only
one single unified path, and a dedicated function `GetUnifiedPath()` to get it.
This discrepancy between v1 and v2 cgroupManager API leads to the
following problems with the code:
- multiple if/else code blocks that have to treat v1 and v2 separately;
- backward-compatible GetPaths() methods in v2 controllers;
- - repeated writing of the PID into the same cgroup for v2;
Overall, it's hard to write the right code with all this, and the code
that is written is kinda hard to follow.
The solution is to slightly change the API to do the 3 things outlined
above in the same manner for v1 and v2:
1. Use `GetPaths()` for state saving and setns process cgroups entering.
2. Introduce and use Path(subsys string) to obtain a path to a
subsystem. For v2, the argument is ignored and the unified path is
returned.
This commit converts all the controllers to the new API, and modifies
all the users to use it.
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2020-05-07 08:36:28 +08:00
|
|
|
func (m *mockCgroupManager) Path(subsys string) string {
|
|
|
|
return m.paths[subsys]
|
2019-10-19 00:40:46 +08:00
|
|
|
}
|
|
|
|
|
2015-02-03 20:27:21 +08:00
|
|
|
func (m *mockCgroupManager) Freeze(state configs.FreezerState) error {
|
2015-01-13 19:52:14 +08:00
|
|
|
return nil
|
|
|
|
}
|
2020-05-11 13:19:30 +08:00
|
|
|
|
2019-12-06 23:43:08 +08:00
|
|
|
func (m *mockCgroupManager) GetCgroups() (*configs.Cgroup, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2015-01-13 19:52:14 +08:00
|
|
|
|
2020-05-11 13:19:30 +08:00
|
|
|
func (m *mockCgroupManager) GetFreezerState() (configs.FreezerState, error) {
|
|
|
|
return configs.Thawed, nil
|
|
|
|
}
|
|
|
|
|
2017-08-30 19:35:09 +08:00
|
|
|
func (m *mockIntelRdtManager) Apply(pid int) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockIntelRdtManager) GetStats() (*intelrdt.Stats, error) {
|
|
|
|
return m.stats, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockIntelRdtManager) Destroy() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockIntelRdtManager) GetPath() string {
|
|
|
|
return m.path
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockIntelRdtManager) Set(container *configs.Config) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-12-06 23:43:08 +08:00
|
|
|
func (m *mockIntelRdtManager) GetCgroups() (*configs.Cgroup, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2015-02-12 06:45:07 +08:00
|
|
|
type mockProcess struct {
|
|
|
|
_pid int
|
2017-06-15 06:38:45 +08:00
|
|
|
started uint64
|
2015-02-12 06:45:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockProcess) terminate() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockProcess) pid() int {
|
|
|
|
return m._pid
|
|
|
|
}
|
|
|
|
|
2017-06-15 06:38:45 +08:00
|
|
|
func (m *mockProcess) startTime() (uint64, error) {
|
2015-02-12 06:45:07 +08:00
|
|
|
return m.started, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockProcess) start() error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockProcess) wait() (*os.ProcessState, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *mockProcess) signal(_ os.Signal) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-04-12 16:12:23 +08:00
|
|
|
func (m *mockProcess) externalDescriptors() []string {
|
2015-04-29 23:14:54 +08:00
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
|
2016-04-12 16:12:23 +08:00
|
|
|
func (m *mockProcess) setExternalDescriptors(newFds []string) {
|
2015-04-29 23:14:54 +08:00
|
|
|
}
|
|
|
|
|
2019-04-04 19:57:28 +08:00
|
|
|
func (m *mockProcess) forwardChildLogs() {
|
|
|
|
}
|
|
|
|
|
2014-10-23 07:53:28 +08:00
|
|
|
func TestGetContainerPids(t *testing.T) {
|
2020-04-23 21:33:47 +08:00
|
|
|
pid := 1
|
|
|
|
stat, err := system.Stat(pid)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("can't stat pid %d, got %v", pid, err)
|
|
|
|
}
|
2014-10-23 07:53:28 +08:00
|
|
|
container := &linuxContainer{
|
2020-04-23 21:33:47 +08:00
|
|
|
id: "myid",
|
|
|
|
config: &configs.Config{},
|
|
|
|
cgroupManager: &mockCgroupManager{
|
|
|
|
allPids: []int{1, 2, 3},
|
|
|
|
paths: map[string]string{
|
|
|
|
"device": "/proc/self/cgroups",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
initProcess: &mockProcess{
|
|
|
|
_pid: 1,
|
|
|
|
started: 10,
|
|
|
|
},
|
|
|
|
initProcessStartTime: stat.StartTime,
|
2014-10-23 07:53:28 +08:00
|
|
|
}
|
2020-04-23 21:33:47 +08:00
|
|
|
container.state = &runningState{c: container}
|
2014-10-23 07:53:28 +08:00
|
|
|
pids, err := container.Processes()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
for i, expected := range []int{1, 2, 3} {
|
|
|
|
if pids[i] != expected {
|
|
|
|
t.Fatalf("expected pid %d but received %d", expected, pids[i])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetContainerStats(t *testing.T) {
|
|
|
|
container := &linuxContainer{
|
|
|
|
id: "myid",
|
2014-12-17 17:12:23 +08:00
|
|
|
config: &configs.Config{},
|
2014-10-23 07:53:28 +08:00
|
|
|
cgroupManager: &mockCgroupManager{
|
|
|
|
pids: []int{1, 2, 3},
|
|
|
|
stats: &cgroups.Stats{
|
|
|
|
MemoryStats: cgroups.MemoryStats{
|
2015-05-15 17:37:58 +08:00
|
|
|
Usage: cgroups.MemoryData{
|
|
|
|
Usage: 1024,
|
|
|
|
},
|
2014-10-23 07:53:28 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-08-30 19:35:09 +08:00
|
|
|
intelRdtManager: &mockIntelRdtManager{
|
|
|
|
stats: &intelrdt.Stats{
|
|
|
|
L3CacheSchema: "L3:0=f;1=f0",
|
2018-10-16 12:38:00 +08:00
|
|
|
MemBwSchema: "MB:0=20;1=70",
|
2017-08-30 19:35:09 +08:00
|
|
|
},
|
|
|
|
},
|
2014-10-23 07:53:28 +08:00
|
|
|
}
|
|
|
|
stats, err := container.Stats()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if stats.CgroupStats == nil {
|
|
|
|
t.Fatal("cgroup stats are nil")
|
|
|
|
}
|
2015-05-15 17:37:58 +08:00
|
|
|
if stats.CgroupStats.MemoryStats.Usage.Usage != 1024 {
|
2018-09-07 11:37:40 +08:00
|
|
|
t.Fatalf("expected memory usage 1024 but received %d", stats.CgroupStats.MemoryStats.Usage.Usage)
|
2014-10-23 07:53:28 +08:00
|
|
|
}
|
2018-10-16 12:38:00 +08:00
|
|
|
if intelrdt.IsCatEnabled() {
|
2017-08-30 19:35:09 +08:00
|
|
|
if stats.IntelRdtStats == nil {
|
|
|
|
t.Fatal("intel rdt stats are nil")
|
|
|
|
}
|
|
|
|
if stats.IntelRdtStats.L3CacheSchema != "L3:0=f;1=f0" {
|
2018-12-19 20:22:48 +08:00
|
|
|
t.Fatalf("expected L3CacheSchema L3:0=f;1=f0 but received %s", stats.IntelRdtStats.L3CacheSchema)
|
2017-08-30 19:35:09 +08:00
|
|
|
}
|
|
|
|
}
|
2018-10-16 12:38:00 +08:00
|
|
|
if intelrdt.IsMbaEnabled() {
|
|
|
|
if stats.IntelRdtStats == nil {
|
|
|
|
t.Fatal("intel rdt stats are nil")
|
|
|
|
}
|
|
|
|
if stats.IntelRdtStats.MemBwSchema != "MB:0=20;1=70" {
|
2018-12-19 20:22:48 +08:00
|
|
|
t.Fatalf("expected MemBwSchema MB:0=20;1=70 but received %s", stats.IntelRdtStats.MemBwSchema)
|
2018-10-16 12:38:00 +08:00
|
|
|
}
|
|
|
|
}
|
2014-10-23 07:53:28 +08:00
|
|
|
}
|
2015-02-12 06:45:07 +08:00
|
|
|
|
|
|
|
func TestGetContainerState(t *testing.T) {
|
|
|
|
var (
|
2017-08-30 19:35:09 +08:00
|
|
|
pid = os.Getpid()
|
|
|
|
expectedMemoryPath = "/sys/fs/cgroup/memory/myid"
|
|
|
|
expectedNetworkPath = fmt.Sprintf("/proc/%d/ns/net", pid)
|
|
|
|
expectedIntelRdtPath = "/sys/fs/resctrl/myid"
|
2015-02-12 06:45:07 +08:00
|
|
|
)
|
|
|
|
container := &linuxContainer{
|
|
|
|
id: "myid",
|
|
|
|
config: &configs.Config{
|
2015-03-06 02:16:35 +08:00
|
|
|
Namespaces: []configs.Namespace{
|
2015-02-12 06:45:07 +08:00
|
|
|
{Type: configs.NEWPID},
|
|
|
|
{Type: configs.NEWNS},
|
|
|
|
{Type: configs.NEWNET, Path: expectedNetworkPath},
|
|
|
|
{Type: configs.NEWUTS},
|
2015-04-08 05:16:29 +08:00
|
|
|
// emulate host for IPC
|
|
|
|
//{Type: configs.NEWIPC},
|
2019-05-21 08:10:38 +08:00
|
|
|
{Type: configs.NEWCGROUP},
|
2015-02-12 06:45:07 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
initProcess: &mockProcess{
|
|
|
|
_pid: pid,
|
2017-06-15 06:38:45 +08:00
|
|
|
started: 10,
|
2015-02-12 06:45:07 +08:00
|
|
|
},
|
|
|
|
cgroupManager: &mockCgroupManager{
|
|
|
|
pids: []int{1, 2, 3},
|
|
|
|
stats: &cgroups.Stats{
|
|
|
|
MemoryStats: cgroups.MemoryStats{
|
2015-05-15 17:37:58 +08:00
|
|
|
Usage: cgroups.MemoryData{
|
|
|
|
Usage: 1024,
|
|
|
|
},
|
2015-02-12 06:45:07 +08:00
|
|
|
},
|
|
|
|
},
|
|
|
|
paths: map[string]string{
|
|
|
|
"memory": expectedMemoryPath,
|
|
|
|
},
|
|
|
|
},
|
2017-08-30 19:35:09 +08:00
|
|
|
intelRdtManager: &mockIntelRdtManager{
|
|
|
|
stats: &intelrdt.Stats{
|
|
|
|
L3CacheSchema: "L3:0=f0;1=f",
|
2018-10-16 12:38:00 +08:00
|
|
|
MemBwSchema: "MB:0=70;1=20",
|
2017-08-30 19:35:09 +08:00
|
|
|
},
|
|
|
|
path: expectedIntelRdtPath,
|
|
|
|
},
|
2015-02-12 06:45:07 +08:00
|
|
|
}
|
2016-01-25 12:52:52 +08:00
|
|
|
container.state = &createdState{c: container}
|
2015-02-12 06:45:07 +08:00
|
|
|
state, err := container.State()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if state.InitProcessPid != pid {
|
|
|
|
t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
|
|
|
|
}
|
2017-06-15 06:38:45 +08:00
|
|
|
if state.InitProcessStartTime != 10 {
|
|
|
|
t.Fatalf("expected process start time 10 but received %d", state.InitProcessStartTime)
|
2015-02-12 06:45:07 +08:00
|
|
|
}
|
|
|
|
paths := state.CgroupPaths
|
|
|
|
if paths == nil {
|
|
|
|
t.Fatal("cgroup paths should not be nil")
|
|
|
|
}
|
|
|
|
if memPath := paths["memory"]; memPath != expectedMemoryPath {
|
|
|
|
t.Fatalf("expected memory path %q but received %q", expectedMemoryPath, memPath)
|
|
|
|
}
|
2018-10-16 12:38:00 +08:00
|
|
|
if intelrdt.IsCatEnabled() || intelrdt.IsMbaEnabled() {
|
2017-08-30 19:35:09 +08:00
|
|
|
intelRdtPath := state.IntelRdtPath
|
|
|
|
if intelRdtPath == "" {
|
|
|
|
t.Fatal("intel rdt path should not be empty")
|
|
|
|
}
|
|
|
|
if intelRdtPath != expectedIntelRdtPath {
|
|
|
|
t.Fatalf("expected intel rdt path %q but received %q", expectedIntelRdtPath, intelRdtPath)
|
|
|
|
}
|
|
|
|
}
|
2015-02-12 06:45:07 +08:00
|
|
|
for _, ns := range container.config.Namespaces {
|
2015-02-25 03:54:58 +08:00
|
|
|
path := state.NamespacePaths[ns.Type]
|
2015-02-12 06:45:07 +08:00
|
|
|
if path == "" {
|
|
|
|
t.Fatalf("expected non nil namespace path for %s", ns.Type)
|
|
|
|
}
|
|
|
|
if ns.Type == configs.NEWNET {
|
|
|
|
if path != expectedNetworkPath {
|
|
|
|
t.Fatalf("expected path %q but received %q", expectedNetworkPath, path)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
file := ""
|
|
|
|
switch ns.Type {
|
|
|
|
case configs.NEWNET:
|
|
|
|
file = "net"
|
|
|
|
case configs.NEWNS:
|
|
|
|
file = "mnt"
|
|
|
|
case configs.NEWPID:
|
|
|
|
file = "pid"
|
|
|
|
case configs.NEWIPC:
|
|
|
|
file = "ipc"
|
|
|
|
case configs.NEWUSER:
|
|
|
|
file = "user"
|
|
|
|
case configs.NEWUTS:
|
|
|
|
file = "uts"
|
2019-05-21 08:10:38 +08:00
|
|
|
case configs.NEWCGROUP:
|
|
|
|
file = "cgroup"
|
2015-02-12 06:45:07 +08:00
|
|
|
}
|
|
|
|
expected := fmt.Sprintf("/proc/%d/ns/%s", pid, file)
|
|
|
|
if expected != path {
|
|
|
|
t.Fatalf("expected path %q but received %q", expected, path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-15 14:30:58 +08:00
|
|
|
|
|
|
|
func TestGetContainerStateAfterUpdate(t *testing.T) {
|
|
|
|
var (
|
|
|
|
pid = os.Getpid()
|
|
|
|
)
|
|
|
|
stat, err := system.Stat(pid)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
2017-11-27 16:43:10 +08:00
|
|
|
|
|
|
|
rootDir, err := ioutil.TempDir("", "TestGetContainerStateAfterUpdate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer os.RemoveAll(rootDir)
|
|
|
|
|
2017-08-15 14:30:58 +08:00
|
|
|
container := &linuxContainer{
|
2017-11-27 16:43:10 +08:00
|
|
|
root: rootDir,
|
|
|
|
id: "myid",
|
2017-08-15 14:30:58 +08:00
|
|
|
config: &configs.Config{
|
|
|
|
Namespaces: []configs.Namespace{
|
|
|
|
{Type: configs.NEWPID},
|
|
|
|
{Type: configs.NEWNS},
|
|
|
|
{Type: configs.NEWNET},
|
|
|
|
{Type: configs.NEWUTS},
|
|
|
|
{Type: configs.NEWIPC},
|
|
|
|
},
|
|
|
|
Cgroups: &configs.Cgroup{
|
|
|
|
Resources: &configs.Resources{
|
|
|
|
Memory: 1024,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
initProcess: &mockProcess{
|
|
|
|
_pid: pid,
|
|
|
|
started: stat.StartTime,
|
|
|
|
},
|
|
|
|
cgroupManager: &mockCgroupManager{},
|
|
|
|
}
|
|
|
|
container.state = &createdState{c: container}
|
|
|
|
state, err := container.State()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if state.InitProcessPid != pid {
|
|
|
|
t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
|
|
|
|
}
|
|
|
|
if state.InitProcessStartTime != stat.StartTime {
|
|
|
|
t.Fatalf("expected process start time %d but received %d", stat.StartTime, state.InitProcessStartTime)
|
|
|
|
}
|
|
|
|
if state.Config.Cgroups.Resources.Memory != 1024 {
|
|
|
|
t.Fatalf("expected Memory to be 1024 but received %q", state.Config.Cgroups.Memory)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set initProcessStartTime so we fake to be running
|
|
|
|
container.initProcessStartTime = state.InitProcessStartTime
|
|
|
|
container.state = &runningState{c: container}
|
|
|
|
newConfig := container.Config()
|
|
|
|
newConfig.Cgroups.Resources.Memory = 2048
|
|
|
|
if err := container.Set(newConfig); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
state, err = container.State()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
if state.Config.Cgroups.Resources.Memory != 2048 {
|
|
|
|
t.Fatalf("expected Memory to be 2048 but received %q", state.Config.Cgroups.Memory)
|
|
|
|
}
|
|
|
|
}
|