github.com/darciopacifico/docker@v1.9.0-rc1/pkg/discovery/kv/kv.go (about) 1 package kv 2 3 import ( 4 "fmt" 5 "path" 6 "strings" 7 "time" 8 9 log "github.com/Sirupsen/logrus" 10 "github.com/docker/docker/pkg/discovery" 11 "github.com/docker/docker/pkg/tlsconfig" 12 "github.com/docker/libkv" 13 "github.com/docker/libkv/store" 14 "github.com/docker/libkv/store/consul" 15 "github.com/docker/libkv/store/etcd" 16 "github.com/docker/libkv/store/zookeeper" 17 ) 18 19 const ( 20 discoveryPath = "docker/nodes" 21 ) 22 23 // Discovery is exported 24 type Discovery struct { 25 backend store.Backend 26 store store.Store 27 heartbeat time.Duration 28 ttl time.Duration 29 prefix string 30 path string 31 } 32 33 func init() { 34 Init() 35 } 36 37 // Init is exported 38 func Init() { 39 // Register to libkv 40 zookeeper.Register() 41 consul.Register() 42 etcd.Register() 43 44 // Register to internal discovery service 45 discovery.Register("zk", &Discovery{backend: store.ZK}) 46 discovery.Register("consul", &Discovery{backend: store.CONSUL}) 47 discovery.Register("etcd", &Discovery{backend: store.ETCD}) 48 } 49 50 // Initialize is exported 51 func (s *Discovery) Initialize(uris string, heartbeat time.Duration, ttl time.Duration, clusterOpts map[string]string) error { 52 var ( 53 parts = strings.SplitN(uris, "/", 2) 54 addrs = strings.Split(parts[0], ",") 55 err error 56 ) 57 58 // A custom prefix to the path can be optionally used. 59 if len(parts) == 2 { 60 s.prefix = parts[1] 61 } 62 63 s.heartbeat = heartbeat 64 s.ttl = ttl 65 s.path = path.Join(s.prefix, discoveryPath) 66 67 var config *store.Config 68 if clusterOpts["kv.cacertfile"] != "" && clusterOpts["kv.certfile"] != "" && clusterOpts["kv.keyfile"] != "" { 69 log.Info("Initializing discovery with TLS") 70 tlsConfig, err := tlsconfig.Client(tlsconfig.Options{ 71 CAFile: clusterOpts["kv.cacertfile"], 72 CertFile: clusterOpts["kv.certfile"], 73 KeyFile: clusterOpts["kv.keyfile"], 74 }) 75 if err != nil { 76 return err 77 } 78 config = &store.Config{ 79 // Set ClientTLS to trigger https (bug in libkv/etcd) 80 ClientTLS: &store.ClientTLSConfig{ 81 CACertFile: clusterOpts["kv.cacertfile"], 82 CertFile: clusterOpts["kv.certfile"], 83 KeyFile: clusterOpts["kv.keyfile"], 84 }, 85 // The actual TLS config that will be used 86 TLS: tlsConfig, 87 } 88 } else { 89 log.Info("Initializing discovery without TLS") 90 } 91 92 // Creates a new store, will ignore options given 93 // if not supported by the chosen store 94 s.store, err = libkv.NewStore(s.backend, addrs, config) 95 return err 96 } 97 98 // Watch the store until either there's a store error or we receive a stop request. 99 // Returns false if we shouldn't attempt watching the store anymore (stop request received). 100 func (s *Discovery) watchOnce(stopCh <-chan struct{}, watchCh <-chan []*store.KVPair, discoveryCh chan discovery.Entries, errCh chan error) bool { 101 for { 102 select { 103 case pairs := <-watchCh: 104 if pairs == nil { 105 return true 106 } 107 108 log.WithField("discovery", s.backend).Debugf("Watch triggered with %d nodes", len(pairs)) 109 110 // Convert `KVPair` into `discovery.Entry`. 111 addrs := make([]string, len(pairs)) 112 for _, pair := range pairs { 113 addrs = append(addrs, string(pair.Value)) 114 } 115 116 entries, err := discovery.CreateEntries(addrs) 117 if err != nil { 118 errCh <- err 119 } else { 120 discoveryCh <- entries 121 } 122 case <-stopCh: 123 // We were requested to stop watching. 124 return false 125 } 126 } 127 } 128 129 // Watch is exported 130 func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) { 131 ch := make(chan discovery.Entries) 132 errCh := make(chan error) 133 134 go func() { 135 defer close(ch) 136 defer close(errCh) 137 138 // Forever: Create a store watch, watch until we get an error and then try again. 139 // Will only stop if we receive a stopCh request. 140 for { 141 // Set up a watch. 142 watchCh, err := s.store.WatchTree(s.path, stopCh) 143 if err != nil { 144 errCh <- err 145 } else { 146 if !s.watchOnce(stopCh, watchCh, ch, errCh) { 147 return 148 } 149 } 150 151 // If we get here it means the store watch channel was closed. This 152 // is unexpected so let's retry later. 153 errCh <- fmt.Errorf("Unexpected watch error") 154 time.Sleep(s.heartbeat) 155 } 156 }() 157 return ch, errCh 158 } 159 160 // Register is exported 161 func (s *Discovery) Register(addr string) error { 162 opts := &store.WriteOptions{TTL: s.ttl} 163 return s.store.Put(path.Join(s.path, addr), []byte(addr), opts) 164 } 165 166 // Store returns the underlying store used by KV discovery. 167 func (s *Discovery) Store() store.Store { 168 return s.store 169 } 170 171 // Prefix returns the store prefix 172 func (s *Discovery) Prefix() string { 173 return s.prefix 174 }