github.com/zhyoulun/cilium@v1.6.12/pkg/kvstore/store/store_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 store
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/cilium/cilium/pkg/defaults"
    26  	"github.com/cilium/cilium/pkg/kvstore"
    27  	"github.com/cilium/cilium/pkg/lock"
    28  	"github.com/cilium/cilium/pkg/option"
    29  	"github.com/cilium/cilium/pkg/testutils"
    30  
    31  	. "gopkg.in/check.v1"
    32  )
    33  
    34  const (
    35  	testPrefix = "store-tests"
    36  )
    37  
    38  func Test(t *testing.T) {
    39  	TestingT(t)
    40  }
    41  
    42  type StoreSuite struct{}
    43  
    44  type StoreEtcdSuite struct {
    45  	StoreSuite
    46  }
    47  
    48  var _ = Suite(&StoreEtcdSuite{})
    49  
    50  func (e *StoreEtcdSuite) SetUpTest(c *C) {
    51  	kvstore.SetupDummy("etcd")
    52  }
    53  
    54  func (e *StoreEtcdSuite) TearDownTest(c *C) {
    55  	kvstore.Client().DeletePrefix(testPrefix)
    56  	kvstore.Client().Close()
    57  }
    58  
    59  type StoreConsulSuite struct {
    60  	StoreSuite
    61  }
    62  
    63  var _ = Suite(&StoreConsulSuite{})
    64  
    65  func (e *StoreConsulSuite) SetUpTest(c *C) {
    66  	kvstore.SetupDummy("consul")
    67  }
    68  
    69  func (e *StoreConsulSuite) TearDownTest(c *C) {
    70  	kvstore.Client().DeletePrefix(testPrefix)
    71  	kvstore.Client().Close()
    72  	time.Sleep(defaults.NodeDeleteDelay + 5*time.Second)
    73  }
    74  
    75  type TestType struct {
    76  	Name string
    77  }
    78  
    79  var _ = TestType{}
    80  
    81  func (t *TestType) GetKeyName() string          { return t.Name }
    82  func (t *TestType) DeepKeyCopy() LocalKey       { return &TestType{Name: t.Name} }
    83  func (t *TestType) Marshal() ([]byte, error)    { return json.Marshal(t) }
    84  func (t *TestType) Unmarshal(data []byte) error { return json.Unmarshal(data, t) }
    85  
    86  type opCounter struct {
    87  	deleted int
    88  	updated int
    89  }
    90  
    91  var (
    92  	counter     = map[string]*opCounter{}
    93  	counterLock lock.RWMutex
    94  )
    95  
    96  func (t *TestType) deleted() int {
    97  	counterLock.RLock()
    98  	defer counterLock.RUnlock()
    99  	return counter[t.Name].deleted
   100  }
   101  
   102  func (t *TestType) updated() int {
   103  	counterLock.RLock()
   104  	defer counterLock.RUnlock()
   105  	return counter[t.Name].updated
   106  }
   107  
   108  func initTestType(name string) TestType {
   109  	t := TestType{}
   110  	t.Name = name
   111  	counterLock.Lock()
   112  	counter[name] = &opCounter{}
   113  	counterLock.Unlock()
   114  	return t
   115  }
   116  
   117  type observer struct{}
   118  
   119  func (o *observer) OnUpdate(k Key) {
   120  	counterLock.Lock()
   121  	if c, ok := counter[k.(*TestType).Name]; ok {
   122  		c.updated++
   123  	}
   124  	counterLock.Unlock()
   125  }
   126  func (o *observer) OnDelete(k NamedKey) {
   127  	counterLock.Lock()
   128  	counter[k.(*TestType).Name].deleted++
   129  	counterLock.Unlock()
   130  }
   131  
   132  func newTestType() Key {
   133  	t := TestType{}
   134  	return &t
   135  }
   136  
   137  func (s *StoreSuite) TestStoreCreation(c *C) {
   138  	// Missing Prefix must result in error
   139  	store, err := JoinSharedStore(Configuration{})
   140  	c.Assert(err, ErrorMatches, "prefix must be specified")
   141  	c.Assert(store, IsNil)
   142  
   143  	// Missing KeyCreator must result in error
   144  	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune()})
   145  	c.Assert(err, ErrorMatches, "KeyCreator must be specified")
   146  	c.Assert(store, IsNil)
   147  
   148  	// Basic creation should result in default values
   149  	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType})
   150  	c.Assert(err, IsNil)
   151  	c.Assert(store, Not(IsNil))
   152  	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
   153  	store.Close()
   154  
   155  	// Test with kvstore client specified
   156  	store, err = JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType, Backend: kvstore.Client()})
   157  	c.Assert(err, IsNil)
   158  	c.Assert(store, Not(IsNil))
   159  	c.Assert(store.conf.SynchronizationInterval, Equals, option.Config.KVstorePeriodicSync)
   160  	store.Close()
   161  }
   162  
   163  func expect(check func() bool) error {
   164  	start := time.Now()
   165  	for {
   166  		if check() {
   167  			return nil
   168  		}
   169  
   170  		if time.Since(start) > defaults.NodeDeleteDelay+10*time.Second {
   171  			return fmt.Errorf("timeout while waiting for expected value")
   172  		}
   173  
   174  		time.Sleep(10 * time.Millisecond)
   175  	}
   176  }
   177  
   178  func (s *StoreSuite) TestStoreOperations(c *C) {
   179  	// Basic creation should result in default values
   180  	store, err := JoinSharedStore(Configuration{Prefix: testutils.RandomRune(), KeyCreator: newTestType, Observer: &observer{}})
   181  	c.Assert(err, IsNil)
   182  	c.Assert(store, Not(IsNil))
   183  	defer store.Close()
   184  
   185  	localKey1 := initTestType("local1")
   186  	localKey2 := initTestType("local2")
   187  	localKey3 := initTestType("local3")
   188  
   189  	err = store.UpdateLocalKeySync(&localKey1)
   190  	c.Assert(err, IsNil)
   191  	err = store.UpdateLocalKeySync(&localKey2)
   192  	c.Assert(err, IsNil)
   193  
   194  	// due to the short sync interval, it is possible that multiple updates
   195  	// have occurred, make the test reliable by succeeding on at lest one
   196  	// update
   197  	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
   198  	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)
   199  	c.Assert(expect(func() bool { return localKey3.updated() == 0 }), IsNil)
   200  
   201  	store.DeleteLocalKey(&localKey1)
   202  	// localKey1 will be deleted 2 times, one from local key and other from
   203  	// the kvstore watcher
   204  	c.Assert(expect(func() bool { return localKey1.deleted() == 2 }), IsNil)
   205  	c.Assert(expect(func() bool { return localKey2.deleted() == 0 }), IsNil)
   206  	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)
   207  
   208  	store.DeleteLocalKey(&localKey3)
   209  	// localKey3 won't be deleted because it was never added
   210  	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)
   211  
   212  	store.DeleteLocalKey(&localKey2)
   213  	c.Assert(expect(func() bool { return localKey1.deleted() == 2 }), IsNil)
   214  	// localKey2 will be deleted 2 times, one from local key and other from
   215  	// the kvstore watcher
   216  	c.Assert(expect(func() bool { return localKey2.deleted() == 2 }), IsNil)
   217  	c.Assert(expect(func() bool { return localKey3.deleted() == 0 }), IsNil)
   218  }
   219  
   220  func (s *StoreSuite) TestStorePeriodicSync(c *C) {
   221  	// Create a store with a very short periodic sync interval
   222  	store, err := JoinSharedStore(Configuration{
   223  		Prefix:                  testutils.RandomRune(),
   224  		KeyCreator:              newTestType,
   225  		SynchronizationInterval: 10 * time.Millisecond,
   226  		Observer:                &observer{},
   227  	})
   228  	c.Assert(err, IsNil)
   229  	c.Assert(store, Not(IsNil))
   230  	defer store.Close()
   231  
   232  	localKey1 := initTestType("local1")
   233  	localKey2 := initTestType("local2")
   234  
   235  	err = store.UpdateLocalKeySync(&localKey1)
   236  	c.Assert(err, IsNil)
   237  	err = store.UpdateLocalKeySync(&localKey2)
   238  	c.Assert(err, IsNil)
   239  
   240  	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
   241  	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)
   242  
   243  	store.DeleteLocalKey(&localKey1)
   244  	store.DeleteLocalKey(&localKey2)
   245  
   246  	c.Assert(expect(func() bool { return localKey1.deleted() == 1 }), IsNil)
   247  	c.Assert(expect(func() bool { return localKey2.deleted() == 1 }), IsNil)
   248  }
   249  
   250  func (s *StoreSuite) TestStoreLocalKeyProtection(c *C) {
   251  	store, err := JoinSharedStore(Configuration{
   252  		Prefix:                  testutils.RandomRune(),
   253  		KeyCreator:              newTestType,
   254  		SynchronizationInterval: time.Hour, // ensure that periodic sync does not interfer
   255  		Observer:                &observer{},
   256  	})
   257  	c.Assert(err, IsNil)
   258  	c.Assert(store, Not(IsNil))
   259  	defer store.Close()
   260  
   261  	localKey1 := initTestType("local1")
   262  
   263  	err = store.UpdateLocalKeySync(&localKey1)
   264  	c.Assert(err, IsNil)
   265  
   266  	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
   267  	// delete all keys
   268  	kvstore.Client().DeletePrefix(store.conf.Prefix)
   269  	c.Assert(expect(func() bool {
   270  		v, err := kvstore.Client().Get(store.keyPath(&localKey1))
   271  		return err == nil && string(v) != ""
   272  	}), IsNil)
   273  }
   274  
   275  func setupStoreCollaboration(c *C, storePrefix, keyPrefix string) *SharedStore {
   276  	store, err := JoinSharedStore(Configuration{
   277  		Prefix:                  storePrefix,
   278  		KeyCreator:              newTestType,
   279  		SynchronizationInterval: time.Second,
   280  		Observer:                &observer{},
   281  	})
   282  	c.Assert(err, IsNil)
   283  	c.Assert(store, Not(IsNil))
   284  
   285  	localKey1 := initTestType(keyPrefix + "-local1")
   286  	err = store.UpdateLocalKeySync(&localKey1)
   287  	c.Assert(err, IsNil)
   288  
   289  	localKey2 := initTestType(keyPrefix + "-local2")
   290  	err = store.UpdateLocalKeySync(&localKey2)
   291  	c.Assert(err, IsNil)
   292  
   293  	// wait until local keys was inserted and until the kvstore has confirmed the
   294  	c.Assert(expect(func() bool { return localKey1.updated() >= 1 }), IsNil)
   295  	c.Assert(expect(func() bool { return localKey2.updated() >= 1 }), IsNil)
   296  
   297  	c.Assert(len(store.getLocalKeys()), Equals, 2)
   298  
   299  	return store
   300  }
   301  
   302  func (s *StoreSuite) TestStoreCollaboration(c *C) {
   303  	storePrefix := testutils.RandomRune()
   304  
   305  	collab1 := setupStoreCollaboration(c, storePrefix, testutils.RandomRune())
   306  	defer collab1.Close()
   307  
   308  	collab2 := setupStoreCollaboration(c, storePrefix, testutils.RandomRune())
   309  	defer collab2.Close()
   310  
   311  	c.Assert(expect(func() bool {
   312  		totalKeys := len(collab1.getLocalKeys()) + len(collab2.getLocalKeys())
   313  		keys1, keys2 := collab1.getSharedKeys(), collab2.getSharedKeys()
   314  
   315  		log.Debugf("totalKeys %d == keys1 %d == keys2 %d", totalKeys, len(keys1), len(keys2))
   316  		return len(keys1) == totalKeys && len(keys1) == len(keys2)
   317  	}), IsNil)
   318  }