2015-08-31 20:23:17 +00:00
|
|
|
package file
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/docker/docker/pkg/discovery"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Discovery is exported
|
|
|
|
type Discovery struct {
|
|
|
|
heartbeat time.Duration
|
|
|
|
path string
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
Init()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Init is exported
|
|
|
|
func Init() {
|
|
|
|
discovery.Register("file", &Discovery{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize is exported
|
2015-09-28 23:22:57 +00:00
|
|
|
func (s *Discovery) Initialize(path string, heartbeat time.Duration, ttl time.Duration, _ map[string]string) error {
|
2015-08-31 20:23:17 +00:00
|
|
|
s.path = path
|
|
|
|
s.heartbeat = heartbeat
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseFileContent(content []byte) []string {
|
|
|
|
var result []string
|
|
|
|
for _, line := range strings.Split(strings.TrimSpace(string(content)), "\n") {
|
|
|
|
line = strings.TrimSpace(line)
|
|
|
|
// Ignoring line starts with #
|
|
|
|
if strings.HasPrefix(line, "#") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Inlined # comment also ignored.
|
|
|
|
if strings.Contains(line, "#") {
|
|
|
|
line = line[0:strings.Index(line, "#")]
|
|
|
|
// Trim additional spaces caused by above stripping.
|
|
|
|
line = strings.TrimSpace(line)
|
|
|
|
}
|
|
|
|
for _, ip := range discovery.Generate(line) {
|
|
|
|
result = append(result, ip)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Discovery) fetch() (discovery.Entries, error) {
|
|
|
|
fileContent, err := ioutil.ReadFile(s.path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to read '%s': %v", s.path, err)
|
|
|
|
}
|
|
|
|
return discovery.CreateEntries(parseFileContent(fileContent))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Watch is exported
|
|
|
|
func (s *Discovery) Watch(stopCh <-chan struct{}) (<-chan discovery.Entries, <-chan error) {
|
|
|
|
ch := make(chan discovery.Entries)
|
|
|
|
errCh := make(chan error)
|
|
|
|
ticker := time.NewTicker(s.heartbeat)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
defer close(errCh)
|
|
|
|
defer close(ch)
|
|
|
|
|
|
|
|
// Send the initial entries if available.
|
|
|
|
currentEntries, err := s.fetch()
|
|
|
|
if err != nil {
|
|
|
|
errCh <- err
|
|
|
|
} else {
|
|
|
|
ch <- currentEntries
|
|
|
|
}
|
|
|
|
|
|
|
|
// Periodically send updates.
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case <-ticker.C:
|
|
|
|
newEntries, err := s.fetch()
|
|
|
|
if err != nil {
|
|
|
|
errCh <- err
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the file has really changed.
|
|
|
|
if !newEntries.Equals(currentEntries) {
|
|
|
|
ch <- newEntries
|
|
|
|
}
|
|
|
|
currentEntries = newEntries
|
|
|
|
case <-stopCh:
|
|
|
|
ticker.Stop()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return ch, errCh
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register is exported
|
|
|
|
func (s *Discovery) Register(addr string) error {
|
|
|
|
return discovery.ErrNotImplemented
|
|
|
|
}
|