2015-11-01 16:35:01 +01:00
package provider
2015-09-12 15:10:03 +02:00
2015-09-08 00:15:14 +02:00
import (
2015-09-15 22:32:09 +02:00
"errors"
2016-08-25 00:22:06 -04:00
"math"
2016-08-05 11:02:46 -03:00
"net"
2016-04-08 14:20:54 +02:00
"net/http"
2015-09-24 17:16:13 +02:00
"strconv"
"strings"
"text/template"
"time"
2016-04-08 14:20:54 +02:00
"golang.org/x/net/context"
2015-09-10 22:54:37 +02:00
"github.com/BurntSushi/ty/fun"
2016-09-19 19:08:39 +02:00
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
2016-09-23 18:27:01 +02:00
"github.com/containous/traefik/log"
2016-03-31 18:57:08 +02:00
"github.com/containous/traefik/safe"
2016-02-24 16:43:39 +01:00
"github.com/containous/traefik/types"
2016-07-21 16:33:49 +02:00
"github.com/containous/traefik/version"
2016-04-08 14:20:54 +02:00
"github.com/docker/engine-api/client"
dockertypes "github.com/docker/engine-api/types"
2016-08-05 11:02:46 -03:00
dockercontainertypes "github.com/docker/engine-api/types/container"
2016-04-08 14:20:54 +02:00
eventtypes "github.com/docker/engine-api/types/events"
"github.com/docker/engine-api/types/filters"
2016-08-05 11:02:46 -03:00
"github.com/docker/engine-api/types/swarm"
swarmtypes "github.com/docker/engine-api/types/swarm"
"github.com/docker/go-connections/nat"
2016-04-08 14:20:54 +02:00
"github.com/docker/go-connections/sockets"
"github.com/vdemeester/docker-events"
2015-09-07 10:38:58 +02:00
)
2015-09-09 22:39:08 +02:00
2016-08-05 11:02:46 -03:00
const (
// DockerAPIVersion is a constant holding the version of the Docker API traefik will use
DockerAPIVersion string = "1.21"
// SwarmAPIVersion is a constant holding the version of the Docker API traefik will use
SwarmAPIVersion string = "1.24"
// SwarmDefaultWatchTime is the duration of the interval when polling docker
SwarmDefaultWatchTime = 15 * time . Second
)
2016-04-08 14:20:54 +02:00
2016-08-16 19:13:18 +02:00
var _ Provider = ( * Docker ) ( nil )
2015-11-01 19:29:47 +01:00
// Docker holds configurations of the Docker provider.
2015-11-02 19:48:34 +01:00
type Docker struct {
2016-06-24 09:58:42 +02:00
BaseProvider ` mapstructure:",squash" `
2016-07-14 11:32:15 +02:00
Endpoint string ` description:"Docker server endpoint. Can be a tcp or a unix socket endpoint" `
Domain string ` description:"Default domain used" `
2016-06-27 16:14:56 +02:00
TLS * ClientTLS ` description:"Enable Docker TLS support" `
2016-07-14 11:32:15 +02:00
ExposedByDefault bool ` description:"Expose containers by default" `
2016-09-20 14:52:35 +02:00
UseBindPortIP bool ` description:"Use the ip address from the bound port, rather than from the inner network" `
2016-08-05 11:02:46 -03:00
SwarmMode bool ` description:"Use Docker on Swarm Mode" `
}
// dockerData holds the need data to the Docker provider
type dockerData struct {
Name string
Labels map [ string ] string // List of labels set to container or service
NetworkSettings networkSettings
2016-10-03 11:01:37 +02:00
Health string
2016-08-05 11:02:46 -03:00
}
// NetworkSettings holds the networks data to the Docker provider
type networkSettings struct {
NetworkMode dockercontainertypes . NetworkMode
Ports nat . PortMap
Networks map [ string ] * networkData
}
// Network holds the network data to the Docker provider
type networkData struct {
Name string
Addr string
Port int
Protocol string
ID string
2015-11-20 23:05:06 +08:00
}
2016-04-08 14:20:54 +02:00
func ( provider * Docker ) createClient ( ) ( client . APIClient , error ) {
var httpClient * http . Client
httpHeaders := map [ string ] string {
2016-07-21 16:33:49 +02:00
"User-Agent" : "Traefik " + version . Version ,
2016-04-08 14:20:54 +02:00
}
if provider . TLS != nil {
2016-06-27 16:14:56 +02:00
config , err := provider . TLS . CreateTLSConfig ( )
2016-04-08 14:20:54 +02:00
if err != nil {
return nil , err
}
tr := & http . Transport {
TLSClientConfig : config ,
}
proto , addr , _ , err := client . ParseHost ( provider . Endpoint )
if err != nil {
return nil , err
}
sockets . ConfigureTransport ( tr , proto , addr )
httpClient = & http . Client {
Transport : tr ,
}
2016-06-27 16:14:56 +02:00
2016-04-08 14:20:54 +02:00
}
2016-08-05 11:02:46 -03:00
var version string
if provider . SwarmMode {
version = SwarmAPIVersion
} else {
version = DockerAPIVersion
}
return client . NewClient ( provider . Endpoint , version , httpClient , httpHeaders )
2016-04-08 14:20:54 +02:00
}
2015-11-01 19:29:47 +01:00
// Provide allows the provider to provide configurations to traefik
// using the given configuration channel.
2016-11-09 19:27:04 +01:00
func ( provider * Docker ) Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool , constraints types . Constraints ) error {
2016-05-30 15:05:58 +02:00
provider . Constraints = append ( provider . Constraints , constraints ... )
2016-04-13 20:36:23 +02:00
// TODO register this routine in pool, and watch for stop channel
2016-03-31 18:57:08 +02:00
safe . Go ( func ( ) {
2016-02-25 18:30:13 +01:00
operation := func ( ) error {
var err error
2015-11-01 19:29:47 +01:00
2016-04-08 14:20:54 +02:00
dockerClient , err := provider . createClient ( )
2016-02-25 18:30:13 +01:00
if err != nil {
log . Errorf ( "Failed to create a client for docker, error: %s" , err )
return err
}
2016-06-08 19:39:38 +02:00
ctx := context . Background ( )
version , err := dockerClient . ServerVersion ( ctx )
2016-04-08 14:20:54 +02:00
log . Debugf ( "Docker connection established with docker %s (API %s)" , version . Version , version . APIVersion )
2016-08-05 11:02:46 -03:00
var dockerDataList [ ] dockerData
if provider . SwarmMode {
dockerDataList , err = listServices ( ctx , dockerClient )
if err != nil {
log . Errorf ( "Failed to list services for docker swarm mode, error %s" , err )
return err
}
} else {
dockerDataList , err = listContainers ( ctx , dockerClient )
if err != nil {
log . Errorf ( "Failed to list containers for docker, error %s" , err )
return err
}
2016-02-25 18:30:13 +01:00
}
2016-08-05 11:02:46 -03:00
configuration := provider . loadDockerConfig ( dockerDataList )
2016-02-25 18:30:13 +01:00
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
if provider . Watch {
2016-06-08 19:39:38 +02:00
ctx , cancel := context . WithCancel ( ctx )
2016-08-05 11:02:46 -03:00
if provider . SwarmMode {
// TODO: This need to be change. Linked to Swarm events docker/docker#23827
ticker := time . NewTicker ( SwarmDefaultWatchTime )
pool . Go ( func ( stop chan bool ) {
for {
select {
case <- ticker . C :
services , err := listServices ( ctx , dockerClient )
if err != nil {
log . Errorf ( "Failed to list services for docker, error %s" , err )
return
}
configuration := provider . loadDockerConfig ( services )
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
}
case <- stop :
ticker . Stop ( )
cancel ( )
return
}
}
} )
} else {
pool . Go ( func ( stop chan bool ) {
for {
select {
case <- stop :
cancel ( )
return
}
}
} )
f := filters . NewArgs ( )
f . Add ( "type" , "container" )
options := dockertypes . EventsOptions {
Filters : f ,
}
eventHandler := events . NewHandler ( events . ByAction )
startStopHandle := func ( m eventtypes . Message ) {
log . Debugf ( "Docker event received %+v" , m )
containers , err := listContainers ( ctx , dockerClient )
if err != nil {
log . Errorf ( "Failed to list containers for docker, error %s" , err )
// Call cancel to get out of the monitor
2016-06-16 22:49:57 +02:00
cancel ( )
return
}
2016-08-05 11:02:46 -03:00
configuration := provider . loadDockerConfig ( containers )
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
2015-09-10 22:54:37 +02:00
}
2015-09-10 09:06:37 +02:00
}
2016-08-05 11:02:46 -03:00
eventHandler . Handle ( "start" , startStopHandle )
eventHandler . Handle ( "die" , startStopHandle )
2016-10-03 11:01:37 +02:00
eventHandler . Handle ( "health_status: healthy" , startStopHandle )
eventHandler . Handle ( "health_status: unhealthy" , startStopHandle )
eventHandler . Handle ( "health_status: starting" , startStopHandle )
2016-04-13 20:36:23 +02:00
2016-08-05 11:02:46 -03:00
errChan := events . MonitorWithHandler ( ctx , dockerClient , options , eventHandler )
if err := <- errChan ; err != nil {
return err
}
2016-04-08 14:20:54 +02:00
}
2015-11-01 19:29:47 +01:00
}
2016-02-25 18:30:13 +01:00
return nil
}
notify := func ( err error , time time . Duration ) {
log . Errorf ( "Docker connection error %+v, retrying in %s" , err , time )
}
2016-09-19 19:08:39 +02:00
err := backoff . RetryNotify ( operation , job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , notify )
2016-02-25 18:30:13 +01:00
if err != nil {
2016-08-19 10:36:54 +02:00
log . Errorf ( "Cannot connect to docker server %+v" , err )
2016-02-25 18:30:13 +01:00
}
2016-03-31 18:57:08 +02:00
} )
2015-11-01 19:29:47 +01:00
2015-10-01 12:04:25 +02:00
return nil
2015-09-07 10:38:58 +02:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) loadDockerConfig ( containersInspected [ ] dockerData ) * types . Configuration {
2015-10-08 21:21:51 +02:00
var DockerFuncMap = template . FuncMap {
2016-08-25 00:22:06 -04:00
"getBackend" : provider . getBackend ,
"getIPAddress" : provider . getIPAddress ,
"getPort" : provider . getPort ,
"getWeight" : provider . getWeight ,
"getDomain" : provider . getDomain ,
"getProtocol" : provider . getProtocol ,
"getPassHostHeader" : provider . getPassHostHeader ,
"getPriority" : provider . getPriority ,
"getEntryPoints" : provider . getEntryPoints ,
"getFrontendRule" : provider . getFrontendRule ,
"hasCircuitBreakerLabel" : provider . hasCircuitBreakerLabel ,
"getCircuitBreakerExpression" : provider . getCircuitBreakerExpression ,
"hasLoadBalancerLabel" : provider . hasLoadBalancerLabel ,
"getLoadBalancerMethod" : provider . getLoadBalancerMethod ,
"hasMaxConnLabels" : provider . hasMaxConnLabels ,
"getMaxConnAmount" : provider . getMaxConnAmount ,
"getMaxConnExtractorFunc" : provider . getMaxConnExtractorFunc ,
2016-06-08 15:08:03 -04:00
"getSticky" : provider . getSticky ,
2016-08-25 00:22:06 -04:00
"replace" : replace ,
2015-09-10 22:54:37 +02:00
}
// filter containers
2016-08-05 11:02:46 -03:00
filteredContainers := fun . Filter ( func ( container dockerData ) bool {
2016-09-20 14:52:35 +02:00
return provider . containerFilter ( container )
2016-08-05 11:02:46 -03:00
} , containersInspected ) . ( [ ] dockerData )
2015-09-10 22:54:37 +02:00
2016-08-05 11:02:46 -03:00
frontends := map [ string ] [ ] dockerData { }
2016-09-28 16:29:19 +02:00
backends := map [ string ] dockerData { }
servers := map [ string ] [ ] dockerData { }
2015-09-10 22:54:37 +02:00
for _ , container := range filteredContainers {
2016-06-06 21:59:58 +02:00
frontendName := provider . getFrontendName ( container )
frontends [ frontendName ] = append ( frontends [ frontendName ] , container )
2016-09-28 16:29:19 +02:00
backendName := provider . getBackend ( container )
backends [ backendName ] = container
servers [ backendName ] = append ( servers [ backendName ] , container )
2015-09-07 10:38:58 +02:00
}
2015-09-09 16:49:51 +02:00
templateObjects := struct {
2016-09-28 13:28:20 +02:00
Containers [ ] dockerData
Frontends map [ string ] [ ] dockerData
2016-09-28 16:29:19 +02:00
Backends map [ string ] dockerData
Servers map [ string ] [ ] dockerData
2016-09-28 13:28:20 +02:00
Domain string
2015-09-07 10:38:58 +02:00
} {
2015-09-10 22:54:37 +02:00
filteredContainers ,
2015-10-23 09:49:19 +02:00
frontends ,
2016-09-28 16:29:19 +02:00
backends ,
servers ,
2015-09-09 17:10:43 +02:00
provider . Domain ,
2015-09-07 10:38:58 +02:00
}
2015-11-13 11:50:32 +01:00
configuration , err := provider . getConfiguration ( "templates/docker.tmpl" , DockerFuncMap , templateObjects )
2015-09-07 10:38:58 +02:00
if err != nil {
2015-11-13 11:50:32 +01:00
log . Error ( err )
2015-09-07 10:38:58 +02:00
}
2015-11-13 11:50:32 +01:00
return configuration
}
2015-09-07 10:38:58 +02:00
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) hasCircuitBreakerLabel ( container dockerData ) bool {
2016-08-25 00:22:06 -04:00
if _ , err := getLabel ( container , "traefik.backend.circuitbreaker.expression" ) ; err != nil {
return false
}
return true
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) hasLoadBalancerLabel ( container dockerData ) bool {
2016-09-28 13:28:20 +02:00
_ , errMethod := getLabel ( container , "traefik.backend.loadbalancer.method" )
_ , errSticky := getLabel ( container , "traefik.backend.loadbalancer.sticky" )
if errMethod != nil && errSticky != nil {
2016-08-25 00:22:06 -04:00
return false
}
return true
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) hasMaxConnLabels ( container dockerData ) bool {
2016-08-25 00:22:06 -04:00
if _ , err := getLabel ( container , "traefik.backend.maxconn.amount" ) ; err != nil {
return false
}
if _ , err := getLabel ( container , "traefik.backend.maxconn.extractorfunc" ) ; err != nil {
return false
}
return true
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getCircuitBreakerExpression ( container dockerData ) string {
2016-08-25 00:22:06 -04:00
if label , err := getLabel ( container , "traefik.backend.circuitbreaker.expression" ) ; err == nil {
return label
}
return "NetworkErrorRatio() > 1"
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getLoadBalancerMethod ( container dockerData ) string {
2016-08-25 00:22:06 -04:00
if label , err := getLabel ( container , "traefik.backend.loadbalancer.method" ) ; err == nil {
return label
}
return "wrr"
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getMaxConnAmount ( container dockerData ) int64 {
2016-08-25 00:22:06 -04:00
if label , err := getLabel ( container , "traefik.backend.maxconn.amount" ) ; err == nil {
i , errConv := strconv . ParseInt ( label , 10 , 64 )
if errConv != nil {
log . Errorf ( "Unable to parse traefik.backend.maxconn.amount %s" , label )
return math . MaxInt64
}
return i
}
return math . MaxInt64
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getMaxConnExtractorFunc ( container dockerData ) string {
2016-08-25 00:22:06 -04:00
if label , err := getLabel ( container , "traefik.backend.maxconn.extractorfunc" ) ; err == nil {
return label
}
return "request.host"
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) containerFilter ( container dockerData ) bool {
_ , err := strconv . Atoi ( container . Labels [ "traefik.port" ] )
2016-05-28 15:16:57 -07:00
if len ( container . NetworkSettings . Ports ) == 0 && err != nil {
log . Debugf ( "Filtering container without port and no traefik.port label %s" , container . Name )
2015-11-13 11:50:32 +01:00
return false
2015-09-07 10:38:58 +02:00
}
2015-11-13 11:50:32 +01:00
2016-09-20 14:52:35 +02:00
if ! isContainerEnabled ( container , provider . ExposedByDefault ) {
2015-11-13 11:50:32 +01:00
log . Debugf ( "Filtering disabled container %s" , container . Name )
return false
}
2016-08-05 11:02:46 -03:00
constraintTags := strings . Split ( container . Labels [ "traefik.tags" ] , "," )
2016-06-11 19:05:54 +02:00
if ok , failingConstraint := provider . MatchConstraints ( constraintTags ) ; ! ok {
2016-06-06 21:59:58 +02:00
if failingConstraint != nil {
log . Debugf ( "Container %v pruned by '%v' constraint" , container . Name , failingConstraint . String ( ) )
}
return false
}
2016-10-03 11:01:37 +02:00
if container . Health != "" && container . Health != "healthy" {
log . Debugf ( "Filtering unhealthy or starting container %s" , container . Name )
return false
}
2015-11-13 11:50:32 +01:00
return true
2015-09-09 16:49:51 +02:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getFrontendName ( container dockerData ) string {
2015-10-23 09:49:19 +02:00
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
2016-03-27 01:05:17 +01:00
return normalize ( provider . getFrontendRule ( container ) )
2015-10-23 09:49:19 +02:00
}
2015-11-13 11:50:32 +01: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.
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getFrontendRule ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if label , err := getLabel ( container , "traefik.frontend.rule" ) ; err == nil {
return label
}
2016-06-01 16:47:39 +02:00
return "Host:" + provider . getSubDomain ( container . Name ) + "." + provider . Domain
2015-11-13 11:50:32 +01:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getBackend ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if label , err := getLabel ( container , "traefik.backend" ) ; err == nil {
2016-11-23 21:31:37 +01:00
return normalize ( label )
2015-11-13 11:50:32 +01:00
}
2016-03-27 01:05:17 +01:00
return normalize ( container . Name )
2015-11-13 11:50:32 +01:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getIPAddress ( container dockerData ) string {
2016-05-31 22:11:17 -07:00
if label , err := getLabel ( container , "traefik.docker.network" ) ; err == nil && label != "" {
2016-08-05 11:02:46 -03:00
networkSettings := container . NetworkSettings
if networkSettings . Networks != nil {
network := networkSettings . Networks [ label ]
2016-05-31 22:11:17 -07:00
if network != nil {
2016-08-05 11:02:46 -03:00
return network . Addr
2016-05-31 22:11:17 -07:00
}
}
}
2016-07-05 11:44:30 +02:00
// If net==host, quick n' dirty, we return 127.0.0.1
// This will work locally, but will fail with swarm.
2016-08-05 11:02:46 -03:00
if "host" == container . NetworkSettings . NetworkMode {
2016-07-05 11:44:30 +02:00
return "127.0.0.1"
}
2016-09-20 14:52:35 +02:00
if provider . UseBindPortIP {
port := provider . getPort ( container )
for netport , portBindings := range container . NetworkSettings . Ports {
if string ( netport ) == port + "/TCP" || string ( netport ) == port + "/UDP" {
for _ , p := range portBindings {
return p . HostIP
}
}
}
}
2016-07-05 11:44:30 +02:00
for _ , network := range container . NetworkSettings . Networks {
2016-08-05 11:02:46 -03:00
return network . Addr
2016-05-31 22:11:17 -07:00
}
return ""
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getPort ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if label , err := getLabel ( container , "traefik.port" ) ; err == nil {
return label
}
for key := range container . NetworkSettings . Ports {
return key . Port ( )
}
return ""
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getWeight ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if label , err := getLabel ( container , "traefik.weight" ) ; err == nil {
return label
}
2016-11-23 14:49:55 +01:00
return "0"
2015-11-13 11:50:32 +01:00
}
2016-09-28 13:28:20 +02:00
func ( provider * Docker ) getSticky ( container dockerData ) string {
if _ , err := getLabel ( container , "traefik.backend.loadbalancer.sticky" ) ; err == nil {
2016-06-08 15:08:03 -04:00
return "true"
2016-06-02 14:36:14 -04:00
}
2016-06-08 15:08:03 -04:00
return "false"
2016-06-02 14:36:14 -04:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getDomain ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if label , err := getLabel ( container , "traefik.domain" ) ; err == nil {
return label
}
return provider . Domain
2015-10-23 09:49:19 +02:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getProtocol ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if label , err := getLabel ( container , "traefik.protocol" ) ; err == nil {
return label
}
return "http"
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getPassHostHeader ( container dockerData ) string {
2015-11-13 11:50:32 +01:00
if passHostHeader , err := getLabel ( container , "traefik.frontend.passHostHeader" ) ; err == nil {
return passHostHeader
}
2016-05-10 07:43:24 -04:00
return "true"
2015-11-13 11:50:32 +01:00
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getPriority ( container dockerData ) string {
2016-06-06 22:30:23 +02:00
if priority , err := getLabel ( container , "traefik.frontend.priority" ) ; err == nil {
return priority
}
return "0"
}
2016-08-05 11:02:46 -03:00
func ( provider * Docker ) getEntryPoints ( container dockerData ) [ ] string {
2016-02-01 16:08:58 +01:00
if entryPoints , err := getLabel ( container , "traefik.frontend.entryPoints" ) ; err == nil {
return strings . Split ( entryPoints , "," )
}
return [ ] string { }
}
2016-08-05 11:02:46 -03:00
func isContainerEnabled ( container dockerData , exposedByDefault bool ) bool {
return exposedByDefault && container . Labels [ "traefik.enable" ] != "false" || container . Labels [ "traefik.enable" ] == "true"
2016-07-14 11:32:15 +02:00
}
2016-08-05 11:02:46 -03:00
func getLabel ( container dockerData , label string ) ( string , error ) {
for key , value := range container . Labels {
2015-10-23 09:49:19 +02:00
if key == label {
return value , nil
2015-09-09 16:49:51 +02:00
}
}
2015-10-23 09:49:19 +02:00
return "" , errors . New ( "Label not found:" + label )
}
2016-08-05 11:02:46 -03:00
func getLabels ( container dockerData , labels [ ] string ) ( map [ string ] string , error ) {
2015-11-05 15:14:25 +01:00
var globalErr error
2015-10-27 00:26:35 +01:00
foundLabels := map [ string ] string { }
for _ , label := range labels {
2015-11-13 11:50:32 +01:00
foundLabel , err := getLabel ( container , label )
2015-11-05 15:14:25 +01:00
// Error out only if one of them is defined.
2015-11-01 19:29:47 +01:00
if err != nil {
2015-11-05 15:14:25 +01:00
globalErr = errors . New ( "Label not found: " + label )
continue
2015-10-27 00:26:35 +01:00
}
2015-11-01 19:29:47 +01:00
foundLabels [ label ] = foundLabel
2015-11-05 15:14:25 +01:00
2015-10-27 00:26:35 +01:00
}
2015-11-05 15:14:25 +01:00
return foundLabels , globalErr
2015-10-27 00:26:35 +01:00
}
2016-08-05 11:02:46 -03:00
func listContainers ( ctx context . Context , dockerClient client . ContainerAPIClient ) ( [ ] dockerData , error ) {
2016-06-08 19:39:38 +02:00
containerList , err := dockerClient . ContainerList ( ctx , dockertypes . ContainerListOptions { } )
2016-04-08 14:20:54 +02:00
if err != nil {
2016-08-05 11:02:46 -03:00
return [ ] dockerData { } , err
2016-04-08 14:20:54 +02:00
}
2016-08-05 11:02:46 -03:00
containersInspected := [ ] dockerData { }
2015-10-23 09:49:19 +02:00
2015-11-13 11:50:32 +01:00
// get inspect containers
for _ , container := range containerList {
2016-06-08 19:39:38 +02:00
containerInspected , err := dockerClient . ContainerInspect ( ctx , container . ID )
2016-04-08 14:20:54 +02:00
if err != nil {
2016-06-20 12:15:31 +02:00
log . Warnf ( "Failed to inspect container %s, error: %s" , container . ID , err )
2016-06-08 19:39:38 +02:00
} else {
2016-08-05 11:02:46 -03:00
dockerData := parseContainer ( containerInspected )
containersInspected = append ( containersInspected , dockerData )
2016-04-08 14:20:54 +02:00
}
2015-10-23 09:49:19 +02:00
}
2016-04-08 14:20:54 +02:00
return containersInspected , nil
2015-09-12 15:10:03 +02:00
}
2016-05-31 23:23:23 +02:00
2016-08-05 11:02:46 -03:00
func parseContainer ( container dockertypes . ContainerJSON ) dockerData {
dockerData := dockerData {
NetworkSettings : networkSettings { } ,
}
if container . ContainerJSONBase != nil {
dockerData . Name = container . ContainerJSONBase . Name
if container . ContainerJSONBase . HostConfig != nil {
dockerData . NetworkSettings . NetworkMode = container . ContainerJSONBase . HostConfig . NetworkMode
}
2016-11-28 16:46:37 +01:00
if container . State != nil && container . State . Health != nil {
dockerData . Health = container . State . Health . Status
}
2016-08-05 11:02:46 -03:00
}
if container . Config != nil && container . Config . Labels != nil {
dockerData . Labels = container . Config . Labels
}
if container . NetworkSettings != nil {
if container . NetworkSettings . Ports != nil {
dockerData . NetworkSettings . Ports = container . NetworkSettings . Ports
}
if container . NetworkSettings . Networks != nil {
dockerData . NetworkSettings . Networks = make ( map [ string ] * networkData )
for name , containerNetwork := range container . NetworkSettings . Networks {
dockerData . NetworkSettings . Networks [ name ] = & networkData {
ID : containerNetwork . NetworkID ,
Name : name ,
Addr : containerNetwork . IPAddress ,
}
}
}
}
return dockerData
}
2016-05-31 23:23:23 +02:00
// Escape beginning slash "/", convert all others to dash "-"
2016-06-01 16:47:39 +02:00
func ( provider * Docker ) getSubDomain ( name string ) string {
2016-05-31 23:23:23 +02:00
return strings . Replace ( strings . TrimPrefix ( name , "/" ) , "/" , "-" , - 1 )
}
2016-08-05 11:02:46 -03:00
func listServices ( ctx context . Context , dockerClient client . APIClient ) ( [ ] dockerData , error ) {
serviceList , err := dockerClient . ServiceList ( ctx , dockertypes . ServiceListOptions { } )
if err != nil {
return [ ] dockerData { } , err
}
networkListArgs := filters . NewArgs ( )
networkListArgs . Add ( "driver" , "overlay" )
networkList , err := dockerClient . NetworkList ( ctx , dockertypes . NetworkListOptions { Filters : networkListArgs } )
networkMap := make ( map [ string ] * dockertypes . NetworkResource )
if err != nil {
log . Debug ( "Failed to network inspect on client for docker, error: %s" , err )
return [ ] dockerData { } , err
}
for _ , network := range networkList {
2016-10-07 16:35:27 +02:00
networkToAdd := network
networkMap [ network . ID ] = & networkToAdd
2016-08-05 11:02:46 -03:00
}
var dockerDataList [ ] dockerData
for _ , service := range serviceList {
dockerData := parseService ( service , networkMap )
dockerDataList = append ( dockerDataList , dockerData )
}
return dockerDataList , err
}
func parseService ( service swarmtypes . Service , networkMap map [ string ] * dockertypes . NetworkResource ) dockerData {
dockerData := dockerData {
Name : service . Spec . Annotations . Name ,
Labels : service . Spec . Annotations . Labels ,
NetworkSettings : networkSettings { } ,
}
if service . Spec . EndpointSpec != nil {
switch service . Spec . EndpointSpec . Mode {
case swarm . ResolutionModeDNSRR :
log . Debug ( "Ignored endpoint-mode not supported, service name: %s" , dockerData . Name )
case swarm . ResolutionModeVIP :
dockerData . NetworkSettings . Networks = make ( map [ string ] * networkData )
for _ , virtualIP := range service . Endpoint . VirtualIPs {
networkService := networkMap [ virtualIP . NetworkID ]
if networkService != nil {
ip , _ , _ := net . ParseCIDR ( virtualIP . Addr )
network := & networkData {
Name : networkService . Name ,
ID : virtualIP . NetworkID ,
Addr : ip . String ( ) ,
}
dockerData . NetworkSettings . Networks [ network . Name ] = network
} else {
log . Debug ( "Network not found, id: %s" , virtualIP . NetworkID )
}
}
}
}
return dockerData
}