diff --git a/README.md b/README.md index c3151649e..103a58983 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ Usage of /bin/registrator: -tags="": Append tags for all registered services -ttl=0: TTL for services (default is no expiry) -ttl-refresh=0: Frequency with which service TTLs are refreshed + -envFile="": Read envs from within container from specified file. Updates on each refresh ``` ## Contributing diff --git a/bridge/bridge.go b/bridge/bridge.go index f02ca99c9..e8dd89b29 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -1,7 +1,10 @@ package bridge import ( + "archive/tar" + "bufio" "errors" + "io" "log" "net" "net/url" @@ -75,9 +78,25 @@ func (b *Bridge) Refresh() { } } + if b.config.DynamicEnvFile != "" { + // Refresh service name, tags and attrs from withib container... + for _, services := range b.services { + isGroup := len(services) > 1 + + for _, service := range services { + newService := b.newService(service.Origin, isGroup) + // We can't change ID of already registered service + service.Name = newService.Name + service.Tags = newService.Tags + service.Attrs = newService.Attrs + } + } + } + for containerId, services := range b.services { for _, service := range services { err := b.registry.Refresh(service) + if err != nil { log.Println("refresh failed:", service.ID, err) continue @@ -246,6 +265,40 @@ func (b *Bridge) add(containerId string, quiet bool) { } } +func (b *Bridge) readEnvFileFromContainer(containerID, path string) []string { + r, w := io.Pipe() + defer r.Close() + + go func() { + opts := dockerapi.DownloadFromContainerOptions{ + Path: path, + OutputStream: w, + } + w.CloseWithError(b.docker.DownloadFromContainer(containerID, opts)) + }() + + tarreader := tar.NewReader(r) + _, err := tarreader.Next() + if err != nil { + log.Printf("unable to read file %s from container %s: %s", path, containerID, err) + return nil + } + + result := []string{} + + scanner := bufio.NewScanner(tarreader) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + parts := strings.Split(line, "=") + if len(parts) == 2 { + result = append(result, line) + } + } + + return result +} + func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { container := port.container defaultName := strings.Split(path.Base(container.Config.Image), ":")[0] @@ -266,7 +319,15 @@ func (b *Bridge) newService(port ServicePort, isgroup bool) *Service { port.HostIP = b.config.HostIp } - metadata, metadataFromPort := serviceMetaData(container.Config, port.ExposedPort) + // metadata that takes priority when parsing in serviceMetaData + meta := []string{} + + // Try to read env-file from within container if its path specified + if b.config.DynamicEnvFile != "" { + meta = b.readEnvFileFromContainer(container.ID, b.config.DynamicEnvFile) + } + + metadata, metadataFromPort := serviceMetaData(meta, container.Config, port.ExposedPort) ignore := mapDefault(metadata, "ignore", "") if ignore != "" { diff --git a/bridge/types.go b/bridge/types.go index e643ed3f5..ce73e4990 100644 --- a/bridge/types.go +++ b/bridge/types.go @@ -29,6 +29,7 @@ type Config struct { RefreshInterval int DeregisterCheck string Cleanup bool + DynamicEnvFile string } type Service struct { diff --git a/bridge/util.go b/bridge/util.go index e3c295cfb..e126468b4 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -54,11 +54,20 @@ func combineTags(tagParts ...string) []string { return tags } -func serviceMetaData(config *dockerapi.Config, port string) (map[string]string, map[string]bool) { - meta := config.Env +// serviceMetaData extracts metadata from Docker-container config. +// meta - is highest priority metadata from other source +func serviceMetaData(meta []string, config *dockerapi.Config, port string) (map[string]string, map[string]bool) { + if meta == nil { + meta = make([]string, 0) + } + + for _, line := range config.Env { + meta = append(meta, line) + } for k, v := range config.Labels { meta = append(meta, k+"="+v) } + metadata := make(map[string]string) metadataFromPort := make(map[string]bool) for _, kv := range meta { diff --git a/consul/consul.go b/consul/consul.go index 13e8f18b3..014de6a41 100644 --- a/consul/consul.go +++ b/consul/consul.go @@ -153,7 +153,10 @@ func (r *ConsulAdapter) Deregister(service *bridge.Service) error { } func (r *ConsulAdapter) Refresh(service *bridge.Service) error { - return nil + // It is safe for consul to update(re-register) service with same ID. ID is an unique identifier across whole Consul cluster. + // So if our service description changes we can update it in Consul as long as ID stay same.... + // We never change service ID anyway. + return r.Register(service) } func (r *ConsulAdapter) Services() ([]*bridge.Service, error) { diff --git a/docs/user/run.md b/docs/user/run.md index 5c084474b..88ef217d7 100644 --- a/docs/user/run.md +++ b/docs/user/run.md @@ -44,6 +44,7 @@ Option | Since | Description `-ttl ` | | TTL for services. Default: 0, no expiry (supported backends only) `-ttl-refresh ` | | Frequency service TTLs are refreshed (supported backends only) `-useIpFromLabel