1 package libcontainer
2
3 import (
4 "fmt"
5 "os"
6 "testing"
7
8 "github.com/opencontainers/runc/libcontainer/cgroups"
9 "github.com/opencontainers/runc/libcontainer/configs"
10 "github.com/opencontainers/runc/libcontainer/system"
11 )
12
13 type mockCgroupManager struct {
14 pids []int
15 allPids []int
16 paths map[string]string
17 }
18
19 func (m *mockCgroupManager) GetPids() ([]int, error) {
20 return m.pids, nil
21 }
22
23 func (m *mockCgroupManager) GetAllPids() ([]int, error) {
24 return m.allPids, nil
25 }
26
27 func (m *mockCgroupManager) GetStats() (*cgroups.Stats, error) {
28 return nil, nil
29 }
30
31 func (m *mockCgroupManager) Apply(pid int) error {
32 return nil
33 }
34
35 func (m *mockCgroupManager) Set(_ *configs.Resources) error {
36 return nil
37 }
38
39 func (m *mockCgroupManager) Destroy() error {
40 return nil
41 }
42
43 func (m *mockCgroupManager) Exists() bool {
44 _, err := os.Lstat(m.Path("devices"))
45 return err == nil
46 }
47
48 func (m *mockCgroupManager) OOMKillCount() (uint64, error) {
49 return 0, nil
50 }
51
52 func (m *mockCgroupManager) GetPaths() map[string]string {
53 return m.paths
54 }
55
56 func (m *mockCgroupManager) Path(subsys string) string {
57 return m.paths[subsys]
58 }
59
60 func (m *mockCgroupManager) Freeze(state configs.FreezerState) error {
61 return nil
62 }
63
64 func (m *mockCgroupManager) GetCgroups() (*configs.Cgroup, error) {
65 return nil, nil
66 }
67
68 func (m *mockCgroupManager) GetFreezerState() (configs.FreezerState, error) {
69 return configs.Thawed, nil
70 }
71
72 type mockProcess struct {
73 _pid int
74 started uint64
75 }
76
77 func (m *mockProcess) terminate() error {
78 return nil
79 }
80
81 func (m *mockProcess) pid() int {
82 return m._pid
83 }
84
85 func (m *mockProcess) startTime() (uint64, error) {
86 return m.started, nil
87 }
88
89 func (m *mockProcess) start() error {
90 return nil
91 }
92
93 func (m *mockProcess) wait() (*os.ProcessState, error) {
94 return nil, nil
95 }
96
97 func (m *mockProcess) signal(_ os.Signal) error {
98 return nil
99 }
100
101 func (m *mockProcess) externalDescriptors() []string {
102 return []string{}
103 }
104
105 func (m *mockProcess) setExternalDescriptors(newFds []string) {
106 }
107
108 func (m *mockProcess) forwardChildLogs() chan error {
109 return nil
110 }
111
112 func TestGetContainerPids(t *testing.T) {
113 pid := 1
114 stat, err := system.Stat(pid)
115 if err != nil {
116 t.Fatalf("can't stat pid %d, got %v", pid, err)
117 }
118 container := &linuxContainer{
119 id: "myid",
120 config: &configs.Config{},
121 cgroupManager: &mockCgroupManager{
122 allPids: []int{1, 2, 3},
123 paths: map[string]string{
124 "device": "/proc/self/cgroups",
125 },
126 },
127 initProcess: &mockProcess{
128 _pid: 1,
129 started: 10,
130 },
131 initProcessStartTime: stat.StartTime,
132 }
133 container.state = &runningState{c: container}
134 pids, err := container.Processes()
135 if err != nil {
136 t.Fatal(err)
137 }
138 for i, expected := range []int{1, 2, 3} {
139 if pids[i] != expected {
140 t.Fatalf("expected pid %d but received %d", expected, pids[i])
141 }
142 }
143 }
144
145 func TestGetContainerState(t *testing.T) {
146 var (
147 pid = os.Getpid()
148 expectedMemoryPath = "/sys/fs/cgroup/memory/myid"
149 expectedNetworkPath = fmt.Sprintf("/proc/%d/ns/net", pid)
150 )
151 container := &linuxContainer{
152 id: "myid",
153 config: &configs.Config{
154 Namespaces: []configs.Namespace{
155 {Type: configs.NEWPID},
156 {Type: configs.NEWNS},
157 {Type: configs.NEWNET, Path: expectedNetworkPath},
158 {Type: configs.NEWUTS},
159
160
161 {Type: configs.NEWCGROUP},
162 },
163 },
164 initProcess: &mockProcess{
165 _pid: pid,
166 started: 10,
167 },
168 cgroupManager: &mockCgroupManager{
169 pids: []int{1, 2, 3},
170 paths: map[string]string{
171 "memory": expectedMemoryPath,
172 },
173 },
174 }
175 container.state = &createdState{c: container}
176 state, err := container.State()
177 if err != nil {
178 t.Fatal(err)
179 }
180 if state.InitProcessPid != pid {
181 t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
182 }
183 if state.InitProcessStartTime != 10 {
184 t.Fatalf("expected process start time 10 but received %d", state.InitProcessStartTime)
185 }
186 paths := state.CgroupPaths
187 if paths == nil {
188 t.Fatal("cgroup paths should not be nil")
189 }
190 if memPath := paths["memory"]; memPath != expectedMemoryPath {
191 t.Fatalf("expected memory path %q but received %q", expectedMemoryPath, memPath)
192 }
193 for _, ns := range container.config.Namespaces {
194 path := state.NamespacePaths[ns.Type]
195 if path == "" {
196 t.Fatalf("expected non nil namespace path for %s", ns.Type)
197 }
198 if ns.Type == configs.NEWNET {
199 if path != expectedNetworkPath {
200 t.Fatalf("expected path %q but received %q", expectedNetworkPath, path)
201 }
202 } else {
203 file := ""
204 switch ns.Type {
205 case configs.NEWNET:
206 file = "net"
207 case configs.NEWNS:
208 file = "mnt"
209 case configs.NEWPID:
210 file = "pid"
211 case configs.NEWIPC:
212 file = "ipc"
213 case configs.NEWUSER:
214 file = "user"
215 case configs.NEWUTS:
216 file = "uts"
217 case configs.NEWCGROUP:
218 file = "cgroup"
219 }
220 expected := fmt.Sprintf("/proc/%d/ns/%s", pid, file)
221 if expected != path {
222 t.Fatalf("expected path %q but received %q", expected, path)
223 }
224 }
225 }
226 }
227
228 func TestGetContainerStateAfterUpdate(t *testing.T) {
229 pid := os.Getpid()
230 stat, err := system.Stat(pid)
231 if err != nil {
232 t.Fatal(err)
233 }
234
235 container := &linuxContainer{
236 root: t.TempDir(),
237 id: "myid",
238 config: &configs.Config{
239 Namespaces: []configs.Namespace{
240 {Type: configs.NEWPID},
241 {Type: configs.NEWNS},
242 {Type: configs.NEWNET},
243 {Type: configs.NEWUTS},
244 {Type: configs.NEWIPC},
245 },
246 Cgroups: &configs.Cgroup{
247 Resources: &configs.Resources{
248 Memory: 1024,
249 },
250 },
251 },
252 initProcess: &mockProcess{
253 _pid: pid,
254 started: stat.StartTime,
255 },
256 cgroupManager: &mockCgroupManager{},
257 }
258 container.state = &createdState{c: container}
259 state, err := container.State()
260 if err != nil {
261 t.Fatal(err)
262 }
263 if state.InitProcessPid != pid {
264 t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid)
265 }
266 if state.InitProcessStartTime != stat.StartTime {
267 t.Fatalf("expected process start time %d but received %d", stat.StartTime, state.InitProcessStartTime)
268 }
269 if state.Config.Cgroups.Resources.Memory != 1024 {
270 t.Fatalf("expected Memory to be 1024 but received %q", state.Config.Cgroups.Memory)
271 }
272
273
274 container.initProcessStartTime = state.InitProcessStartTime
275 container.state = &runningState{c: container}
276 newConfig := container.Config()
277 newConfig.Cgroups.Resources.Memory = 2048
278 if err := container.Set(newConfig); err != nil {
279 t.Fatal(err)
280 }
281 state, err = container.State()
282 if err != nil {
283 t.Fatal(err)
284 }
285 if state.Config.Cgroups.Resources.Memory != 2048 {
286 t.Fatalf("expected Memory to be 2048 but received %q", state.Config.Cgroups.Memory)
287 }
288 }
289
View as plain text