1- package publisher
1+ package server
22
33import (
44 "errors"
@@ -17,47 +17,51 @@ const (
1717 appNameRegex string = `([a-z0-9-]+)_v([1-9][0-9]*).(cmd|web).([1-9][0-9])*`
1818)
1919
20+ // Server is the main entrypoint for a publisher. It listens on a docker client for events
21+ // and publishes their host:port to the etcd client.
2022type Server struct {
2123 DockerClient * docker.Client
2224 EtcdClient * etcd.Client
2325}
2426
27+ // Listen adds an event listener to the docker client and publishes containers that were started.
2528func (s * Server ) Listen (ttl time.Duration ) {
2629 listener := make (chan * docker.APIEvents )
2730 // TODO: figure out why we need to sleep for 10 milliseconds
2831 // https://github.com/fsouza/go-dockerclient/blob/0236a64c6c4bd563ec277ba00e370cc753e1677c/event_test.go#L43
2932 defer func () { time .Sleep (10 * time .Millisecond ); s .DockerClient .RemoveEventListener (listener ) }()
30- err := s .DockerClient .AddEventListener (listener )
31- if err != nil {
33+ if err := s .DockerClient .AddEventListener (listener ); err != nil {
3234 log .Fatal (err )
3335 }
3436 for {
3537 select {
3638 case event := <- listener :
3739 if event .Status == "start" {
38- container , err := s .GetContainer (event .ID )
40+ container , err := s .getContainer (event .ID )
3941 if err != nil {
4042 log .Println (err )
4143 continue
4244 }
43- s .PublishContainer (container , ttl )
45+ s .publishContainer (container , ttl )
4446 }
4547 }
4648 }
4749}
4850
51+ // Poll lists all containers from the docker client every time the TTL comes up and publishes them to etcd
4952func (s * Server ) Poll (ttl time.Duration ) {
5053 containers , err := s .DockerClient .ListContainers (docker.ListContainersOptions {})
5154 if err != nil {
5255 log .Fatal (err )
5356 }
5457 for _ , container := range containers {
5558 // send container to channel for processing
56- s .PublishContainer (& container , ttl )
59+ s .publishContainer (& container , ttl )
5760 }
5861}
5962
60- func (s * Server ) GetContainer (id string ) (* docker.APIContainers , error ) {
63+ // getContainer retrieves a container from the docker client based on id
64+ func (s * Server ) getContainer (id string ) (* docker.APIContainers , error ) {
6165 containers , err := s .DockerClient .ListContainers (docker.ListContainersOptions {})
6266 if err != nil {
6367 return nil , err
@@ -71,8 +75,10 @@ func (s *Server) GetContainer(id string) (*docker.APIContainers, error) {
7175 return nil , errors .New ("could not find container" )
7276}
7377
74- func (s * Server ) PublishContainer (container * docker.APIContainers , ttl time.Duration ) {
78+ // publishContainer publishes the docker container to etcd.
79+ func (s * Server ) publishContainer (container * docker.APIContainers , ttl time.Duration ) {
7580 r := regexp .MustCompile (appNameRegex )
81+ host := os .Getenv ("HOST" )
7682 for _ , name := range container .Names {
7783 // HACK: remove slash from container name
7884 // see https://github.com/docker/docker/issues/7519
@@ -84,7 +90,6 @@ func (s *Server) PublishContainer(container *docker.APIContainers, ttl time.Dura
8490 appName := match [1 ]
8591 keyPath := fmt .Sprintf ("/deis/services/%s/%s" , appName , containerName )
8692 for _ , p := range container .Ports {
87- host := os .Getenv ("HOST" )
8893 port := strconv .Itoa (int (p .PublicPort ))
8994 if s .IsPublishableApp (containerName ) {
9095 s .setEtcd (keyPath , host + ":" + port , uint64 (ttl .Seconds ()))
@@ -103,7 +108,11 @@ func (s *Server) IsPublishableApp(name string) bool {
103108 return false
104109 }
105110 appName := match [1 ]
106- version , _ := strconv .Atoi (match [2 ])
111+ version , err := strconv .Atoi (match [2 ])
112+ if err != nil {
113+ log .Println (err )
114+ return false
115+ }
107116 if version >= latestRunningVersion (s .EtcdClient , appName ) {
108117 return true
109118 } else {
@@ -116,8 +125,8 @@ func (s *Server) IsPublishableApp(name string) bool {
116125func latestRunningVersion (client * etcd.Client , appName string ) int {
117126 r := regexp .MustCompile (appNameRegex )
118127 if client == nil {
119- // TODO: refactor for tests
120- if appName == "test " {
128+ // FIXME: client should only be nil during tests. This should be properly refactored.
129+ if appName == "ceci-nest-pas-une-app " {
121130 return 3
122131 }
123132 return 0
@@ -134,12 +143,17 @@ func latestRunningVersion(client *etcd.Client, appName string) int {
134143 if match == nil {
135144 continue
136145 }
137- version , _ := strconv .Atoi (match [2 ])
146+ version , err := strconv .Atoi (match [2 ])
147+ if err != nil {
148+ log .Println (err )
149+ return 0
150+ }
138151 versions = append (versions , version )
139152 }
140153 return max (versions )
141154}
142155
156+ // max returns the maximum value in n
143157func max (n []int ) int {
144158 val := 0
145159 for _ , i := range n {
@@ -150,9 +164,9 @@ func max(n []int) int {
150164 return val
151165}
152166
167+ // setEtcd sets the corresponding etcd key with the value and ttl
153168func (s * Server ) setEtcd (key , value string , ttl uint64 ) {
154- _ , err := s .EtcdClient .Set (key , value , ttl )
155- if err != nil {
169+ if _ , err := s .EtcdClient .Set (key , value , ttl ); err != nil {
156170 log .Println (err )
157171 }
158172 log .Println ("set" , key , "->" , value )
0 commit comments