github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/container_linux_test.go (about) 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 func (m *mockCgroupManager) GetEffectiveCPUs() string { 73 return "" 74 } 75 76 type mockProcess struct { 77 _pid int 78 started uint64 79 } 80 81 func (m *mockProcess) terminate() error { 82 return nil 83 } 84 85 func (m *mockProcess) pid() int { 86 return m._pid 87 } 88 89 func (m *mockProcess) startTime() (uint64, error) { 90 return m.started, nil 91 } 92 93 func (m *mockProcess) start() error { 94 return nil 95 } 96 97 func (m *mockProcess) wait() (*os.ProcessState, error) { 98 return nil, nil 99 } 100 101 func (m *mockProcess) signal(_ os.Signal) error { 102 return nil 103 } 104 105 func (m *mockProcess) externalDescriptors() []string { 106 return []string{} 107 } 108 109 func (m *mockProcess) setExternalDescriptors(newFds []string) { 110 } 111 112 func (m *mockProcess) forwardChildLogs() chan error { 113 return nil 114 } 115 116 func TestGetContainerPids(t *testing.T) { 117 pid := 1 118 stat, err := system.Stat(pid) 119 if err != nil { 120 t.Fatalf("can't stat pid %d, got %v", pid, err) 121 } 122 container := &Container{ 123 id: "myid", 124 config: &configs.Config{}, 125 cgroupManager: &mockCgroupManager{ 126 allPids: []int{1, 2, 3}, 127 paths: map[string]string{ 128 "device": "/proc/self/cgroups", 129 }, 130 }, 131 initProcess: &mockProcess{ 132 _pid: 1, 133 started: 10, 134 }, 135 initProcessStartTime: stat.StartTime, 136 } 137 container.state = &runningState{c: container} 138 pids, err := container.Processes() 139 if err != nil { 140 t.Fatal(err) 141 } 142 for i, expected := range []int{1, 2, 3} { 143 if pids[i] != expected { 144 t.Fatalf("expected pid %d but received %d", expected, pids[i]) 145 } 146 } 147 } 148 149 func TestGetContainerState(t *testing.T) { 150 var ( 151 pid = os.Getpid() 152 expectedMemoryPath = "/sys/fs/cgroup/memory/myid" 153 expectedNetworkPath = fmt.Sprintf("/proc/%d/ns/net", pid) 154 ) 155 container := &Container{ 156 id: "myid", 157 config: &configs.Config{ 158 Namespaces: []configs.Namespace{ 159 {Type: configs.NEWPID}, 160 {Type: configs.NEWNS}, 161 {Type: configs.NEWNET, Path: expectedNetworkPath}, 162 {Type: configs.NEWUTS}, 163 // emulate host for IPC 164 //{Type: configs.NEWIPC}, 165 {Type: configs.NEWCGROUP}, 166 }, 167 }, 168 initProcess: &mockProcess{ 169 _pid: pid, 170 started: 10, 171 }, 172 cgroupManager: &mockCgroupManager{ 173 pids: []int{1, 2, 3}, 174 paths: map[string]string{ 175 "memory": expectedMemoryPath, 176 }, 177 }, 178 } 179 container.state = &createdState{c: container} 180 state, err := container.State() 181 if err != nil { 182 t.Fatal(err) 183 } 184 if state.InitProcessPid != pid { 185 t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid) 186 } 187 if state.InitProcessStartTime != 10 { 188 t.Fatalf("expected process start time 10 but received %d", state.InitProcessStartTime) 189 } 190 paths := state.CgroupPaths 191 if paths == nil { 192 t.Fatal("cgroup paths should not be nil") 193 } 194 if memPath := paths["memory"]; memPath != expectedMemoryPath { 195 t.Fatalf("expected memory path %q but received %q", expectedMemoryPath, memPath) 196 } 197 for _, ns := range container.config.Namespaces { 198 path := state.NamespacePaths[ns.Type] 199 if path == "" { 200 t.Fatalf("expected non nil namespace path for %s", ns.Type) 201 } 202 if ns.Type == configs.NEWNET { 203 if path != expectedNetworkPath { 204 t.Fatalf("expected path %q but received %q", expectedNetworkPath, path) 205 } 206 } else { 207 file := "" 208 switch ns.Type { 209 case configs.NEWNET: 210 file = "net" 211 case configs.NEWNS: 212 file = "mnt" 213 case configs.NEWPID: 214 file = "pid" 215 case configs.NEWIPC: 216 file = "ipc" 217 case configs.NEWUSER: 218 file = "user" 219 case configs.NEWUTS: 220 file = "uts" 221 case configs.NEWCGROUP: 222 file = "cgroup" 223 } 224 expected := fmt.Sprintf("/proc/%d/ns/%s", pid, file) 225 if expected != path { 226 t.Fatalf("expected path %q but received %q", expected, path) 227 } 228 } 229 } 230 } 231 232 func TestGetContainerStateAfterUpdate(t *testing.T) { 233 pid := os.Getpid() 234 stat, err := system.Stat(pid) 235 if err != nil { 236 t.Fatal(err) 237 } 238 239 container := &Container{ 240 stateDir: t.TempDir(), 241 id: "myid", 242 config: &configs.Config{ 243 Namespaces: []configs.Namespace{ 244 {Type: configs.NEWPID}, 245 {Type: configs.NEWNS}, 246 {Type: configs.NEWNET}, 247 {Type: configs.NEWUTS}, 248 {Type: configs.NEWIPC}, 249 }, 250 Cgroups: &configs.Cgroup{ 251 Resources: &configs.Resources{ 252 Memory: 1024, 253 }, 254 }, 255 }, 256 initProcess: &mockProcess{ 257 _pid: pid, 258 started: stat.StartTime, 259 }, 260 cgroupManager: &mockCgroupManager{}, 261 } 262 container.state = &createdState{c: container} 263 state, err := container.State() 264 if err != nil { 265 t.Fatal(err) 266 } 267 if state.InitProcessPid != pid { 268 t.Fatalf("expected pid %d but received %d", pid, state.InitProcessPid) 269 } 270 if state.InitProcessStartTime != stat.StartTime { 271 t.Fatalf("expected process start time %d but received %d", stat.StartTime, state.InitProcessStartTime) 272 } 273 if state.Config.Cgroups.Resources.Memory != 1024 { 274 t.Fatalf("expected Memory to be 1024 but received %q", state.Config.Cgroups.Memory) 275 } 276 277 // Set initProcessStartTime so we fake to be running 278 container.initProcessStartTime = state.InitProcessStartTime 279 container.state = &runningState{c: container} 280 newConfig := container.Config() 281 newConfig.Cgroups.Resources.Memory = 2048 282 if err := container.Set(newConfig); err != nil { 283 t.Fatal(err) 284 } 285 state, err = container.State() 286 if err != nil { 287 t.Fatal(err) 288 } 289 if state.Config.Cgroups.Resources.Memory != 2048 { 290 t.Fatalf("expected Memory to be 2048 but received %q", state.Config.Cgroups.Memory) 291 } 292 }