github.com/elfadel/cilium@v1.6.12/pkg/datapath/loader/cache_test.go (about)

     1  // Copyright 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  	"time"
    25  
    26  	"github.com/cilium/cilium/pkg/datapath/linux"
    27  	"github.com/cilium/cilium/pkg/testutils"
    28  
    29  	. "gopkg.in/check.v1"
    30  )
    31  
    32  func (s *LoaderTestSuite) TestobjectCache(c *C) {
    33  	tmpDir, err := ioutil.TempDir("", "cilium_test")
    34  	c.Assert(err, IsNil)
    35  	defer os.RemoveAll(tmpDir)
    36  
    37  	ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
    38  	defer cancel()
    39  
    40  	cache := newObjectCache(linux.NewDatapath(linux.DatapathConfiguration{}, nil), nil, tmpDir)
    41  	realEP := testutils.NewTestEndpoint()
    42  
    43  	// First run should compile and generate the object.
    44  	_, isNew, err := cache.fetchOrCompile(ctx, &realEP, nil)
    45  	c.Assert(err, IsNil)
    46  	c.Assert(isNew, Equals, true)
    47  
    48  	// Same EP should not be compiled twice.
    49  	_, isNew, err = cache.fetchOrCompile(ctx, &realEP, nil)
    50  	c.Assert(err, IsNil)
    51  	c.Assert(isNew, Equals, false)
    52  
    53  	// Changing the ID should not generate a new object.
    54  	realEP.Id++
    55  	_, isNew, err = cache.fetchOrCompile(ctx, &realEP, nil)
    56  	c.Assert(err, IsNil)
    57  	c.Assert(isNew, Equals, false)
    58  
    59  	// Changing a setting on the EP should generate a new object.
    60  	realEP.Opts.SetBool("foo", true)
    61  	_, isNew, err = cache.fetchOrCompile(ctx, &realEP, nil)
    62  	c.Assert(err, IsNil)
    63  	c.Assert(isNew, Equals, true)
    64  }
    65  
    66  type buildResult struct {
    67  	goroutine int
    68  	path      string
    69  	compiled  bool
    70  	err       error
    71  }
    72  
    73  func receiveResult(c *C, results chan buildResult) (*buildResult, error) {
    74  	select {
    75  	case result := <-results:
    76  		if result.err != nil {
    77  			return nil, result.err
    78  		}
    79  		return &result, nil
    80  	case <-time.After(contextTimeout):
    81  		return nil, fmt.Errorf("Timed out waiting for goroutines to return")
    82  	}
    83  }
    84  
    85  func (s *LoaderTestSuite) TestobjectCacheParallel(c *C) {
    86  	tmpDir, err := ioutil.TempDir("", "cilium_test")
    87  	c.Assert(err, IsNil)
    88  	defer os.RemoveAll(tmpDir)
    89  
    90  	ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
    91  	defer cancel()
    92  
    93  	tests := []struct {
    94  		description string
    95  		builds      int
    96  		divisor     int
    97  	}{
    98  		{
    99  			description: "One build, multiple blocking goroutines",
   100  			builds:      8,
   101  			divisor:     8,
   102  		},
   103  		{
   104  			description: "Eight builds, half compile, half block",
   105  			builds:      8,
   106  			divisor:     2,
   107  		},
   108  		{
   109  			description: "Eight unique builds",
   110  			builds:      8,
   111  			divisor:     1,
   112  		},
   113  	}
   114  
   115  	for _, t := range tests {
   116  		c.Logf("  %s", t.description)
   117  
   118  		results := make(chan buildResult, t.builds)
   119  		cache := newObjectCache(linux.NewDatapath(linux.DatapathConfiguration{}, nil), nil, tmpDir)
   120  		for i := 0; i < t.builds; i++ {
   121  			go func(i int) {
   122  				ep := testutils.NewTestEndpoint()
   123  				opt := fmt.Sprintf("OPT%d", i/t.divisor)
   124  				ep.Opts.SetBool(opt, true)
   125  				path, isNew, err := cache.fetchOrCompile(ctx, &ep, nil)
   126  				results <- buildResult{
   127  					goroutine: i,
   128  					path:      path,
   129  					compiled:  isNew,
   130  					err:       err,
   131  				}
   132  			}(i)
   133  		}
   134  
   135  		// First result will always be a compilation for the new set of options
   136  		compiled := make(map[string]int, t.builds)
   137  		used := make(map[string]int, t.builds)
   138  		for i := 0; i < t.builds; i++ {
   139  			result, err := receiveResult(c, results)
   140  			c.Assert(err, IsNil)
   141  
   142  			used[result.path] = used[result.path] + 1
   143  			if result.compiled {
   144  				compiled[result.path] = compiled[result.path] + 1
   145  			}
   146  		}
   147  
   148  		c.Assert(len(compiled), Equals, t.builds/t.divisor)
   149  		c.Assert(len(used), Equals, t.builds/t.divisor)
   150  		for _, templateCompileCount := range compiled {
   151  			// Only one goroutine compiles each template
   152  			c.Assert(templateCompileCount, Equals, 1)
   153  		}
   154  		for _, templateUseCount := range used {
   155  			// Based on the test parameters, a number of goroutines
   156  			// may share the same template.
   157  			c.Assert(templateUseCount, Equals, t.divisor)
   158  		}
   159  	}
   160  }