github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/discovery/kv/kv_test.go (about) 1 package kv // import "github.com/demonoid81/moby/pkg/discovery/kv" 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "path" 8 "testing" 9 "time" 10 11 "github.com/demonoid81/moby/internal/test/suite" 12 "github.com/demonoid81/moby/pkg/discovery" 13 "github.com/demonoid81/libkv" 14 "github.com/demonoid81/libkv/store" 15 "gotest.tools/v3/assert" 16 ) 17 18 // Hook up gocheck into the "go test" runner. 19 func Test(t *testing.T) { 20 suite.Run(t, &DiscoverySuite{}) 21 } 22 23 type DiscoverySuite struct{} 24 25 func (ds *DiscoverySuite) TestInitialize(c *testing.T) { 26 storeMock := &FakeStore{ 27 Endpoints: []string{"127.0.0.1"}, 28 } 29 d := &Discovery{backend: store.CONSUL} 30 d.Initialize("127.0.0.1", 0, 0, nil) 31 d.store = storeMock 32 33 s := d.store.(*FakeStore) 34 assert.Equal(c, len(s.Endpoints), 1) 35 assert.Equal(c, s.Endpoints[0], "127.0.0.1") 36 assert.Equal(c, d.path, defaultDiscoveryPath) 37 38 storeMock = &FakeStore{ 39 Endpoints: []string{"127.0.0.1:1234"}, 40 } 41 d = &Discovery{backend: store.CONSUL} 42 d.Initialize("127.0.0.1:1234/path", 0, 0, nil) 43 d.store = storeMock 44 45 s = d.store.(*FakeStore) 46 assert.Equal(c, len(s.Endpoints), 1) 47 assert.Equal(c, s.Endpoints[0], "127.0.0.1:1234") 48 assert.Equal(c, d.path, "path/"+defaultDiscoveryPath) 49 50 storeMock = &FakeStore{ 51 Endpoints: []string{"127.0.0.1:1234", "127.0.0.2:1234", "127.0.0.3:1234"}, 52 } 53 d = &Discovery{backend: store.CONSUL} 54 d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0, nil) 55 d.store = storeMock 56 57 s = d.store.(*FakeStore) 58 assert.Equal(c, len(s.Endpoints), 3) 59 assert.Equal(c, s.Endpoints[0], "127.0.0.1:1234") 60 assert.Equal(c, s.Endpoints[1], "127.0.0.2:1234") 61 assert.Equal(c, s.Endpoints[2], "127.0.0.3:1234") 62 63 assert.Equal(c, d.path, "path/"+defaultDiscoveryPath) 64 } 65 66 // Extremely limited mock store so we can test initialization 67 type Mock struct { 68 // Endpoints passed to InitializeMock 69 Endpoints []string 70 71 // Options passed to InitializeMock 72 Options *store.Config 73 } 74 75 func NewMock(endpoints []string, options *store.Config) (store.Store, error) { 76 s := &Mock{} 77 s.Endpoints = endpoints 78 s.Options = options 79 return s, nil 80 } 81 func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error { 82 return errors.New("Put not supported") 83 } 84 func (s *Mock) Get(key string) (*store.KVPair, error) { 85 return nil, errors.New("Get not supported") 86 } 87 func (s *Mock) Delete(key string) error { 88 return errors.New("Delete not supported") 89 } 90 91 // Exists mock 92 func (s *Mock) Exists(key string) (bool, error) { 93 return false, errors.New("Exists not supported") 94 } 95 96 // Watch mock 97 func (s *Mock) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) { 98 return nil, errors.New("Watch not supported") 99 } 100 101 // WatchTree mock 102 func (s *Mock) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) { 103 return nil, errors.New("WatchTree not supported") 104 } 105 106 // NewLock mock 107 func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, error) { 108 return nil, errors.New("NewLock not supported") 109 } 110 111 // List mock 112 func (s *Mock) List(prefix string) ([]*store.KVPair, error) { 113 return nil, errors.New("List not supported") 114 } 115 116 // DeleteTree mock 117 func (s *Mock) DeleteTree(prefix string) error { 118 return errors.New("DeleteTree not supported") 119 } 120 121 // AtomicPut mock 122 func (s *Mock) AtomicPut(key string, value []byte, previous *store.KVPair, opts *store.WriteOptions) (bool, *store.KVPair, error) { 123 return false, nil, errors.New("AtomicPut not supported") 124 } 125 126 // AtomicDelete mock 127 func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) { 128 return false, errors.New("AtomicDelete not supported") 129 } 130 131 // Close mock 132 func (s *Mock) Close() { 133 } 134 135 func (ds *DiscoverySuite) TestInitializeWithCerts(c *testing.T) { 136 cert := `-----BEGIN CERTIFICATE----- 137 MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT 138 B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD 139 VQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wRC 140 O+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4+zE9h80aC4hz+6caRpds 141 +J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhRSoSi3nY+B7F2E8cuz14q 142 V2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZrpXUyXxAvzXfpFXo1RhSb 143 UywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUerVYrCPq8vqfn//01qz55 144 Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHojxOpXTBepUCIJLbtNnWFT 145 V44t9gh5IqIWtoBReQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/ 146 BAgwBgEB/wIBAjAdBgNVHQ4EFgQUZKUI8IIjIww7X/6hvwggQK4bD24wHwYDVR0j 147 BBgwFoAUZKUI8IIjIww7X/6hvwggQK4bD24wCwYJKoZIhvcNAQELA4IBAQDES2cz 148 7sCQfDCxCIWH7X8kpi/JWExzUyQEJ0rBzN1m3/x8ySRxtXyGekimBqQwQdFqlwMI 149 xzAQKkh3ue8tNSzRbwqMSyH14N1KrSxYS9e9szJHfUasoTpQGPmDmGIoRJuq1h6M 150 ej5x1SCJ7GWCR6xEXKUIE9OftXm9TdFzWa7Ja3OHz/mXteii8VXDuZ5ACq6EE5bY 151 8sP4gcICfJ5fTrpTlk9FIqEWWQrCGa5wk95PGEj+GJpNogjXQ97wVoo/Y3p1brEn 152 t5zjN9PAq4H1fuCMdNNA+p1DHNwd+ELTxcMAnb2ajwHvV6lKPXutrTFc4umJToBX 153 FpTxDmJHEV4bzUzh 154 -----END CERTIFICATE----- 155 ` 156 key := `-----BEGIN RSA PRIVATE KEY----- 157 MIIEpQIBAAKCAQEA1wRCO+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4 158 +zE9h80aC4hz+6caRpds+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhR 159 SoSi3nY+B7F2E8cuz14qV2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZr 160 pXUyXxAvzXfpFXo1RhSbUywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUe 161 rVYrCPq8vqfn//01qz55Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHoj 162 xOpXTBepUCIJLbtNnWFTV44t9gh5IqIWtoBReQIDAQABAoIBAHSWipORGp/uKFXj 163 i/mut776x8ofsAxhnLBARQr93ID+i49W8H7EJGkOfaDjTICYC1dbpGrri61qk8sx 164 qX7p3v/5NzKwOIfEpirgwVIqSNYe/ncbxnhxkx6tXtUtFKmEx40JskvSpSYAhmmO 165 1XSx0E/PWaEN/nLgX/f1eWJIlxlQkk3QeqL+FGbCXI48DEtlJ9+MzMu4pAwZTpj5 166 5qtXo5JJ0jRGfJVPAOznRsYqv864AhMdMIWguzk6EGnbaCWwPcfcn+h9a5LMdony 167 MDHfBS7bb5tkF3+AfnVY3IBMVx7YlsD9eAyajlgiKu4zLbwTRHjXgShy+4Oussz0 168 ugNGnkECgYEA/hi+McrZC8C4gg6XqK8+9joD8tnyDZDz88BQB7CZqABUSwvjDqlP 169 L8hcwo/lzvjBNYGkqaFPUICGWKjeCtd8pPS2DCVXxDQX4aHF1vUur0uYNncJiV3N 170 XQz4Iemsa6wnKf6M67b5vMXICw7dw0HZCdIHD1hnhdtDz0uVpeevLZ8CgYEA2KCT 171 Y43lorjrbCgMqtlefkr3GJA9dey+hTzCiWEOOqn9RqGoEGUday0sKhiLofOgmN2B 172 LEukpKIey8s+Q/cb6lReajDVPDsMweX8i7hz3Wa4Ugp4Xa5BpHqu8qIAE2JUZ7bU 173 t88aQAYE58pUF+/Lq1QzAQdrjjzQBx6SrBxieecCgYEAvukoPZEC8mmiN1VvbTX+ 174 QFHmlZha3QaDxChB+QUe7bMRojEUL/fVnzkTOLuVFqSfxevaI/km9n0ac5KtAchV 175 xjp2bTnBb5EUQFqjopYktWA+xO07JRJtMfSEmjZPbbay1kKC7rdTfBm961EIHaRj 176 xZUf6M+rOE8964oGrdgdLlECgYEA046GQmx6fh7/82FtdZDRQp9tj3SWQUtSiQZc 177 qhO59Lq8mjUXz+MgBuJXxkiwXRpzlbaFB0Bca1fUoYw8o915SrDYf/Zu2OKGQ/qa 178 V81sgiVmDuEgycR7YOlbX6OsVUHrUlpwhY3hgfMe6UtkMvhBvHF/WhroBEIJm1pV 179 PXZ/CbMCgYEApNWVktFBjOaYfY6SNn4iSts1jgsQbbpglg3kT7PLKjCAhI6lNsbk 180 dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL 181 BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= 182 -----END RSA PRIVATE KEY----- 183 ` 184 certFile, err := ioutil.TempFile("", "cert") 185 assert.Assert(c, err == nil) 186 defer os.Remove(certFile.Name()) 187 certFile.Write([]byte(cert)) 188 certFile.Close() 189 keyFile, err := ioutil.TempFile("", "key") 190 assert.Assert(c, err == nil) 191 defer os.Remove(keyFile.Name()) 192 keyFile.Write([]byte(key)) 193 keyFile.Close() 194 195 libkv.AddStore("mock", NewMock) 196 d := &Discovery{backend: "mock"} 197 err = d.Initialize("127.0.0.3:1234", 0, 0, map[string]string{ 198 "kv.cacertfile": certFile.Name(), 199 "kv.certfile": certFile.Name(), 200 "kv.keyfile": keyFile.Name(), 201 }) 202 assert.Assert(c, err == nil) 203 s := d.store.(*Mock) 204 assert.Assert(c, s.Options.TLS != nil) 205 assert.Assert(c, s.Options.TLS.RootCAs != nil) 206 assert.Equal(c, len(s.Options.TLS.Certificates), 1) 207 } 208 209 func (ds *DiscoverySuite) TestWatch(c *testing.T) { 210 mockCh := make(chan []*store.KVPair) 211 212 storeMock := &FakeStore{ 213 Endpoints: []string{"127.0.0.1:1234"}, 214 mockKVChan: mockCh, 215 } 216 217 d := &Discovery{backend: store.CONSUL} 218 d.Initialize("127.0.0.1:1234/path", 0, 0, nil) 219 d.store = storeMock 220 221 expected := discovery.Entries{ 222 &discovery.Entry{Host: "1.1.1.1", Port: "1111"}, 223 &discovery.Entry{Host: "2.2.2.2", Port: "2222"}, 224 } 225 kvs := []*store.KVPair{ 226 {Key: path.Join("path", defaultDiscoveryPath, "1.1.1.1"), Value: []byte("1.1.1.1:1111")}, 227 {Key: path.Join("path", defaultDiscoveryPath, "2.2.2.2"), Value: []byte("2.2.2.2:2222")}, 228 } 229 230 stopCh := make(chan struct{}) 231 ch, errCh := d.Watch(stopCh) 232 233 // It should fire an error since the first WatchTree call failed. 234 assert.ErrorContains(c, <-errCh, "test error") 235 // We have to drain the error channel otherwise Watch will get stuck. 236 go func() { 237 for range errCh { 238 } 239 }() 240 241 // Push the entries into the store channel and make sure discovery emits. 242 mockCh <- kvs 243 assert.DeepEqual(c, <-ch, expected) 244 245 // Add a new entry. 246 expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"}) 247 kvs = append(kvs, &store.KVPair{Key: path.Join("path", defaultDiscoveryPath, "3.3.3.3"), Value: []byte("3.3.3.3:3333")}) 248 mockCh <- kvs 249 assert.DeepEqual(c, <-ch, expected) 250 251 close(mockCh) 252 // Give it enough time to call WatchTree. 253 time.Sleep(3 * time.Second) 254 255 // Stop and make sure it closes all channels. 256 close(stopCh) 257 assert.Assert(c, <-ch == nil) 258 assert.Assert(c, <-errCh == nil) 259 } 260 261 // FakeStore implements store.Store methods. It mocks all store 262 // function in a simple, naive way. 263 type FakeStore struct { 264 Endpoints []string 265 Options *store.Config 266 mockKVChan <-chan []*store.KVPair 267 268 watchTreeCallCount int 269 } 270 271 func (s *FakeStore) Put(key string, value []byte, options *store.WriteOptions) error { 272 return nil 273 } 274 275 func (s *FakeStore) Get(key string) (*store.KVPair, error) { 276 return nil, nil 277 } 278 279 func (s *FakeStore) Delete(key string) error { 280 return nil 281 } 282 283 func (s *FakeStore) Exists(key string) (bool, error) { 284 return true, nil 285 } 286 287 func (s *FakeStore) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) { 288 return nil, nil 289 } 290 291 // WatchTree will fail the first time, and return the mockKVchan afterwards. 292 // This is the behavior we need for testing.. If we need 'moar', should update this. 293 func (s *FakeStore) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) { 294 if s.watchTreeCallCount == 0 { 295 s.watchTreeCallCount = 1 296 return nil, errors.New("test error") 297 } 298 // First calls error 299 return s.mockKVChan, nil 300 } 301 302 func (s *FakeStore) NewLock(key string, options *store.LockOptions) (store.Locker, error) { 303 return nil, nil 304 } 305 306 func (s *FakeStore) List(directory string) ([]*store.KVPair, error) { 307 return []*store.KVPair{}, nil 308 } 309 310 func (s *FakeStore) DeleteTree(directory string) error { 311 return nil 312 } 313 314 func (s *FakeStore) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) { 315 return true, nil, nil 316 } 317 318 func (s *FakeStore) AtomicDelete(key string, previous *store.KVPair) (bool, error) { 319 return true, nil 320 } 321 322 func (s *FakeStore) Close() { 323 }