2015-11-01 15:35:01 +00:00
package provider
2015-09-12 13:10:03 +00:00
2015-09-07 22:15:14 +00:00
import (
2015-09-15 20:32:09 +00:00
"errors"
2015-09-24 15:16:13 +00:00
"strconv"
"strings"
"text/template"
"time"
2015-09-10 20:54:37 +00:00
"github.com/BurntSushi/ty/fun"
2015-09-24 12:32:37 +00:00
log "github.com/Sirupsen/logrus"
2015-09-15 20:32:09 +00:00
"github.com/cenkalti/backoff"
2016-03-31 16:57:08 +00:00
"github.com/containous/traefik/safe"
2016-02-24 15:43:39 +00:00
"github.com/containous/traefik/types"
2015-09-12 13:10:03 +00:00
"github.com/fsouza/go-dockerclient"
2015-09-07 08:38:58 +00:00
)
2015-09-09 20:39:08 +00:00
2015-11-01 18:29:47 +00:00
// Docker holds configurations of the Docker provider.
2015-11-02 18:48:34 +00:00
type Docker struct {
2016-01-13 21:46:44 +00:00
BaseProvider ` mapstructure:",squash" `
Endpoint string
Domain string
TLS * DockerTLS
2015-11-20 15:05:06 +00:00
}
// DockerTLS holds TLS specific configurations
type DockerTLS struct {
CA string
Cert string
Key string
InsecureSkipVerify bool
2015-09-09 20:39:08 +00:00
}
2015-11-01 18:29:47 +00:00
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
2015-11-02 18:48:34 +00:00
func ( provider * Docker ) Provide ( configurationChan chan <- types . ConfigMessage ) error {
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-02-25 17:30:13 +00:00
operation := func ( ) error {
var dockerClient * docker . Client
var err error
2015-11-01 18:29:47 +00:00
2016-02-25 17:30:13 +00:00
if provider . TLS != nil {
dockerClient , err = docker . NewTLSClient ( provider . Endpoint ,
provider . TLS . Cert , provider . TLS . Key , provider . TLS . CA )
if err == nil {
dockerClient . TLSConfig . InsecureSkipVerify = provider . TLS . InsecureSkipVerify
}
} else {
dockerClient , err = docker . NewClient ( provider . Endpoint )
}
if err != nil {
log . Errorf ( "Failed to create a client for docker, error: %s" , err )
return err
}
err = dockerClient . Ping ( )
if err != nil {
log . Errorf ( "Docker connection error %+v" , err )
return err
}
log . Debug ( "Docker connection established" )
configuration := provider . loadDockerConfig ( listContainers ( dockerClient ) )
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
if provider . Watch {
dockerEvents := make ( chan * docker . APIEvents )
dockerClient . AddEventListener ( dockerEvents )
log . Debug ( "Docker listening" )
2015-11-01 18:29:47 +00:00
for {
event := <- dockerEvents
if event == nil {
return errors . New ( "Docker event nil" )
// log.Fatalf("Docker connection error")
}
if event . Status == "start" || event . Status == "die" {
log . Debugf ( "Docker event receveived %+v" , event )
2015-11-13 10:50:32 +00:00
configuration := provider . loadDockerConfig ( listContainers ( dockerClient ) )
2015-11-01 18:29:47 +00:00
if configuration != nil {
2015-11-13 10:50:32 +00:00
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
2015-09-10 20:54:37 +00:00
}
2015-09-10 07:06:37 +00:00
}
2015-09-09 20:39:08 +00:00
}
2015-11-01 18:29:47 +00:00
}
2016-02-25 17:30:13 +00:00
return nil
}
notify := func ( err error , time time . Duration ) {
log . Errorf ( "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 )
}
2016-03-31 16:57:08 +00:00
} )
2015-11-01 18:29:47 +00:00
2015-10-01 10:04:25 +00:00
return nil
2015-09-07 08:38:58 +00:00
}
2015-11-13 10:50:32 +00:00
func ( provider * Docker ) loadDockerConfig ( containersInspected [ ] docker . Container ) * types . Configuration {
2015-10-08 19:21:51 +00:00
var DockerFuncMap = template . FuncMap {
2015-11-13 10:50:32 +00:00
"getBackend" : provider . getBackend ,
"getPort" : provider . getPort ,
"getWeight" : provider . getWeight ,
"getDomain" : provider . getDomain ,
"getProtocol" : provider . getProtocol ,
"getPassHostHeader" : provider . getPassHostHeader ,
2016-02-01 15:08:58 +00:00
"getEntryPoints" : provider . getEntryPoints ,
2015-11-13 10:50:32 +00:00
"getFrontendRule" : provider . getFrontendRule ,
"replace" : replace ,
2015-09-10 20:54:37 +00:00
}
// filter containers
2015-11-13 10:50:32 +00:00
filteredContainers := fun . Filter ( containerFilter , containersInspected ) . ( [ ] docker . Container )
2015-09-10 20:54:37 +00:00
2015-11-13 10:50:32 +00:00
frontends := map [ string ] [ ] docker . Container { }
2015-09-10 20:54:37 +00:00
for _ , container := range filteredContainers {
2015-10-23 07:49:19 +00:00
frontends [ provider . getFrontendName ( container ) ] = append ( frontends [ provider . getFrontendName ( container ) ] , container )
2015-09-07 08:38:58 +00:00
}
2015-09-09 14:49:51 +00:00
templateObjects := struct {
2015-09-07 08:38:58 +00:00
Containers [ ] docker . Container
2015-10-23 07:49:19 +00:00
Frontends map [ string ] [ ] docker . Container
2015-09-09 15:50:02 +00:00
Domain string
2015-09-07 08:38:58 +00:00
} {
2015-09-10 20:54:37 +00:00
filteredContainers ,
2015-10-23 07:49:19 +00:00
frontends ,
2015-09-09 15:10:43 +00:00
provider . Domain ,
2015-09-07 08:38:58 +00:00
}
2015-11-13 10:50:32 +00:00
configuration , err := provider . getConfiguration ( "templates/docker.tmpl" , DockerFuncMap , templateObjects )
2015-09-07 08:38:58 +00:00
if err != nil {
2015-11-13 10:50:32 +00:00
log . Error ( err )
2015-09-07 08:38:58 +00:00
}
2015-11-13 10:50:32 +00:00
return configuration
}
2015-09-07 08:38:58 +00:00
2015-11-13 10:50:32 +00:00
func containerFilter ( container docker . Container ) bool {
if len ( container . NetworkSettings . Ports ) == 0 {
log . Debugf ( "Filtering container without port %s" , container . Name )
return false
2015-09-07 08:38:58 +00:00
}
2015-11-13 10:50:32 +00:00
_ , err := strconv . Atoi ( container . Config . Labels [ "traefik.port" ] )
if len ( container . NetworkSettings . Ports ) > 1 && err != nil {
log . Debugf ( "Filtering container with more than 1 port and no traefik.port label %s" , container . Name )
return false
}
if container . Config . Labels [ "traefik.enable" ] == "false" {
log . Debugf ( "Filtering disabled container %s" , container . Name )
return false
}
return true
2015-09-09 14:49:51 +00:00
}
2015-11-02 18:48:34 +00:00
func ( provider * Docker ) getFrontendName ( container docker . Container ) string {
2015-10-23 07:49:19 +00:00
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
2016-03-27 00:05:17 +00:00
return normalize ( provider . getFrontendRule ( container ) )
2015-10-23 07:49:19 +00:00
}
2015-11-13 10:50:32 +00:00
// GetFrontendRule returns the frontend rule for the specified container, using
// it's label. It returns a default one (Host) if the label is not present.
func ( provider * Docker ) getFrontendRule ( container docker . Container ) string {
2016-03-30 17:05:43 +00:00
// ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
// TODO: backwards compatibility with DEPRECATED rule.Value
if value , ok := container . Config . Labels [ "traefik.frontend.value" ] ; ok {
2016-03-31 10:59:50 +00:00
log . Warnf ( "Label traefik.frontend.value=%s is DEPRECATED (will be removed in v1.0.0), please refer to the rule label: https://github.com/containous/traefik/blob/master/docs/index.md#docker" , value )
2016-03-30 17:05:43 +00:00
rule , _ := container . Config . Labels [ "traefik.frontend.rule" ]
return rule + ":" + value
}
// ⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠⚠
2015-11-13 10:50:32 +00:00
if label , err := getLabel ( container , "traefik.frontend.rule" ) ; err == nil {
return label
}
2016-03-27 00:05:17 +00:00
return "Host:" + getEscapedName ( container . Name ) + "." + provider . Domain
2015-11-13 10:50:32 +00:00
}
func ( provider * Docker ) getBackend ( container docker . Container ) string {
if label , err := getLabel ( container , "traefik.backend" ) ; err == nil {
return label
}
2016-03-27 00:05:17 +00:00
return normalize ( container . Name )
2015-11-13 10:50:32 +00:00
}
func ( provider * Docker ) getPort ( container docker . Container ) string {
if label , err := getLabel ( container , "traefik.port" ) ; err == nil {
return label
}
for key := range container . NetworkSettings . Ports {
return key . Port ( )
}
return ""
}
func ( provider * Docker ) getWeight ( container docker . Container ) string {
if label , err := getLabel ( container , "traefik.weight" ) ; err == nil {
return label
}
2016-03-27 00:05:17 +00:00
return "1"
2015-11-13 10:50:32 +00:00
}
func ( provider * Docker ) getDomain ( container docker . Container ) string {
if label , err := getLabel ( container , "traefik.domain" ) ; err == nil {
return label
}
return provider . Domain
2015-10-23 07:49:19 +00:00
}
2015-11-13 10:50:32 +00:00
func ( provider * Docker ) getProtocol ( container docker . Container ) string {
if label , err := getLabel ( container , "traefik.protocol" ) ; err == nil {
return label
}
return "http"
}
func ( provider * Docker ) getPassHostHeader ( container docker . Container ) string {
if passHostHeader , err := getLabel ( container , "traefik.frontend.passHostHeader" ) ; err == nil {
return passHostHeader
}
return "false"
}
2016-02-01 15:08:58 +00:00
func ( provider * Docker ) getEntryPoints ( container docker . Container ) [ ] string {
if entryPoints , err := getLabel ( container , "traefik.frontend.entryPoints" ) ; err == nil {
return strings . Split ( entryPoints , "," )
}
return [ ] string { }
}
2015-11-13 10:50:32 +00:00
func getLabel ( container docker . Container , label string ) ( string , error ) {
2015-09-09 14:49:51 +00:00
for key , value := range container . Config . Labels {
2015-10-23 07:49:19 +00:00
if key == label {
return value , nil
2015-09-09 14:49:51 +00:00
}
}
2015-10-23 07:49:19 +00:00
return "" , errors . New ( "Label not found:" + label )
}
2015-11-13 10:50:32 +00:00
func getLabels ( container docker . Container , labels [ ] string ) ( map [ string ] string , error ) {
2015-11-05 14:14:25 +00:00
var globalErr error
2015-10-26 23:26:35 +00:00
foundLabels := map [ string ] string { }
for _ , label := range labels {
2015-11-13 10:50:32 +00:00
foundLabel , err := getLabel ( container , label )
2015-11-05 14:14:25 +00:00
// Error out only if one of them is defined.
2015-11-01 18:29:47 +00:00
if err != nil {
2015-11-05 14:14:25 +00:00
globalErr = errors . New ( "Label not found: " + label )
continue
2015-10-26 23:26:35 +00:00
}
2015-11-01 18:29:47 +00:00
foundLabels [ label ] = foundLabel
2015-11-05 14:14:25 +00:00
2015-10-26 23:26:35 +00:00
}
2015-11-05 14:14:25 +00:00
return foundLabels , globalErr
2015-10-26 23:26:35 +00:00
}
2015-11-13 10:50:32 +00:00
func listContainers ( dockerClient * docker . Client ) [ ] docker . Container {
containerList , _ := dockerClient . ListContainers ( docker . ListContainersOptions { } )
containersInspected := [ ] docker . Container { }
2015-10-23 07:49:19 +00:00
2015-11-13 10:50:32 +00:00
// get inspect containers
for _ , container := range containerList {
containerInspected , _ := dockerClient . InspectContainer ( container . ID )
containersInspected = append ( containersInspected , * containerInspected )
2015-10-23 07:49:19 +00:00
}
2015-11-13 10:50:32 +00:00
return containersInspected
2015-09-12 13:10:03 +00:00
}