This leverages recent additions to libkv enabling client
authentication via TLS so the discovery back-end can be locked
down with mutual TLS. Example usage:
docker daemon [other args] \
--cluster-advertise 192.168.122.168:2376 \
--cluster-store etcd://192.168.122.168:2379 \
--cluster-store-opt kv.cacertfile=/path/to/ca.pem \
--cluster-store-opt kv.certfile=/path/to/cert.pem \
--cluster-store-opt kv.keyfile=/path/to/key.pem
Signed-off-by: Daniel Hiltgen <daniel.hiltgen@docker.com>
... | ... |
@@ -39,6 +39,10 @@ type CommonConfig struct { |
39 | 39 |
// mechanism. |
40 | 40 |
ClusterStore string |
41 | 41 |
|
42 |
+ // ClusterOpts is used to pass options to the discovery package for tuning libkv settings, such |
|
43 |
+ // as TLS configuration settings. |
|
44 |
+ ClusterOpts map[string]string |
|
45 |
+ |
|
42 | 46 |
// ClusterAdvertise is the network endpoint that the Engine advertises for the purpose of node |
43 | 47 |
// discovery. This should be a 'host:port' combination on which that daemon instance is |
44 | 48 |
// reachable by other hosts. |
... | ... |
@@ -68,4 +72,5 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) |
68 | 68 |
cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options")) |
69 | 69 |
cmd.StringVar(&config.ClusterAdvertise, []string{"-cluster-advertise"}, "", usageFn("Address of the daemon instance to advertise")) |
70 | 70 |
cmd.StringVar(&config.ClusterStore, []string{"-cluster-store"}, "", usageFn("Set the cluster store")) |
71 |
+ cmd.Var(opts.NewMapOpts(config.ClusterOpts, nil), []string{"-cluster-store-opt"}, usageFn("Set cluster store options")) |
|
71 | 72 |
} |
... | ... |
@@ -756,7 +756,7 @@ func NewDaemon(config *Config, registryService *registry.Service) (daemon *Daemo |
756 | 756 |
// DiscoveryWatcher version. |
757 | 757 |
if config.ClusterStore != "" && config.ClusterAdvertise != "" { |
758 | 758 |
var err error |
759 |
- if d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise); err != nil { |
|
759 |
+ if d.discoveryWatcher, err = initDiscovery(config.ClusterStore, config.ClusterAdvertise, config.ClusterOpts); err != nil { |
|
760 | 760 |
return nil, fmt.Errorf("discovery initialization failed (%v)", err) |
761 | 761 |
} |
762 | 762 |
} |
... | ... |
@@ -20,12 +20,12 @@ const ( |
20 | 20 |
|
21 | 21 |
// initDiscovery initialized the nodes discovery subsystem by connecting to the specified backend |
22 | 22 |
// and start a registration loop to advertise the current node under the specified address. |
23 |
-func initDiscovery(backend, address string) (discovery.Backend, error) { |
|
23 |
+func initDiscovery(backend, address string, clusterOpts map[string]string) (discovery.Backend, error) { |
|
24 | 24 |
var ( |
25 | 25 |
discoveryBackend discovery.Backend |
26 | 26 |
err error |
27 | 27 |
) |
28 |
- if discoveryBackend, err = discovery.New(backend, defaultDiscoveryHeartbeat, defaultDiscoveryTTL); err != nil { |
|
28 |
+ if discoveryBackend, err = discovery.New(backend, defaultDiscoveryHeartbeat, defaultDiscoveryTTL, clusterOpts); err != nil { |
|
29 | 29 |
return nil, err |
30 | 30 |
} |
31 | 31 |
|
... | ... |
@@ -71,6 +71,7 @@ func NewDaemonCli() *DaemonCli { |
71 | 71 |
// TODO(tiborvass): remove InstallFlags? |
72 | 72 |
daemonConfig := new(daemon.Config) |
73 | 73 |
daemonConfig.LogConfig.Config = make(map[string]string) |
74 |
+ daemonConfig.ClusterOpts = make(map[string]string) |
|
74 | 75 |
daemonConfig.InstallFlags(daemonFlags, presentInHelp) |
75 | 76 |
daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) |
76 | 77 |
registryOptions := new(registry.Options) |
... | ... |
@@ -24,6 +24,7 @@ weight=1 |
24 | 24 |
--default-gateway-v6="" Container default gateway IPv6 address |
25 | 25 |
--cluster-store="" URL of the distributed storage backend |
26 | 26 |
--cluster-advertise="" Address of the daemon instance to advertise |
27 |
+ --cluster-store-opt=map[] Set cluster options |
|
27 | 28 |
--dns=[] DNS server to use |
28 | 29 |
--dns-opt=[] DNS options to use |
29 | 30 |
--dns-search=[] DNS search domains to use |
... | ... |
@@ -537,6 +538,20 @@ please check the [run](run.md) reference. |
537 | 537 |
daemon instance should use when advertising itself to the cluster. The daemon |
538 | 538 |
should be reachable by remote hosts on this 'host:port' combination. |
539 | 539 |
|
540 |
+The daemon uses [libkv](https://github.com/docker/libkv/) to advertise |
|
541 |
+the node within the cluster. Some Key/Value backends support mutual |
|
542 |
+TLS, and the client TLS settings used by the daemon can be configured |
|
543 |
+using the `--cluster-store-opt` flag, specifying the paths to PEM encoded |
|
544 |
+files. For example: |
|
545 |
+ |
|
546 |
+```bash |
|
547 |
+ --cluster-advertise 192.168.1.2:2376 \ |
|
548 |
+ --cluster-store etcd://192.168.1.2:2379 \ |
|
549 |
+ --cluster-store-opt kv.cacertfile=/path/to/ca.pem \ |
|
550 |
+ --cluster-store-opt kv.certfile=/path/to/cert.pem \ |
|
551 |
+ --cluster-store-opt kv.keyfile=/path/to/key.pem |
|
552 |
+``` |
|
553 |
+ |
|
540 | 554 |
## Miscellaneous options |
541 | 555 |
|
542 | 556 |
IP masquerading uses address translation to allow containers without a public |
... | ... |
@@ -41,11 +41,11 @@ func parse(rawurl string) (string, string) { |
41 | 41 |
|
42 | 42 |
// New returns a new Discovery given a URL, heartbeat and ttl settings. |
43 | 43 |
// Returns an error if the URL scheme is not supported. |
44 |
-func New(rawurl string, heartbeat time.Duration, ttl time.Duration) (Backend, error) { |
|
44 |
+func New(rawurl string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) (Backend, error) { |
|
45 | 45 |
scheme, uri := parse(rawurl) |
46 | 46 |
if backend, exists := backends[scheme]; exists { |
47 | 47 |
log.WithFields(log.Fields{"name": scheme, "uri": uri}).Debug("Initializing discovery service") |
48 |
- err := backend.Initialize(uri, heartbeat, ttl) |
|
48 |
+ err := backend.Initialize(uri, heartbeat, ttl, clusterOpts) |
|
49 | 49 |
return backend, err |
50 | 50 |
} |
51 | 51 |
|
... | ... |
@@ -27,8 +27,8 @@ type Backend interface { |
27 | 27 |
// Watcher must be provided by every backend. |
28 | 28 |
Watcher |
29 | 29 |
|
30 |
- // Initialize the discovery with URIs, a heartbeat and a ttl. |
|
31 |
- Initialize(string, time.Duration, time.Duration) error |
|
30 |
+ // Initialize the discovery with URIs, a heartbeat, a ttl and optional settings. |
|
31 |
+ Initialize(string, time.Duration, time.Duration, map[string]string) error |
|
32 | 32 |
|
33 | 33 |
// Register to the discovery. |
34 | 34 |
Register(string) error |
... | ... |
@@ -25,7 +25,7 @@ func Init() { |
25 | 25 |
} |
26 | 26 |
|
27 | 27 |
// Initialize is exported |
28 |
-func (s *Discovery) Initialize(path string, heartbeat time.Duration, ttl time.Duration) error { |
|
28 |
+func (s *Discovery) Initialize(path string, heartbeat time.Duration, ttl time.Duration, _ map[string]string) error { |
|
29 | 29 |
s.path = path |
30 | 30 |
s.heartbeat = heartbeat |
31 | 31 |
return nil |
... | ... |
@@ -19,12 +19,12 @@ var _ = check.Suite(&DiscoverySuite{}) |
19 | 19 |
|
20 | 20 |
func (s *DiscoverySuite) TestInitialize(c *check.C) { |
21 | 21 |
d := &Discovery{} |
22 |
- d.Initialize("/path/to/file", 1000, 0) |
|
22 |
+ d.Initialize("/path/to/file", 1000, 0, nil) |
|
23 | 23 |
c.Assert(d.path, check.Equals, "/path/to/file") |
24 | 24 |
} |
25 | 25 |
|
26 | 26 |
func (s *DiscoverySuite) TestNew(c *check.C) { |
27 |
- d, err := discovery.New("file:///path/to/file", 0, 0) |
|
27 |
+ d, err := discovery.New("file:///path/to/file", 0, 0, nil) |
|
28 | 28 |
c.Assert(err, check.IsNil) |
29 | 29 |
c.Assert(d.(*Discovery).path, check.Equals, "/path/to/file") |
30 | 30 |
} |
... | ... |
@@ -81,7 +81,7 @@ func (s *DiscoverySuite) TestWatch(c *check.C) { |
81 | 81 |
|
82 | 82 |
// Set up file discovery. |
83 | 83 |
d := &Discovery{} |
84 |
- d.Initialize(tmp.Name(), 1000, 0) |
|
84 |
+ d.Initialize(tmp.Name(), 1000, 0, nil) |
|
85 | 85 |
stopCh := make(chan struct{}) |
86 | 86 |
ch, errCh := d.Watch(stopCh) |
87 | 87 |
|
... | ... |
@@ -8,6 +8,7 @@ import ( |
8 | 8 |
|
9 | 9 |
log "github.com/Sirupsen/logrus" |
10 | 10 |
"github.com/docker/docker/pkg/discovery" |
11 |
+ "github.com/docker/docker/pkg/tlsconfig" |
|
11 | 12 |
"github.com/docker/libkv" |
12 | 13 |
"github.com/docker/libkv/store" |
13 | 14 |
"github.com/docker/libkv/store/consul" |
... | ... |
@@ -47,7 +48,7 @@ func Init() { |
47 | 47 |
} |
48 | 48 |
|
49 | 49 |
// Initialize is exported |
50 |
-func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration) error { |
|
50 |
+func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) error { |
|
51 | 51 |
var ( |
52 | 52 |
parts = strings.SplitN(uris, "/", 2) |
53 | 53 |
addrs = strings.Split(parts[0], ",") |
... | ... |
@@ -63,9 +64,34 @@ func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Du |
63 | 63 |
s.ttl = ttl |
64 | 64 |
s.path = path.Join(s.prefix, discoveryPath) |
65 | 65 |
|
66 |
+ var config *store.Config |
|
67 |
+ if clusterOpts["kv.cacertfile"] != "" && clusterOpts["kv.certfile"] != "" && clusterOpts["kv.keyfile"] != "" { |
|
68 |
+ log.Info("Initializing discovery with TLS") |
|
69 |
+ tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ |
|
70 |
+ CAFile: clusterOpts["kv.cacertfile"], |
|
71 |
+ CertFile: clusterOpts["kv.certfile"], |
|
72 |
+ KeyFile: clusterOpts["kv.keyfile"], |
|
73 |
+ }) |
|
74 |
+ if err != nil { |
|
75 |
+ return err |
|
76 |
+ } |
|
77 |
+ config = &store.Config{ |
|
78 |
+ // Set ClientTLS to trigger https (bug in libkv/etcd) |
|
79 |
+ ClientTLS: &store.ClientTLSConfig{ |
|
80 |
+ CACertFile: clusterOpts["kv.cacertfile"], |
|
81 |
+ CertFile: clusterOpts["kv.certfile"], |
|
82 |
+ KeyFile: clusterOpts["kv.keyfile"], |
|
83 |
+ }, |
|
84 |
+ // The actual TLS config that will be used |
|
85 |
+ TLS: tlsConfig, |
|
86 |
+ } |
|
87 |
+ } else { |
|
88 |
+ log.Info("Initializing discovery without TLS") |
|
89 |
+ } |
|
90 |
+ |
|
66 | 91 |
// Creates a new store, will ignore options given |
67 | 92 |
// if not supported by the chosen store |
68 |
- s.store, err = libkv.NewStore(s.backend, addrs, nil) |
|
93 |
+ s.store, err = libkv.NewStore(s.backend, addrs, config) |
|
69 | 94 |
return err |
70 | 95 |
} |
71 | 96 |
|
... | ... |
@@ -2,11 +2,14 @@ package kv |
2 | 2 |
|
3 | 3 |
import ( |
4 | 4 |
"errors" |
5 |
+ "io/ioutil" |
|
6 |
+ "os" |
|
5 | 7 |
"path" |
6 | 8 |
"testing" |
7 | 9 |
"time" |
8 | 10 |
|
9 | 11 |
"github.com/docker/docker/pkg/discovery" |
12 |
+ "github.com/docker/libkv" |
|
10 | 13 |
"github.com/docker/libkv/store" |
11 | 14 |
|
12 | 15 |
"github.com/go-check/check" |
... | ... |
@@ -24,7 +27,7 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { |
24 | 24 |
Endpoints: []string{"127.0.0.1"}, |
25 | 25 |
} |
26 | 26 |
d := &Discovery{backend: store.CONSUL} |
27 |
- d.Initialize("127.0.0.1", 0, 0) |
|
27 |
+ d.Initialize("127.0.0.1", 0, 0, nil) |
|
28 | 28 |
d.store = storeMock |
29 | 29 |
|
30 | 30 |
s := d.store.(*FakeStore) |
... | ... |
@@ -36,7 +39,7 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { |
36 | 36 |
Endpoints: []string{"127.0.0.1:1234"}, |
37 | 37 |
} |
38 | 38 |
d = &Discovery{backend: store.CONSUL} |
39 |
- d.Initialize("127.0.0.1:1234/path", 0, 0) |
|
39 |
+ d.Initialize("127.0.0.1:1234/path", 0, 0, nil) |
|
40 | 40 |
d.store = storeMock |
41 | 41 |
|
42 | 42 |
s = d.store.(*FakeStore) |
... | ... |
@@ -48,7 +51,7 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { |
48 | 48 |
Endpoints: []string{"127.0.0.1:1234", "127.0.0.2:1234", "127.0.0.3:1234"}, |
49 | 49 |
} |
50 | 50 |
d = &Discovery{backend: store.CONSUL} |
51 |
- d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0) |
|
51 |
+ d.Initialize("127.0.0.1:1234,127.0.0.2:1234,127.0.0.3:1234/path", 0, 0, nil) |
|
52 | 52 |
d.store = storeMock |
53 | 53 |
|
54 | 54 |
s = d.store.(*FakeStore) |
... | ... |
@@ -60,6 +63,150 @@ func (ds *DiscoverySuite) TestInitialize(c *check.C) { |
60 | 60 |
c.Assert(d.path, check.Equals, "path/"+discoveryPath) |
61 | 61 |
} |
62 | 62 |
|
63 |
+// Extremely limited mock store so we can test initialization |
|
64 |
+type Mock struct { |
|
65 |
+ // Endpoints passed to InitializeMock |
|
66 |
+ Endpoints []string |
|
67 |
+ |
|
68 |
+ // Options passed to InitializeMock |
|
69 |
+ Options *store.Config |
|
70 |
+} |
|
71 |
+ |
|
72 |
+func NewMock(endpoints []string, options *store.Config) (store.Store, error) { |
|
73 |
+ s := &Mock{} |
|
74 |
+ s.Endpoints = endpoints |
|
75 |
+ s.Options = options |
|
76 |
+ return s, nil |
|
77 |
+} |
|
78 |
+func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error { |
|
79 |
+ return errors.New("Put not supported") |
|
80 |
+} |
|
81 |
+func (s *Mock) Get(key string) (*store.KVPair, error) { |
|
82 |
+ return nil, errors.New("Get not supported") |
|
83 |
+} |
|
84 |
+func (s *Mock) Delete(key string) error { |
|
85 |
+ return errors.New("Delete not supported") |
|
86 |
+} |
|
87 |
+ |
|
88 |
+// Exists mock |
|
89 |
+func (s *Mock) Exists(key string) (bool, error) { |
|
90 |
+ return false, errors.New("Exists not supported") |
|
91 |
+} |
|
92 |
+ |
|
93 |
+// Watch mock |
|
94 |
+func (s *Mock) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) { |
|
95 |
+ return nil, errors.New("Watch not supported") |
|
96 |
+} |
|
97 |
+ |
|
98 |
+// WatchTree mock |
|
99 |
+func (s *Mock) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) { |
|
100 |
+ return nil, errors.New("WatchTree not supported") |
|
101 |
+} |
|
102 |
+ |
|
103 |
+// NewLock mock |
|
104 |
+func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, error) { |
|
105 |
+ return nil, errors.New("NewLock not supported") |
|
106 |
+} |
|
107 |
+ |
|
108 |
+// List mock |
|
109 |
+func (s *Mock) List(prefix string) ([]*store.KVPair, error) { |
|
110 |
+ return nil, errors.New("List not supported") |
|
111 |
+} |
|
112 |
+ |
|
113 |
+// DeleteTree mock |
|
114 |
+func (s *Mock) DeleteTree(prefix string) error { |
|
115 |
+ return errors.New("DeleteTree not supported") |
|
116 |
+} |
|
117 |
+ |
|
118 |
+// AtomicPut mock |
|
119 |
+func (s *Mock) AtomicPut(key string, value []byte, previous *store.KVPair, opts *store.WriteOptions) (bool, *store.KVPair, error) { |
|
120 |
+ return false, nil, errors.New("AtomicPut not supported") |
|
121 |
+} |
|
122 |
+ |
|
123 |
+// AtomicDelete mock |
|
124 |
+func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) { |
|
125 |
+ return false, errors.New("AtomicDelete not supported") |
|
126 |
+} |
|
127 |
+ |
|
128 |
+// Close mock |
|
129 |
+func (s *Mock) Close() { |
|
130 |
+ return |
|
131 |
+} |
|
132 |
+ |
|
133 |
+func (ds *DiscoverySuite) TestInitializeWithCerts(c *check.C) { |
|
134 |
+ cert := `-----BEGIN CERTIFICATE----- |
|
135 |
+MIIDCDCCAfKgAwIBAgIICifG7YeiQOEwCwYJKoZIhvcNAQELMBIxEDAOBgNVBAMT |
|
136 |
+B1Rlc3QgQ0EwHhcNMTUxMDAxMjMwMDAwWhcNMjAwOTI5MjMwMDAwWjASMRAwDgYD |
|
137 |
+VQQDEwdUZXN0IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1wRC |
|
138 |
+O+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4+zE9h80aC4hz+6caRpds |
|
139 |
++J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhRSoSi3nY+B7F2E8cuz14q |
|
140 |
+V2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZrpXUyXxAvzXfpFXo1RhSb |
|
141 |
+UywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUerVYrCPq8vqfn//01qz55 |
|
142 |
+Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHojxOpXTBepUCIJLbtNnWFT |
|
143 |
+V44t9gh5IqIWtoBReQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/ |
|
144 |
+BAgwBgEB/wIBAjAdBgNVHQ4EFgQUZKUI8IIjIww7X/6hvwggQK4bD24wHwYDVR0j |
|
145 |
+BBgwFoAUZKUI8IIjIww7X/6hvwggQK4bD24wCwYJKoZIhvcNAQELA4IBAQDES2cz |
|
146 |
+7sCQfDCxCIWH7X8kpi/JWExzUyQEJ0rBzN1m3/x8ySRxtXyGekimBqQwQdFqlwMI |
|
147 |
+xzAQKkh3ue8tNSzRbwqMSyH14N1KrSxYS9e9szJHfUasoTpQGPmDmGIoRJuq1h6M |
|
148 |
+ej5x1SCJ7GWCR6xEXKUIE9OftXm9TdFzWa7Ja3OHz/mXteii8VXDuZ5ACq6EE5bY |
|
149 |
+8sP4gcICfJ5fTrpTlk9FIqEWWQrCGa5wk95PGEj+GJpNogjXQ97wVoo/Y3p1brEn |
|
150 |
+t5zjN9PAq4H1fuCMdNNA+p1DHNwd+ELTxcMAnb2ajwHvV6lKPXutrTFc4umJToBX |
|
151 |
+FpTxDmJHEV4bzUzh |
|
152 |
+-----END CERTIFICATE----- |
|
153 |
+` |
|
154 |
+ key := `-----BEGIN RSA PRIVATE KEY----- |
|
155 |
+MIIEpQIBAAKCAQEA1wRCO+flnLTK5ImjTurNRHwSejuqGbc4CAvpB0hS+z0QlSs4 |
|
156 |
++zE9h80aC4hz+6caRpds+J908Q+RvAittMHbpc7VjbZP72G6fiXk7yPPl6C10HhR |
|
157 |
+SoSi3nY+B7F2E8cuz14qV2e+ejhWhSrBb/keyXpcyjoW1BOAAJ2TIclRRkICSCZr |
|
158 |
+pXUyXxAvzXfpFXo1RhSbUywN11pfiCQzDUN7sPww9UzFHuAHZHoyfTr27XnJYVUe |
|
159 |
+rVYrCPq8vqfn//01qz55Xs0hvzGdlTFXhuabFtQnKFH5SNwo/fcznhB7rePOwHoj |
|
160 |
+xOpXTBepUCIJLbtNnWFTV44t9gh5IqIWtoBReQIDAQABAoIBAHSWipORGp/uKFXj |
|
161 |
+i/mut776x8ofsAxhnLBARQr93ID+i49W8H7EJGkOfaDjTICYC1dbpGrri61qk8sx |
|
162 |
+qX7p3v/5NzKwOIfEpirgwVIqSNYe/ncbxnhxkx6tXtUtFKmEx40JskvSpSYAhmmO |
|
163 |
+1XSx0E/PWaEN/nLgX/f1eWJIlxlQkk3QeqL+FGbCXI48DEtlJ9+MzMu4pAwZTpj5 |
|
164 |
+5qtXo5JJ0jRGfJVPAOznRsYqv864AhMdMIWguzk6EGnbaCWwPcfcn+h9a5LMdony |
|
165 |
+MDHfBS7bb5tkF3+AfnVY3IBMVx7YlsD9eAyajlgiKu4zLbwTRHjXgShy+4Oussz0 |
|
166 |
+ugNGnkECgYEA/hi+McrZC8C4gg6XqK8+9joD8tnyDZDz88BQB7CZqABUSwvjDqlP |
|
167 |
+L8hcwo/lzvjBNYGkqaFPUICGWKjeCtd8pPS2DCVXxDQX4aHF1vUur0uYNncJiV3N |
|
168 |
+XQz4Iemsa6wnKf6M67b5vMXICw7dw0HZCdIHD1hnhdtDz0uVpeevLZ8CgYEA2KCT |
|
169 |
+Y43lorjrbCgMqtlefkr3GJA9dey+hTzCiWEOOqn9RqGoEGUday0sKhiLofOgmN2B |
|
170 |
+LEukpKIey8s+Q/cb6lReajDVPDsMweX8i7hz3Wa4Ugp4Xa5BpHqu8qIAE2JUZ7bU |
|
171 |
+t88aQAYE58pUF+/Lq1QzAQdrjjzQBx6SrBxieecCgYEAvukoPZEC8mmiN1VvbTX+ |
|
172 |
+QFHmlZha3QaDxChB+QUe7bMRojEUL/fVnzkTOLuVFqSfxevaI/km9n0ac5KtAchV |
|
173 |
+xjp2bTnBb5EUQFqjopYktWA+xO07JRJtMfSEmjZPbbay1kKC7rdTfBm961EIHaRj |
|
174 |
+xZUf6M+rOE8964oGrdgdLlECgYEA046GQmx6fh7/82FtdZDRQp9tj3SWQUtSiQZc |
|
175 |
+qhO59Lq8mjUXz+MgBuJXxkiwXRpzlbaFB0Bca1fUoYw8o915SrDYf/Zu2OKGQ/qa |
|
176 |
+V81sgiVmDuEgycR7YOlbX6OsVUHrUlpwhY3hgfMe6UtkMvhBvHF/WhroBEIJm1pV |
|
177 |
+PXZ/CbMCgYEApNWVktFBjOaYfY6SNn4iSts1jgsQbbpglg3kT7PLKjCAhI6lNsbk |
|
178 |
+dyT7ut01PL6RaW4SeQWtrJIVQaM6vF3pprMKqlc5XihOGAmVqH7rQx9rtQB5TicL |
|
179 |
+BFrwkQE4HQtQBV60hYQUzzlSk44VFDz+jxIEtacRHaomDRh2FtOTz+I= |
|
180 |
+-----END RSA PRIVATE KEY----- |
|
181 |
+` |
|
182 |
+ certFile, err := ioutil.TempFile("", "cert") |
|
183 |
+ c.Assert(err, check.IsNil) |
|
184 |
+ defer os.Remove(certFile.Name()) |
|
185 |
+ certFile.Write([]byte(cert)) |
|
186 |
+ certFile.Close() |
|
187 |
+ keyFile, err := ioutil.TempFile("", "key") |
|
188 |
+ c.Assert(err, check.IsNil) |
|
189 |
+ defer os.Remove(keyFile.Name()) |
|
190 |
+ keyFile.Write([]byte(key)) |
|
191 |
+ keyFile.Close() |
|
192 |
+ |
|
193 |
+ libkv.AddStore("mock", NewMock) |
|
194 |
+ d := &Discovery{backend: "mock"} |
|
195 |
+ err = d.Initialize("127.0.0.3:1234", 0, 0, map[string]string{ |
|
196 |
+ "kv.cacertfile": certFile.Name(), |
|
197 |
+ "kv.certfile": certFile.Name(), |
|
198 |
+ "kv.keyfile": keyFile.Name(), |
|
199 |
+ }) |
|
200 |
+ c.Assert(err, check.IsNil) |
|
201 |
+ s := d.store.(*Mock) |
|
202 |
+ c.Assert(s.Options.TLS, check.NotNil) |
|
203 |
+ c.Assert(s.Options.TLS.RootCAs, check.NotNil) |
|
204 |
+ c.Assert(s.Options.TLS.Certificates, check.HasLen, 1) |
|
205 |
+} |
|
206 |
+ |
|
63 | 207 |
func (ds *DiscoverySuite) TestWatch(c *check.C) { |
64 | 208 |
mockCh := make(chan []*store.KVPair) |
65 | 209 |
|
... | ... |
@@ -69,7 +216,7 @@ func (ds *DiscoverySuite) TestWatch(c *check.C) { |
69 | 69 |
} |
70 | 70 |
|
71 | 71 |
d := &Discovery{backend: store.CONSUL} |
72 |
- d.Initialize("127.0.0.1:1234/path", 0, 0) |
|
72 |
+ d.Initialize("127.0.0.1:1234/path", 0, 0, nil) |
|
73 | 73 |
d.store = storeMock |
74 | 74 |
|
75 | 75 |
expected := discovery.Entries{ |
... | ... |
@@ -23,7 +23,7 @@ func Init() { |
23 | 23 |
} |
24 | 24 |
|
25 | 25 |
// Initialize is exported |
26 |
-func (s *Discovery) Initialize(uris string, _ time.Duration, _ time.Duration) error { |
|
26 |
+func (s *Discovery) Initialize(uris string, _ time.Duration, _ time.Duration, _ map[string]string) error { |
|
27 | 27 |
for _, input := range strings.Split(uris, ",") { |
28 | 28 |
for _, ip := range discovery.Generate(input) { |
29 | 29 |
entry, err := discovery.NewEntry(ip) |
... | ... |
@@ -17,7 +17,7 @@ var _ = check.Suite(&DiscoverySuite{}) |
17 | 17 |
|
18 | 18 |
func (s *DiscoverySuite) TestInitialize(c *check.C) { |
19 | 19 |
d := &Discovery{} |
20 |
- d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0) |
|
20 |
+ d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0, nil) |
|
21 | 21 |
c.Assert(len(d.entries), check.Equals, 2) |
22 | 22 |
c.Assert(d.entries[0].String(), check.Equals, "1.1.1.1:1111") |
23 | 23 |
c.Assert(d.entries[1].String(), check.Equals, "2.2.2.2:2222") |
... | ... |
@@ -25,7 +25,7 @@ func (s *DiscoverySuite) TestInitialize(c *check.C) { |
25 | 25 |
|
26 | 26 |
func (s *DiscoverySuite) TestInitializeWithPattern(c *check.C) { |
27 | 27 |
d := &Discovery{} |
28 |
- d.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0, 0) |
|
28 |
+ d.Initialize("1.1.1.[1:2]:1111,2.2.2.[2:4]:2222", 0, 0, nil) |
|
29 | 29 |
c.Assert(len(d.entries), check.Equals, 5) |
30 | 30 |
c.Assert(d.entries[0].String(), check.Equals, "1.1.1.1:1111") |
31 | 31 |
c.Assert(d.entries[1].String(), check.Equals, "1.1.1.2:1111") |
... | ... |
@@ -36,7 +36,7 @@ func (s *DiscoverySuite) TestInitializeWithPattern(c *check.C) { |
36 | 36 |
|
37 | 37 |
func (s *DiscoverySuite) TestWatch(c *check.C) { |
38 | 38 |
d := &Discovery{} |
39 |
- d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0) |
|
39 |
+ d.Initialize("1.1.1.1:1111,2.2.2.2:2222", 0, 0, nil) |
|
40 | 40 |
expected := discovery.Entries{ |
41 | 41 |
&discovery.Entry{Host: "1.1.1.1", Port: "1111"}, |
42 | 42 |
&discovery.Entry{Host: "2.2.2.2", Port: "2222"}, |