diff --git a/docs/content/reference/routing-configuration/other-providers/docker.md b/docs/content/reference/routing-configuration/other-providers/docker.md index a6773b190..a17ae2a09 100644 --- a/docs/content/reference/routing-configuration/other-providers/docker.md +++ b/docs/content/reference/routing-configuration/other-providers/docker.md @@ -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`. +#### `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` ```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). !!! warning + When deploying a stack from a compose file `stack`, the networks defined are prefixed with `stack`. diff --git a/integration/docker_test.go b/integration/docker_test.go index 1a767ff6f..08606b60b 100644 --- a/integration/docker_test.go +++ b/integration/docker_test.go @@ -32,7 +32,7 @@ func (s *DockerSuite) TearDownSuite() { } 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() { @@ -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")) 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) +} diff --git a/integration/resources/compose/docker.yml b/integration/resources/compose/docker.yml index 788866e34..783d5be38 100644 --- a/integration/resources/compose/docker.yml +++ b/integration/resources/compose/docker.yml @@ -35,3 +35,9 @@ services: labels: traefik.http.Routers.Super.Rule: Host(`my.super.host`) 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" diff --git a/pkg/provider/docker/builder_test.go b/pkg/provider/docker/builder_test.go index c0b4f1d7f..4d001c8ce 100644 --- a/pkg/provider/docker/builder_test.go +++ b/pkg/provider/docker/builder_test.go @@ -12,6 +12,7 @@ func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes. ContainerJSONBase: &containertypes.ContainerJSONBase{ Name: "fake", HostConfig: &containertypes.HostConfig{}, + State: &containertypes.State{}, }, Config: &containertypes.Config{}, NetworkSettings: &containertypes.NetworkSettings{ diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 713586295..c0140655e 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -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 { 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 { 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 { return nil } @@ -196,6 +211,19 @@ func (p *DynConfBuilder) keepContainer(ctx context.Context, container dockerData 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 { logger.Debug().Msg("Filtering unhealthy or starting container") return false diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index e54b980c3..33b8e390a 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "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/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) { type expected struct { ip string diff --git a/pkg/provider/docker/data.go b/pkg/provider/docker/data.go index 4c42d396e..22dde04e7 100644 --- a/pkg/provider/docker/data.go +++ b/pkg/provider/docker/data.go @@ -10,6 +10,7 @@ type dockerData struct { ID string ServiceName string Name string + Status string Labels map[string]string // List of labels set to container or service NetworkSettings networkSettings Health string diff --git a/pkg/provider/docker/pdocker.go b/pkg/provider/docker/pdocker.go index 0720746a3..0ed6e53aa 100644 --- a/pkg/provider/docker/pdocker.go +++ b/pkg/provider/docker/pdocker.go @@ -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) { - containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{}) + containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{ + All: true, + }) if err != nil { return nil, err } diff --git a/pkg/provider/docker/shared.go b/pkg/provider/docker/shared.go index 8d2ef4001..01eb92e19 100644 --- a/pkg/provider/docker/shared.go +++ b/pkg/provider/docker/shared.go @@ -43,9 +43,9 @@ func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClie return dockerData{} } - // This condition is here to avoid to have empty IP https://github.com/traefik/traefik/issues/2459 - // We register only container which are running - if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running { + // Always parse all containers (running and stopped) + // The allowNonRunning filtering will be applied later in service configuration + if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil { return parseContainer(containerInspected) } @@ -61,6 +61,7 @@ func parseContainer(container containertypes.InspectResponse) dockerData { dData.ID = container.ContainerJSONBase.ID dData.Name = container.ContainerJSONBase.Name dData.ServiceName = dData.Name // Default ServiceName to be the container's Name. + dData.Status = container.ContainerJSONBase.State.Status if container.ContainerJSONBase.HostConfig != nil { dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode diff --git a/pkg/provider/docker/shared_labels.go b/pkg/provider/docker/shared_labels.go index 6eab7143d..7d3bed8c1 100644 --- a/pkg/provider/docker/shared_labels.go +++ b/pkg/provider/docker/shared_labels.go @@ -16,17 +16,24 @@ const ( // configuration contains information from the labels that are globals (not related to the dynamic configuration) // or specific to the provider. type configuration struct { - Enable bool - Network string - LBSwarm bool + Enable bool + Network string + LBSwarm bool + AllowNonRunning bool } type labelConfiguration struct { Enable bool - Docker *specificConfiguration + Docker *dockerSpecificConfiguration Swarm *specificConfiguration } +type dockerSpecificConfiguration struct { + Network *string + LBSwarm bool + AllowNonRunning bool +} + type specificConfiguration struct { Network *string LBSwarm bool @@ -43,9 +50,15 @@ func (p *Shared) extractDockerLabels(container dockerData) (configuration, error network = *conf.Docker.Network } + var allowNonRunning bool + if conf.Docker != nil { + allowNonRunning = conf.Docker.AllowNonRunning + } + return configuration{ - Enable: conf.Enable, - Network: network, + Enable: conf.Enable, + Network: network, + AllowNonRunning: allowNonRunning, }, nil }