diff --git a/README.md b/README.md index 6232759..0343414 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,6 @@ providers: Run `traefik-kop` on your other nodes via docker-compose: ```yaml -version: "3" services: traefik-kop: image: "ghcr.io/jittering/traefik-kop:latest" @@ -100,8 +99,9 @@ GLOBAL OPTIONS: --docker-host value Docker endpoint (default: "unix:///var/run/docker.sock") [$DOCKER_HOST] --docker-config value Docker provider config (file must end in .yaml) [$DOCKER_CONFIG] --poll-interval value Poll interval for refreshing container list (default: 60) [$KOP_POLL_INTERVAL] - --verbose Enable debug logging (default: true) [$VERBOSE, $DEBUG] - --help, -h show help (default: false) + --namespace value Namespace to process containers for [$NAMESPACE] + --verbose Enable debug logging (default: false) [$VERBOSE, $DEBUG] + --help, -h show help --version, -V Print the version (default: false) ``` @@ -185,6 +185,40 @@ service port on the host and tell traefik to bind to *that* port (8088 in the example above) in the load balancer config, not the internal port (80). This is so that traefik can reach it over the network. +## Namespaces + +traefik-kop has the ability to target containers via namespaces. Simply +configure `kop` with a namespace: + +```yaml +services: + traefik-kop: + image: "ghcr.io/jittering/traefik-kop:latest" + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock + environment: + - "REDIS_ADDR=192.168.1.50:6379" + - "BIND_IP=192.168.1.75" + - "NAMESPACE=staging" +``` + +Then add the `kop.namespace` label to your target services, along with the usual traefik labels: + +```yaml +services: + nginx: + image: "nginx:alpine" + restart: unless-stopped + ports: + - 8088:80 + labels: + - "kop.namespace=staging" + - "traefik.enable=true" + - "traefik..." +``` + + ## Docker API traefik-kop expects to connect to the Docker host API via a unix socket, by diff --git a/bin/traefik-kop/main.go b/bin/traefik-kop/main.go index dad49d9..0832fe0 100644 --- a/bin/traefik-kop/main.go +++ b/bin/traefik-kop/main.go @@ -99,6 +99,11 @@ func flags() { Value: 60, EnvVars: []string{"KOP_POLL_INTERVAL"}, }, + &cli.StringFlag{ + Name: "namespace", + Usage: "Namespace to process containers for", + EnvVars: []string{"NAMESPACE"}, + }, &cli.BoolFlag{ Name: "verbose", Usage: "Enable debug logging", @@ -141,6 +146,7 @@ func doStart(c *cli.Context) error { DockerHost: c.String("docker-host"), DockerConfig: c.String("docker-config"), PollInterval: c.Int64("poll-interval"), + Namespace: c.String("namespace"), } setupLogging(c.Bool("verbose")) diff --git a/config.go b/config.go index 24f5713..58d8b0f 100644 --- a/config.go +++ b/config.go @@ -21,6 +21,7 @@ type Config struct { Pass string DB int PollInterval int64 + Namespace string } type ConfigFile struct { diff --git a/docker.go b/docker.go index a237bd1..1065026 100644 --- a/docker.go +++ b/docker.go @@ -16,6 +16,12 @@ import ( // Copied from traefik. See docker provider package for original impl +type dockerCache struct { + client client.APIClient + list []types.Container + details map[string]types.ContainerJSON +} + // Must be 0 for unix socket? // Non-zero throws an error const defaultTimeout = time.Duration(0) @@ -51,19 +57,30 @@ func getClientOpts(endpoint string) ([]client.Opt, error) { } // looks up the docker container by finding the matching service or router traefik label -func findContainerByServiceName(dc client.APIClient, svcType string, svcName string, routerName string) (types.ContainerJSON, error) { +func (dc *dockerCache) findContainerByServiceName(svcType string, svcName string, routerName string) (types.ContainerJSON, error) { svcName = strings.TrimSuffix(svcName, "@docker") routerName = strings.TrimSuffix(routerName, "@docker") - list, err := dc.ContainerList(context.Background(), types.ContainerListOptions{}) - if err != nil { - return types.ContainerJSON{}, errors.Wrap(err, "failed to list containers") - } - for _, c := range list { - container, err := dc.ContainerInspect(context.Background(), c.ID) + if dc.list == nil { + var err error + dc.list, err = dc.client.ContainerList(context.Background(), types.ContainerListOptions{}) if err != nil { - return types.ContainerJSON{}, errors.Wrapf(err, "failed to inspect container %s", c.ID) + return types.ContainerJSON{}, errors.Wrap(err, "failed to list containers") } + } + + for _, c := range dc.list { + var container types.ContainerJSON + var ok bool + if container, ok = dc.details[c.ID]; !ok { + var err error + container, err = dc.client.ContainerInspect(context.Background(), c.ID) + if err != nil { + return types.ContainerJSON{}, errors.Wrapf(err, "failed to inspect container %s", c.ID) + } + dc.details[c.ID] = container + } + // check labels svcNeedle := fmt.Sprintf("traefik.%s.services.%s.", svcType, svcName) routerNeedle := fmt.Sprintf("traefik.%s.routers.%s.", svcType, routerName) diff --git a/docker_helpers_test.go b/docker_helpers_test.go index 2814be7..9ac8cff 100644 --- a/docker_helpers_test.go +++ b/docker_helpers_test.go @@ -174,22 +174,8 @@ func loadYAMLWithEnv(yaml []byte, env map[string]string) (*compose.Config, error return loader.Load(buildConfigDetails(dict, env)) } -func doTest(t *testing.T, file string) *testStore { - p := path.Join("fixtures", file) - f, err := os.Open(p) - assert.NoError(t, err) - - b, err := io.ReadAll(f) - assert.NoError(t, err) - - composeConfig, err := loadYAML(b) - assert.NoError(t, err) - - store := &testStore{} - - // fmt.Printf("%+v\n", composeConfig) - - // convert compose services to containers +// convert compose services to containers +func createContainers(composeConfig *compose.Config) []types.Container { containers := make([]types.Container, 0) for _, service := range composeConfig.Services { container := types.Container{ @@ -211,9 +197,11 @@ func doTest(t *testing.T, file string) *testStore { container.Ports = ports containers = append(containers, container) } - dockerAPI.containers = containers + return containers +} - // convert compose services to containersJSON +// convert compose services to containersJSON +func createContainersJSON(composeConfig *compose.Config) map[string]types.ContainerJSON { containersJSON := make(map[string]types.ContainerJSON) for _, service := range composeConfig.Services { containerJSON := types.ContainerJSON{ @@ -260,15 +248,39 @@ func doTest(t *testing.T, file string) *testStore { } containersJSON[service.Name] = containerJSON } - dockerAPI.containersJSON = containersJSON + return containersJSON +} + +func doTest(t *testing.T, file string, config *Config) *testStore { + p := path.Join("fixtures", file) + f, err := os.Open(p) + assert.NoError(t, err) + + b, err := io.ReadAll(f) + assert.NoError(t, err) + + composeConfig, err := loadYAML(b) + assert.NoError(t, err) + + store := &testStore{} + + // fmt.Printf("%+v\n", composeConfig) + + dockerAPI.containers = createContainers(composeConfig) + dockerAPI.containersJSON = createContainersJSON(composeConfig) dp := &docker.Provider{} dp.Watch = false dp.Endpoint = dockerEndpoint - config := &Config{ - BindIP: "192.168.100.100", + if config == nil { + config = &Config{ + BindIP: "192.168.100.100", + } + } else { + config.BindIP = "192.168.100.100" } + handleConfigChange := createConfigHandler(*config, store, dp, dc) routinesPool := safe.NewPool(context.Background()) diff --git a/docker_test.go b/docker_test.go index 0c1c8e4..4ef76f8 100644 --- a/docker_test.go +++ b/docker_test.go @@ -49,7 +49,7 @@ func Test_httpServerVersion(t *testing.T) { } func Test_helloWorld(t *testing.T) { - store := doTest(t, "helloworld.yml") + store := doTest(t, "helloworld.yml", nil) assert.NotNil(t, store) assert.NotNil(t, store.kv) @@ -71,7 +71,7 @@ func Test_helloWorld(t *testing.T) { func Test_helloDetect(t *testing.T) { // both services get mapped to the same port (error case) - store := doTest(t, "hellodetect.yml") + store := doTest(t, "hellodetect.yml", nil) assertServiceIPs(t, store, []svc{ {"hello-detect", "http", "http://192.168.100.100:5577"}, {"hello-detect2", "http", "http://192.168.100.100:5577"}, @@ -80,7 +80,7 @@ func Test_helloDetect(t *testing.T) { func Test_helloIP(t *testing.T) { // override ip via labels - store := doTest(t, "helloip.yml") + store := doTest(t, "helloip.yml", nil) assertServiceIPs(t, store, []svc{ {"helloip", "http", "http://4.4.4.4:5599"}, {"helloip2", "http", "http://3.3.3.3:5599"}, @@ -89,7 +89,7 @@ func Test_helloIP(t *testing.T) { func Test_helloNetwork(t *testing.T) { // use ip from specific docker network - store := doTest(t, "network.yml") + store := doTest(t, "network.yml", nil) assertServiceIPs(t, store, []svc{ {"hello1", "http", "http://10.10.10.5:5555"}, }) @@ -97,7 +97,7 @@ func Test_helloNetwork(t *testing.T) { func Test_TCP(t *testing.T) { // tcp service - store := doTest(t, "gitea.yml") + store := doTest(t, "gitea.yml", nil) assertServiceIPs(t, store, []svc{ {"gitea-ssh", "tcp", "192.168.100.100:20022"}, }) @@ -105,7 +105,7 @@ func Test_TCP(t *testing.T) { func Test_TCPMQTT(t *testing.T) { // from https://github.com/jittering/traefik-kop/issues/35 - store := doTest(t, "mqtt.yml") + store := doTest(t, "mqtt.yml", nil) assertServiceIPs(t, store, []svc{ {"mqtt", "http", "http://192.168.100.100:9001"}, {"mqtt", "tcp", "192.168.100.100:1883"}, @@ -113,7 +113,7 @@ func Test_TCPMQTT(t *testing.T) { } func Test_helloWorldNoCert(t *testing.T) { - store := doTest(t, "hello-no-cert.yml") + store := doTest(t, "hello-no-cert.yml", nil) assert.Equal(t, "hello1", store.kv["traefik/http/routers/hello1/service"]) assert.Nil(t, store.kv["traefik/http/routers/hello1/tls/certResolver"]) @@ -121,8 +121,17 @@ func Test_helloWorldNoCert(t *testing.T) { assertServiceIPs(t, store, []svc{ {"hello1", "http", "http://192.168.100.100:5555"}, }) +} + +func Test_helloWorldIgnore(t *testing.T) { + store := doTest(t, "hello-ignore.yml", nil) + assert.Nil(t, store.kv["traefik/http/routers/hello1/service"]) - // assert.Fail(t, "TODO: check for no cert") + store = doTest(t, "hello-ignore.yml", &Config{Namespace: "foobar"}) + assert.Equal(t, "hello1", store.kv["traefik/http/routers/hello1/service"]) + assertServiceIPs(t, store, []svc{ + {"hello1", "http", "http://192.168.100.100:5555"}, + }) } func Test_samePrefix(t *testing.T) { diff --git a/traefik_kop.go b/traefik_kop.go index a469a90..9267dc7 100644 --- a/traefik_kop.go +++ b/traefik_kop.go @@ -7,6 +7,7 @@ import ( "strings" "time" + "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/sirupsen/logrus" ptypes "github.com/traefik/paerser/types" @@ -57,9 +58,18 @@ func createConfigHandler(config Config, store TraefikStore, dp *docker.Provider, // logrus.Printf("got new conf..\n") // fmt.Printf("%s\n", dumpJson(conf)) logrus.Infoln("refreshing traefik-kop configuration") + + dc := &dockerCache{ + client: dockerClient, + list: nil, + details: make(map[string]types.ContainerJSON), + } + + filterServices(dc, &conf, config.Namespace) + if !dp.UseBindPortIP { // if not using traefik's built in IP/Port detection, use our own - replaceIPs(dockerClient, &conf, config.BindIP) + replaceIPs(dc, &conf, config.BindIP) } err := store.Store(conf) if err != nil { @@ -122,6 +132,102 @@ func Start(config Config) { select {} // go forever } +func keepContainer(ns string, container types.ContainerJSON) bool { + containerNS := container.Config.Labels["kop.namespace"] + return ns == containerNS || (ns == "" && containerNS == "") +} + +// filter out services by namespace +// ns is traefik-kop's configured namespace to match against. +func filterServices(dc *dockerCache, conf *dynamic.Configuration, ns string) { + if conf.HTTP != nil && conf.HTTP.Services != nil { + for svcName := range conf.HTTP.Services { + container, err := dc.findContainerByServiceName("http", svcName, getRouterOfService(conf, svcName, "http")) + if err != nil { + logrus.Warnf("failed to find container for service '%s': %s", svcName, err) + continue + } + if !keepContainer(ns, container) { + logrus.Infof("skipping service %s (not in namespace %s)", svcName, ns) + delete(conf.HTTP.Services, svcName) + } + } + } + + if conf.HTTP != nil && conf.HTTP.Routers != nil { + for routerName, router := range conf.HTTP.Routers { + svcName := router.Service + container, err := dc.findContainerByServiceName("http", svcName, routerName) + if err != nil { + logrus.Warnf("failed to find container for service '%s': %s", svcName, err) + continue + } + if !keepContainer(ns, container) { + logrus.Infof("skipping router %s (not in namespace %s)", routerName, ns) + delete(conf.HTTP.Routers, routerName) + } + } + } + + if conf.TCP != nil && conf.TCP.Services != nil { + for svcName := range conf.TCP.Services { + container, err := dc.findContainerByServiceName("tcp", svcName, getRouterOfService(conf, svcName, "tcp")) + if err != nil { + logrus.Warnf("failed to find container for service '%s': %s", svcName, err) + continue + } + if !keepContainer(ns, container) { + logrus.Infof("skipping service %s (not in namespace %s)", svcName, ns) + delete(conf.TCP.Services, svcName) + } + } + } + + if conf.TCP != nil && conf.TCP.Routers != nil { + for routerName, router := range conf.TCP.Routers { + svcName := router.Service + container, err := dc.findContainerByServiceName("tcp", svcName, routerName) + if err != nil { + logrus.Warnf("failed to find container for service '%s': %s", svcName, err) + continue + } + if !keepContainer(ns, container) { + logrus.Infof("skipping router %s (not in namespace %s)", routerName, ns) + delete(conf.TCP.Routers, routerName) + } + } + } + + if conf.UDP != nil && conf.UDP.Services != nil { + for svcName := range conf.UDP.Services { + container, err := dc.findContainerByServiceName("udp", svcName, getRouterOfService(conf, svcName, "udp")) + if err != nil { + logrus.Warnf("failed to find container for service '%s': %s", svcName, err) + continue + } + if !keepContainer(ns, container) { + logrus.Warnf("service %s is not running: removing from config", svcName) + delete(conf.UDP.Services, svcName) + } + } + } + + if conf.UDP != nil && conf.UDP.Routers != nil { + for routerName, router := range conf.UDP.Routers { + svcName := router.Service + container, err := dc.findContainerByServiceName("udp", svcName, routerName) + if err != nil { + logrus.Warnf("failed to find container for service '%s': %s", svcName, err) + continue + } + if !keepContainer(ns, container) { + logrus.Infof("skipping router %s (not in namespace %s)", routerName, ns) + delete(conf.UDP.Routers, routerName) + } + } + } +} + // replaceIPs for all service endpoints // // By default, traefik finds the local/internal docker IP for each container. @@ -130,17 +236,17 @@ func Start(config Config) { // // When using CNI, as indicated by the container label `traefik.docker.network`, // we will stick with the container IP. -func replaceIPs(dockerClient client.APIClient, conf *dynamic.Configuration, ip string) { +func replaceIPs(dc *dockerCache, conf *dynamic.Configuration, ip string) { // modify HTTP URLs if conf.HTTP != nil && conf.HTTP.Services != nil { for svcName, svc := range conf.HTTP.Services { log := logrus.WithFields(logrus.Fields{"service": svcName, "service-type": "http"}) log.Debugf("found http service: %s", svcName) for i := range svc.LoadBalancer.Servers { - ip, changed := getKopOverrideBinding(dockerClient, conf, "http", svcName, ip) + ip, changed := getKopOverrideBinding(dc, conf, "http", svcName, ip) if !changed { // override with container IP if we have a routable IP - ip = getContainerNetworkIP(dockerClient, conf, "http", svcName, ip) + ip = getContainerNetworkIP(dc, conf, "http", svcName, ip) } // replace ip into URLs @@ -154,7 +260,7 @@ func replaceIPs(dockerClient client.APIClient, conf *dynamic.Configuration, ip s // labels ourselves. log.Debugf("using load balancer URL for port detection: %s", server.URL) u, _ := url.Parse(server.URL) - p := getContainerPort(dockerClient, conf, "http", svcName, u.Port()) + p := getContainerPort(dc, conf, "http", svcName, u.Port()) if p != "" { u.Host = ip + ":" + p } else { @@ -167,7 +273,7 @@ func replaceIPs(dockerClient client.APIClient, conf *dynamic.Configuration, ip s scheme = server.Scheme } server.URL = fmt.Sprintf("%s://%s", scheme, ip) - port := getContainerPort(dockerClient, conf, "http", svcName, server.Port) + port := getContainerPort(dc, conf, "http", svcName, server.Port) if port != "" { server.URL += ":" + server.Port } @@ -192,10 +298,10 @@ func replaceIPs(dockerClient client.APIClient, conf *dynamic.Configuration, ip s log.Debugf("found tcp service: %s", svcName) for i := range svc.LoadBalancer.Servers { // override with container IP if we have a routable IP - ip = getContainerNetworkIP(dockerClient, conf, "tcp", svcName, ip) + ip = getContainerNetworkIP(dc, conf, "tcp", svcName, ip) server := &svc.LoadBalancer.Servers[i] - server.Port = getContainerPort(dockerClient, conf, "tcp", svcName, server.Port) + server.Port = getContainerPort(dc, conf, "tcp", svcName, server.Port) log.Debugf("using ip '%s' and port '%s' for %s", ip, server.Port, svcName) server.Address = ip if server.Port != "" { @@ -213,10 +319,10 @@ func replaceIPs(dockerClient client.APIClient, conf *dynamic.Configuration, ip s log.Debugf("found udp service: %s", svcName) for i := range svc.LoadBalancer.Servers { // override with container IP if we have a routable IP - ip = getContainerNetworkIP(dockerClient, conf, "udp", svcName, ip) + ip = getContainerNetworkIP(dc, conf, "udp", svcName, ip) server := &svc.LoadBalancer.Servers[i] - server.Port = getContainerPort(dockerClient, conf, "udp", svcName, server.Port) + server.Port = getContainerPort(dc, conf, "udp", svcName, server.Port) log.Debugf("using ip '%s' and port '%s' for %s", ip, server.Port, svcName) server.Address = ip if server.Port != "" { @@ -270,9 +376,9 @@ func getRouterOfService(conf *dynamic.Configuration, svcName string, svcType str // traefik during its config parsing (possibly an container-internal port). The // purpose of this method is to see if we can find a better match, specifically // by looking at the host-port bindings in the docker config. -func getContainerPort(dockerClient client.APIClient, conf *dynamic.Configuration, svcType string, svcName string, port string) string { +func getContainerPort(dc *dockerCache, conf *dynamic.Configuration, svcType string, svcName string, port string) string { log := logrus.WithFields(logrus.Fields{"service": svcName, "service-type": svcType}) - container, err := findContainerByServiceName(dockerClient, svcType, svcName, getRouterOfService(conf, svcName, svcType)) + container, err := dc.findContainerByServiceName(svcType, svcName, getRouterOfService(conf, svcName, svcType)) if err != nil { log.Warnf("failed to find host-port: %s", err) return port @@ -303,8 +409,8 @@ func getContainerPort(dockerClient client.APIClient, conf *dynamic.Configuration // (i.e., via CNI plugins such as calico or weave) // // If not configured, returns the globally bound hostIP -func getContainerNetworkIP(dockerClient client.APIClient, conf *dynamic.Configuration, svcType string, svcName string, hostIP string) string { - container, err := findContainerByServiceName(dockerClient, svcType, svcName, getRouterOfService(conf, svcName, svcType)) +func getContainerNetworkIP(dc *dockerCache, conf *dynamic.Configuration, svcType string, svcName string, hostIP string) string { + container, err := dc.findContainerByServiceName(svcType, svcName, getRouterOfService(conf, svcName, svcType)) if err != nil { logrus.Debugf("failed to find container for service '%s': %s", svcName, err) return hostIP @@ -337,8 +443,8 @@ func getContainerNetworkIP(dockerClient client.APIClient, conf *dynamic.Configur // // For a container with only a single exposed service, or where all services use // the same IP, the latter is sufficient. -func getKopOverrideBinding(dockerClient client.APIClient, conf *dynamic.Configuration, svcType string, svcName string, hostIP string) (string, bool) { - container, err := findContainerByServiceName(dockerClient, svcType, svcName, getRouterOfService(conf, svcName, svcType)) +func getKopOverrideBinding(dc *dockerCache, conf *dynamic.Configuration, svcType string, svcName string, hostIP string) (string, bool) { + container, err := dc.findContainerByServiceName(svcType, svcName, getRouterOfService(conf, svcName, svcType)) if err != nil { logrus.Debugf("failed to find container for service '%s': %s", svcName, err) return hostIP, false diff --git a/traefik_kop_test.go b/traefik_kop_test.go index 2b7fa6b..780fd09 100644 --- a/traefik_kop_test.go +++ b/traefik_kop_test.go @@ -44,8 +44,10 @@ func Test_replaceIPs(t *testing.T) { require.NoError(t, err) require.Contains(t, cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "172.20.0.2") + fc := &dockerCache{client: &fakeDockerClient{}, list: nil, details: make(map[string]types.ContainerJSON)} + // replace and test check again - replaceIPs(&fakeDockerClient{}, cfg, "7.7.7.7") + replaceIPs(fc, cfg, "7.7.7.7") require.NotContains(t, cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "172.20.0.2") // full url @@ -56,7 +58,7 @@ func Test_replaceIPs(t *testing.T) { _, err = toml.DecodeFile("./fixtures/sample.toml", &cfg) require.NoError(t, err) require.Equal(t, "foobar", cfg.TCP.Services["TCPService0"].LoadBalancer.Servers[0].Address) - replaceIPs(&fakeDockerClient{}, cfg, "7.7.7.7") + replaceIPs(fc, cfg, "7.7.7.7") require.Equal(t, "7.7.7.7", cfg.TCP.Services["TCPService0"].LoadBalancer.Servers[0].Address) } @@ -94,6 +96,8 @@ func Test_replacePorts(t *testing.T) { portLabel: "8888", }) + fc := &dockerCache{client: dc, list: nil, details: make(map[string]types.ContainerJSON)} + cfg := &dynamic.Configuration{} err := json.Unmarshal([]byte(NGINX_CONF_JSON), cfg) require.NoError(t, err) @@ -101,19 +105,19 @@ func Test_replacePorts(t *testing.T) { require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "172.20.0.2:80")) // explicit label present - replaceIPs(dc, cfg, "4.4.4.4") + replaceIPs(fc, cfg, "4.4.4.4") require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:8888"), "URL '%s' should end with '%s'", cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:8888") // without label but no port binding delete(dc.container.Config.Labels, portLabel) json.Unmarshal([]byte(NGINX_CONF_JSON), cfg) - replaceIPs(dc, cfg, "4.4.4.4") + replaceIPs(fc, cfg, "4.4.4.4") require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:80")) // with port binding dc.container.HostConfig.PortBindings = portMap json.Unmarshal([]byte(NGINX_CONF_JSON), cfg) - replaceIPs(dc, cfg, "4.4.4.4") + replaceIPs(fc, cfg, "4.4.4.4") require.False(t, strings.HasSuffix(cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:80")) require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:8888")) } @@ -129,6 +133,7 @@ func Test_replacePortsNoService(t *testing.T) { dc := createTestClient(map[string]string{ "traefik.http.routers.nginx.entrypoints": "web-secure", }) + fc := &dockerCache{client: dc, list: nil, details: make(map[string]types.ContainerJSON)} cfg := &dynamic.Configuration{} err := json.Unmarshal([]byte(NGINX_CONF_JSON_DIFFRENT_SERVICE_NAME), cfg) @@ -137,18 +142,18 @@ func Test_replacePortsNoService(t *testing.T) { require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx-nginx@docker"].LoadBalancer.Servers[0].URL, "172.20.0.2:80")) // explicit label present - replaceIPs(dc, cfg, "4.4.4.4") + replaceIPs(fc, cfg, "4.4.4.4") require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx-nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:80")) // without label but no port binding json.Unmarshal([]byte(NGINX_CONF_JSON_DIFFRENT_SERVICE_NAME), cfg) - replaceIPs(dc, cfg, "4.4.4.4") + replaceIPs(fc, cfg, "4.4.4.4") require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx-nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:80")) // with port binding dc.container.HostConfig.PortBindings = portMap json.Unmarshal([]byte(NGINX_CONF_JSON_DIFFRENT_SERVICE_NAME), cfg) - replaceIPs(dc, cfg, "4.4.4.4") + replaceIPs(fc, cfg, "4.4.4.4") require.False(t, strings.HasSuffix(cfg.HTTP.Services["nginx-nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:80")) require.True(t, strings.HasSuffix(cfg.HTTP.Services["nginx-nginx@docker"].LoadBalancer.Servers[0].URL, "4.4.4.4:8888")) }