From 119c8d1dd48a5ec7015a913ae89005ca6b4808e3 Mon Sep 17 00:00:00 2001 From: Adi <6841988+DeepSpace2@users.noreply.github.com> Date: Thu, 29 Jan 2026 21:04:46 +0200 Subject: [PATCH 1/4] feat: multiple docker hosts support --- .example.env | 1 + README.md | 14 ++-- docs/index.md | 7 +- e2e_tests/e2e_test.go | 4 +- main.go | 61 ++++----------- pkg/cli/cli.go | 8 +- pkg/clients/adguardhome/adguardhome.go | 8 +- pkg/clients/clients.go | 56 ++++++++++++++ pkg/clients/{ => common}/common.go | 2 +- pkg/clients/docker/docker.go | 18 +++-- pkg/clients/docker/events.go | 17 ++--- pkg/clients/docker/types.go | 8 ++ pkg/clients/npm/npm.go | 10 +-- pkg/clients/pihole/pihole.go | 16 ++-- pkg/config/config.go | 1 + pkg/processor/processor.go | 102 +++++++++++++------------ 16 files changed, 186 insertions(+), 147 deletions(-) create mode 100644 pkg/clients/clients.go rename pkg/clients/{ => common}/common.go (99%) diff --git a/.example.env b/.example.env index b2ddc86..dc65dd1 100644 --- a/.example.env +++ b/.example.env @@ -1,4 +1,5 @@ DOCKER_HOST= # optional but highly recommended, use a docker socket proxy instead of mounting the docker socket +DOCKER_HOSTS= # optional, comma-separated list of docker hosts NGINX_PROXY_MANAGER_HOST= # required NGINX_PROXY_MANAGER_USERNAME= # required NGINX_PROXY_MANAGER_PASSWORD= # required diff --git a/README.md b/README.md index bdf288d..430fa11 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ as local DNS/CNAME records in **Pi-Hole** (or DNS Rewrites in **AdGuard Home**) ## Key Features -- Automatic Docker container detection. -- Local DNS/CNAME record creation/deletion in Pi-hole. -- DNS Rewrites creation/deletion in AdGuard Home. -- Nginx Proxy Manager host creation. -- Support for Docker socket proxy. +- Automatic Docker container detection (monitoring multiple hosts is supported) +- Local DNS/CNAME record creation/deletion in Pi-hole +- DNS Rewrites creation/deletion in AdGuard Home +- Nginx Proxy Manager host creation +- Support for Docker socket proxy **Pi-Hole's and AdGuard Home's functionality can be toggled individually. By default Pi-Hole is enabled and AdGuard Home is disabled.** @@ -78,7 +78,7 @@ services: restart: unless-stopped ``` -#### Not Recommended: Mounting the Docker Socket +#### Not Recommended: Directly mounting the Docker Socket ```yaml services: @@ -92,7 +92,7 @@ services: - PIHOLE_HOST=... - PIHOLE_PASSWORD=... volumes: - - /var/run/docker.sock:/var/run/docker.sock + - /var/run/docker.sock:/var/run/docker.sock:ro restart: unless-stopped ``` diff --git a/docs/index.md b/docs/index.md index 37d3961..d1fa812 100644 --- a/docs/index.md +++ b/docs/index.md @@ -71,6 +71,7 @@ See [Per Container Configuration ➔ Pi-Hole](#piholeTargetDomainLabel). |
| Set to `false` to enable AdGuard Home functionality | `true` | | `DEBUG` | Set to `true` to enable DEBUG level logs | `false` | | `DOCKER_HOST` | The URL of a docker socket proxy. If set, you don't need to mount the docker socket as a volume. Querying containers must be allowed (typically done by setting the `CONTAINERS` environment variable to `1`). | *None* | +| `DOCKER_HOSTS` | Comma-separated list of multiple docker hosts to monitor, with an empty string meaning the default local host.labels:
- plugNPiN.ip=192.168.0.100:8000
- plugNPiN.url=service.home
- \|
plugNPiN.npmOptions.advancedConfig=location / {
allow 192.168.0.1/15;
deny all;
} |
+| `plugNPiN.npmOptions.blockExploits` | Enables or disables the "Block Common Exploits" option on the proxy host. Set to `true` or `false` | `true` | |
+| `plugNPiN.npmOptions.cachingEnabled` | Enables or disables the "Cache Assets" option on the proxy host. Set to `true` or `false` | `false` | |
+| `plugNPiN.npmOptions.certificateName` | Certificate to use for this host. Must already exist on the NPM instance | | |
+| `plugNPiN.npmOptions.forceSsl` | Force SSL | `false` | |
+| `plugNPiN.npmOptions.http2Support` | Enable HTTP/2 Support | `false` | |
+| `plugNPiN.npmOptions.hstsEnabled` | Enable HSTS | `false` | |
+| `plugNPiN.npmOptions.hstsSubdomains` | Enable HSTS Subdomains | `false` | |
+| `plugNPiN.npmOptions.scheme` | The scheme used to forward traffic to the container. Can be `http` or `https` | `http` | |
+| `plugNPiN.npmOptions.websocketsSupport` | Enables or disables the "Allow Websocket Upgrade" option on the proxy host. Set to `true` or `false` | `false` | |
+
+### Pi-Hole
+
+| Label {: style="width:35%"} | Description | Default {: style="width:10%"} |
+|---|---|---|
+| `plugNPiN.piholeOptions.targetDomain` | If provided, a CNAME record will be created **instead** of a DNS record | |
+
+*[NPM]: Nginx Proxy Manager
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 0000000..8429843
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,3 @@
+# Contributing
+
+Contributions are very welcome! If you have a feature request, bug report, or want to contribute yourself, please feel free to open an [issue](https://github.com/DeepSpace2/PlugNPiN/issues/new){: target="\_blank" } or submit a pull request.
diff --git a/docs/index.md b/docs/index.md
index 37d3961..1eba22a 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -8,11 +8,12 @@
**Plug and play your docker containers into Pi-Hole/AdGuard Home & Nginx Proxy Manager**
Automatically detect running Docker containers based on labels, add them
-as local DNS/[CNAME](#piholeTargetDomainLabel) records in **Pi-Hole** (or DNS Rewrites in **AdGuard Home**) and create matching proxy hosts in
+as local DNS/CNAME records in **Pi-Hole** (or DNS Rewrites in **AdGuard Home**) and create matching proxy hosts in
**Nginx Proxy Manager**.
**Pi-Hole's and AdGuard Home's functionality can be toggled individually. By default Pi-Hole is enabled and AdGuard Home is disabled.**
-See [Optional Environment Variables](#optional).
+See [Optional Environment Variables](./configuration.md#optional).
+
## How It Works
@@ -38,138 +39,10 @@ When a container is processed in either mode, PlugNPiN will:
To create a DNS Rewrite as a CNAME, set the `plugNPiN.adguardHomeOptions.targetDomain` label.
-See [Per Container Configuration ➔ AdGuard Home](#adguardHomeTargetDomainLabel).
+See [Per Container Configuration ➔ AdGuard Home](./configuration.md#adguard-home).
#### Pi-Hole
To create A CNAME record instead of local DNS records ("A record"), set the `plugNPiN.piholeOptions.targetDomain` label.
-See [Per Container Configuration ➔ Pi-Hole](#piholeTargetDomainLabel).
-
-
-## Configuration
-
-### Environment Variables
-
-#### Required
-
-| Variable {: style="width:35%" } | Description | Notes |
-|---|---|---|
-| `ADGUARD_HOME_HOST` | The URL of your AdGuard Home instance | Only required if [`ADGUARD_HOME_DISABLED`](#adguardHomeDisabledEnvVar) is set to `false` |
-| `ADGUARD_HOME_USERNAME` | Your AdGuard Home username | Only required if [`ADGUARD_HOME_DISABLED`](#adguardHomeDisabledEnvVar) is set to `false` |
-| `ADGUARD_HOME_PASSWORD` | Your AdGuard Home password | Only required if [`ADGUARD_HOME_DISABLED`](#adguardHomeDisabledEnvVar) is set to `false` |
-| `NGINX_PROXY_MANAGER_HOST` | The URL of your Nginx Proxy Manager instance. | |
-| `NGINX_PROXY_MANAGER_USERNAME` | Your Nginx Proxy Manager username. | |
-| `NGINX_PROXY_MANAGER_PASSWORD` | Your Nginx Proxy Manager password. labels:
- plugNPiN.ip=192.168.0.100:8000
- plugNPiN.url=service.home
- \|
plugNPiN.npmOptions.advancedConfig=location / {
allow 192.168.0.1/15;
deny all;
} |
-| `plugNPiN.npmOptions.blockExploits` | Enables or disables the "Block Common Exploits" option on the proxy host. Set to `true` or `false` | `true` | |
-| `plugNPiN.npmOptions.cachingEnabled` | Enables or disables the "Cache Assets" option on the proxy host. Set to `true` or `false` | `false` | |
-| `plugNPiN.npmOptions.certificateName` | Certificate to use for this host. Must already exist on the NPM instance | | |
-| `plugNPiN.npmOptions.forceSsl` | Force SSL | `false` | |
-| `plugNPiN.npmOptions.http2Support` | Enable HTTP/2 Support | `false` | |
-| `plugNPiN.npmOptions.hstsEnabled` | Enable HSTS | `false` | |
-| `plugNPiN.npmOptions.hstsSubdomains` | Enable HSTS Subdomains | `false` | |
-| `plugNPiN.npmOptions.scheme` | The scheme used to forward traffic to the container. Can be `http` or `https` | `http` | |
-| `plugNPiN.npmOptions.websocketsSupport` | Enables or disables the "Allow Websocket Upgrade" option on the proxy host. Set to `true` or `false` | `false` | |
-
-#### Pi-Hole
-
-| Label {: style="width:35%"} | Description | Default {: style="width:10%"} |
-|---|---|---|
-| | If provided, a CNAME record will be created **instead** of a DNS record | |
-
-
-## Usage
-
-### Docker Compose
-
-It is **highly recommended** to use a Docker socket proxy to avoid giving the container direct access to the Docker daemon. This improves security by limiting the container's privileges.
-
-#### Recommended: Using a Docker Socket Proxy
-
-```yaml
-services:
- socket-proxy:
- image: lscr.io/linuxserver/socket-proxy:latest
- container_name: socket-proxy
- environment:
- # Allow access to the container list
- - CONTAINERS=1
- volumes:
- - /var/run/docker.sock:/var/run/docker.sock:ro
- read_only: true
- tmpfs:
- - /run
-
- plugnpin:
- image: ghcr.io/deepspace2/plugnpin:latest
- container_name: plugnpin
- depends_on:
- - socket-proxy
- environment:
- - DOCKER_HOST=tcp://socket-proxy:2375
- - NGINX_PROXY_MANAGER_HOST=...
- - NGINX_PROXY_MANAGER_USERNAME=...
- - NGINX_PROXY_MANAGER_PASSWORD=...
- - PIHOLE_HOST=...
- - PIHOLE_PASSWORD=...
- restart: unless-stopped
-```
-
-#### Not Recommended: Mounting the Docker Socket
-
-```yaml
-services:
- plugnpin:
- image: ghcr.io/deepspace2/plugnpin:latest
- container_name: plugnpin
- environment:
- - NGINX_PROXY_MANAGER_HOST=...
- - NGINX_PROXY_MANAGER_USERNAME=...
- - NGINX_PROXY_MANAGER_PASSWORD=...
- - PIHOLE_HOST=...
- - PIHOLE_PASSWORD=...
- volumes:
- - /var/run/docker.sock:/var/run/docker.sock
- restart: unless-stopped
-```
-
-## Contributing
-
-Contributions are very welcome! If you have a feature request, bug report, or want to contribute yourself, please feel free to open an issue or submit a pull request.
-
-*[NPM]: Nginx Proxy Manager
+See [Per Container Configuration ➔ Pi-Hole](./configuration.md#pi-hole).
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 0000000..1e57fcb
--- /dev/null
+++ b/docs/usage.md
@@ -0,0 +1,60 @@
+# Usage
+
+## CLI Flags
+
+| Flag {: style="width:35%" } | Description |
+| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
+| `--dry-run`, `-d` | Simulates the process of adding DNS/CNAME records and proxy hosts without making any actual changes to Pi-Hole or Nginx Proxy Manager. |
+
+## Docker Compose
+
+It is **highly recommended** to use a Docker socket proxy to avoid giving the container direct access to the Docker daemon. This improves security by limiting the container's privileges.
+
+=== "Recommended: Using a Docker Socket Proxy"
+
+ ```yaml
+ services:
+ socket-proxy:
+ image: lscr.io/linuxserver/socket-proxy:latest
+ container_name: socket-proxy
+ environment:
+ # Allow access to the container list
+ - CONTAINERS=1
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ read_only: true
+ tmpfs:
+ - /run
+
+ plugnpin:
+ image: ghcr.io/deepspace2/plugnpin:latest
+ container_name: plugnpin
+ depends_on:
+ - socket-proxy
+ environment:
+ - DOCKER_HOST=tcp://socket-proxy:2375
+ - NGINX_PROXY_MANAGER_HOST=...
+ - NGINX_PROXY_MANAGER_USERNAME=...
+ - NGINX_PROXY_MANAGER_PASSWORD=...
+ - PIHOLE_HOST=...
+ - PIHOLE_PASSWORD=...
+ restart: unless-stopped
+ ```
+
+=== "Not Recommended: Mounting the Docker Socket"
+
+ ```yaml
+ services:
+ plugnpin:
+ image: ghcr.io/deepspace2/plugnpin:latest
+ container_name: plugnpin
+ environment:
+ - NGINX_PROXY_MANAGER_HOST=...
+ - NGINX_PROXY_MANAGER_USERNAME=...
+ - NGINX_PROXY_MANAGER_PASSWORD=...
+ - PIHOLE_HOST=...
+ - PIHOLE_PASSWORD...
+ volumes:
+ - /var/run/docker.sock:/var/run/docker.sock:ro
+ restart: unless-stopped
+ ```
diff --git a/mkdocs.yml b/mkdocs.yml
index bc310e1..0c69294 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -3,10 +3,11 @@
site_name: PlugNPiN
site_url: https://deepspace2.github.io/plugnpin
repo_url: https://github.com/deepspace2/plugnpin
-extra_css:
- - css/anchor-jump-highlight.css
-extra_javascript:
- - js/anchor-jump-highlight.js
+nav:
+ - About: index.md
+ - Configuration: configuration.md
+ - Usage: usage.md
+ - Contributing: contributing.md
theme:
name: material
custom_dir: docs/theme/overrides
@@ -15,6 +16,9 @@ theme:
features:
- content.code.copy
- content.tooltips
+ - navigation.footer
+ - navigation.instant
+ - navigation.instant.progress
- navigation.top
- search.highlight
- search.share
@@ -45,14 +49,12 @@ extra:
alias: true
provider: mike
markdown_extensions:
- - admonition
- abbr
- attr_list
- - pymdownx.details
- - pymdownx.inlinehilite
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- - pymdownx.snippets
+ - pymdownx.tabbed:
+ alternate_style: true
- pymdownx.superfences
From f13742083b6416b6c72290bc1a63792d239ce667 Mon Sep 17 00:00:00 2001
From: Adi <6841988+DeepSpace2@users.noreply.github.com>
Date: Thu, 29 Jan 2026 21:04:46 +0200
Subject: [PATCH 3/4] feat: multiple docker hosts support
---
.example.env | 1 +
README.md | 14 ++--
docs/index.md | 1 -
e2e_tests/e2e_test.go | 4 +-
main.go | 61 ++++-----------
pkg/cli/cli.go | 8 +-
pkg/clients/adguardhome/adguardhome.go | 8 +-
pkg/clients/clients.go | 56 ++++++++++++++
pkg/clients/{ => common}/common.go | 2 +-
pkg/clients/docker/docker.go | 18 +++--
pkg/clients/docker/events.go | 17 ++---
pkg/clients/docker/types.go | 8 ++
pkg/clients/npm/npm.go | 10 +--
pkg/clients/pihole/pihole.go | 16 ++--
pkg/config/config.go | 1 +
pkg/processor/processor.go | 102 +++++++++++++------------
16 files changed, 182 insertions(+), 145 deletions(-)
create mode 100644 pkg/clients/clients.go
rename pkg/clients/{ => common}/common.go (99%)
diff --git a/.example.env b/.example.env
index b2ddc86..dc65dd1 100644
--- a/.example.env
+++ b/.example.env
@@ -1,4 +1,5 @@
DOCKER_HOST= # optional but highly recommended, use a docker socket proxy instead of mounting the docker socket
+DOCKER_HOSTS= # optional, comma-separated list of docker hosts
NGINX_PROXY_MANAGER_HOST= # required
NGINX_PROXY_MANAGER_USERNAME= # required
NGINX_PROXY_MANAGER_PASSWORD= # required
diff --git a/README.md b/README.md
index bdf288d..430fa11 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,11 @@ as local DNS/CNAME records in **Pi-Hole** (or DNS Rewrites in **AdGuard Home**)
## Key Features
-- Automatic Docker container detection.
-- Local DNS/CNAME record creation/deletion in Pi-hole.
-- DNS Rewrites creation/deletion in AdGuard Home.
-- Nginx Proxy Manager host creation.
-- Support for Docker socket proxy.
+- Automatic Docker container detection (monitoring multiple hosts is supported)
+- Local DNS/CNAME record creation/deletion in Pi-hole
+- DNS Rewrites creation/deletion in AdGuard Home
+- Nginx Proxy Manager host creation
+- Support for Docker socket proxy
**Pi-Hole's and AdGuard Home's functionality can be toggled individually. By default Pi-Hole is enabled and AdGuard Home is disabled.**
@@ -78,7 +78,7 @@ services:
restart: unless-stopped
```
-#### Not Recommended: Mounting the Docker Socket
+#### Not Recommended: Directly mounting the Docker Socket
```yaml
services:
@@ -92,7 +92,7 @@ services:
- PIHOLE_HOST=...
- PIHOLE_PASSWORD=...
volumes:
- - /var/run/docker.sock:/var/run/docker.sock
+ - /var/run/docker.sock:/var/run/docker.sock:ro
restart: unless-stopped
```
diff --git a/docs/index.md b/docs/index.md
index 1eba22a..2139d08 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -14,7 +14,6 @@ as local DNS/CNAME records in **Pi-Hole** (or DNS Rewrites in **AdGuard Home**)
**Pi-Hole's and AdGuard Home's functionality can be toggled individually. By default Pi-Hole is enabled and AdGuard Home is disabled.**
See [Optional Environment Variables](./configuration.md#optional).
-
## How It Works
PlugNPiN discovers services by scanning for Docker containers that have the following labels:
diff --git a/e2e_tests/e2e_test.go b/e2e_tests/e2e_test.go
index f10006b..97fbbe3 100644
--- a/e2e_tests/e2e_test.go
+++ b/e2e_tests/e2e_test.go
@@ -128,7 +128,7 @@ func startRequiredContainers(t *testing.T, ctx context.Context, dockerCli *docke
}
func setClients(t *testing.T, containers []Container) (*docker.Client, *pihole.Client, *npm.Client, *adguardhome.Client) {
- dockerClient, err := docker.NewClient()
+ dockerClient, err := docker.NewClient("")
if err != nil {
t.Fatalf("Failed to create docker client: %v", err)
}
@@ -308,7 +308,7 @@ func TestE2E(t *testing.T) {
time.Sleep(2 * time.Second)
proc := processor.New(
- dockerClient,
+ map[string]*docker.Client{dockerClient.Host: dockerClient},
adguardHomeClient,
piholeClient,
npmClient,
diff --git a/main.go b/main.go
index 85dd6f0..df2c26b 100644
--- a/main.go
+++ b/main.go
@@ -9,10 +9,7 @@ import (
"syscall"
"github.com/deepspace2/plugnpin/pkg/cli"
- "github.com/deepspace2/plugnpin/pkg/clients/adguardhome"
- "github.com/deepspace2/plugnpin/pkg/clients/docker"
- "github.com/deepspace2/plugnpin/pkg/clients/npm"
- "github.com/deepspace2/plugnpin/pkg/clients/pihole"
+ "github.com/deepspace2/plugnpin/pkg/clients"
"github.com/deepspace2/plugnpin/pkg/config"
"github.com/deepspace2/plugnpin/pkg/logging"
"github.com/deepspace2/plugnpin/pkg/processor"
@@ -20,18 +17,6 @@ import (
var log = logging.GetLogger()
-func shutdown(cancelCtx context.CancelFunc, wg *sync.WaitGroup) {
- shutdownChan := make(chan os.Signal, 1)
- signal.Notify(shutdownChan, syscall.SIGINT, syscall.SIGTERM)
-
- <-shutdownChan
-
- log.Info("Shutdown signal received, exiting gracefully.")
- cancelCtx()
- wg.Wait()
- log.Info("Shutdown complete.")
-}
-
func main() {
cliFlags := cli.ParseFlags()
@@ -51,40 +36,12 @@ func main() {
log.Info(fmt.Sprintf("Will run every %v", conf.RunInterval))
}
- var adguardHomeClient *adguardhome.Client
- var piholeClient *pihole.Client
- var npmClient *npm.Client
-
- if !cliFlags.DryRun {
- if !conf.PiholeDisabled {
- piholeClient = pihole.NewClient(conf.PiholeHost)
- err = piholeClient.Login(conf.PiholePassword)
- if err != nil {
- log.Error("Failed to login to Pi-Hole", "error", err)
- os.Exit(1)
- }
- }
-
- if !conf.AdguardHomeDisabled {
- adguardHomeClient = adguardhome.NewClient(conf.AdguardHomeHost, conf.AdguardHomeUsername, conf.AdguardHomePassword)
- }
-
- npmClient = npm.NewClient(conf.NpmHost, conf.NpmUsername, conf.NpmPassword)
- err = npmClient.Login()
- if err != nil {
- log.Error("Failed to login to Nginx Proxy Manager", "error", err)
- os.Exit(1)
- }
- }
-
- dockerClient, err := docker.NewClient()
+ dockerClients, adguardHomeClient, piholeClient, npmClient, err := clients.GetClients(cliFlags, conf)
if err != nil {
- log.Error("Failed to create docker client", "error", err)
os.Exit(1)
}
- defer dockerClient.Close()
- proc := processor.New(dockerClient, adguardHomeClient, piholeClient, npmClient, cliFlags.DryRun)
+ proc := processor.New(dockerClients, adguardHomeClient, piholeClient, npmClient, cliFlags.DryRun)
if conf.RunInterval == 0 {
log.Info("RUN_INTERVAL is 0, will run once")
@@ -109,3 +66,15 @@ func main() {
shutdown(cancel, &wg)
}
+
+func shutdown(cancelCtx context.CancelFunc, wg *sync.WaitGroup) {
+ shutdownChan := make(chan os.Signal, 1)
+ signal.Notify(shutdownChan, syscall.SIGINT, syscall.SIGTERM)
+
+ <-shutdownChan
+
+ log.Info("Shutdown signal received, exiting gracefully.")
+ cancelCtx()
+ wg.Wait()
+ log.Info("Shutdown complete.")
+}
diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go
index 57e6970..b39eb41 100644
--- a/pkg/cli/cli.go
+++ b/pkg/cli/cli.go
@@ -4,14 +4,14 @@ import (
flag "github.com/spf13/pflag"
)
-type f struct {
+type Flags struct {
DryRun bool
}
-var flags f = f{}
+var flags = Flags{}
-func ParseFlags() f {
- flag.BoolVarP(&flags.DryRun, "dry-run", "d", false, "Simulates the process of adding DNS records and proxy hosts without making any actual changes to Pi-Hole or Nginx Proxy Manager.")
+func ParseFlags() Flags {
+ flag.BoolVarP(&flags.DryRun, "dry-run", "d", false, "Simulates the process of adding DNS records and proxy hosts without applying changes to Pi-Hole, AdGuard Home or Nginx Proxy Manager.")
flag.Parse()
return flags
}
diff --git a/pkg/clients/adguardhome/adguardhome.go b/pkg/clients/adguardhome/adguardhome.go
index fdde405..4c67214 100644
--- a/pkg/clients/adguardhome/adguardhome.go
+++ b/pkg/clients/adguardhome/adguardhome.go
@@ -7,7 +7,7 @@ import (
"fmt"
"net/http"
- "github.com/deepspace2/plugnpin/pkg/clients"
+ "github.com/deepspace2/plugnpin/pkg/clients/common"
"github.com/deepspace2/plugnpin/pkg/logging"
)
@@ -34,7 +34,7 @@ func NewClient(baseURL, username, password string) *Client {
}
func (ad *Client) GetDnsRewrites() (DnsRewrites, error) {
- dnsRewritesResponseString, _, err := clients.Get(&ad.Client, ad.baseURL+"/rewrite/list", headers)
+ dnsRewritesResponseString, _, err := common.Get(&ad.Client, ad.baseURL+"/rewrite/list", headers)
if err != nil {
return nil, err
}
@@ -65,7 +65,7 @@ func (ad *Client) AddDnsRewrite(domain, ip string) error {
return err
}
payloadString := string(payload)
- _, statusCode, err := clients.Post(&ad.Client, ad.baseURL+"/rewrite/add", headers, &payloadString)
+ _, statusCode, err := common.Post(&ad.Client, ad.baseURL+"/rewrite/add", headers, &payloadString)
if err != nil {
return err
}
@@ -83,7 +83,7 @@ func (ad *Client) DeleteDnsRewrite(domain, ip string) error {
return err
}
payloadString := string(payload)
- _, statusCode, err := clients.Post(&ad.Client, ad.baseURL+"/rewrite/delete", headers, &payloadString)
+ _, statusCode, err := common.Post(&ad.Client, ad.baseURL+"/rewrite/delete", headers, &payloadString)
if err != nil {
return err
}
diff --git a/pkg/clients/clients.go b/pkg/clients/clients.go
new file mode 100644
index 0000000..79668de
--- /dev/null
+++ b/pkg/clients/clients.go
@@ -0,0 +1,56 @@
+package clients
+
+import (
+ "github.com/deepspace2/plugnpin/pkg/cli"
+ "github.com/deepspace2/plugnpin/pkg/clients/adguardhome"
+ "github.com/deepspace2/plugnpin/pkg/clients/docker"
+ "github.com/deepspace2/plugnpin/pkg/clients/npm"
+ "github.com/deepspace2/plugnpin/pkg/clients/pihole"
+ "github.com/deepspace2/plugnpin/pkg/config"
+ "github.com/deepspace2/plugnpin/pkg/logging"
+)
+
+var log = logging.GetLogger()
+
+func GetClients(cliFlags cli.Flags, conf *config.Config) (map[string]*docker.Client, *adguardhome.Client, *pihole.Client, *npm.Client, error) {
+ var adguardHomeClient *adguardhome.Client
+ var piholeClient *pihole.Client
+ var npmClient *npm.Client
+
+ if !cliFlags.DryRun {
+ if !conf.PiholeDisabled {
+ piholeClient = pihole.NewClient(conf.PiholeHost)
+ err := piholeClient.Login(conf.PiholePassword)
+ if err != nil {
+ log.Error("Failed to login to Pi-Hole", "error", err)
+ return nil, nil, nil, nil, err
+ }
+ }
+
+ if !conf.AdguardHomeDisabled {
+ adguardHomeClient = adguardhome.NewClient(conf.AdguardHomeHost, conf.AdguardHomeUsername, conf.AdguardHomePassword)
+ }
+
+ npmClient = npm.NewClient(conf.NpmHost, conf.NpmUsername, conf.NpmPassword)
+ err := npmClient.Login()
+ if err != nil {
+ log.Error("Failed to login to Nginx Proxy Manager", "error", err)
+ return nil, nil, nil, nil, err
+ }
+ }
+
+ dockerClients := make(map[string]*docker.Client)
+ if len(conf.DockerHosts) == 0 {
+ conf.DockerHosts = append(conf.DockerHosts, conf.DockerHost)
+ }
+ for _, host := range conf.DockerHosts {
+ dockerClient, err := docker.NewClient(host)
+ if err != nil {
+ log.Error("Failed to create docker client", "host", host, "error", err)
+ continue
+ }
+ dockerClients[dockerClient.Host] = dockerClient
+ }
+
+ return dockerClients, adguardHomeClient, piholeClient, npmClient, nil
+}
diff --git a/pkg/clients/common.go b/pkg/clients/common/common.go
similarity index 99%
rename from pkg/clients/common.go
rename to pkg/clients/common/common.go
index afa7702..7a73e74 100644
--- a/pkg/clients/common.go
+++ b/pkg/clients/common/common.go
@@ -1,4 +1,4 @@
-package clients
+package common
import (
"io"
diff --git a/pkg/clients/docker/docker.go b/pkg/clients/docker/docker.go
index 966da5d..85337fc 100644
--- a/pkg/clients/docker/docker.go
+++ b/pkg/clients/docker/docker.go
@@ -46,13 +46,15 @@ const (
var labels []string = []string{IpLabel, UrlLabel}
-type Client struct {
- *dockerSdk.Client
-}
-
-func NewClient() (*Client, error) {
- client, err := dockerSdk.New(context.Background())
- return &Client{client}, err
+func NewClient(host string) (*Client, error) {
+ client, err := dockerSdk.New(context.Background(), dockerSdk.WithDockerHost(host))
+ var displayHost string
+ if host == "" {
+ displayHost = "local"
+ } else {
+ displayHost = host
+ }
+ return &Client{Client: client, Host: host, DisplayHost: displayHost}, err
}
func (d *Client) GetRelevantContainers() ([]container.Summary, error) {
@@ -61,7 +63,7 @@ func (d *Client) GetRelevantContainers() ([]container.Summary, error) {
f.Add("label", label)
}
- log.Info(fmt.Sprintf("Getting containers with labels: %v", strings.Join(labels, ", ")))
+ log.Info(fmt.Sprintf("Getting containers with labels: %v", strings.Join(labels, ", ")), "host", d.DisplayHost)
return d.ContainerList(
context.Background(),
diff --git a/pkg/clients/docker/events.go b/pkg/clients/docker/events.go
index fdd3585..ef17291 100644
--- a/pkg/clients/docker/events.go
+++ b/pkg/clients/docker/events.go
@@ -5,22 +5,17 @@ import (
"github.com/docker/docker/api/types/events"
"github.com/docker/docker/api/types/filters"
- dockerClient "github.com/docker/docker/client"
)
-func Listen(ctx context.Context, handler func(events.Message)) error {
- c, err := dockerClient.NewClientWithOpts(dockerClient.WithHostFromEnv())
- if err != nil {
- return err
- }
- defer c.Close()
-
+func Listen(ctx context.Context, dockerClient *Client, handler func(events.Message)) error {
f := filters.NewArgs()
f.Add("type", "container")
f.Add("event", ContainerEvent.Start.String())
f.Add("event", ContainerEvent.Die.String())
- log.Info("Listening for Docker events...")
+ log.Info("Listening for Docker events...", "host", dockerClient.DisplayHost)
+
+ c, _ := dockerClient.Client.Client()
messages, errs := c.Events(ctx, events.ListOptions{
Filters: f,
@@ -29,13 +24,13 @@ func Listen(ctx context.Context, handler func(events.Message)) error {
for {
select {
case <-ctx.Done():
- log.Info("Stopping stream of Docker events")
+ log.Info("Stopping stream of Docker events", "host", dockerClient.DisplayHost)
return ctx.Err()
case event := <-messages:
handler(event)
case err := <-errs:
if err != nil {
- log.Error("Failed to receive event", "error", err)
+ log.Error("Failed to receive event", "host", dockerClient.DisplayHost, "error", err)
}
return err
}
diff --git a/pkg/clients/docker/types.go b/pkg/clients/docker/types.go
index 6493fe8..b3902c6 100644
--- a/pkg/clients/docker/types.go
+++ b/pkg/clients/docker/types.go
@@ -1,10 +1,18 @@
package docker
+import dockerSdk "github.com/docker/go-sdk/client"
+
const (
start = "start"
die = "die"
)
+type Client struct {
+ *dockerSdk.Client
+ DisplayHost string
+ Host string
+}
+
type EventType string
type ContainerEventEnum struct {
diff --git a/pkg/clients/npm/npm.go b/pkg/clients/npm/npm.go
index f0865de..d598026 100644
--- a/pkg/clients/npm/npm.go
+++ b/pkg/clients/npm/npm.go
@@ -10,7 +10,7 @@ import (
"sync"
"time"
- "github.com/deepspace2/plugnpin/pkg/clients"
+ "github.com/deepspace2/plugnpin/pkg/clients/common"
"github.com/deepspace2/plugnpin/pkg/logging"
)
@@ -58,7 +58,7 @@ func (n *Client) Login() error {
return err
}
payloadString := string(payloadBytes)
- loginResponseString, statusCode, err := clients.Post(&n.Client, n.baseURL+"/tokens", n.headers, &payloadString)
+ loginResponseString, statusCode, err := common.Post(&n.Client, n.baseURL+"/tokens", n.headers, &payloadString)
if err != nil {
return err
}
@@ -124,11 +124,11 @@ func (n *Client) makeRequest(method, url string, payload *string) (string, int,
doRequest := func() (string, int, error) {
switch method {
case http.MethodGet:
- return clients.Get(&n.Client, url, n.headers)
+ return common.Get(&n.Client, url, n.headers)
case http.MethodPost:
- return clients.Post(&n.Client, url, n.headers, payload)
+ return common.Post(&n.Client, url, n.headers, payload)
case http.MethodDelete:
- return clients.Delete(&n.Client, url, n.headers)
+ return common.Delete(&n.Client, url, n.headers)
default:
return "", 0, fmt.Errorf("unsupported http method: %s", method)
}
diff --git a/pkg/clients/pihole/pihole.go b/pkg/clients/pihole/pihole.go
index 177db03..012b835 100644
--- a/pkg/clients/pihole/pihole.go
+++ b/pkg/clients/pihole/pihole.go
@@ -8,7 +8,7 @@ import (
"os"
"strings"
- "github.com/deepspace2/plugnpin/pkg/clients"
+ "github.com/deepspace2/plugnpin/pkg/clients/common"
"github.com/deepspace2/plugnpin/pkg/logging"
)
@@ -36,7 +36,7 @@ func NewClient(baseURL string) *Client {
func (p *Client) Login(password string) error {
loginPayload := fmt.Sprintf(`{"password": "%v"}`, password)
- loginResponseString, statusCode, err := clients.Post(&p.Client, p.baseURL+"/auth", headers, &loginPayload)
+ loginResponseString, statusCode, err := common.Post(&p.Client, p.baseURL+"/auth", headers, &loginPayload)
if err != nil {
return err
}
@@ -73,7 +73,7 @@ func (p *Client) GetDnsRecords() (DnsRecords, error) {
os.Exit(1)
}
headers["X-FTL-SID"] = p.sid
- configResponseString, _, err := clients.Get(&p.Client, p.baseURL+"/config", headers)
+ configResponseString, _, err := common.Get(&p.Client, p.baseURL+"/config", headers)
if err != nil {
return nil, err
}
@@ -123,7 +123,7 @@ func (p *Client) AddDnsRecord(domain, ip string) error {
os.Exit(1)
}
headers["X-FTL-SID"] = p.sid
- resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
+ resp, statusCode, err := common.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
if err != nil {
return err
}
@@ -172,7 +172,7 @@ func (p *Client) DeleteDnsRecord(domain string) error {
os.Exit(1)
}
headers["X-FTL-SID"] = p.sid
- resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
+ resp, statusCode, err := common.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
if err != nil {
return err
}
@@ -210,7 +210,7 @@ func (p *Client) getCNameRecords() (CNameRecords, error) {
os.Exit(1)
}
headers["X-FTL-SID"] = p.sid
- configResponseString, _, err := clients.Get(&p.Client, p.baseURL+"/config", headers)
+ configResponseString, _, err := common.Get(&p.Client, p.baseURL+"/config", headers)
if err != nil {
return nil, err
}
@@ -260,7 +260,7 @@ func (p *Client) AddCNameRecord(domain, target string) error {
os.Exit(1)
}
headers["X-FTL-SID"] = p.sid
- resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
+ resp, statusCode, err := common.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
if err != nil {
return err
}
@@ -309,7 +309,7 @@ func (p *Client) DeleteCNameRecord(domain, target string) error {
os.Exit(1)
}
headers["X-FTL-SID"] = p.sid
- resp, statusCode, err := clients.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
+ resp, statusCode, err := common.Patch(&p.Client, p.baseURL+"/config", headers, string(payloadString))
if err != nil {
return err
}
diff --git a/pkg/config/config.go b/pkg/config/config.go
index 6e84c92..668a4a1 100644
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -30,6 +30,7 @@ type Config struct {
PiholePassword string `env:"PIHOLE_PASSWORD"`
DockerHost string `env:"DOCKER_HOST"`
+ DockerHosts []string `env:"DOCKER_HOSTS"`
RunInterval time.Duration `env:"RUN_INTERVAL" envDefault:"1h"`
}
diff --git a/pkg/processor/processor.go b/pkg/processor/processor.go
index b0c32b0..58a0706 100644
--- a/pkg/processor/processor.go
+++ b/pkg/processor/processor.go
@@ -19,16 +19,16 @@ import (
var log = logging.GetLogger()
type Processor struct {
- dockerClient *docker.Client
+ dockerClients map[string]*docker.Client
adguardHomeClient *adguardhome.Client
piholeClient *pihole.Client
npmClient *npm.Client
dryRun bool
}
-func New(dockerClient *docker.Client, adguardHomeClient *adguardhome.Client, piholeClient *pihole.Client, npmClient *npm.Client, dryRun bool) *Processor {
+func New(dockerClients map[string]*docker.Client, adguardHomeClient *adguardhome.Client, piholeClient *pihole.Client, npmClient *npm.Client, dryRun bool) *Processor {
return &Processor{
- dockerClient: dockerClient,
+ dockerClients: dockerClients,
adguardHomeClient: adguardHomeClient,
piholeClient: piholeClient,
npmClient: npmClient,
@@ -60,31 +60,37 @@ func (p *Processor) RunScheduled(ctx context.Context, interval time.Duration) {
}
func (p *Processor) ListenForEvents(ctx context.Context) {
- err := docker.Listen(ctx, func(event events.Message) {
- p.handleDockerEvent(event)
- })
-
- if err != nil && err != context.Canceled {
- log.Error("Docker event listener stopped", "error", err)
+ for _, client := range p.dockerClients {
+ go func(c *docker.Client) {
+ log.Info("Starting event listener", "host", c.DisplayHost)
+ err := docker.Listen(ctx, c, func(event events.Message) {
+ p.handleDockerEvent(event, c.DisplayHost)
+ })
+ if err != nil && err != context.Canceled {
+ log.Error("Docker event listener stopped", "host", c.DisplayHost, "error", err)
+ }
+ }(client)
}
}
func (p *Processor) RunOnce() {
- containers, err := p.dockerClient.GetRelevantContainers()
- if err != nil {
- log.Error("Failed to get containers", "error", err)
- return
- }
+ for _, dockerClient := range p.dockerClients {
+ containers, err := dockerClient.GetRelevantContainers()
+ if err != nil {
+ log.Error("Failed to get containers", "host", dockerClient.DisplayHost, "error", err)
+ continue
+ }
- log.Info(fmt.Sprintf("Found %v containers", len(containers)))
+ log.Info(fmt.Sprintf("Found %v containers", len(containers)), "host", dockerClient.DisplayHost)
- for _, container := range containers {
- p.preprocessContainer(container)
+ for _, container := range containers {
+ p.preprocessContainer(container, dockerClient.DisplayHost)
+ }
}
log.Info("Done")
}
-func (p *Processor) preprocessContainer(container container.Summary) {
+func (p *Processor) preprocessContainer(container container.Summary, host string) {
parsedContainerName := docker.GetParsedContainerName(container)
ip, url, port, opts, err := docker.GetValuesFromLabels(container.Labels)
@@ -97,13 +103,13 @@ func (p *Processor) preprocessContainer(container container.Summary) {
}
return
}
- p.processContainer(docker.ContainerEvent.Start, parsedContainerName, ip, url, port, opts)
+ p.processContainer(docker.ContainerEvent.Start, host, parsedContainerName, ip, url, port, opts)
}
-func (p *Processor) handleDockerEvent(event events.Message) {
+func (p *Processor) handleDockerEvent(event events.Message, host string) {
containerName, ok := event.Actor.Attributes["name"]
if !ok {
- log.Info(fmt.Sprintf("Skipping event for container with no name: %v", event.Actor.ID))
+ log.Info(fmt.Sprintf("Skipping event for container with no name: %v", event.Actor.ID), "host", host)
return
}
@@ -114,15 +120,15 @@ func (p *Processor) handleDockerEvent(event events.Message) {
// This is not an error, it just means the container is not relevant for us
return
case *errors.MalformedIPLabelError, *errors.InvalidSchemeError:
- log.Error("Failed to handle event for container", "container", containerName, "error", err)
+ log.Error("Failed to handle event for container", "host", host, "container", containerName, "error", err)
}
return
}
containerEvent, _ := docker.ContainerEvent.ParseString(string(event.Action))
- p.processContainer(containerEvent, containerName, ip, url, port, opts)
+ p.processContainer(containerEvent, host, containerName, ip, url, port, opts)
}
-func (p *Processor) handleAdguardHome(containerEvent docker.EventType, containerName, url, ip string, adguardHomeOptions adguardhome.AdguardHomeOptions) {
+func (p *Processor) handleAdguardHome(host string, containerEvent docker.EventType, containerName, url, ip string, adguardHomeOptions adguardhome.AdguardHomeOptions) {
if p.adguardHomeClient != nil {
if adguardHomeOptions.TargetDomain != "" {
// quick "workaround" for the fact that adguard unifies "local DNS records" and "CNAME records"
@@ -131,57 +137,57 @@ func (p *Processor) handleAdguardHome(containerEvent docker.EventType, container
switch containerEvent {
case docker.ContainerEvent.Start:
- log.Info("Adding a DNS rewrite to AdGuard Home", "container", containerName, "domain", url, "answer", ip)
+ log.Info("Adding a DNS rewrite to AdGuard Home", "host", host, "container", containerName, "domain", url, "answer", ip)
err := p.adguardHomeClient.AddDnsRewrite(url, ip)
if err != nil {
- log.Error("Failed to add a DNS rewrite to AdGuard Home", "container", containerName, "domain", url, "answer", ip, "error", err)
+ log.Error("Failed to add a DNS rewrite to AdGuard Home", "host", host, "container", containerName, "domain", url, "answer", ip, "error", err)
}
case docker.ContainerEvent.Die:
- log.Info("Deleting DNS rewrite from AdGuard Home", "container", containerName, "domain", url)
+ log.Info("Deleting DNS rewrite from AdGuard Home", "host", host, "container", containerName, "domain", url)
err := p.adguardHomeClient.DeleteDnsRewrite(url, ip)
if err != nil {
- log.Error("Failed to delete DNS rewrite from AdGuard Home", "container", containerName, "domain", url, "error", err)
+ log.Error("Failed to delete DNS rewrite from AdGuard Home", "host", host, "container", containerName, "domain", url, "error", err)
}
}
}
}
-func (p *Processor) handlePiHole(containerEvent docker.EventType, containerName, url, ip string, piholeOptions pihole.PiHoleOptions) {
+func (p *Processor) handlePiHole(host string, containerEvent docker.EventType, containerName, url, ip string, piholeOptions pihole.PiHoleOptions) {
if p.piholeClient != nil {
switch containerEvent {
case docker.ContainerEvent.Start:
if piholeOptions.TargetDomain == "" {
- log.Info("Adding a local DNS record to Pi-Hole", "container", containerName, "url", url, "ip", ip)
+ log.Info("Adding a local DNS record to Pi-Hole", "host", host, "container", containerName, "url", url, "ip", ip)
err := p.piholeClient.AddDnsRecord(url, ip)
if err != nil {
- log.Error("Failed to add a local DNS record to Pi-Hole", "container", containerName, "url", url, "ip", ip, "error", err)
+ log.Error("Failed to add a local DNS record to Pi-Hole", "host", host, "container", containerName, "url", url, "ip", ip, "error", err)
}
} else {
- log.Info("Adding a local CNAME record to Pi-Hole", "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain)
+ log.Info("Adding a local CNAME record to Pi-Hole", "host", host, "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain)
err := p.piholeClient.AddCNameRecord(url, piholeOptions.TargetDomain)
if err != nil {
- log.Error("Failed to add a local CNAME record to Pi-Hole", "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain, "error", err)
+ log.Error("Failed to add a local CNAME record to Pi-Hole", "host", host, "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain, "error", err)
}
}
case docker.ContainerEvent.Die:
if piholeOptions.TargetDomain == "" {
- log.Info("Deleting local DNS record from Pi-Hole", "container", containerName, "url", url)
+ log.Info("Deleting local DNS record from Pi-Hole", "host", host, "container", containerName, "url", url)
err := p.piholeClient.DeleteDnsRecord(url)
if err != nil {
- log.Error("Failed to delete local DNS record from Pi-Hole", "container", containerName, "url", url, "error", err)
+ log.Error("Failed to delete local DNS record from Pi-Hole", "host", host, "container", containerName, "url", url, "error", err)
}
} else {
- log.Info("Deleting local CNAME record from Pi-Hole", "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain)
+ log.Info("Deleting local CNAME record from Pi-Hole", "host", host, "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain)
err := p.piholeClient.DeleteCNameRecord(url, piholeOptions.TargetDomain)
if err != nil {
- log.Error("Failed to delete local CNAME record from Pi-Hole", "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain, "error", err)
+ log.Error("Failed to delete local CNAME record from Pi-Hole", "host", host, "container", containerName, "url", url, "targetDomain", piholeOptions.TargetDomain, "error", err)
}
}
}
}
}
-func (p *Processor) handleNpm(containerEvent docker.EventType, containerName, url, ip string, port int, npmProxyHostOptions npm.NpmProxyHostOptions) {
+func (p *Processor) handleNpm(host string, containerEvent docker.EventType, containerName, url, ip string, port int, npmProxyHostOptions npm.NpmProxyHostOptions) {
switch containerEvent {
case docker.ContainerEvent.Start:
npmProxyHost := npm.ProxyHost{
@@ -209,42 +215,42 @@ func (p *Processor) handleNpm(containerEvent docker.EventType, containerName, ur
}
}
- log.Info("Adding entry to Nginx Proxy Manager", "container", containerName)
+ log.Info("Adding entry to Nginx Proxy Manager", "host", host, "container", containerName)
err := p.npmClient.AddProxyHost(npmProxyHost)
if err != nil {
- log.Error("Failed to add entry to Nginx Proxy Manager", "container", containerName, "error", err)
+ log.Error("Failed to add entry to Nginx Proxy Manager", "host", host, "container", containerName, "error", err)
}
case docker.ContainerEvent.Die:
- log.Info("Deleting entry from Nginx Proxy Manager", "container", containerName)
+ log.Info("Deleting entry from Nginx Proxy Manager", "host", host, "container", containerName)
err := p.npmClient.DeleteProxyHost(url)
if err != nil {
- log.Error("Failed to delete entry from Nginx Proxy Manager", "container", containerName, "error", err)
+ log.Error("Failed to delete entry from Nginx Proxy Manager", "host", host, "container", containerName, "error", err)
}
}
}
-func (p *Processor) processContainer(containerEvent docker.EventType, containerName, ip, url string, port int, opts *docker.ClientOptions) {
+func (p *Processor) processContainer(containerEvent docker.EventType, host, containerName, ip, url string, port int, opts *docker.ClientOptions) {
msg := "Handling container"
if p.dryRun {
msg += ". In dry run mode, not doing anything."
- log.Info(msg, "container", containerName, "ip", ip, "port", port, "url", url)
+ log.Info(msg, "host", host, "container", containerName, "ip", ip, "port", port, "url", url)
return
}
- log.Info(msg, "container", containerName, "ip", ip, "port", port, "url", url)
+ log.Info(msg, "host", host, "container", containerName, "ip", ip, "port", port, "url", url)
if p.npmClient != nil {
npmHost := p.npmClient.GetIP()
if opts.AdguardHome != nil {
- p.handleAdguardHome(containerEvent, containerName, url, npmHost, *opts.AdguardHome)
+ p.handleAdguardHome(host, containerEvent, containerName, url, npmHost, *opts.AdguardHome)
}
if opts.Pihole != nil {
- p.handlePiHole(containerEvent, containerName, url, npmHost, *opts.Pihole)
+ p.handlePiHole(host, containerEvent, containerName, url, npmHost, *opts.Pihole)
}
if opts.NPM != nil {
- p.handleNpm(containerEvent, containerName, url, ip, port, *opts.NPM)
+ p.handleNpm(host, containerEvent, containerName, url, ip, port, *opts.NPM)
}
}
}
From 1e096fba31145dd44a186f90da5bb64e45c25e25 Mon Sep 17 00:00:00 2001
From: Adi <6841988+DeepSpace2@users.noreply.github.com>
Date: Tue, 3 Feb 2026 19:34:19 +0200
Subject: [PATCH 4/4] updated docs
---
docs/configuration.md | 1 +
docs/index.md | 1 +
2 files changed, 2 insertions(+)
diff --git a/docs/configuration.md b/docs/configuration.md
index 600b6f1..8f73f2d 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -22,6 +22,7 @@
| `ADGUARD_HOME_DISABLED` | Set to `false` to enable AdGuard Home functionality | `true` |
| `DEBUG` | Set to `true` to enable DEBUG level logs | `false` |
| `DOCKER_HOST` | The URL of a docker socket proxy. If set, you don't need to mount the docker socket as a volume. Querying containers must be allowed (typically done by setting the `CONTAINERS` environment variable to `1`). | *None* |
+| `DOCKER_HOSTS` | Comma-separated list of multiple docker hosts to monitor, with an empty string meaning the default local host.