github.com/cilium/cilium@v1.16.2/pkg/datapath/linux/sysctl/sysctl_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package sysctl
     5  
     6  import (
     7  	"context"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/cilium/hive/cell"
    13  	"github.com/cilium/hive/hivetest"
    14  	"github.com/cilium/statedb"
    15  	"github.com/cilium/statedb/reconciler"
    16  	"github.com/spf13/afero"
    17  	"github.com/stretchr/testify/assert"
    18  	"go.uber.org/goleak"
    19  
    20  	"github.com/cilium/cilium/pkg/datapath/tables"
    21  	"github.com/cilium/cilium/pkg/hive"
    22  	"github.com/cilium/cilium/pkg/time"
    23  )
    24  
    25  func TestFullPath(t *testing.T) {
    26  	testCases := []struct {
    27  		name        []string
    28  		expected    string
    29  		expectedErr bool
    30  	}{
    31  		{
    32  			name:     []string{"net", "ipv4", "ip_forward"},
    33  			expected: "/proc/sys/net/ipv4/ip_forward",
    34  		},
    35  		{
    36  			name:     []string{"net", "ipv4", "conf", "all", "forwarding"},
    37  			expected: "/proc/sys/net/ipv4/conf/all/forwarding",
    38  		},
    39  		{
    40  			name:     []string{"net", "ipv6", "conf", "all", "forwarding"},
    41  			expected: "/proc/sys/net/ipv6/conf/all/forwarding",
    42  		},
    43  		{
    44  			name:     []string{"net", "ipv6", "conf", "eth0.100", "forwarding"},
    45  			expected: "/proc/sys/net/ipv6/conf/eth0.100/forwarding",
    46  		},
    47  		{
    48  			name:     []string{"foo", "bar"},
    49  			expected: "/proc/sys/foo/bar",
    50  		},
    51  		{
    52  			name:        []string{"double", "", "dot"},
    53  			expectedErr: true,
    54  		},
    55  		{
    56  			name:        []string{"invalid", "char$"},
    57  			expectedErr: true,
    58  		},
    59  		{
    60  			name:     []string{"Foo", "Bar"},
    61  			expected: "/proc/sys/Foo/Bar",
    62  		},
    63  	}
    64  
    65  	for _, tc := range testCases {
    66  		path, err := parameterPath("/proc", tc.name)
    67  		if tc.expectedErr {
    68  			assert.Error(t, err)
    69  		} else {
    70  			assert.NoError(t, err)
    71  			assert.Equal(t, path, tc.expected)
    72  		}
    73  	}
    74  }
    75  
    76  func TestWaitForReconciliation(t *testing.T) {
    77  	defer goleak.VerifyNone(t)
    78  
    79  	const paramName = "fake-parameter"
    80  
    81  	var (
    82  		db       *statedb.DB
    83  		settings statedb.RWTable[*tables.Sysctl]
    84  	)
    85  
    86  	hive := hive.New(
    87  		cell.Module(
    88  			"sysctl-test",
    89  			"sysctl-test",
    90  
    91  			cell.Provide(func(db *statedb.DB) (statedb.RWTable[*tables.Sysctl], statedb.Index[*tables.Sysctl, reconciler.StatusKind], error) {
    92  				return tables.NewSysctlTable(db)
    93  			}),
    94  			cell.Invoke(func(db *statedb.DB, settings statedb.RWTable[*tables.Sysctl]) {
    95  				db.RegisterTable(settings)
    96  			}),
    97  
    98  			cell.Invoke(func(statedb *statedb.DB, tb statedb.RWTable[*tables.Sysctl]) {
    99  				db = statedb
   100  				settings = tb
   101  			}),
   102  		),
   103  	)
   104  
   105  	tlog := hivetest.Logger(t)
   106  	assert.NoError(t, hive.Start(tlog, context.Background()))
   107  
   108  	time.MaxInternalTimerDelay = time.Millisecond
   109  	t.Cleanup(func() { time.MaxInternalTimerDelay = 0 })
   110  
   111  	sysctl := &reconcilingSysctl{db, settings, nil, ""}
   112  	sysctl.Enable([]string{paramName})
   113  
   114  	// waitForReconciliation should timeout
   115  	assert.Error(t, sysctl.waitForReconciliation([]string{paramName}))
   116  
   117  	// fake a successful reconciliation
   118  	txn := db.WriteTxn(settings)
   119  	old, _, found := settings.Get(txn, tables.SysctlNameIndex.Query(paramName))
   120  	_, exist, err := settings.Insert(txn, old.Clone().SetStatus(reconciler.StatusDone()))
   121  	txn.Commit()
   122  
   123  	assert.True(t, found)
   124  	assert.Equal(t, old.Status.Kind, reconciler.StatusKindPending)
   125  	assert.True(t, exist)
   126  	assert.NoError(t, err)
   127  
   128  	// waitForReconciliation should return without error
   129  	assert.NoError(t, sysctl.waitForReconciliation([]string{paramName}))
   130  
   131  	assert.NoError(t, hive.Stop(tlog, context.Background()))
   132  }
   133  
   134  func TestSysctl(t *testing.T) {
   135  	defer goleak.VerifyNone(t)
   136  
   137  	settings := [][]string{
   138  		{"net", "ipv4", "ip_forward"},
   139  		{"net", "ipv4", "conf", "all", "forwarding"},
   140  		{"net", "ipv6", "conf", "all", "forwarding"},
   141  	}
   142  
   143  	var sysctl Sysctl
   144  
   145  	hive := hive.New(
   146  		cell.Module(
   147  			"sysctl-test",
   148  			"sysctl-test",
   149  			cell.Config(Config{}),
   150  
   151  			cell.Provide(
   152  				func() afero.Fs {
   153  					return afero.NewMemMapFs()
   154  				},
   155  			),
   156  			cell.Provide(
   157  				newReconcilingSysctl,
   158  				tables.NewSysctlTable,
   159  				newReconciler,
   160  				newOps,
   161  			),
   162  		),
   163  
   164  		cell.Invoke(func(s Sysctl) {
   165  			sysctl = s
   166  		}),
   167  		cell.Invoke(func(fs afero.Fs) {
   168  			for _, s := range settings {
   169  				path := sysctlToPath(s)
   170  				if err := fs.MkdirAll(filepath.Dir(path), os.ModeDir); err != nil {
   171  					t.Fatalf("unable to create directory %q: %s", filepath.Dir(path), err)
   172  				}
   173  				f, err := fs.Create(path)
   174  				if err != nil {
   175  					t.Fatalf("unable to create test file %q: %s", path, err)
   176  				}
   177  				if _, err := f.WriteString("0"); err != nil {
   178  					t.Fatalf("unable to write to test file %q: %s", path, err)
   179  				}
   180  				if err := f.Close(); err != nil {
   181  					t.Fatalf("unable to close test file %q: %s", path, err)
   182  				}
   183  			}
   184  		}),
   185  	)
   186  
   187  	tlog := hivetest.Logger(t)
   188  	assert.NoError(t, hive.Start(tlog, context.Background()))
   189  
   190  	for _, s := range settings {
   191  		assert.NoError(t, sysctl.Write(s, "1"))
   192  
   193  		val, err := sysctl.Read(s)
   194  		assert.NoError(t, err)
   195  		assert.Equal(t, "1", val, "unexpected value for parameter %q", s)
   196  	}
   197  
   198  	for _, s := range settings {
   199  		assert.NoError(t, sysctl.WriteInt(s, 7))
   200  
   201  		val, err := sysctl.ReadInt(s)
   202  		assert.NoError(t, err)
   203  		assert.Equal(t, int64(7), val, "unexpected value for parameter %q", s)
   204  	}
   205  
   206  	for _, s := range settings {
   207  		assert.NoError(t, sysctl.Disable(s))
   208  
   209  		val, err := sysctl.Read(s)
   210  		assert.NoError(t, err)
   211  		assert.Equal(t, "0", val, "unexpected value for parameter %q", s)
   212  	}
   213  
   214  	for _, s := range settings {
   215  		assert.NoError(t, sysctl.Enable(s))
   216  
   217  		val, err := sysctl.Read(s)
   218  		assert.NoError(t, err)
   219  		assert.Equal(t, "1", val, "unexpected value for parameter %q", s)
   220  	}
   221  
   222  	batch := make([]tables.Sysctl, len(settings))
   223  	for i, s := range settings {
   224  		batch[i].Name = s
   225  		batch[i].Val = "2"
   226  	}
   227  	assert.NoError(t, sysctl.ApplySettings(batch))
   228  	for _, s := range batch {
   229  		val, err := sysctl.Read(s.Name)
   230  		assert.NoError(t, err)
   231  		assert.Equal(t, s.Val, val, "unexpected value %q for parameter %q", val, s.Name)
   232  	}
   233  
   234  	assert.NoError(t, hive.Stop(tlog, context.Background()))
   235  }
   236  
   237  func TestSysctlIgnoreErr(t *testing.T) {
   238  	defer goleak.VerifyNone(t)
   239  
   240  	parameter := tables.Sysctl{Name: []string{"net", "core", "bpf_jit_enable"}, Val: "1", IgnoreErr: true}
   241  
   242  	var sysctl Sysctl
   243  
   244  	hive := hive.New(
   245  		cell.Module(
   246  			"sysctl-test",
   247  			"sysctl-test",
   248  			cell.Config(Config{}),
   249  
   250  			cell.Provide(
   251  				func() afero.Fs {
   252  					return afero.NewMemMapFs()
   253  				},
   254  			),
   255  			cell.Provide(
   256  				newReconcilingSysctl,
   257  				tables.NewSysctlTable,
   258  				newReconciler,
   259  				newOps,
   260  			),
   261  		),
   262  
   263  		cell.Invoke(func(s Sysctl) {
   264  			sysctl = s
   265  		}),
   266  	)
   267  
   268  	tlog := hivetest.Logger(t)
   269  	assert.NoError(t, hive.Start(tlog, context.Background()))
   270  
   271  	// should not return an error since the parameter is marked as IgnoreErr
   272  	assert.NoError(t, sysctl.ApplySettings([]tables.Sysctl{parameter}))
   273  
   274  	assert.NoError(t, hive.Stop(tlog, context.Background()))
   275  }
   276  
   277  func sysctlToPath(name []string) string {
   278  	return filepath.Join(append([]string{"/proc", "sys"}, name...)...)
   279  }