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  }