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 }