github.com/elfadel/cilium@v1.6.12/pkg/datapath/loader/loader_test.go (about) 1 // Copyright 2018-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build privileged_tests 16 17 package loader 18 19 import ( 20 "context" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path/filepath" 25 "strings" 26 "testing" 27 "time" 28 29 "github.com/cilium/cilium/pkg/bpf" 30 "github.com/cilium/cilium/pkg/datapath/linux" 31 "github.com/cilium/cilium/pkg/elf" 32 bpfconfig "github.com/cilium/cilium/pkg/maps/configmap" 33 "github.com/cilium/cilium/pkg/maps/policymap" 34 "github.com/cilium/cilium/pkg/testutils" 35 36 "github.com/vishvananda/netlink" 37 . "gopkg.in/check.v1" 38 ) 39 40 // Hook up gocheck into the "go test" runner. 41 type LoaderTestSuite struct{} 42 43 var ( 44 _ = Suite(&LoaderTestSuite{}) 45 contextTimeout = 10 * time.Second 46 benchTimeout = 5*time.Minute + 5*time.Second 47 48 dirInfo *directoryInfo 49 ep = testutils.NewTestEndpoint() 50 bpfDir = filepath.Join(testutils.CiliumRootDir, "bpf") 51 ) 52 53 // SetTestIncludes allows test files to configure additional include flags. 54 func SetTestIncludes(includes []string) { 55 testIncludes = includes 56 } 57 58 func Test(t *testing.T) { 59 TestingT(t) 60 } 61 62 func (s *LoaderTestSuite) SetUpSuite(c *C) { 63 SetTestIncludes([]string{ 64 fmt.Sprintf("-I%s", bpfDir), 65 fmt.Sprintf("-I%s", filepath.Join(bpfDir, "include")), 66 }) 67 68 err := bpf.ConfigureResourceLimits() 69 c.Assert(err, IsNil) 70 sourceFile := filepath.Join(bpfDir, endpointProg) 71 err = os.Symlink(sourceFile, endpointProg) 72 c.Assert(err, IsNil) 73 } 74 75 func (s *LoaderTestSuite) TearDownSuite(c *C) { 76 SetTestIncludes(nil) 77 os.RemoveAll(endpointProg) 78 } 79 80 func (s *LoaderTestSuite) TearDownTest(c *C) { 81 // Old map names as created by older versions of these tests 82 // 83 // FIXME GH-6701: Remove for 1.5.0 84 os.Remove("/sys/fs/bpf/tc/globals/cilium_policy_foo") 85 os.Remove("/sys/fs/bpf/tc/globals/cilium_calls_111") 86 os.Remove("/sys/fs/bpf/tc/globals/cilium_ep_config_111") 87 88 files, err := filepath.Glob("/sys/fs/bpf/tc/globals/test_*") 89 if err != nil { 90 panic(err) 91 } 92 for _, f := range files { 93 if err := os.Remove(f); err != nil { 94 panic(err) 95 } 96 } 97 } 98 99 // runTests configures devices for running the whole testsuite, and runs the 100 // tests. It is kept separate from TestMain() so that this function can defer 101 // cleanups and pass the exit code of the test run to the caller which can run 102 // os.Exit() with the result. 103 func runTests(m *testing.M) (int, error) { 104 SetTestIncludes([]string{"-I/usr/include/x86_64-linux-gnu/"}) 105 defer SetTestIncludes(nil) 106 107 tmpDir, err := ioutil.TempDir("/tmp/", "cilium_") 108 if err != nil { 109 return 1, fmt.Errorf("Failed to create temporary directory: %s", err) 110 } 111 defer os.RemoveAll(tmpDir) 112 dirInfo = getDirs(tmpDir) 113 114 cleanup, err := prepareEnv(&ep) 115 if err != nil { 116 return 1, fmt.Errorf("Failed to prepare environment: %s", err) 117 } 118 defer func() { 119 if err := cleanup(); err != nil { 120 log.Error(err.Error()) 121 } 122 }() 123 124 return m.Run(), nil 125 } 126 127 func TestMain(m *testing.M) { 128 exitCode, err := runTests(m) 129 if err != nil { 130 log.Fatal(err) 131 } 132 os.Exit(exitCode) 133 } 134 135 func prepareEnv(ep *testutils.TestEndpoint) (func() error, error) { 136 link := netlink.Dummy{ 137 LinkAttrs: netlink.LinkAttrs{ 138 Name: ep.InterfaceName(), 139 }, 140 } 141 if err := netlink.LinkAdd(&link); err != nil { 142 if !os.IsExist(err) { 143 return nil, fmt.Errorf("Failed to add link: %s", err) 144 } 145 } 146 cleanupFn := func() error { 147 if err := netlink.LinkDel(&link); err != nil { 148 return fmt.Errorf("Failed to delete link: %s", err) 149 } 150 return nil 151 } 152 return cleanupFn, nil 153 } 154 155 func getDirs(tmpDir string) *directoryInfo { 156 return &directoryInfo{ 157 Library: bpfDir, 158 Runtime: bpfDir, 159 State: bpfDir, 160 Output: tmpDir, 161 } 162 } 163 164 // TestCompileAndLoad checks that the datapath can be compiled and loaded. 165 func (s *LoaderTestSuite) TestCompileAndLoad(c *C) { 166 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 167 defer cancel() 168 stats := &SpanStat{} 169 170 err := compileAndLoad(ctx, &ep, dirInfo, stats) 171 c.Assert(err, IsNil) 172 } 173 174 // TestReload compiles and attaches the datapath multiple times. 175 func (s *LoaderTestSuite) TestReload(c *C) { 176 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 177 defer cancel() 178 179 err := compileDatapath(ctx, dirInfo, true, log) 180 c.Assert(err, IsNil) 181 182 objPath := fmt.Sprintf("%s/%s", dirInfo.Output, endpointObj) 183 err = replaceDatapath(ctx, ep.InterfaceName(), objPath, symbolFromEndpoint, dirIngress) 184 c.Assert(err, IsNil) 185 186 err = replaceDatapath(ctx, ep.InterfaceName(), objPath, symbolFromEndpoint, dirIngress) 187 c.Assert(err, IsNil) 188 } 189 190 // TestCompileFailure attempts to compile then cancels the context and ensures 191 // that the failure paths may be hit. 192 func (s *LoaderTestSuite) TestCompileFailure(c *C) { 193 ctx, cancel := context.WithTimeout(context.Background(), contextTimeout) 194 defer cancel() 195 196 exit := make(chan bool) 197 defer close(exit) 198 go func() { 199 select { 200 case <-time.After(100 * time.Millisecond): 201 cancel() 202 case <-exit: 203 break 204 } 205 }() 206 207 timeout := time.Now().Add(contextTimeout) 208 var err error 209 stats := &SpanStat{} 210 for err == nil && time.Now().Before(timeout) { 211 err = compileAndLoad(ctx, &ep, dirInfo, stats) 212 } 213 c.Assert(err, NotNil) 214 } 215 216 // BenchmarkCompileOnly benchmarks the just the entire compilation process. 217 func BenchmarkCompileOnly(b *testing.B) { 218 ctx, cancel := context.WithTimeout(context.Background(), benchTimeout) 219 defer cancel() 220 221 b.ResetTimer() 222 for i := 0; i < b.N; i++ { 223 debug := false // Otherwise we compile lots more. 224 if err := compileDatapath(ctx, dirInfo, debug, log); err != nil { 225 b.Fatal(err) 226 } 227 } 228 } 229 230 // BenchmarkCompileAndLoad benchmarks the entire compilation + loading process. 231 func BenchmarkCompileAndLoad(b *testing.B) { 232 stats := &SpanStat{} 233 ctx, cancel := context.WithTimeout(context.Background(), benchTimeout) 234 defer cancel() 235 236 b.ResetTimer() 237 for i := 0; i < b.N; i++ { 238 if err := compileAndLoad(ctx, &ep, dirInfo, stats); err != nil { 239 b.Fatal(err) 240 } 241 } 242 } 243 244 // BenchmarkReplaceDatapath compiles the datapath program, then benchmarks only 245 // the loading of the program into the kernel. 246 func BenchmarkReplaceDatapath(b *testing.B) { 247 ctx, cancel := context.WithTimeout(context.Background(), benchTimeout) 248 defer cancel() 249 250 if err := compileDatapath(ctx, dirInfo, false, log); err != nil { 251 b.Fatal(err) 252 } 253 objPath := fmt.Sprintf("%s/%s", dirInfo.Output, endpointObj) 254 b.ResetTimer() 255 for i := 0; i < b.N; i++ { 256 if err := replaceDatapath(ctx, ep.InterfaceName(), objPath, symbolFromEndpoint, dirIngress); err != nil { 257 b.Fatal(err) 258 } 259 } 260 } 261 262 // BenchmarkCompileOrLoad benchmarks the ELF rewrite process. 263 func BenchmarkCompileOrLoad(b *testing.B) { 264 ignorePrefixes := append(ignoredELFPrefixes, "test_cilium_policy") 265 for _, p := range ignoredELFPrefixes { 266 if strings.HasPrefix(p, "cilium_") { 267 testPrefix := fmt.Sprintf("test_%s", p) 268 ignorePrefixes = append(ignorePrefixes, testPrefix) 269 } 270 } 271 elf.IgnoreSymbolPrefixes(ignorePrefixes) 272 273 SetTestIncludes([]string{ 274 fmt.Sprintf("-I%s", bpfDir), 275 fmt.Sprintf("-I%s", filepath.Join(bpfDir, "include")), 276 }) 277 defer SetTestIncludes(nil) 278 279 elfMapPrefixes = []string{ 280 fmt.Sprintf("test_%s", policymap.MapName), 281 fmt.Sprintf("test_%s", CallsMapName), 282 fmt.Sprintf("test_%s", bpfconfig.MapNamePrefix), 283 } 284 285 sourceFile := filepath.Join(bpfDir, endpointProg) 286 if err := os.Symlink(sourceFile, endpointProg); err != nil { 287 b.Fatal(err) 288 } 289 defer os.RemoveAll(endpointProg) 290 291 ctx, cancel := context.WithTimeout(context.Background(), benchTimeout) 292 defer cancel() 293 294 tmpDir, err := ioutil.TempDir("", "cilium_test") 295 if err != nil { 296 b.Fatal(err) 297 } 298 defer os.RemoveAll(tmpDir) 299 300 epDir := ep.StateDir() 301 if err := os.MkdirAll(epDir, 0755); err != nil { 302 b.Fatal(err) 303 } 304 defer os.RemoveAll(epDir) 305 306 templateCache = newObjectCache(linux.NewDatapath(linux.DatapathConfiguration{}, nil), nil, tmpDir) 307 if err := CompileOrLoad(ctx, &ep, nil); err != nil { 308 log.Warningf("Failure in %s: %s", tmpDir, err) 309 time.Sleep(1 * time.Minute) 310 b.Fatal(err) 311 } 312 b.ResetTimer() 313 for i := 0; i < b.N; i++ { 314 if err := CompileOrLoad(ctx, &ep, nil); err != nil { 315 b.Fatal(err) 316 } 317 } 318 }