2017-04-15 13:49:53 +00:00
package docker
2015-09-12 13:10:03 +00:00
2015-09-07 22:15:14 +00:00
import (
2016-08-16 15:26:10 +00:00
"context"
2016-08-25 04:22:06 +00:00
"math"
2016-08-05 14:02:46 +00:00
"net"
2016-04-08 12:20:54 +00:00
"net/http"
2017-04-15 13:49:53 +00:00
"regexp"
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"
2016-09-19 17:08:39 +00:00
"github.com/cenk/backoff"
"github.com/containous/traefik/job"
2016-09-23 16:27:01 +00:00
"github.com/containous/traefik/log"
2017-04-15 13:49:53 +00:00
"github.com/containous/traefik/provider"
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"
2016-07-21 14:33:49 +00:00
"github.com/containous/traefik/version"
2017-07-06 14:28:13 +00:00
dockertypes "github.com/docker/docker/api/types"
dockercontainertypes "github.com/docker/docker/api/types/container"
eventtypes "github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
swarmtypes "github.com/docker/docker/api/types/swarm"
2017-10-23 08:33:02 +00:00
"github.com/docker/docker/api/types/versions"
2017-07-06 14:28:13 +00:00
"github.com/docker/docker/client"
2016-08-05 14:02:46 +00:00
"github.com/docker/go-connections/nat"
2016-04-08 12:20:54 +00:00
"github.com/docker/go-connections/sockets"
2017-10-30 14:10:05 +00:00
"github.com/pkg/errors"
2015-09-07 08:38:58 +00:00
)
2015-09-09 20:39:08 +00:00
2016-08-05 14:02:46 +00:00
const (
2017-04-15 13:49:53 +00:00
// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use
2017-11-03 12:51:24 +00:00
SwarmAPIVersion = "1.24"
2016-08-05 14:02:46 +00:00
// SwarmDefaultWatchTime is the duration of the interval when polling docker
SwarmDefaultWatchTime = 15 * time . Second
2017-07-10 14:58:12 +00:00
2017-11-28 10:16:03 +00:00
defaultWeight = "0"
defaultProtocol = "http"
defaultPassHostHeader = "true"
defaultFrontendPriority = "0"
defaultCircuitBreakerExpression = "NetworkErrorRatio() > 1"
defaultFrontendRedirect = ""
defaultBackendLoadBalancerMethod = "wrr"
defaultBackendMaxconnExtractorfunc = "request.host"
2016-08-05 14:02:46 +00:00
)
2016-04-08 12:20:54 +00:00
2017-04-15 13:49:53 +00:00
var _ provider . Provider = ( * Provider ) ( nil )
2016-08-16 17:13:18 +00:00
2017-04-17 10:50:02 +00:00
// Provider holds configurations of the provider.
2017-04-15 13:49:53 +00:00
type Provider struct {
2017-10-02 08:32:02 +00:00
provider . BaseProvider ` mapstructure:",squash" export:"true" `
2017-08-25 16:22:03 +00:00
Endpoint string ` description:"Docker server endpoint. Can be a tcp or a unix socket endpoint" `
Domain string ` description:"Default domain used" `
2017-10-02 08:32:02 +00:00
TLS * types . ClientTLS ` description:"Enable Docker TLS support" export:"true" `
ExposedByDefault bool ` description:"Expose containers by default" export:"true" `
UseBindPortIP bool ` description:"Use the ip address from the bound port, rather than from the inner network" export:"true" `
SwarmMode bool ` description:"Use Docker on Swarm Mode" export:"true" `
2016-08-05 14:02:46 +00:00
}
2017-04-15 13:49:53 +00:00
// dockerData holds the need data to the Provider p
2016-08-05 14:02:46 +00:00
type dockerData struct {
2017-02-03 01:18:12 +00:00
ServiceName string
2016-08-05 14:02:46 +00:00
Name string
Labels map [ string ] string // List of labels set to container or service
NetworkSettings networkSettings
2016-10-03 09:01:37 +00:00
Health string
2017-10-25 18:20:03 +00:00
Node * dockertypes . ContainerNode
2016-08-05 14:02:46 +00:00
}
2017-04-15 13:49:53 +00:00
// NetworkSettings holds the networks data to the Provider p
2016-08-05 14:02:46 +00:00
type networkSettings struct {
NetworkMode dockercontainertypes . NetworkMode
Ports nat . PortMap
Networks map [ string ] * networkData
}
2017-04-15 13:49:53 +00:00
// Network holds the network data to the Provider p
2016-08-05 14:02:46 +00:00
type networkData struct {
Name string
Addr string
Port int
Protocol string
ID string
2015-11-20 15:05:06 +00:00
}
2017-12-04 10:40:03 +00:00
func ( p * Provider ) createClient ( ) ( client . APIClient , error ) {
2016-04-08 12:20:54 +00:00
var httpClient * http . Client
2017-11-28 10:16:03 +00:00
2017-04-15 13:49:53 +00:00
if p . TLS != nil {
config , err := p . TLS . CreateTLSConfig ( )
2016-04-08 12:20:54 +00:00
if err != nil {
return nil , err
}
tr := & http . Transport {
TLSClientConfig : config ,
}
2017-04-15 13:49:53 +00:00
proto , addr , _ , err := client . ParseHost ( p . Endpoint )
2016-04-08 12:20:54 +00:00
if err != nil {
return nil , err
}
sockets . ConfigureTransport ( tr , proto , addr )
httpClient = & http . Client {
Transport : tr ,
}
2017-11-28 10:16:03 +00:00
}
2016-06-27 14:14:56 +00:00
2017-11-28 10:16:03 +00:00
httpHeaders := map [ string ] string {
"User-Agent" : "Traefik " + version . Version ,
2016-04-08 12:20:54 +00:00
}
2017-11-28 10:16:03 +00:00
var apiVersion string
2017-04-15 13:49:53 +00:00
if p . SwarmMode {
2017-11-28 10:16:03 +00:00
apiVersion = SwarmAPIVersion
2016-08-05 14:02:46 +00:00
} else {
2017-11-28 10:16:03 +00:00
apiVersion = DockerAPIVersion
2016-08-05 14:02:46 +00:00
}
2017-11-28 10:16:03 +00:00
return client . NewClient ( p . Endpoint , apiVersion , httpClient , httpHeaders )
2016-04-08 12:20:54 +00:00
}
2017-04-17 10:50:02 +00:00
// Provide allows the docker provider to provide configurations to traefik
2015-11-01 18:29:47 +00:00
// using the given configuration channel.
2017-04-15 13:49:53 +00:00
func ( p * Provider ) Provide ( configurationChan chan <- types . ConfigMessage , pool * safe . Pool , constraints types . Constraints ) error {
p . Constraints = append ( p . Constraints , constraints ... )
2016-04-13 18:36:23 +00:00
// TODO register this routine in pool, and watch for stop channel
2016-03-31 16:57:08 +00:00
safe . Go ( func ( ) {
2016-02-25 17:30:13 +00:00
operation := func ( ) error {
var err error
2015-11-01 18:29:47 +00:00
2017-04-15 13:49:53 +00:00
dockerClient , err := p . createClient ( )
2016-02-25 17:30:13 +00:00
if err != nil {
log . Errorf ( "Failed to create a client for docker, error: %s" , err )
return err
}
2016-06-08 17:39:38 +00:00
ctx := context . Background ( )
2017-11-28 10:16:03 +00:00
serverVersion , err := dockerClient . ServerVersion ( ctx )
2017-08-18 00:18:02 +00:00
if err != nil {
log . Errorf ( "Failed to retrieve information of the docker client and server host: %s" , err )
return err
}
2017-11-28 10:16:03 +00:00
log . Debugf ( "Provider connection established with docker %s (API %s)" , serverVersion . Version , serverVersion . APIVersion )
2016-08-05 14:02:46 +00:00
var dockerDataList [ ] dockerData
2017-04-15 13:49:53 +00:00
if p . SwarmMode {
2017-11-28 10:16:03 +00:00
dockerDataList , err = listServices ( ctx , dockerClient )
2016-08-05 14:02:46 +00:00
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 17:30:13 +00:00
}
2016-08-05 14:02:46 +00:00
2017-04-15 13:49:53 +00:00
configuration := p . loadDockerConfig ( dockerDataList )
2016-02-25 17:30:13 +00:00
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
2017-04-15 13:49:53 +00:00
if p . Watch {
2016-06-08 17:39:38 +00:00
ctx , cancel := context . WithCancel ( ctx )
2017-04-15 13:49:53 +00:00
if p . SwarmMode {
2017-05-02 15:20:20 +00:00
errChan := make ( chan error )
2016-08-05 14:02:46 +00:00
// TODO: This need to be change. Linked to Swarm events docker/docker#23827
ticker := time . NewTicker ( SwarmDefaultWatchTime )
pool . Go ( func ( stop chan bool ) {
2017-05-02 15:20:20 +00:00
defer close ( errChan )
2016-08-05 14:02:46 +00:00
for {
select {
case <- ticker . C :
2017-11-28 10:16:03 +00:00
services , err := listServices ( ctx , dockerClient )
2016-08-05 14:02:46 +00:00
if err != nil {
log . Errorf ( "Failed to list services for docker, error %s" , err )
2017-05-02 15:20:20 +00:00
errChan <- err
2016-08-05 14:02:46 +00:00
return
}
2017-04-15 13:49:53 +00:00
configuration := p . loadDockerConfig ( services )
2016-08-05 14:02:46 +00:00
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
}
case <- stop :
ticker . Stop ( )
cancel ( )
return
}
}
} )
2017-05-02 15:20:20 +00:00
if err , ok := <- errChan ; ok {
return err
}
// channel closed
2016-08-05 14:02:46 +00:00
} 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 ,
}
2017-07-06 14:28:13 +00:00
2016-08-05 14:02:46 +00:00
startStopHandle := func ( m eventtypes . Message ) {
2017-04-15 13:49:53 +00:00
log . Debugf ( "Provider event received %+v" , m )
2016-08-05 14:02:46 +00:00
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 20:49:57 +00:00
cancel ( )
return
}
2017-04-15 13:49:53 +00:00
configuration := p . loadDockerConfig ( containers )
2016-08-05 14:02:46 +00:00
if configuration != nil {
configurationChan <- types . ConfigMessage {
ProviderName : "docker" ,
Configuration : configuration ,
}
2015-09-10 20:54:37 +00:00
}
2015-09-10 07:06:37 +00:00
}
2017-07-06 14:28:13 +00:00
eventsc , errc := dockerClient . Events ( ctx , options )
for event := range eventsc {
if event . Action == "start" ||
event . Action == "die" ||
strings . HasPrefix ( event . Action , "health_status" ) {
startStopHandle ( event )
}
}
if err := <- errc ; err != nil {
2016-08-05 14:02:46 +00:00
return err
}
2016-04-08 12:20:54 +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 ) {
2017-04-15 13:49:53 +00:00
log . Errorf ( "Provider connection error %+v, retrying in %s" , err , time )
2016-02-25 17:30:13 +00:00
}
2016-12-08 12:32:12 +00:00
err := backoff . RetryNotify ( safe . OperationWithRecover ( operation ) , job . NewBackOff ( backoff . NewExponentialBackOff ( ) ) , notify )
2016-02-25 17:30:13 +00:00
if err != nil {
2016-08-19 08:36:54 +00:00
log . Errorf ( "Cannot connect to docker server %+v" , err )
2016-02-25 17:30:13 +00:00
}
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
}
2017-04-15 13:49:53 +00:00
func ( p * Provider ) loadDockerConfig ( containersInspected [ ] dockerData ) * types . Configuration {
2015-10-08 19:21:51 +00:00
var DockerFuncMap = template . FuncMap {
2017-11-28 10:16:03 +00:00
"getBackend" : getBackend ,
"getIPAddress" : p . getIPAddress ,
"getPort" : getPort ,
"getWeight" : getFuncStringLabel ( types . LabelWeight , defaultWeight ) ,
"getDomain" : getFuncStringLabel ( types . LabelDomain , p . Domain ) ,
"getProtocol" : getFuncStringLabel ( types . LabelProtocol , defaultProtocol ) ,
"getPassHostHeader" : getFuncStringLabel ( types . LabelFrontendPassHostHeader , defaultPassHostHeader ) ,
"getPriority" : getFuncStringLabel ( types . LabelFrontendPriority , defaultFrontendPriority ) ,
"getEntryPoints" : getFuncSliceStringLabel ( types . LabelFrontendEntryPoints ) ,
"getBasicAuth" : getFuncSliceStringLabel ( types . LabelFrontendAuthBasic ) ,
"getFrontendRule" : p . getFrontendRule ,
"getRedirect" : getFuncStringLabel ( types . LabelFrontendRedirect , "" ) ,
"hasCircuitBreakerLabel" : hasLabel ( types . LabelBackendCircuitbreakerExpression ) ,
"getCircuitBreakerExpression" : getFuncStringLabel ( types . LabelBackendCircuitbreakerExpression , defaultCircuitBreakerExpression ) ,
"hasLoadBalancerLabel" : hasLoadBalancerLabel ,
"getLoadBalancerMethod" : getFuncStringLabel ( types . LabelBackendLoadbalancerMethod , defaultBackendLoadBalancerMethod ) ,
"hasMaxConnLabels" : hasMaxConnLabels ,
"getMaxConnAmount" : getFuncInt64Label ( types . LabelBackendMaxconnAmount , math . MaxInt64 ) ,
"getMaxConnExtractorFunc" : getFuncStringLabel ( types . LabelBackendMaxconnExtractorfunc , defaultBackendMaxconnExtractorfunc ) ,
"getSticky" : getSticky ,
"hasStickinessLabel" : hasStickinessLabel ,
"getStickinessCookieName" : getFuncStringLabel ( types . LabelBackendLoadbalancerStickinessCookieName , "" ) ,
"getIsBackendLBSwarm" : getIsBackendLBSwarm ,
"getServiceBackend" : getServiceBackend ,
"getServiceRedirect" : getFuncServiceStringLabel ( types . SuffixFrontendRedirect , defaultFrontendRedirect ) ,
"getWhitelistSourceRange" : getFuncSliceStringLabel ( types . LabelTraefikFrontendWhitelistSourceRange ) ,
2017-12-04 10:40:03 +00:00
"hasRequestHeaders" : hasLabel ( types . LabelFrontendRequestHeaders ) ,
"getRequestHeaders" : getFuncMapLabel ( types . LabelFrontendRequestHeaders ) ,
"hasResponseHeaders" : hasLabel ( types . LabelFrontendResponseHeaders ) ,
"getResponseHeaders" : getFuncMapLabel ( types . LabelFrontendResponseHeaders ) ,
2017-11-28 10:16:03 +00:00
"hasAllowedHostsHeaders" : hasLabel ( types . LabelFrontendAllowedHosts ) ,
"getAllowedHostsHeaders" : getFuncSliceStringLabel ( types . LabelFrontendAllowedHosts ) ,
"hasHostsProxyHeaders" : hasLabel ( types . LabelFrontendHostsProxyHeaders ) ,
"getHostsProxyHeaders" : getFuncSliceStringLabel ( types . LabelFrontendHostsProxyHeaders ) ,
"hasSSLRedirectHeaders" : hasLabel ( types . LabelFrontendSSLRedirect ) ,
"getSSLRedirectHeaders" : getFuncBoolLabel ( types . LabelFrontendSSLRedirect ) ,
"hasSSLTemporaryRedirectHeaders" : hasLabel ( types . LabelFrontendSSLTemporaryRedirect ) ,
"getSSLTemporaryRedirectHeaders" : getFuncBoolLabel ( types . LabelFrontendSSLTemporaryRedirect ) ,
"hasSSLHostHeaders" : hasLabel ( types . LabelFrontendSSLHost ) ,
"getSSLHostHeaders" : getFuncStringLabel ( types . LabelFrontendSSLHost , "" ) ,
"hasSSLProxyHeaders" : hasLabel ( types . LabelFrontendSSLProxyHeaders ) ,
"getSSLProxyHeaders" : getFuncMapLabel ( types . LabelFrontendSSLProxyHeaders ) ,
"hasSTSSecondsHeaders" : hasLabel ( types . LabelFrontendSTSSeconds ) ,
"getSTSSecondsHeaders" : getFuncInt64Label ( types . LabelFrontendSTSSeconds , 0 ) ,
"hasSTSIncludeSubdomainsHeaders" : hasLabel ( types . LabelFrontendSTSIncludeSubdomains ) ,
"getSTSIncludeSubdomainsHeaders" : getFuncBoolLabel ( types . LabelFrontendSTSIncludeSubdomains ) ,
"hasSTSPreloadHeaders" : hasLabel ( types . LabelFrontendSTSPreload ) ,
"getSTSPreloadHeaders" : getFuncBoolLabel ( types . LabelFrontendSTSPreload ) ,
"hasForceSTSHeaderHeaders" : hasLabel ( types . LabelFrontendForceSTSHeader ) ,
"getForceSTSHeaderHeaders" : getFuncBoolLabel ( types . LabelFrontendForceSTSHeader ) ,
"hasFrameDenyHeaders" : hasLabel ( types . LabelFrontendFrameDeny ) ,
"getFrameDenyHeaders" : getFuncBoolLabel ( types . LabelFrontendFrameDeny ) ,
"hasCustomFrameOptionsValueHeaders" : hasLabel ( types . LabelFrontendCustomFrameOptionsValue ) ,
"getCustomFrameOptionsValueHeaders" : getFuncStringLabel ( types . LabelFrontendCustomFrameOptionsValue , "" ) ,
"hasContentTypeNosniffHeaders" : hasLabel ( types . LabelFrontendContentTypeNosniff ) ,
"getContentTypeNosniffHeaders" : getFuncBoolLabel ( types . LabelFrontendContentTypeNosniff ) ,
"hasBrowserXSSFilterHeaders" : hasLabel ( types . LabelFrontendBrowserXSSFilter ) ,
"getBrowserXSSFilterHeaders" : getFuncBoolLabel ( types . LabelFrontendBrowserXSSFilter ) ,
"hasContentSecurityPolicyHeaders" : hasLabel ( types . LabelFrontendContentSecurityPolicy ) ,
"getContentSecurityPolicyHeaders" : getFuncStringLabel ( types . LabelFrontendContentSecurityPolicy , "" ) ,
"hasPublicKeyHeaders" : hasLabel ( types . LabelFrontendPublicKey ) ,
"getPublicKeyHeaders" : getFuncStringLabel ( types . LabelFrontendPublicKey , "" ) ,
"hasReferrerPolicyHeaders" : hasLabel ( types . LabelFrontendReferrerPolicy ) ,
"getReferrerPolicyHeaders" : getFuncStringLabel ( types . LabelFrontendReferrerPolicy , "" ) ,
"hasIsDevelopmentHeaders" : hasLabel ( types . LabelFrontendIsDevelopment ) ,
"getIsDevelopmentHeaders" : getFuncBoolLabel ( types . LabelFrontendIsDevelopment ) ,
"hasServices" : hasServices ,
"getServiceNames" : getServiceNames ,
"getServicePort" : getServicePort ,
"getServiceWeight" : getFuncServiceStringLabel ( types . SuffixWeight , defaultWeight ) ,
"getServiceProtocol" : getFuncServiceStringLabel ( types . SuffixProtocol , defaultProtocol ) ,
"getServiceEntryPoints" : getFuncServiceSliceStringLabel ( types . SuffixFrontendEntryPoints ) ,
"getServiceBasicAuth" : getFuncServiceSliceStringLabel ( types . SuffixFrontendAuthBasic ) ,
"getServiceFrontendRule" : p . getServiceFrontendRule ,
"getServicePassHostHeader" : getFuncServiceStringLabel ( types . SuffixFrontendPassHostHeader , defaultPassHostHeader ) ,
"getServicePriority" : getFuncServiceStringLabel ( types . SuffixFrontendPriority , defaultFrontendPriority ) ,
2015-09-10 20:54:37 +00:00
}
// filter containers
2016-08-05 14:02:46 +00:00
filteredContainers := fun . Filter ( func ( container dockerData ) bool {
2017-04-15 13:49:53 +00:00
return p . containerFilter ( container )
2016-08-05 14:02:46 +00:00
} , containersInspected ) . ( [ ] dockerData )
2015-09-10 20:54:37 +00:00
2016-08-05 14:02:46 +00:00
frontends := map [ string ] [ ] dockerData { }
2016-09-28 14:29:19 +00:00
backends := map [ string ] dockerData { }
servers := map [ string ] [ ] dockerData { }
2017-11-20 14:16:03 +00:00
serviceNames := make ( map [ string ] struct { } )
2017-10-30 11:06:03 +00:00
for idx , container := range filteredContainers {
2017-11-20 14:16:03 +00:00
if _ , exists := serviceNames [ container . ServiceName ] ; ! exists {
frontendName := p . getFrontendName ( container , idx )
frontends [ frontendName ] = append ( frontends [ frontendName ] , container )
if len ( container . ServiceName ) > 0 {
serviceNames [ container . ServiceName ] = struct { } { }
}
}
2017-11-28 10:16:03 +00:00
backendName := getBackend ( container )
2016-09-28 14:29:19 +00:00
backends [ backendName ] = container
servers [ backendName ] = append ( servers [ backendName ] , container )
2015-09-07 08:38:58 +00:00
}
2015-09-09 14:49:51 +00:00
templateObjects := struct {
2016-09-28 11:28:20 +00:00
Containers [ ] dockerData
Frontends map [ string ] [ ] dockerData
2016-09-28 14:29:19 +00:00
Backends map [ string ] dockerData
Servers map [ string ] [ ] dockerData
2016-09-28 11:28:20 +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 ,
2016-09-28 14:29:19 +00:00
backends ,
servers ,
2017-04-15 13:49:53 +00:00
p . Domain ,
2015-09-07 08:38:58 +00:00
}
2017-04-15 13:49:53 +00:00
configuration , err := p . 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
}
2017-11-18 12:50:03 +00:00
2015-11-13 10:50:32 +00:00
return configuration
}
2015-09-07 08:38:58 +00:00
2017-03-08 14:10:21 +00:00
// Regexp used to extract the name of the service and the name of the property for this service
// All properties are under the format traefik.<servicename>.frontent.*= except the port/weight/protocol directly after traefik.<servicename>.
2017-08-21 08:46:03 +00:00
var servicesPropertiesRegexp = regexp . MustCompile ( ` ^traefik\.(?P<service_name>.+?)\.(?P<property_name>port|weight|protocol|frontend\.(.*))$ ` )
2017-03-08 14:10:21 +00:00
// Check if for the given container, we find labels that are defining services
2017-11-28 10:16:03 +00:00
func hasServices ( container dockerData ) bool {
2017-03-08 14:10:21 +00:00
return len ( extractServicesLabels ( container . Labels ) ) > 0
}
// Gets array of service names for a given container
2017-11-28 10:16:03 +00:00
func getServiceNames ( container dockerData ) [ ] string {
2017-03-08 14:10:21 +00:00
labelServiceProperties := extractServicesLabels ( container . Labels )
keys := make ( [ ] string , 0 , len ( labelServiceProperties ) )
for k := range labelServiceProperties {
keys = append ( keys , k )
}
return keys
}
// Extract backend from labels for a given service and a given docker container
2017-11-28 10:16:03 +00:00
func getServiceBackend ( container dockerData , serviceName string ) string {
if value , ok := getContainerServiceLabel ( container , serviceName , types . SuffixFrontendBackend ) ; ok {
2017-11-20 14:16:03 +00:00
return container . ServiceName + "-" + value
2017-03-08 14:10:21 +00:00
}
2017-11-28 10:16:03 +00:00
return strings . TrimPrefix ( container . ServiceName , "/" ) + "-" + getBackend ( container ) + "-" + provider . Normalize ( serviceName )
2017-03-08 14:10:21 +00:00
}
// Extract rule from labels for a given service and a given docker container
2017-11-28 10:16:03 +00:00
func ( p Provider ) getServiceFrontendRule ( container dockerData , serviceName string ) string {
if value , ok := getContainerServiceLabel ( container , serviceName , types . SuffixFrontendRule ) ; ok {
2017-03-08 14:10:21 +00:00
return value
}
2017-04-15 13:49:53 +00:00
return p . getFrontendRule ( container )
2017-03-08 14:10:21 +00:00
}
// Extract port from labels for a given service and a given docker container
2017-11-28 10:16:03 +00:00
func getServicePort ( container dockerData , serviceName string ) string {
if value , ok := getContainerServiceLabel ( container , serviceName , types . SuffixPort ) ; ok {
2017-03-08 14:10:21 +00:00
return value
}
2017-11-28 10:16:03 +00:00
return getPort ( container )
2017-03-08 14:10:21 +00:00
}
2017-11-28 10:16:03 +00:00
func hasLoadBalancerLabel ( container dockerData ) bool {
2017-07-10 14:58:12 +00:00
_ , errMethod := getLabel ( container , types . LabelBackendLoadbalancerMethod )
_ , errSticky := getLabel ( container , types . LabelBackendLoadbalancerSticky )
2017-10-16 15:38:03 +00:00
_ , errStickiness := getLabel ( container , types . LabelBackendLoadbalancerStickiness )
_ , errCookieName := getLabel ( container , types . LabelBackendLoadbalancerStickinessCookieName )
return errMethod == nil || errSticky == nil || errStickiness == nil || errCookieName == nil
2016-08-25 04:22:06 +00:00
}
2017-11-28 10:16:03 +00:00
func hasMaxConnLabels ( container dockerData ) bool {
2017-07-10 14:58:12 +00:00
if _ , err := getLabel ( container , types . LabelBackendMaxconnAmount ) ; err != nil {
2016-08-25 04:22:06 +00:00
return false
}
2017-07-10 14:58:12 +00:00
if _ , err := getLabel ( container , types . LabelBackendMaxconnExtractorfunc ) ; err != nil {
2016-08-25 04:22:06 +00:00
return false
}
return true
}
2017-11-28 10:16:03 +00:00
func ( p Provider ) containerFilter ( container dockerData ) bool {
2017-11-09 11:16:03 +00:00
if ! isContainerEnabled ( container , p . ExposedByDefault ) {
log . Debugf ( "Filtering disabled container %s" , container . Name )
return false
}
2017-10-30 14:10:05 +00:00
var err error
portLabel := "traefik.port label"
2017-11-28 10:16:03 +00:00
if hasServices ( container ) {
2017-10-30 14:10:05 +00:00
portLabel = "traefik.<serviceName>.port or " + portLabel + "s"
err = checkServiceLabelPort ( container )
} else {
_ , err = strconv . Atoi ( container . Labels [ types . LabelPort ] )
}
2016-05-28 22:16:57 +00:00
if len ( container . NetworkSettings . Ports ) == 0 && err != nil {
2017-10-30 14:10:05 +00:00
log . Debugf ( "Filtering container without port and no %s %s : %s" , portLabel , container . Name , err . Error ( ) )
2015-11-13 10:50:32 +00:00
return false
2015-09-07 08:38:58 +00:00
}
2015-11-13 10:50:32 +00:00
2017-07-10 14:58:12 +00:00
constraintTags := strings . Split ( container . Labels [ types . LabelTags ] , "," )
2017-04-15 13:49:53 +00:00
if ok , failingConstraint := p . MatchConstraints ( constraintTags ) ; ! ok {
2016-06-06 19:59:58 +00:00
if failingConstraint != nil {
log . Debugf ( "Container %v pruned by '%v' constraint" , container . Name , failingConstraint . String ( ) )
}
return false
}
2016-10-03 09:01:37 +00:00
if container . Health != "" && container . Health != "healthy" {
log . Debugf ( "Filtering unhealthy or starting container %s" , container . Name )
return false
}
2017-04-07 14:30:57 +00:00
if len ( p . getFrontendRule ( container ) ) == 0 {
log . Debugf ( "Filtering container with empty frontend rule %s" , container . Name )
return false
}
2015-11-13 10:50:32 +00:00
return true
2015-09-09 14:49:51 +00:00
}
2017-10-30 14:10:05 +00:00
// checkServiceLabelPort checks if all service names have a port service label
// or if port container label exists for default value
func checkServiceLabelPort ( container dockerData ) error {
// If port container label is present, there is a default values for all ports, use it for the check
_ , err := strconv . Atoi ( container . Labels [ types . LabelPort ] )
if err != nil {
serviceLabelPorts := make ( map [ string ] struct { } )
serviceLabels := make ( map [ string ] struct { } )
portRegexp := regexp . MustCompile ( ` ^traefik\.(?P<service_name>.+?)\.port$ ` )
for label := range container . Labels {
// Get all port service labels
portLabel := portRegexp . FindStringSubmatch ( label )
if portLabel != nil && len ( portLabel ) > 0 {
serviceLabelPorts [ portLabel [ 0 ] ] = struct { } { }
}
// Get only one instance of all service names from service labels
servicesLabelNames := servicesPropertiesRegexp . FindStringSubmatch ( label )
if servicesLabelNames != nil && len ( servicesLabelNames ) > 0 {
serviceLabels [ strings . Split ( servicesLabelNames [ 0 ] , "." ) [ 1 ] ] = struct { } { }
}
}
// If the number of service labels is different than the number of port services label
// there is an error
if len ( serviceLabels ) == len ( serviceLabelPorts ) {
for labelPort := range serviceLabelPorts {
_ , err = strconv . Atoi ( container . Labels [ labelPort ] )
if err != nil {
break
}
}
} else {
err = errors . New ( "Port service labels missing, please use traefik.port as default value or define all port service labels" )
}
}
return err
}
2017-11-28 10:16:03 +00:00
func ( p Provider ) getFrontendName ( container dockerData , idx int ) 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
2017-10-30 11:06:03 +00:00
return provider . Normalize ( p . getFrontendRule ( container ) + "-" + strconv . Itoa ( idx ) )
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.
2017-11-28 10:16:03 +00:00
func ( p Provider ) getFrontendRule ( container dockerData ) string {
2017-07-10 14:58:12 +00:00
if label , err := getLabel ( container , types . LabelFrontendRule ) ; err == nil {
2015-11-13 10:50:32 +00:00
return label
}
2017-07-10 14:58:12 +00:00
if labels , err := getLabels ( container , [ ] string { labelDockerComposeProject , labelDockerComposeService } ) ; err == nil {
2017-10-23 08:33:02 +00:00
return "Host:" + getSubDomain ( labels [ labelDockerComposeService ] + "." + labels [ labelDockerComposeProject ] ) + "." + p . Domain
2017-05-03 21:24:53 +00:00
}
2017-04-07 14:30:57 +00:00
if len ( p . Domain ) > 0 {
2017-10-23 08:33:02 +00:00
return "Host:" + getSubDomain ( container . ServiceName ) + "." + p . Domain
2017-03-04 12:31:07 +00:00
}
2017-04-07 14:30:57 +00:00
return ""
2015-11-13 10:50:32 +00:00
}
2017-11-28 10:16:03 +00:00
func getBackend ( container dockerData ) string {
2017-07-10 14:58:12 +00:00
if label , err := getLabel ( container , types . LabelBackend ) ; err == nil {
2017-04-15 13:49:53 +00:00
return provider . Normalize ( label )
2015-11-13 10:50:32 +00:00
}
2017-07-10 14:58:12 +00:00
if labels , err := getLabels ( container , [ ] string { labelDockerComposeProject , labelDockerComposeService } ) ; err == nil {
return provider . Normalize ( labels [ labelDockerComposeService ] + "_" + labels [ labelDockerComposeProject ] )
2017-03-04 12:31:07 +00:00
}
2017-04-15 13:49:53 +00:00
return provider . Normalize ( container . ServiceName )
2015-11-13 10:50:32 +00:00
}
2017-11-28 10:16:03 +00:00
func ( p Provider ) getIPAddress ( container dockerData ) string {
2017-07-10 14:58:12 +00:00
if label , err := getLabel ( container , labelDockerNetwork ) ; err == nil && label != "" {
2016-08-05 14:02:46 +00:00
networkSettings := container . NetworkSettings
if networkSettings . Networks != nil {
network := networkSettings . Networks [ label ]
2016-06-01 05:11:17 +00:00
if network != nil {
2016-08-05 14:02:46 +00:00
return network . Addr
2016-06-01 05:11:17 +00:00
}
2017-03-19 17:40:09 +00:00
log . Warnf ( "Could not find network named '%s' for container '%s'! Maybe you're missing the project's prefix in the label? Defaulting to first available network." , label , container . Name )
2016-06-01 05:11:17 +00:00
}
}
2016-07-05 09:44:30 +00:00
2017-10-30 13:36:04 +00:00
if container . NetworkSettings . NetworkMode . IsHost ( ) {
2017-10-25 18:20:03 +00:00
if container . Node != nil {
if container . Node . IPAddress != "" {
return container . Node . IPAddress
}
}
2016-07-05 09:44:30 +00:00
return "127.0.0.1"
}
2017-10-30 13:36:04 +00:00
if container . NetworkSettings . NetworkMode . IsContainer ( ) {
dockerClient , err := p . createClient ( )
if err != nil {
log . Warnf ( "Unable to get IP address for container %s, error: %s" , container . Name , err )
return ""
}
ctx := context . Background ( )
containerInspected , err := dockerClient . ContainerInspect ( ctx , container . NetworkSettings . NetworkMode . ConnectedContainer ( ) )
if err != nil {
log . Warnf ( "Unable to get IP address for container %s : Failed to inspect container ID %s, error: %s" , container . Name , container . NetworkSettings . NetworkMode . ConnectedContainer ( ) , err )
return ""
}
return p . getIPAddress ( parseContainer ( containerInspected ) )
}
2017-04-15 13:49:53 +00:00
if p . UseBindPortIP {
2017-11-28 10:16:03 +00:00
port := getPort ( container )
for netPort , portBindings := range container . NetworkSettings . Ports {
if string ( netPort ) == port + "/TCP" || string ( netPort ) == port + "/UDP" {
2016-09-20 12:52:35 +00:00
for _ , p := range portBindings {
return p . HostIP
}
}
}
}
2016-07-05 09:44:30 +00:00
for _ , network := range container . NetworkSettings . Networks {
2016-08-05 14:02:46 +00:00
return network . Addr
2016-06-01 05:11:17 +00:00
}
return ""
}
2017-11-28 10:16:03 +00:00
func getPort ( container dockerData ) string {
2017-07-10 14:58:12 +00:00
if label , err := getLabel ( container , types . LabelPort ) ; err == nil {
2015-11-13 10:50:32 +00:00
return label
}
2017-04-28 08:05:54 +00:00
// See iteration order in https://blog.golang.org/go-maps-in-action
var ports [ ] nat . Port
2017-11-28 10:16:03 +00:00
for port := range container . NetworkSettings . Ports {
ports = append ( ports , port )
2017-04-28 08:05:54 +00:00
}
less := func ( i , j nat . Port ) bool {
return i . Int ( ) < j . Int ( )
2015-11-13 10:50:32 +00:00
}
2017-04-28 08:05:54 +00:00
nat . Sort ( ports , less )
if len ( ports ) > 0 {
min := ports [ 0 ]
return min . Port ( )
}
2015-11-13 10:50:32 +00:00
return ""
}
2017-11-28 10:16:03 +00:00
func hasStickinessLabel ( container dockerData ) bool {
2017-10-12 15:50:03 +00:00
labelStickiness , errStickiness := getLabel ( container , types . LabelBackendLoadbalancerStickiness )
2017-10-16 15:38:03 +00:00
return errStickiness == nil && len ( labelStickiness ) > 0 && strings . EqualFold ( strings . TrimSpace ( labelStickiness ) , "true" )
}
2017-10-10 09:10:02 +00:00
2017-11-28 10:16:03 +00:00
// Deprecated replaced by Stickiness
func getSticky ( container dockerData ) string {
2017-10-16 15:38:03 +00:00
if label , err := getLabel ( container , types . LabelBackendLoadbalancerSticky ) ; err == nil {
if len ( label ) > 0 {
log . Warnf ( "Deprecated configuration found: %s. Please use %s." , types . LabelBackendLoadbalancerSticky , types . LabelBackendLoadbalancerStickiness )
}
return label
2017-10-10 09:10:02 +00:00
}
2017-10-16 15:38:03 +00:00
return "false"
2017-10-10 09:10:02 +00:00
}
2017-11-28 10:16:03 +00:00
func getIsBackendLBSwarm ( container dockerData ) string {
return getStringLabel ( container , labelBackendLoadBalancerSwarm , "false" )
2017-11-18 12:50:03 +00:00
}
2016-08-05 14:02:46 +00:00
func isContainerEnabled ( container dockerData , exposedByDefault bool ) bool {
2017-07-10 14:58:12 +00:00
return exposedByDefault && container . Labels [ types . LabelEnable ] != "false" || container . Labels [ types . LabelEnable ] == "true"
2016-07-14 09:32:15 +00:00
}
2016-08-05 14:02:46 +00:00
func listContainers ( ctx context . Context , dockerClient client . ContainerAPIClient ) ( [ ] dockerData , error ) {
2016-06-08 17:39:38 +00:00
containerList , err := dockerClient . ContainerList ( ctx , dockertypes . ContainerListOptions { } )
2016-04-08 12:20:54 +00:00
if err != nil {
2016-08-05 14:02:46 +00:00
return [ ] dockerData { } , err
2016-04-08 12:20:54 +00:00
}
2015-10-23 07:49:19 +00:00
2017-11-28 10:16:03 +00:00
var containersInspected [ ] dockerData
2015-11-13 10:50:32 +00:00
// get inspect containers
for _ , container := range containerList {
2016-06-08 17:39:38 +00:00
containerInspected , err := dockerClient . ContainerInspect ( ctx , container . ID )
2016-04-08 12:20:54 +00:00
if err != nil {
2016-06-20 10:15:31 +00:00
log . Warnf ( "Failed to inspect container %s, error: %s" , container . ID , err )
2016-06-08 17:39:38 +00:00
} else {
2017-11-28 12:58:04 +00:00
// This condition is here to avoid to have empty IP https://github.com/containous/traefik/issues/2459
// We register only container which are running
if containerInspected . ContainerJSONBase != nil && containerInspected . ContainerJSONBase . State != nil && containerInspected . ContainerJSONBase . State . Running {
dockerData := parseContainer ( containerInspected )
containersInspected = append ( containersInspected , dockerData )
}
2016-04-08 12:20:54 +00:00
}
2015-10-23 07:49:19 +00:00
}
2016-04-08 12:20:54 +00:00
return containersInspected , nil
2015-09-12 13:10:03 +00:00
}
2016-05-31 21:23:23 +00:00
2016-08-05 14:02:46 +00:00
func parseContainer ( container dockertypes . ContainerJSON ) dockerData {
dockerData := dockerData {
NetworkSettings : networkSettings { } ,
}
if container . ContainerJSONBase != nil {
dockerData . Name = container . ContainerJSONBase . Name
2017-02-03 01:18:12 +00:00
dockerData . ServiceName = dockerData . Name //Default ServiceName to be the container's Name.
2017-10-25 18:20:03 +00:00
dockerData . Node = container . ContainerJSONBase . Node
2016-08-05 14:02:46 +00:00
if container . ContainerJSONBase . HostConfig != nil {
dockerData . NetworkSettings . NetworkMode = container . ContainerJSONBase . HostConfig . NetworkMode
}
2016-11-28 15:46:37 +00:00
if container . State != nil && container . State . Health != nil {
dockerData . Health = container . State . Health . Status
}
2016-08-05 14:02:46 +00: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
}
2017-01-24 10:29:45 +00:00
// Escape beginning slash "/", convert all others to dash "-", and convert underscores "_" to dash "-"
2017-10-23 08:33:02 +00:00
func getSubDomain ( name string ) string {
2017-01-24 10:29:45 +00:00
return strings . Replace ( strings . Replace ( strings . TrimPrefix ( name , "/" ) , "/" , "-" , - 1 ) , "_" , "-" , - 1 )
2016-05-31 21:23:23 +00:00
}
2016-08-05 14:02:46 +00:00
2017-11-28 10:16:03 +00:00
func listServices ( ctx context . Context , dockerClient client . APIClient ) ( [ ] dockerData , error ) {
2016-08-05 14:02:46 +00:00
serviceList , err := dockerClient . ServiceList ( ctx , dockertypes . ServiceListOptions { } )
if err != nil {
return [ ] dockerData { } , err
}
2017-10-23 08:33:02 +00:00
serverVersion , err := dockerClient . ServerVersion ( ctx )
2016-08-05 14:02:46 +00:00
networkListArgs := filters . NewArgs ( )
2017-10-23 08:33:02 +00:00
// https://docs.docker.com/engine/api/v1.29/#tag/Network (Docker 17.06)
if versions . GreaterThanOrEqualTo ( serverVersion . APIVersion , "1.29" ) {
networkListArgs . Add ( "scope" , "swarm" )
} else {
networkListArgs . Add ( "driver" , "overlay" )
}
2016-08-05 14:02:46 +00:00
networkList , err := dockerClient . NetworkList ( ctx , dockertypes . NetworkListOptions { Filters : networkListArgs } )
networkMap := make ( map [ string ] * dockertypes . NetworkResource )
if err != nil {
2017-12-01 13:34:03 +00:00
log . Debugf ( "Failed to network inspect on client for docker, error: %s" , err )
2016-08-05 14:02:46 +00:00
return [ ] dockerData { } , err
}
for _ , network := range networkList {
2016-10-07 14:35:27 +00:00
networkToAdd := network
networkMap [ network . ID ] = & networkToAdd
2016-08-05 14:02:46 +00:00
}
var dockerDataList [ ] dockerData
2017-01-07 08:20:52 +00:00
var dockerDataListTasks [ ] dockerData
2016-08-05 14:02:46 +00:00
for _ , service := range serviceList {
dockerData := parseService ( service , networkMap )
2017-12-01 13:34:03 +00:00
if len ( dockerData . NetworkSettings . Networks ) > 0 {
useSwarmLB , _ := strconv . ParseBool ( getIsBackendLBSwarm ( dockerData ) )
2016-08-05 14:02:46 +00:00
2017-12-01 13:34:03 +00:00
if useSwarmLB {
dockerDataList = append ( dockerDataList , dockerData )
} else {
isGlobalSvc := service . Spec . Mode . Global != nil
dockerDataListTasks , err = listTasks ( ctx , dockerClient , service . ID , dockerData , networkMap , isGlobalSvc )
2017-01-07 08:20:52 +00:00
2017-12-01 13:34:03 +00:00
for _ , dockerDataTask := range dockerDataListTasks {
dockerDataList = append ( dockerDataList , dockerDataTask )
}
2017-01-07 08:20:52 +00:00
}
}
2016-08-05 14:02:46 +00:00
}
return dockerDataList , err
}
func parseService ( service swarmtypes . Service , networkMap map [ string ] * dockertypes . NetworkResource ) dockerData {
dockerData := dockerData {
2017-02-03 01:18:12 +00:00
ServiceName : service . Spec . Annotations . Name ,
2016-08-05 14:02:46 +00:00
Name : service . Spec . Annotations . Name ,
Labels : service . Spec . Annotations . Labels ,
NetworkSettings : networkSettings { } ,
}
if service . Spec . EndpointSpec != nil {
2017-12-01 13:34:03 +00:00
if service . Spec . EndpointSpec . Mode == swarmtypes . ResolutionModeDNSRR {
log . Warnf ( "Ignored endpoint-mode not supported, service name: %s" , service . Spec . Annotations . Name )
} else if service . Spec . EndpointSpec . Mode == swarmtypes . ResolutionModeVIP {
2016-08-05 14:02:46 +00:00
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 {
2017-12-01 13:34:03 +00:00
log . Debugf ( "Network not found, id: %s" , virtualIP . NetworkID )
2016-08-05 14:02:46 +00:00
}
2017-01-07 08:20:52 +00:00
}
}
}
return dockerData
}
func listTasks ( ctx context . Context , dockerClient client . APIClient , serviceID string ,
2017-02-16 16:50:41 +00:00
serviceDockerData dockerData , networkMap map [ string ] * dockertypes . NetworkResource , isGlobalSvc bool ) ( [ ] dockerData , error ) {
2017-01-07 08:20:52 +00:00
serviceIDFilter := filters . NewArgs ( )
serviceIDFilter . Add ( "service" , serviceID )
2017-02-16 16:50:41 +00:00
serviceIDFilter . Add ( "desired-state" , "running" )
2017-07-06 14:28:13 +00:00
taskList , err := dockerClient . TaskList ( ctx , dockertypes . TaskListOptions { Filters : serviceIDFilter } )
2017-01-07 08:20:52 +00:00
if err != nil {
return [ ] dockerData { } , err
}
var dockerDataList [ ] dockerData
for _ , task := range taskList {
2017-07-06 14:28:13 +00:00
if task . Status . State != swarmtypes . TaskStateRunning {
2017-03-16 14:38:40 +00:00
continue
}
2017-02-16 16:50:41 +00:00
dockerData := parseTasks ( task , serviceDockerData , networkMap , isGlobalSvc )
2017-01-07 08:20:52 +00:00
dockerDataList = append ( dockerDataList , dockerData )
}
return dockerDataList , err
}
2016-08-05 14:02:46 +00:00
2017-02-16 16:50:41 +00:00
func parseTasks ( task swarmtypes . Task , serviceDockerData dockerData , networkMap map [ string ] * dockertypes . NetworkResource , isGlobalSvc bool ) dockerData {
2017-01-07 08:20:52 +00:00
dockerData := dockerData {
2017-02-03 01:18:12 +00:00
ServiceName : serviceDockerData . Name ,
2017-01-07 08:20:52 +00:00
Name : serviceDockerData . Name + "." + strconv . Itoa ( task . Slot ) ,
Labels : serviceDockerData . Labels ,
NetworkSettings : networkSettings { } ,
}
2017-08-18 00:18:02 +00:00
if isGlobalSvc {
2017-02-16 16:50:41 +00:00
dockerData . Name = serviceDockerData . Name + "." + task . ID
}
2017-01-07 08:20:52 +00:00
if task . NetworksAttachments != nil {
dockerData . NetworkSettings . Networks = make ( map [ string ] * networkData )
for _ , virtualIP := range task . NetworksAttachments {
if networkService , present := networkMap [ virtualIP . Network . ID ] ; present {
// Not sure about this next loop - when would a task have multiple IP's for the same network?
for _ , addr := range virtualIP . Addresses {
ip , _ , _ := net . ParseCIDR ( addr )
network := & networkData {
ID : virtualIP . Network . ID ,
Name : networkService . Name ,
Addr : ip . String ( ) ,
}
dockerData . NetworkSettings . Networks [ network . Name ] = network
}
2016-08-05 14:02:46 +00:00
}
}
}
return dockerData
}