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  }