github.com/fafucoder/cilium@v1.6.11/pkg/clustermesh/clustermesh_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 clustermesh 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "io/ioutil" 23 "os" 24 "path" 25 "testing" 26 "time" 27 28 "github.com/cilium/cilium/pkg/identity" 29 "github.com/cilium/cilium/pkg/identity/cache" 30 "github.com/cilium/cilium/pkg/kvstore" 31 "github.com/cilium/cilium/pkg/kvstore/store" 32 "github.com/cilium/cilium/pkg/lock" 33 "github.com/cilium/cilium/pkg/testutils" 34 35 . "gopkg.in/check.v1" 36 ) 37 38 func Test(t *testing.T) { 39 TestingT(t) 40 } 41 42 type ClusterMeshTestSuite struct{} 43 44 var _ = Suite(&ClusterMeshTestSuite{}) 45 46 var ( 47 nodes = map[string]*testNode{} 48 nodesMutex lock.RWMutex 49 ) 50 51 type testNode struct { 52 // Name is the name of the node. This is typically the hostname of the node. 53 Name string 54 55 // Cluster is the name of the cluster the node is associated with 56 Cluster string 57 } 58 59 func (n *testNode) GetKeyName() string { 60 return path.Join(n.Name, n.Cluster) 61 } 62 63 func (n *testNode) DeepKeyCopy() store.LocalKey { 64 return &testNode{ 65 Name: n.Name, 66 Cluster: n.Cluster, 67 } 68 } 69 70 func (n *testNode) Marshal() ([]byte, error) { 71 return json.Marshal(n) 72 } 73 74 func (n *testNode) Unmarshal(data []byte) error { 75 return json.Unmarshal(data, n) 76 } 77 78 var testNodeCreator = func() store.Key { 79 n := testNode{} 80 return &n 81 } 82 83 type testObserver struct{} 84 85 func (o *testObserver) OnUpdate(k store.Key) { 86 n := k.(*testNode) 87 nodesMutex.Lock() 88 nodes[n.GetKeyName()] = n 89 nodesMutex.Unlock() 90 } 91 92 func (o *testObserver) OnDelete(k store.NamedKey) { 93 n := k.(*testNode) 94 nodesMutex.Lock() 95 delete(nodes, n.GetKeyName()) 96 nodesMutex.Unlock() 97 } 98 99 type identityAllocatorOwnerMock struct{} 100 101 func (i *identityAllocatorOwnerMock) UpdateIdentities(added, deleted cache.IdentityCache) {} 102 103 func (i *identityAllocatorOwnerMock) GetNodeSuffix() string { 104 return "foo" 105 } 106 107 func (s *ClusterMeshTestSuite) TestClusterMesh(c *C) { 108 kvstore.SetupDummy("etcd") 109 defer kvstore.Client().Close() 110 111 identity.InitWellKnownIdentities() 112 // The nils are only used by k8s CRD identities. We default to kvstore. 113 <-cache.InitIdentityAllocator(&identityAllocatorOwnerMock{}, nil, nil) 114 defer cache.Close() 115 116 dir, err := ioutil.TempDir("", "multicluster") 117 c.Assert(err, IsNil) 118 defer os.RemoveAll(dir) 119 120 etcdConfig := []byte(fmt.Sprintf("endpoints:\n- %s\n", kvstore.EtcdDummyAddress())) 121 122 config1 := path.Join(dir, "cluster1") 123 err = ioutil.WriteFile(config1, etcdConfig, 0644) 124 c.Assert(err, IsNil) 125 126 config2 := path.Join(dir, "cluster2") 127 err = ioutil.WriteFile(config2, etcdConfig, 0644) 128 c.Assert(err, IsNil) 129 130 cm, err := NewClusterMesh(Configuration{ 131 Name: "test2", 132 ConfigDirectory: dir, 133 NodeKeyCreator: testNodeCreator, 134 nodeObserver: &testObserver{}, 135 }) 136 c.Assert(err, IsNil) 137 c.Assert(cm, Not(IsNil)) 138 139 nodeNames := []string{"foo", "bar", "baz"} 140 141 // wait for both clusters to appear in the list of cm clusters 142 c.Assert(testutils.WaitUntil(func() bool { 143 return cm.NumReadyClusters() == 2 144 }, 10*time.Second), IsNil) 145 146 cm.mutex.RLock() 147 for _, rc := range cm.clusters { 148 rc.mutex.RLock() 149 for _, name := range nodeNames { 150 err = rc.remoteNodes.UpdateLocalKeySync(&testNode{Name: name, Cluster: rc.name}) 151 c.Assert(err, IsNil) 152 } 153 rc.mutex.RUnlock() 154 } 155 cm.mutex.RUnlock() 156 157 // wait for all cm nodes in both clusters to appear in the node list 158 c.Assert(testutils.WaitUntil(func() bool { 159 nodesMutex.RLock() 160 defer nodesMutex.RUnlock() 161 return len(nodes) == 2*len(nodeNames) 162 }, 10*time.Second), IsNil) 163 164 os.RemoveAll(config2) 165 166 // wait for the removed cluster to disappear 167 c.Assert(testutils.WaitUntil(func() bool { 168 return cm.NumReadyClusters() == 1 169 }, 5*time.Second), IsNil) 170 171 // wait for the nodes of the removed cluster to disappear 172 c.Assert(testutils.WaitUntil(func() bool { 173 nodesMutex.RLock() 174 defer nodesMutex.RUnlock() 175 return len(nodes) == len(nodeNames) 176 }, 10*time.Second), IsNil) 177 178 os.RemoveAll(config1) 179 180 // wait for the removed cluster to disappear 181 c.Assert(testutils.WaitUntil(func() bool { 182 return cm.NumReadyClusters() == 0 183 }, 5*time.Second), IsNil) 184 185 // wait for the nodes of the removed cluster to disappear 186 c.Assert(testutils.WaitUntil(func() bool { 187 nodesMutex.RLock() 188 defer nodesMutex.RUnlock() 189 return len(nodes) == 0 190 }, 10*time.Second), IsNil) 191 192 cm.Close() 193 }