2015-09-07 11:38:58 +03:00
package main
2015-09-12 16:10:03 +03:00
2015-09-08 01:15:14 +03:00
import (
2015-09-07 11:38:58 +03:00
"bytes"
2015-09-15 23:32:09 +03:00
"errors"
2015-09-07 11:38:58 +03:00
"github.com/BurntSushi/toml"
2015-09-10 23:54:37 +03:00
"github.com/BurntSushi/ty/fun"
2015-09-15 23:32:09 +03:00
"github.com/cenkalti/backoff"
2015-09-12 16:10:03 +03:00
"github.com/fsouza/go-dockerclient"
"github.com/leekchan/gtf"
2015-09-10 23:54:37 +03:00
"strconv"
2015-09-12 16:10:03 +03:00
"strings"
"text/template"
2015-09-12 20:22:44 +03:00
"time"
2015-09-07 11:38:58 +03:00
)
2015-09-09 23:39:08 +03:00
type DockerProvider struct {
2015-09-15 23:32:09 +03:00
Watch bool
Endpoint string
Filename string
Domain string
2015-09-09 23:39:08 +03:00
}
2015-09-11 20:32:23 +03:00
func NewDockerProvider ( ) * DockerProvider {
dockerProvider := new ( DockerProvider )
// default
dockerProvider . Watch = true
dockerProvider . Domain = "traefik"
return dockerProvider
}
2015-09-09 17:49:51 +03:00
var DockerFuncMap = template . FuncMap {
"getBackend" : func ( container docker . Container ) string {
for key , value := range container . Config . Labels {
2015-09-12 16:10:03 +03:00
if key == "traefik.backend" {
2015-09-09 17:49:51 +03:00
return value
}
}
2015-09-10 16:12:28 +03:00
return getHost ( container )
2015-09-09 17:49:51 +03:00
} ,
"getPort" : func ( container docker . Container ) string {
for key , value := range container . Config . Labels {
2015-09-12 16:10:03 +03:00
if key == "traefik.port" {
2015-09-09 17:49:51 +03:00
return value
}
}
2015-09-15 23:32:09 +03:00
for key := range container . NetworkSettings . Ports {
2015-09-09 17:49:51 +03:00
return key . Port ( )
}
return ""
} ,
2015-09-10 17:14:08 +03:00
"getWeight" : func ( container docker . Container ) string {
for key , value := range container . Config . Labels {
2015-09-12 16:10:03 +03:00
if key == "traefik.weight" {
2015-09-10 17:14:08 +03:00
return value
}
}
return "0"
} ,
2015-09-10 16:12:28 +03:00
"replace" : func ( s1 string , s2 string , s3 string ) string {
return strings . Replace ( s3 , s1 , s2 , - 1 )
} ,
2015-09-09 17:49:51 +03:00
"getHost" : getHost ,
}
2015-09-07 11:38:58 +03:00
2015-09-15 23:32:09 +03:00
func ( provider * DockerProvider ) Provide ( configurationChan chan <- * Configuration ) {
2015-09-12 20:22:44 +03:00
if dockerClient , err := docker . NewClient ( provider . Endpoint ) ; err != nil {
2015-09-09 23:39:08 +03:00
log . Fatalf ( "Failed to create a client for docker, error: %s" , err )
} else {
2015-09-12 20:22:44 +03:00
err := dockerClient . Ping ( )
2015-09-12 16:10:03 +03:00
if err != nil {
2015-09-12 02:00:30 +03:00
log . Fatalf ( "Docker connection error %+v" , err )
}
2015-09-12 02:30:28 +03:00
log . Debug ( "Docker connection established" )
2015-09-09 23:39:08 +03:00
dockerEvents := make ( chan * docker . APIEvents )
2015-09-12 16:10:03 +03:00
if provider . Watch {
2015-09-12 20:22:44 +03:00
dockerClient . AddEventListener ( dockerEvents )
log . Debug ( "Docker listening" )
2015-09-10 10:06:37 +03:00
go func ( ) {
2015-09-12 20:22:44 +03:00
operation := func ( ) error {
for {
event := <- dockerEvents
if event == nil {
return errors . New ( "Docker event nil" )
2015-09-15 23:32:09 +03:00
// log.Fatalf("Docker connection error")
2015-09-12 20:22:44 +03:00
}
2015-09-15 23:32:09 +03:00
if event . Status == "start" || event . Status == "die" {
2015-09-12 20:22:44 +03:00
log . Debug ( "Docker event receveived %+v" , event )
configuration := provider . loadDockerConfig ( dockerClient )
if configuration != nil {
configurationChan <- configuration
}
2015-09-10 23:54:37 +03:00
}
2015-09-10 10:06:37 +03:00
}
2015-09-09 23:39:08 +03:00
}
2015-09-12 20:22:44 +03:00
notify := func ( err error , time time . Duration ) {
log . Error ( "Docker connection error %+v, retrying in %s" , err , time )
}
err := backoff . RetryNotify ( operation , backoff . NewExponentialBackOff ( ) , notify )
if err != nil {
log . Fatalf ( "Cannot connect to docker server %+v" , err )
}
2015-09-10 10:06:37 +03:00
} ( )
}
2015-09-07 11:38:58 +03:00
2015-09-12 20:22:44 +03:00
configuration := provider . loadDockerConfig ( dockerClient )
2015-09-09 23:39:08 +03:00
configurationChan <- configuration
}
2015-09-07 11:38:58 +03:00
}
2015-09-12 20:22:44 +03:00
func ( provider * DockerProvider ) loadDockerConfig ( dockerClient * docker . Client ) * Configuration {
2015-09-08 01:15:14 +03:00
configuration := new ( Configuration )
2015-09-12 20:22:44 +03:00
containerList , _ := dockerClient . ListContainers ( docker . ListContainersOptions { } )
2015-09-07 11:38:58 +03:00
containersInspected := [ ] docker . Container { }
2015-09-09 17:49:51 +03:00
hosts := map [ string ] [ ] docker . Container { }
2015-09-10 23:54:37 +03:00
// get inspect containers
2015-09-07 11:38:58 +03:00
for _ , container := range containerList {
2015-09-12 20:22:44 +03:00
containerInspected , _ := dockerClient . InspectContainer ( container . ID )
2015-09-10 23:54:37 +03:00
containersInspected = append ( containersInspected , * containerInspected )
}
// filter containers
filteredContainers := fun . Filter ( func ( container docker . Container ) bool {
2015-09-12 16:10:03 +03:00
if len ( container . NetworkSettings . Ports ) == 0 {
2015-09-11 17:37:13 +03:00
log . Debug ( "Filtering container without port %s" , container . Name )
2015-09-10 23:54:37 +03:00
return false
2015-09-10 18:31:52 +03:00
}
2015-09-10 23:54:37 +03:00
_ , err := strconv . Atoi ( container . Config . Labels [ "traefik.port" ] )
2015-09-12 16:10:03 +03:00
if len ( container . NetworkSettings . Ports ) > 1 && err != nil {
2015-09-11 17:37:13 +03:00
log . Debug ( "Filtering container with more than 1 port and no traefik.port label %s" , container . Name )
2015-09-10 23:54:37 +03:00
return false
}
2015-09-12 16:10:03 +03:00
if container . Config . Labels [ "traefik.enable" ] == "false" {
2015-09-11 17:37:13 +03:00
log . Debug ( "Filtering disabled container %s" , container . Name )
2015-09-10 23:54:37 +03:00
return false
}
return true
} , containersInspected ) . ( [ ] docker . Container )
for _ , container := range filteredContainers {
hosts [ getHost ( container ) ] = append ( hosts [ getHost ( container ) ] , container )
2015-09-07 11:38:58 +03:00
}
2015-09-09 17:49:51 +03:00
templateObjects := struct {
2015-09-07 11:38:58 +03:00
Containers [ ] docker . Container
2015-09-09 18:10:43 +03:00
Hosts map [ string ] [ ] docker . Container
2015-09-09 18:50:02 +03:00
Domain string
2015-09-07 11:38:58 +03:00
} {
2015-09-10 23:54:37 +03:00
filteredContainers ,
2015-09-09 17:49:51 +03:00
hosts ,
2015-09-09 18:10:43 +03:00
provider . Domain ,
2015-09-07 11:38:58 +03:00
}
2015-09-09 17:49:51 +03:00
gtf . Inject ( DockerFuncMap )
2015-09-11 20:25:49 +03:00
tmpl := template . New ( provider . Filename ) . Funcs ( DockerFuncMap )
2015-09-12 16:10:03 +03:00
if len ( provider . Filename ) > 0 {
2015-09-11 20:25:49 +03:00
_ , err := tmpl . ParseFiles ( provider . Filename )
if err != nil {
log . Error ( "Error reading file" , err )
return nil
}
2015-09-12 16:10:03 +03:00
} else {
2015-09-11 20:25:49 +03:00
buf , err := Asset ( "providerTemplates/docker.tmpl" )
if err != nil {
log . Error ( "Error reading file" , err )
}
_ , err = tmpl . Parse ( string ( buf ) )
if err != nil {
log . Error ( "Error reading file" , err )
return nil
}
2015-09-07 11:38:58 +03:00
}
var buffer bytes . Buffer
2015-09-11 20:25:49 +03:00
err := tmpl . Execute ( & buffer , templateObjects )
2015-09-07 11:38:58 +03:00
if err != nil {
2015-09-11 17:37:13 +03:00
log . Error ( "Error with docker template" , err )
2015-09-07 16:25:13 +03:00
return nil
2015-09-07 11:38:58 +03:00
}
2015-09-08 01:15:14 +03:00
if _ , err := toml . Decode ( buffer . String ( ) , configuration ) ; err != nil {
2015-09-11 17:37:13 +03:00
log . Error ( "Error creating docker configuration" , err )
2015-09-07 11:38:58 +03:00
return nil
}
2015-09-08 01:15:14 +03:00
return configuration
2015-09-09 17:49:51 +03:00
}
func getHost ( container docker . Container ) string {
for key , value := range container . Config . Labels {
2015-09-12 16:10:03 +03:00
if key == "traefik.host" {
2015-09-09 17:49:51 +03:00
return value
}
}
2015-09-10 16:12:28 +03:00
return strings . Replace ( strings . Replace ( container . Name , "/" , "" , - 1 ) , "." , "-" , - 1 )
2015-09-12 16:10:03 +03:00
}