2015-09-07 11:38:58 +03:00
package main
2015-09-08 01:15:14 +03:00
import (
2015-09-07 11:38:58 +03:00
"github.com/fsouza/go-dockerclient"
"github.com/leekchan/gtf"
"bytes"
"github.com/BurntSushi/toml"
2015-09-09 17:49:51 +03:00
"text/template"
"strings"
2015-09-10 23:54:37 +03:00
"github.com/BurntSushi/ty/fun"
"strconv"
2015-09-07 11:38:58 +03:00
)
2015-09-09 23:39:08 +03:00
type DockerProvider struct {
Watch bool
Endpoint string
dockerClient * docker . Client
Filename string
Domain string
}
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-10 17:46:27 +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-10 17:46:27 +03:00
if ( key == "traefik.port" ) {
2015-09-09 17:49:51 +03:00
return value
}
}
for key , _ := range container . NetworkSettings . Ports {
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-10 17:46:27 +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-08 01:15:14 +03:00
func ( provider * DockerProvider ) Provide ( configurationChan chan <- * Configuration ) {
2015-09-09 23:39:08 +03:00
if client , err := docker . NewClient ( provider . Endpoint ) ; err != nil {
log . Fatalf ( "Failed to create a client for docker, error: %s" , err )
} else {
provider . dockerClient = client
2015-09-12 02:30:28 +03:00
_ , err := provider . dockerClient . Info ( )
2015-09-12 02:00:30 +03:00
if ( err != nil ) {
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 )
if ( provider . Watch ) {
provider . dockerClient . AddEventListener ( dockerEvents )
2015-09-10 10:06:37 +03:00
go func ( ) {
for {
event := <- dockerEvents
2015-09-12 14:20:54 +03:00
if ( event == nil ) {
log . Fatalf ( "Docker connection error %+v" , err )
}
2015-09-10 23:54:37 +03:00
if ( event . Status == "start" || event . Status == "die" ) {
2015-09-11 17:37:13 +03:00
log . Debug ( "Docker event receveived %+v" , event )
2015-09-10 23:54:37 +03:00
configuration := provider . loadDockerConfig ( )
if ( configuration != nil ) {
configurationChan <- configuration
}
2015-09-10 10:06:37 +03:00
}
2015-09-09 23:39:08 +03:00
}
2015-09-10 10:06:37 +03:00
} ( )
}
2015-09-07 11:38:58 +03:00
2015-09-09 23:39:08 +03:00
configuration := provider . loadDockerConfig ( )
configurationChan <- configuration
}
2015-09-07 11:38:58 +03:00
}
2015-09-08 01:15:14 +03:00
func ( provider * DockerProvider ) loadDockerConfig ( ) * Configuration {
configuration := new ( Configuration )
2015-09-07 11:38:58 +03:00
containerList , _ := provider . dockerClient . ListContainers ( docker . ListContainersOptions { } )
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-10 23:54:37 +03:00
containerInspected , _ := provider . dockerClient . InspectContainer ( container . ID )
containersInspected = append ( containersInspected , * containerInspected )
}
// filter containers
filteredContainers := fun . Filter ( func ( container docker . Container ) bool {
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" ] )
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
}
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 )
if ( len ( provider . Filename ) > 0 ) {
_ , err := tmpl . ParseFiles ( provider . Filename )
if err != nil {
log . Error ( "Error reading file" , err )
return nil
}
} else {
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-10 17:46:27 +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-07 11:38:58 +03:00
}