Allow discovering non-running Docker containers

This commit is contained in:
Alexis Couvreur 2025-10-24 08:08:04 -04:00 committed by GitHub
parent 5c489c05fc
commit 10be359327
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 600 additions and 11 deletions

View File

@ -688,6 +688,27 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab
This option overrides the value of `exposedByDefault`. This option overrides the value of `exposedByDefault`.
#### `traefik.docker.allownonrunning`
```yaml
- "traefik.docker.allownonrunning=true"
```
By default, Traefik only considers containers in "running" state.
This option controls whether containers that are not in "running" state (e.g., stopped, paused, exited) should still be visible to Traefik for service discovery.
When this label is set to true, Traefik will:
- Keep the router and service configuration even when the container is not running
- Create services with empty backend server lists
- Return 503 Service Unavailable for requests to stopped containers (instead of 404 Not Found)
- Execute the full middleware chain, allowing middlewares to intercept requests
!!! warning "Configuration Collision"
As the `traefik.docker.allownonrunning` enables the discovery of all containers exposing this option disregarding their state,
if multiple stopped containers expose the same router but their configurations diverge, then the routers will be dropped.
#### `traefik.docker.network` #### `traefik.docker.network`
```yaml ```yaml
@ -700,4 +721,5 @@ If a container is linked to several networks, be sure to set the proper network
otherwise it will randomly pick one (depending on how docker is returning them). otherwise it will randomly pick one (depending on how docker is returning them).
!!! warning !!! warning
When deploying a stack from a compose file `stack`, the networks defined are prefixed with `stack`. When deploying a stack from a compose file `stack`, the networks defined are prefixed with `stack`.

View File

@ -32,7 +32,7 @@ func (s *DockerSuite) TearDownSuite() {
} }
func (s *DockerSuite) TearDownTest() { func (s *DockerSuite) TearDownTest() {
s.composeStop("simple", "withtcplabels", "withlabels1", "withlabels2", "withonelabelmissing", "powpow") s.composeStop("simple", "withtcplabels", "withlabels1", "withlabels2", "withonelabelmissing", "powpow", "nonRunning")
} }
func (s *DockerSuite) TestSimpleConfiguration() { func (s *DockerSuite) TestSimpleConfiguration() {
@ -222,3 +222,59 @@ func (s *DockerSuite) TestRestartDockerContainers() {
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow")) err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow"))
require.NoError(s.T(), err) require.NoError(s.T(), err)
} }
func (s *DockerSuite) TestDockerAllowNonRunning() {
tempObjects := struct {
DockerHost string
DefaultRule string
}{
DockerHost: s.getDockerHost(),
DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)",
}
file := s.adaptFile("fixtures/docker/simple.toml", tempObjects)
s.composeUp("nonRunning")
// Start traefik
s.traefikCmd(withConfigFile(file))
// Verify the container is working when running
req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
require.NoError(s.T(), err)
req.Host = "non.running.host"
resp, err := try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK)
require.NoError(s.T(), err)
body, err := io.ReadAll(resp.Body)
require.NoError(s.T(), err)
assert.Contains(s.T(), string(body), "Hostname:")
// Verify the router exists in Traefik configuration
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers", 1*time.Second, try.BodyContains("NonRunning"))
require.NoError(s.T(), err)
// Stop the container
s.composeStop("nonRunning")
// Wait a bit for container stop to be detected
time.Sleep(2 * time.Second)
// Verify the router still exists in configuration even though container is stopped
// This is the key test - the router should persist due to allowNonRunning=true
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers", 10*time.Second, try.BodyContains("NonRunning"))
require.NoError(s.T(), err)
// Verify the service still exists in configuration
err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1*time.Second, try.BodyContains("nonRunning"))
require.NoError(s.T(), err)
// HTTP requests should fail (502 Bad Gateway) since container is stopped but router exists
req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil)
require.NoError(s.T(), err)
req.Host = "non.running.host"
err = try.Request(req, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable))
require.NoError(s.T(), err)
}

View File

@ -35,3 +35,9 @@ services:
labels: labels:
traefik.http.Routers.Super.Rule: Host(`my.super.host`) traefik.http.Routers.Super.Rule: Host(`my.super.host`)
traefik.http.Services.powpow.LoadBalancer.server.Port: 2375 traefik.http.Services.powpow.LoadBalancer.server.Port: 2375
nonRunning:
image: traefik/whoami
labels:
traefik.http.Routers.NonRunning.Rule: Host(`non.running.host`)
traefik.docker.allownonrunning: "true"

View File

@ -12,6 +12,7 @@ func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes.
ContainerJSONBase: &containertypes.ContainerJSONBase{ ContainerJSONBase: &containertypes.ContainerJSONBase{
Name: "fake", Name: "fake",
HostConfig: &containertypes.HostConfig{}, HostConfig: &containertypes.HostConfig{},
State: &containertypes.State{},
}, },
Config: &containertypes.Config{}, Config: &containertypes.Config{},
NetworkSettings: &containertypes.NetworkSettings{ NetworkSettings: &containertypes.NetworkSettings{

View File

@ -114,6 +114,11 @@ func (p *DynConfBuilder) buildTCPServiceConfiguration(ctx context.Context, conta
} }
} }
// Keep an empty server load-balancer for non-running containers.
if container.Status != "" && container.Status != containertypes.StateRunning {
return nil
}
// Keep an empty server load-balancer for unhealthy containers.
if container.Health != "" && container.Health != containertypes.Healthy { if container.Health != "" && container.Health != containertypes.Healthy {
return nil return nil
} }
@ -138,6 +143,11 @@ func (p *DynConfBuilder) buildUDPServiceConfiguration(ctx context.Context, conta
} }
} }
// Keep an empty server load-balancer for non-running containers.
if container.Status != "" && container.Status != containertypes.StateRunning {
return nil
}
// Keep an empty server load-balancer for unhealthy containers.
if container.Health != "" && container.Health != containertypes.Healthy { if container.Health != "" && container.Health != containertypes.Healthy {
return nil return nil
} }
@ -164,6 +174,11 @@ func (p *DynConfBuilder) buildServiceConfiguration(ctx context.Context, containe
} }
} }
// Keep an empty server load-balancer for non-running containers.
if container.Status != "" && container.Status != containertypes.StateRunning {
return nil
}
// Keep an empty server load-balancer for unhealthy containers.
if container.Health != "" && container.Health != containertypes.Healthy { if container.Health != "" && container.Health != containertypes.Healthy {
return nil return nil
} }
@ -196,6 +211,19 @@ func (p *DynConfBuilder) keepContainer(ctx context.Context, container dockerData
return false return false
} }
// AllowNonRunning has precedence over AllowEmptyServices.
// If AllowNonRunning is true, we don't care about the container health/status,
// and we need to quit before checking it.
// Only configurable with the Docker provider.
if container.ExtraConf.AllowNonRunning {
return true
}
if container.Status != "" && container.Status != containertypes.StateRunning {
logger.Debug().Msg("Filtering non running container")
return false
}
if !p.AllowEmptyServices && container.Health != "" && container.Health != containertypes.Healthy { if !p.AllowEmptyServices && container.Health != "" && container.Health != containertypes.Healthy {
logger.Debug().Msg("Filtering unhealthy or starting container") logger.Debug().Msg("Filtering unhealthy or starting container")
return false return false

View File

@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
ptypes "github.com/traefik/paerser/types" ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/provider"
"github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/tls"
"github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/types"
) )
@ -3935,6 +3936,464 @@ func TestDynConfBuilder_build(t *testing.T) {
} }
} }
func TestDynConfBuilder_build_allowNonRunning(t *testing.T) {
testCases := []struct {
desc string
containers []dockerData
expected *dynamic.Configuration
}{
{
desc: "exited container with allowNonRunning=true should create router and service without servers",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "exited",
Health: "",
ExtraConf: configuration{
Enable: true,
AllowNonRunning: true,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Test",
Rule: "Host(`Test`)",
DefaultRule: true,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Test": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: pointer(true),
Strategy: "wrr",
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "exited container with allowNonRunning=false should not create anything",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "exited",
Health: "",
ExtraConf: configuration{
Enable: true,
AllowNonRunning: false,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "running container with allowNonRunning=true should work normally with servers",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "running",
Health: "",
ExtraConf: configuration{
Enable: true,
AllowNonRunning: true,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Test",
Rule: "Host(`Test`)",
DefaultRule: true,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Test": {
LoadBalancer: &dynamic.ServersLoadBalancer{
Servers: []dynamic.Server{
{
URL: "http://127.0.0.1:80",
},
},
PassHostHeader: pointer(true),
Strategy: "wrr",
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "created container with allowNonRunning=true should create router and service without servers)",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "created",
Health: "",
ExtraConf: configuration{
Enable: true,
AllowNonRunning: true,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Test",
Rule: "Host(`Test`)",
DefaultRule: true,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Test": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: pointer(true),
Strategy: "wrr",
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "dead container with allowNonRunning=true should create router and service without servers)",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "dead",
Health: "",
ExtraConf: configuration{
Enable: true,
AllowNonRunning: true,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{
"Test": {
Service: "Test",
Rule: "Host(`Test`)",
DefaultRule: true,
},
},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{
"Test": {
LoadBalancer: &dynamic.ServersLoadBalancer{
PassHostHeader: pointer(true),
Strategy: "wrr",
ResponseForwarding: &dynamic.ResponseForwarding{
FlushInterval: ptypes.Duration(100 * time.Millisecond),
},
},
},
},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "exited container with TCP configuration and allowNonRunning=true should create TCP service without servers",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "exited",
Health: "",
Labels: map[string]string{
"traefik.tcp.routers.Test.rule": "HostSNI(`test.localhost`)",
},
ExtraConf: configuration{
Enable: true,
AllowNonRunning: true,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/tcp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{
"Test": {
Service: "Test",
Rule: "HostSNI(`test.localhost`)",
},
},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{
"Test": {
LoadBalancer: &dynamic.TCPServersLoadBalancer{},
},
},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{},
Services: map[string]*dynamic.UDPService{},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
{
desc: "exited container with UDP configuration and allowNonRunning=true should create UDP service without servers",
containers: []dockerData{
{
ServiceName: "Test",
Name: "Test",
Status: "exited",
Health: "",
Labels: map[string]string{
"traefik.udp.routers.Test.entrypoints": "udp",
},
ExtraConf: configuration{
Enable: true,
AllowNonRunning: true,
},
NetworkSettings: networkSettings{
NetworkMode: "bridge",
Ports: nat.PortMap{
"80/udp": []nat.PortBinding{},
},
Networks: map[string]*networkData{
"bridge": {
Name: "bridge",
Addr: "127.0.0.1",
},
},
},
},
},
expected: &dynamic.Configuration{
TCP: &dynamic.TCPConfiguration{
Routers: map[string]*dynamic.TCPRouter{},
Middlewares: map[string]*dynamic.TCPMiddleware{},
Services: map[string]*dynamic.TCPService{},
ServersTransports: map[string]*dynamic.TCPServersTransport{},
},
UDP: &dynamic.UDPConfiguration{
Routers: map[string]*dynamic.UDPRouter{
"Test": {
Service: "Test",
EntryPoints: []string{"udp"},
},
},
Services: map[string]*dynamic.UDPService{
"Test": {
LoadBalancer: &dynamic.UDPServersLoadBalancer{},
},
},
},
HTTP: &dynamic.HTTPConfiguration{
Routers: map[string]*dynamic.Router{},
Middlewares: map[string]*dynamic.Middleware{},
Services: map[string]*dynamic.Service{},
ServersTransports: map[string]*dynamic.ServersTransport{},
},
TLS: &dynamic.TLSConfiguration{
Stores: map[string]tls.Store{},
},
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
defaultRuleTpl, err := provider.MakeDefaultRuleTemplate(DefaultTemplateRule, nil)
require.NoError(t, err)
p := Shared{
ExposedByDefault: true,
DefaultRule: DefaultTemplateRule,
defaultRuleTpl: defaultRuleTpl,
}
builder := NewDynConfBuilder(p, nil, false)
configuration := builder.build(t.Context(), test.containers)
assert.Equal(t, test.expected, configuration)
})
}
}
func TestDynConfBuilder_getIPPort_docker(t *testing.T) { func TestDynConfBuilder_getIPPort_docker(t *testing.T) {
type expected struct { type expected struct {
ip string ip string

View File

@ -10,6 +10,7 @@ type dockerData struct {
ID string ID string
ServiceName string ServiceName string
Name string Name string
Status string
Labels map[string]string // List of labels set to container or service Labels map[string]string // List of labels set to container or service
NetworkSettings networkSettings NetworkSettings networkSettings
Health string Health string

View File

@ -165,7 +165,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
} }
func (p *Provider) listContainers(ctx context.Context, dockerClient client.ContainerAPIClient) ([]dockerData, error) { func (p *Provider) listContainers(ctx context.Context, dockerClient client.ContainerAPIClient) ([]dockerData, error) {
containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{}) containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{
All: true,
})
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -43,9 +43,9 @@ func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClie
return dockerData{} return dockerData{}
} }
// This condition is here to avoid to have empty IP https://github.com/traefik/traefik/issues/2459 // Always parse all containers (running and stopped)
// We register only container which are running // The allowNonRunning filtering will be applied later in service configuration
if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running { if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil {
return parseContainer(containerInspected) return parseContainer(containerInspected)
} }
@ -61,6 +61,7 @@ func parseContainer(container containertypes.InspectResponse) dockerData {
dData.ID = container.ContainerJSONBase.ID dData.ID = container.ContainerJSONBase.ID
dData.Name = container.ContainerJSONBase.Name dData.Name = container.ContainerJSONBase.Name
dData.ServiceName = dData.Name // Default ServiceName to be the container's Name. dData.ServiceName = dData.Name // Default ServiceName to be the container's Name.
dData.Status = container.ContainerJSONBase.State.Status
if container.ContainerJSONBase.HostConfig != nil { if container.ContainerJSONBase.HostConfig != nil {
dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode

View File

@ -19,14 +19,21 @@ type configuration struct {
Enable bool Enable bool
Network string Network string
LBSwarm bool LBSwarm bool
AllowNonRunning bool
} }
type labelConfiguration struct { type labelConfiguration struct {
Enable bool Enable bool
Docker *specificConfiguration Docker *dockerSpecificConfiguration
Swarm *specificConfiguration Swarm *specificConfiguration
} }
type dockerSpecificConfiguration struct {
Network *string
LBSwarm bool
AllowNonRunning bool
}
type specificConfiguration struct { type specificConfiguration struct {
Network *string Network *string
LBSwarm bool LBSwarm bool
@ -43,9 +50,15 @@ func (p *Shared) extractDockerLabels(container dockerData) (configuration, error
network = *conf.Docker.Network network = *conf.Docker.Network
} }
var allowNonRunning bool
if conf.Docker != nil {
allowNonRunning = conf.Docker.AllowNonRunning
}
return configuration{ return configuration{
Enable: conf.Enable, Enable: conf.Enable,
Network: network, Network: network,
AllowNonRunning: allowNonRunning,
}, nil }, nil
} }