...

Source file src/github.com/opencontainers/runc/libcontainer/cgroups/systemd/v1_test.go

Documentation: github.com/opencontainers/runc/libcontainer/cgroups/systemd

     1  package systemd
     2  
     3  import (
     4  	"os"
     5  	"os/exec"
     6  	"strings"
     7  	"testing"
     8  
     9  	"golang.org/x/sys/unix"
    10  
    11  	"github.com/opencontainers/runc/libcontainer/cgroups"
    12  	"github.com/opencontainers/runc/libcontainer/configs"
    13  )
    14  
    15  func TestFreezeBeforeSet(t *testing.T) {
    16  	requireV1(t)
    17  
    18  	testCases := []struct {
    19  		desc string
    20  		// Test input.
    21  		cg        *configs.Cgroup
    22  		preFreeze bool
    23  		// Expected values.
    24  		// Before unit creation (Apply).
    25  		freeze0, thaw0 bool
    26  		// After unit creation.
    27  		freeze1, thaw1 bool
    28  	}{
    29  		{
    30  			// A slice with SkipDevices.
    31  			desc: "slice,skip-devices",
    32  			cg: &configs.Cgroup{
    33  				Name:   "system-runc_test_freeze_1.slice",
    34  				Parent: "system.slice",
    35  				Resources: &configs.Resources{
    36  					SkipDevices: true,
    37  				},
    38  			},
    39  			// Expected.
    40  			freeze0: false,
    41  			thaw0:   false,
    42  			freeze1: false,
    43  			thaw1:   false,
    44  		},
    45  		{
    46  			// A scope with SkipDevices. Not a realistic scenario with runc
    47  			// (as container can't have SkipDevices == true), but possible
    48  			// for a standalone cgroup manager.
    49  			desc: "scope,skip-devices",
    50  			cg: &configs.Cgroup{
    51  				ScopePrefix: "test",
    52  				Name:        "testFreeze2",
    53  				Parent:      "system.slice",
    54  				Resources: &configs.Resources{
    55  					SkipDevices: true,
    56  				},
    57  			},
    58  			// Expected.
    59  			freeze0: false,
    60  			thaw0:   false,
    61  			freeze1: false,
    62  			thaw1:   false,
    63  		},
    64  		{
    65  			// A slice that is about to be frozen in Set.
    66  			desc: "slice,will-freeze",
    67  			cg: &configs.Cgroup{
    68  				Name:   "system-runc_test_freeze_3.slice",
    69  				Parent: "system.slice",
    70  				Resources: &configs.Resources{
    71  					Freezer: configs.Frozen,
    72  				},
    73  			},
    74  			// Expected.
    75  			freeze0: true,
    76  			thaw0:   false,
    77  			freeze1: true,
    78  			thaw1:   false,
    79  		},
    80  		{
    81  			// A pre-frozen slice that should stay frozen.
    82  			desc: "slice,pre-frozen,will-freeze",
    83  			cg: &configs.Cgroup{
    84  				Name:   "system-runc_test_freeze_4.slice",
    85  				Parent: "system.slice",
    86  				Resources: &configs.Resources{
    87  					Freezer: configs.Frozen,
    88  				},
    89  			},
    90  			preFreeze: true,
    91  			// Expected.
    92  			freeze0: true, // not actually frozen yet.
    93  			thaw0:   false,
    94  			freeze1: false,
    95  			thaw1:   false,
    96  		},
    97  		{
    98  			// A pre-frozen scope with skip devices set.
    99  			desc: "scope,pre-frozen,skip-devices",
   100  			cg: &configs.Cgroup{
   101  				ScopePrefix: "test",
   102  				Name:        "testFreeze5",
   103  				Parent:      "system.slice",
   104  				Resources: &configs.Resources{
   105  					SkipDevices: true,
   106  				},
   107  			},
   108  			preFreeze: true,
   109  			// Expected.
   110  			freeze0: false,
   111  			thaw0:   false,
   112  			freeze1: false,
   113  			thaw1:   false,
   114  		},
   115  		{
   116  			// A pre-frozen scope which will be thawed.
   117  			desc: "scope,pre-frozen",
   118  			cg: &configs.Cgroup{
   119  				ScopePrefix: "test",
   120  				Name:        "testFreeze6",
   121  				Parent:      "system.slice",
   122  				Resources:   &configs.Resources{},
   123  			},
   124  			preFreeze: true,
   125  			// Expected.
   126  			freeze0: true, // not actually frozen yet.
   127  			thaw0:   true,
   128  			freeze1: false,
   129  			thaw1:   false,
   130  		},
   131  	}
   132  
   133  	for _, tc := range testCases {
   134  		tc := tc
   135  		t.Run(tc.desc, func(t *testing.T) {
   136  			m, err := NewLegacyManager(tc.cg, nil)
   137  			if err != nil {
   138  				t.Fatal(err)
   139  			}
   140  			defer m.Destroy() //nolint:errcheck
   141  			lm := m.(*legacyManager)
   142  
   143  			// Checks for a non-existent unit.
   144  			freeze, thaw, err := lm.freezeBeforeSet(getUnitName(tc.cg), tc.cg.Resources)
   145  			if err != nil {
   146  				t.Fatal(err)
   147  			}
   148  			if freeze != tc.freeze0 || thaw != tc.thaw0 {
   149  				t.Errorf("before Apply (non-existent unit): expected freeze: %v, thaw: %v, got freeze: %v, thaw: %v",
   150  					tc.freeze0, tc.thaw0, freeze, thaw)
   151  			}
   152  
   153  			// Create systemd unit.
   154  			pid := -1
   155  			if strings.HasSuffix(getUnitName(tc.cg), ".scope") {
   156  				// Scopes require a process inside.
   157  				cmd := exec.Command("bash", "-c", "sleep 1m")
   158  				if err := cmd.Start(); err != nil {
   159  					t.Fatal(err)
   160  				}
   161  				pid = cmd.Process.Pid
   162  				// Make sure to not leave a zombie.
   163  				defer func() {
   164  					// These may fail, we don't care.
   165  					_ = cmd.Process.Kill()
   166  					_ = cmd.Wait()
   167  				}()
   168  			}
   169  			if err := m.Apply(pid); err != nil {
   170  				t.Fatal(err)
   171  			}
   172  			if tc.preFreeze {
   173  				if err := m.Freeze(configs.Frozen); err != nil {
   174  					t.Error(err)
   175  					return // no more checks
   176  				}
   177  			}
   178  			freeze, thaw, err = lm.freezeBeforeSet(getUnitName(tc.cg), tc.cg.Resources)
   179  			if err != nil {
   180  				t.Error(err)
   181  				return // no more checks
   182  			}
   183  			if freeze != tc.freeze1 || thaw != tc.thaw1 {
   184  				t.Errorf("expected freeze: %v, thaw: %v, got freeze: %v, thaw: %v",
   185  					tc.freeze1, tc.thaw1, freeze, thaw)
   186  			}
   187  			// Destroy() timeouts on a frozen container, so we need to thaw it.
   188  			if tc.preFreeze {
   189  				if err := m.Freeze(configs.Thawed); err != nil {
   190  					t.Error(err)
   191  				}
   192  			}
   193  			// Destroy() does not kill processes in cgroup, so we should.
   194  			if pid != -1 {
   195  				if err = unix.Kill(pid, unix.SIGKILL); err != nil {
   196  					t.Errorf("unable to kill pid %d: %s", pid, err)
   197  				}
   198  			}
   199  			// Not really needed, but may help catch some bugs.
   200  			if err := m.Destroy(); err != nil {
   201  				t.Errorf("destroy: %s", err)
   202  			}
   203  		})
   204  	}
   205  }
   206  
   207  // requireV1 skips the test unless a set of requirements (cgroup v1,
   208  // systemd, root) is met.
   209  func requireV1(t *testing.T) {
   210  	t.Helper()
   211  	if cgroups.IsCgroup2UnifiedMode() {
   212  		t.Skip("Test requires cgroup v1.")
   213  	}
   214  	if !IsRunningSystemd() {
   215  		t.Skip("Test requires systemd.")
   216  	}
   217  	if os.Geteuid() != 0 {
   218  		t.Skip("Test requires root.")
   219  	}
   220  }
   221  

View as plain text