github.com/resin-io/docker@v1.13.1/pkg/discovery/kv/kv_test.go (about) 1 package kv 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "path" 8 "testing" 9 "time" 10 11 "github.com/docker/docker/pkg/discovery" 12 "github.com/docker/libkv" 13 "github.com/docker/libkv/store" 14 15 "github.com/go-check/check" 16 ) 17 18 // Hook up gocheck into the "go test" runner. 19 func Test(t *testing.T) { check.TestingT(t) } 20 21 type DiscoverySuite struct{} 22 23 var _ = check.Suite(&DiscoverySuite{}) 24 25 func (ds *DiscoverySuite) TestInitialize(c *check.C) { 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 c.Assert(s.Endpoints, check.HasLen, 1) 35 c.Assert(s.Endpoints[0], check.Equals, "127.0.0.1") 36 c.Assert(d.path, check.Equals, 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 c.Assert(s.Endpoints, check.HasLen, 1) 47 c.Assert(s.Endpoints[0], check.Equals, "127.0.0.1:1234") 48 c.Assert(d.path, check.Equals, "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 c.Assert(s.Endpoints, check.HasLen, 3) 59 c.Assert(s.Endpoints[0], check.Equals, "127.0.0.1:1234") 60 c.Assert(s.Endpoints[1], check.Equals, "127.0.0.2:1234") 61 c.Assert(s.Endpoints[2], check.Equals, "127.0.0.3:1234") 62 63 c.Assert(d.path, check.Equals, "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 return 134 } 135 136 func (ds *DiscoverySuite) TestInitializeWithCerts(c *check.C) { 137 cert := `-----BEGIN CERTIFICATE----- 138 MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT 139 B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD 140 VQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wRC 141 O+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4+zE9h80aC4hz+6caRpds 142 +J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhRSoSi3nY+B7F2E8cuz14q 143 V2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZrpXUyXxAvzXfpFXo1RhSb 144 UywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUerVYrCPq8vqfn//01qz55 145 Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHojxOpXTBepUCIJLbtNnWFT 146 V44t9gh5IqIWtoBReQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/ 147 BAgwBgEB/wIBAjAdBgNVHQ4EFgQUZKUI8IIjIww7X/6hvwggQK4bD24wHwYDVR0j 148 BBgwFoAUZKUI8IIjIww7X/6hvwggQK4bD24wCwYJKoZIhvcNAQELA4IBAQDES2cz 149 7sCQfDCxCIWH7X8kpi/JWExzUyQEJ0rBzN1m3/x8ySRxtXyGekimBqQwQdFqlwMI 150 xzAQKkh3ue8tNSzRbwqMSyH14N1KrSxYS9e9szJHfUasoTpQGPmDmGIoRJuq1h6M 151 ej5x1SCJ7GWCR6xEXKUIE9OftXm9TdFzWa7Ja3OHz/mXteii8VXDuZ5ACq6EE5bY 152 8sP4gcICfJ5fTrpTlk9FIqEWWQrCGa5wk95PGEj+GJpNogjXQ97wVoo/Y3p1brEn 153 t5zjN9PAq4H1fuCMdNNA+p1DHNwd+ELTxcMAnb2ajwHvV6lKPXutrTFc4umJToBX 154 FpTxDmJHEV4bzUzh 155 -----END CERTIFICATE----- 156 ` 157 key := `-----BEGIN RSA PRIVATE KEY----- 158 MIIEpQIBAAKCAQEA1wRCO+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4 159 +zE9h80aC4hz+6caRpds+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhR 160 SoSi3nY+B7F2E8cuz14qV2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZr 161 pXUyXxAvzXfpFXo1RhSbUywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUe 162 rVYrCPq8vqfn//01qz55Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHoj 163 xOpXTBepUCIJLbtNnWFTV44t9gh5IqIWtoBReQIDAQABAoIBAHSWipORGp/uKFXj 164 i/mut776x8ofsAxhnLBARQr93ID+i49W8H7EJGkOfaDjTICYC1dbpGrri61qk8sx 165 qX7p3v/5NzKwOIfEpirgwVIqSNYe/ncbxnhxkx6tXtUtFKmEx40JskvSpSYAhmmO 166 1XSx0E/PWaEN/nLgX/f1eWJIlxlQkk3QeqL+FGbCXI48DEtlJ9+MzMu4pAwZTpj5 167 5qtXo5JJ0jRGfJVPAOznRsYqv864AhMdMIWguzk6EGnbaCWwPcfcn+h9a5LMdony 168 MDHfBS7bb5tkF3+AfnVY3IBMVx7YlsD9eAyajlgiKu4zLbwTRHjXgShy+4Oussz0 169 ugNGnkECgYEA/hi+McrZC8C4gg6XqK8+9joD8tnyDZDz88BQB7CZqABUSwvjDqlP 170 L8hcwo/lzvjBNYGkqaFPUICGWKjeCtd8pPS2DCVXxDQX4aHF1vUur0uYNncJiV3N 171 XQz4Iemsa6wnKf6M67b5vMXICw7dw0HZCdIHD1hnhdtDz0uVpeevLZ8CgYEA2KCT 172 Y43lorjrbCgMqtlefkr3GJA9dey+hTzCiWEOOqn9RqGoEGUday0sKhiLofOgmN2B 173 LEukpKIey8s+Q/cb6lReajDVPDsMweX8i7hz3Wa4Ugp4Xa5BpHqu8qIAE2JUZ7bU 174 t88aQAYE58pUF+/Lq1QzAQdrjjzQBx6SrBxieecCgYEAvukoPZEC8mmiN1VvbTX+ 175 QFHmlZha3QaDxChB+QUe7bMRojEUL/fVnzkTOLuVFqSfxevaI/km9n0ac5KtAchV 176 xjp2bTnBb5EUQFqjopYktWA+xO07JRJtMfSEmjZPbbay1kKC7rdTfBm961EIHaRj 177 xZUf6M+rOE8964oGrdgdLlECgYEA046GQmx6fh7/82FtdZDRQp9tj3SWQUtSiQZc 178 qhO59Lq8mjUXz+MgBuJXxkiwXRpzlbaFB0Bca1fUoYw8o915SrDYf/Zu2OKGQ/qa 179 V81sgiVmDuEgycR7YOlbX6OsVUHrUlpwhY3hgfMe6UtkMvhBvHF/WhroBEIJm1pV 180 PXZ/CbMCgYEApNWVktFBjOaYfY6SNn4iSts1jgsQbbpglg3kT7PLKjCAhI6lNsbk 181 dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL 182 BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= 183 -----END RSA PRIVATE KEY----- 184 ` 185 certFile, err := ioutil.TempFile("", "cert") 186 c.Assert(err, check.IsNil) 187 defer os.Remove(certFile.Name()) 188 certFile.Write([]byte(cert)) 189 certFile.Close() 190 keyFile, err := ioutil.TempFile("", "key") 191 c.Assert(err, check.IsNil) 192 defer os.Remove(keyFile.Name()) 193 keyFile.Write([]byte(key)) 194 keyFile.Close() 195 196 libkv.AddStore("mock", NewMock) 197 d := &Discovery{backend: "mock"} 198 err = d.Initialize("127.0.0.3:1234", 0, 0, map[string]string{ 199 "kv.cacertfile": certFile.Name(), 200 "kv.certfile": certFile.Name(), 201 "kv.keyfile": keyFile.Name(), 202 }) 203 c.Assert(err, check.IsNil) 204 s := d.store.(*Mock) 205 c.Assert(s.Options.TLS, check.NotNil) 206 c.Assert(s.Options.TLS.RootCAs, check.NotNil) 207 c.Assert(s.Options.TLS.Certificates, check.HasLen, 1) 208 } 209 210 func (ds *DiscoverySuite) TestWatch(c *check.C) { 211 mockCh := make(chan []*store.KVPair) 212 213 storeMock := &FakeStore{ 214 Endpoints: []string{"127.0.0.1:1234"}, 215 mockKVChan: mockCh, 216 } 217 218 d := &Discovery{backend: store.CONSUL} 219 d.Initialize("127.0.0.1:1234/path", 0, 0, nil) 220 d.store = storeMock 221 222 expected := discovery.Entries{ 223 &discovery.Entry{Host: "1.1.1.1", Port: "1111"}, 224 &discovery.Entry{Host: "2.2.2.2", Port: "2222"}, 225 } 226 kvs := []*store.KVPair{ 227 {Key: path.Join("path", defaultDiscoveryPath, "1.1.1.1"), Value: []byte("1.1.1.1:1111")}, 228 {Key: path.Join("path", defaultDiscoveryPath, "2.2.2.2"), Value: []byte("2.2.2.2:2222")}, 229 } 230 231 stopCh := make(chan struct{}) 232 ch, errCh := d.Watch(stopCh) 233 234 // It should fire an error since the first WatchTree call failed. 235 c.Assert(<-errCh, check.ErrorMatches, "test error") 236 // We have to drain the error channel otherwise Watch will get stuck. 237 go func() { 238 for range errCh { 239 } 240 }() 241 242 // Push the entries into the store channel and make sure discovery emits. 243 mockCh <- kvs 244 c.Assert(<-ch, check.DeepEquals, expected) 245 246 // Add a new entry. 247 expected = append(expected, &discovery.Entry{Host: "3.3.3.3", Port: "3333"}) 248 kvs = append(kvs, &store.KVPair{Key: path.Join("path", defaultDiscoveryPath, "3.3.3.3"), Value: []byte("3.3.3.3:3333")}) 249 mockCh <- kvs 250 c.Assert(<-ch, check.DeepEquals, expected) 251 252 close(mockCh) 253 // Give it enough time to call WatchTree. 254 time.Sleep(3 * time.Second) 255 256 // Stop and make sure it closes all channels. 257 close(stopCh) 258 c.Assert(<-ch, check.IsNil) 259 c.Assert(<-errCh, check.IsNil) 260 } 261 262 // FakeStore implements store.Store methods. It mocks all store 263 // function in a simple, naive way. 264 type FakeStore struct { 265 Endpoints []string 266 Options *store.Config 267 mockKVChan <-chan []*store.KVPair 268 269 watchTreeCallCount int 270 } 271 272 func (s *FakeStore) Put(key string, value []byte, options *store.WriteOptions) error { 273 return nil 274 } 275 276 func (s *FakeStore) Get(key string) (*store.KVPair, error) { 277 return nil, nil 278 } 279 280 func (s *FakeStore) Delete(key string) error { 281 return nil 282 } 283 284 func (s *FakeStore) Exists(key string) (bool, error) { 285 return true, nil 286 } 287 288 func (s *FakeStore) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) { 289 return nil, nil 290 } 291 292 // WatchTree will fail the first time, and return the mockKVchan afterwards. 293 // This is the behavior we need for testing.. If we need 'moar', should update this. 294 func (s *FakeStore) WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) { 295 if s.watchTreeCallCount == 0 { 296 s.watchTreeCallCount = 1 297 return nil, errors.New("test error") 298 } 299 // First calls error 300 return s.mockKVChan, nil 301 } 302 303 func (s *FakeStore) NewLock(key string, options *store.LockOptions) (store.Locker, error) { 304 return nil, nil 305 } 306 307 func (s *FakeStore) List(directory string) ([]*store.KVPair, error) { 308 return []*store.KVPair{}, nil 309 } 310 311 func (s *FakeStore) DeleteTree(directory string) error { 312 return nil 313 } 314 315 func (s *FakeStore) AtomicPut(key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) { 316 return true, nil, nil 317 } 318 319 func (s *FakeStore) AtomicDelete(key string, previous *store.KVPair) (bool, error) { 320 return true, nil 321 } 322 323 func (s *FakeStore) Close() { 324 }