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  }