Skip to content

Commit f58442d

Browse files
authored
Merge pull request #623 from nginx-proxy/filter-containers
feat: filter containers seen by docker-gen
2 parents c1ca4a4 + d747fad commit f58442d

File tree

8 files changed

+93
-137
lines changed

8 files changed

+93
-137
lines changed

README.md

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -87,67 +87,75 @@ Usage: docker-gen [options] template [dest]
8787
Generate files from docker container meta-data
8888
8989
Options:
90-
-config value
91-
config files with template directives. Config files will be merged if this option is specified multiple times. (default [])
90+
-config path
91+
config files with template directives.
92+
Config files will be merged if this option is specified multiple times. (default [])
93+
-container-filter key=value
94+
container filter for inclusion by docker-gen.
95+
You can pass this option multiple times to combine filters with AND.
96+
https://docs.docker.com/engine/reference/commandline/ps/#filter
9297
-endpoint string
9398
docker api endpoint (tcp|unix://..). Default unix:///var/run/docker.sock
94-
-event-filter value
99+
-event-filter key=value
95100
additional filter for event watched by docker-gen (e.g -event-filter event=connect -event-filter event=disconnect).
96101
You can pass this option multiple times to combine filters.
97102
By default docker-gen listen for container events start, stop, die and health_status.
98103
https://docs.docker.com/engine/reference/commandline/events/#filtering-events
104+
-include-stopped
105+
include stopped containers.
106+
Bypassed when providing a container status filter (-container-filter status=foo).
99107
-interval int
100108
notify command interval (secs)
101109
-keep-blank-lines
102110
keep blank lines in the output file
103111
-notify restart xyz
104112
run command after template is regenerated (e.g restart xyz)
105-
-notify-output
106-
log the output(stdout/stderr) of notify command
107-
-notify-sighup container-ID
108-
send HUP signal to container.
109-
Equivalent to 'docker kill -s HUP container-ID', or `-notify-container container-ID -notify-signal 1`.
110-
You can pass this option multiple times to send HUP to multiple containers.
111113
-notify-container container-ID
112114
send -notify-signal signal (defaults to 1 / HUP) to container.
113115
You can pass this option multiple times to notify multiple containers.
114116
-notify-filter key=value
115117
container filter for notification (e.g -notify-filter name=foo).
116118
You can pass this option multiple times to combine filters with AND.
117119
https://docs.docker.com/engine/reference/commandline/ps/#filter
120+
-notify-output
121+
log the output(stdout/stderr) of notify command
122+
-notify-sighup container-ID
123+
send HUP signal to container.
124+
Equivalent to 'docker kill -s HUP container-ID', or `-notify-container container-ID -notify-signal 1`.
125+
You can pass this option multiple times to send HUP to multiple containers.
118126
-notify-signal signal
119127
signal to send to the -notify-container and -notify-filter. -1 to call docker restart. Defaults to 1 aka. HUP.
120128
All available signals available on the dockerclient
121129
https://github.com/fsouza/go-dockerclient/blob/main/signal.go
122130
-only-exposed
123-
only include containers with exposed ports
131+
only include containers with exposed ports.
132+
Bypassed when using the exposed filter with (-container-filter exposed=foo).
124133
-only-published
125-
only include containers with published ports (implies -only-exposed)
126-
-include-stopped
127-
include stopped containers
134+
only include containers with published ports (implies -only-exposed).
135+
Bypassed when providing a container published filter (-container-filter published=foo).
128136
-tlscacert string
129-
path to TLS CA certificate file (default "~/.docker/machine/machines/default/ca.pem")
137+
path to TLS CA certificate file (default "~/.docker/ca.pem")
130138
-tlscert string
131-
path to TLS client certificate file (default "~/.docker/machine/machines/default/cert.pem")
139+
path to TLS client certificate file (default "~/.docker/cert.pem")
132140
-tlskey string
133-
path to TLS client key file (default "~/.docker/machine/machines/default/key.pem")
141+
path to TLS client key file (default "~/.docker/key.pem")
134142
-tlsverify
135-
verify docker daemon's TLS certicate (default true)
143+
verify docker daemon's TLS certicate
136144
-version
137145
show version
146+
-wait string
147+
minimum and maximum durations to wait (e.g. "500ms:2s") before triggering generate
138148
-watch
139149
watch for container changes
140-
-wait
141-
minimum (and/or maximum) duration to wait after each container change before triggering
142150
143151
Arguments:
144152
template - path to a template to generate
145-
dest - path to write the template. If not specfied, STDOUT is used
153+
dest - path to write the template to. If not specfied, STDOUT is used
146154
147155
Environment Variables:
148156
DOCKER_HOST - default value for -endpoint
149-
DOCKER_CERT_PATH - directory path containing key.pem, cert.pm and ca.pem
150-
DOCKER_TLS_VERIFY - enable client TLS verification]
157+
DOCKER_CERT_PATH - directory path containing key.pem, cert.pem and ca.pem
158+
DOCKER_TLS_VERIFY - enable client TLS verification
151159
```
152160

153161
If no `<dest>` file is specified, the output is sent to stdout. Mainly useful for debugging.

cmd/docker-gen/main.go

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ var (
3434
onlyExposed bool
3535
onlyPublished bool
3636
includeStopped bool
37+
containerFilter mapstringslice = make(mapstringslice)
3738
configFiles stringslice
3839
configs config.ConfigFile
3940
eventFilter mapstringslice = mapstringslice{"event": {"start", "stop", "die", "health_status"}}
@@ -78,7 +79,7 @@ Options:`)
7879
println(`
7980
Arguments:
8081
template - path to a template to generate
81-
dest - path to a write the template. If not specfied, STDOUT is used`)
82+
dest - path to write the template to. If not specfied, STDOUT is used`)
8283

8384
println(`
8485
Environment Variables:
@@ -98,21 +99,35 @@ func loadConfig(file string) error {
9899
}
99100

100101
func initFlags() {
101-
102102
certPath := filepath.Join(os.Getenv("DOCKER_CERT_PATH"))
103103
if certPath == "" {
104104
certPath = filepath.Join(os.Getenv("HOME"), ".docker")
105105
}
106+
106107
flag.BoolVar(&version, "version", false, "show version")
108+
109+
// General configuration options
107110
flag.BoolVar(&watch, "watch", false, "watch for container changes")
108111
flag.StringVar(&wait, "wait", "", "minimum and maximum durations to wait (e.g. \"500ms:2s\") before triggering generate")
109-
flag.BoolVar(&onlyExposed, "only-exposed", false, "only include containers with exposed ports")
112+
flag.Var(&configFiles, "config", "config files with template directives. Config files will be merged if this option is specified multiple times.")
113+
flag.BoolVar(&keepBlankLines, "keep-blank-lines", false, "keep blank lines in the output file")
110114

115+
// Containers filtering options
116+
flag.BoolVar(&onlyExposed, "only-exposed", false,
117+
"only include containers with exposed ports. Bypassed when providing a container exposed filter (-container-filter exposed=foo).")
111118
flag.BoolVar(&onlyPublished, "only-published", false,
112-
"only include containers with published ports (implies -only-exposed)")
113-
flag.BoolVar(&includeStopped, "include-stopped", false, "include stopped containers")
114-
flag.BoolVar(&notifyOutput, "notify-output", false, "log the output(stdout/stderr) of notify command")
119+
"only include containers with published ports (implies -only-exposed). Bypassed when providing a container published filter (-container-filter published=foo).")
120+
flag.BoolVar(&includeStopped, "include-stopped", false,
121+
"include stopped containers. Bypassed when providing a container status filter (-container-filter status=foo).")
122+
flag.Var(&containerFilter, "container-filter",
123+
"container filter for inclusion by docker-gen. You can pass this option multiple times to combine filters with AND. https://docs.docker.com/engine/reference/commandline/ps/#filter")
124+
125+
// Command notification options
115126
flag.StringVar(&notifyCmd, "notify", "", "run command after template is regenerated (e.g `restart xyz`)")
127+
flag.BoolVar(&notifyOutput, "notify-output", false, "log the output(stdout/stderr) of notify command")
128+
flag.IntVar(&interval, "interval", 0, "notify command interval (secs)")
129+
130+
// Containers notification options
116131
flag.Var(&sighupContainerID, "notify-sighup",
117132
"send HUP signal to container. Equivalent to docker kill -s HUP `container-ID`. You can pass this option multiple times to send HUP to multiple containers.")
118133
flag.Var(&notifyContainerID, "notify-container",
@@ -121,9 +136,8 @@ func initFlags() {
121136
"container filter for notification (e.g -notify-filter name=foo). You can pass this option multiple times to combine filters with AND. https://docs.docker.com/engine/reference/commandline/ps/#filter")
122137
flag.IntVar(&notifyContainerSignal, "notify-signal", int(docker.SIGHUP),
123138
"signal to send to the notify-container and notify-filter. Defaults to SIGHUP")
124-
flag.Var(&configFiles, "config", "config files with template directives. Config files will be merged if this option is specified multiple times.")
125-
flag.IntVar(&interval, "interval", 0, "notify command interval (secs)")
126-
flag.BoolVar(&keepBlankLines, "keep-blank-lines", false, "keep blank lines in the output file")
139+
140+
// Docker API endpoint configuration options
127141
flag.StringVar(&endpoint, "endpoint", "", "docker api endpoint (tcp|unix://..). Default unix:///var/run/docker.sock")
128142
flag.StringVar(&tlsCert, "tlscert", filepath.Join(certPath, "cert.pem"), "path to TLS client certificate file")
129143
flag.StringVar(&tlsKey, "tlskey", filepath.Join(certPath, "key.pem"), "path to TLS client key file")
@@ -177,9 +191,7 @@ func main() {
177191
NotifyCmd: notifyCmd,
178192
NotifyOutput: notifyOutput,
179193
NotifyContainers: make(map[string]int),
180-
OnlyExposed: onlyExposed,
181-
OnlyPublished: onlyPublished,
182-
IncludeStopped: includeStopped,
194+
ContainerFilter: containerFilter,
183195
Interval: interval,
184196
KeepBlankLines: keepBlankLines,
185197
}
@@ -193,25 +205,37 @@ func main() {
193205
cfg.NotifyContainersFilter = notifyContainerFilter
194206
cfg.NotifyContainersSignal = notifyContainerSignal
195207
}
208+
if len(cfg.ContainerFilter["status"]) == 0 {
209+
if includeStopped {
210+
cfg.ContainerFilter["status"] = []string{
211+
"created",
212+
"restarting",
213+
"running",
214+
"removing",
215+
"paused",
216+
"exited",
217+
"dead",
218+
}
219+
} else {
220+
cfg.ContainerFilter["status"] = []string{"running"}
221+
}
222+
}
223+
if onlyPublished && len(cfg.ContainerFilter["publish"]) == 0 {
224+
cfg.ContainerFilter["publish"] = []string{"1-65535"}
225+
} else if onlyExposed && len(cfg.ContainerFilter["expose"]) == 0 {
226+
cfg.ContainerFilter["expose"] = []string{"1-65535"}
227+
}
196228
configs = config.ConfigFile{
197229
Config: []config.Config{cfg},
198230
}
199231
}
200232

201-
all := false
202-
for _, config := range configs.Config {
203-
if config.IncludeStopped {
204-
all = true
205-
}
206-
}
207-
208233
generator, err := generator.NewGenerator(generator.GeneratorConfig{
209234
Endpoint: endpoint,
210235
TLSKey: tlsKey,
211236
TLSCert: tlsCert,
212237
TLSCACert: tlsCaCert,
213238
TLSVerify: tlsVerify,
214-
All: all,
215239
EventFilter: eventFilter,
216240
ConfigFile: configs,
217241
})

internal/config/config.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@ type Config struct {
1616
NotifyContainers map[string]int
1717
NotifyContainersFilter map[string][]string
1818
NotifyContainersSignal int
19-
OnlyExposed bool
20-
OnlyPublished bool
21-
IncludeStopped bool
19+
ContainerFilter map[string][]string
2220
Interval int
2321
KeepBlankLines bool
2422
}

internal/context/context.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,6 @@ func (r *RuntimeContainer) Equals(o RuntimeContainer) bool {
105105
return r.ID == o.ID && r.Image == o.Image
106106
}
107107

108-
func (r *RuntimeContainer) PublishedAddresses() []Address {
109-
mapped := []Address{}
110-
for _, address := range r.Addresses {
111-
if address.HostPort != "" {
112-
mapped = append(mapped, address)
113-
}
114-
}
115-
return mapped
116-
}
117-
118108
type DockerImage struct {
119109
Registry string
120110
Repository string

internal/context/context_test.go

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -132,37 +132,6 @@ func TestGetCurrentContainerEmpty(t *testing.T) {
132132
assert.Equal(t, "", GetCurrentContainerID())
133133
}
134134

135-
func TestPublishedAddresses(t *testing.T) {
136-
container := &RuntimeContainer{
137-
Addresses: []Address{
138-
{
139-
IP: "172.19.0.1",
140-
HostPort: "80",
141-
},
142-
{
143-
IP: "172.19.0.2",
144-
},
145-
{
146-
IP: "172.19.0.3",
147-
HostPort: "8080",
148-
},
149-
},
150-
}
151-
152-
expected := []Address{
153-
{
154-
IP: "172.19.0.1",
155-
HostPort: "80",
156-
},
157-
{
158-
IP: "172.19.0.3",
159-
HostPort: "8080",
160-
},
161-
}
162-
163-
assert.ElementsMatch(t, expected, container.PublishedAddresses())
164-
}
165-
166135
func TestRuntimeContainerEquals(t *testing.T) {
167136
rc1 := &RuntimeContainer{
168137
ID: "baz",

internal/generator/generator.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ type GeneratorConfig struct {
3939
TLSKey string
4040
TLSCACert string
4141
TLSVerify bool
42-
All bool
4342

4443
EventFilter map[string][]string
4544

@@ -72,7 +71,6 @@ func NewGenerator(gc GeneratorConfig) (*generator, error) {
7271
TLSCert: gc.TLSCert,
7372
TLSCaCert: gc.TLSCACert,
7473
TLSKey: gc.TLSKey,
75-
All: gc.All,
7674
EventFilter: gc.EventFilter,
7775
Configs: gc.ConfigFile,
7876
retry: true,
@@ -124,12 +122,13 @@ func (g *generator) generateFromSignals() {
124122
}
125123

126124
func (g *generator) generateFromContainers() {
127-
containers, err := g.getContainers()
128-
if err != nil {
129-
log.Printf("Error listing containers: %s\n", err)
130-
return
131-
}
132125
for _, config := range g.Configs.Config {
126+
containers, err := g.getContainers(config)
127+
if err != nil {
128+
log.Printf("Error listing containers: %s\n", err)
129+
return
130+
}
131+
133132
changed := template.GenerateFile(config, containers)
134133
if !changed {
135134
log.Printf("Contents of %s did not change. Skipping notification '%s'", config.Dest, config.NotifyCmd)
@@ -159,7 +158,7 @@ func (g *generator) generateAtInterval() {
159158
for {
160159
select {
161160
case <-ticker.C:
162-
containers, err := g.getContainers()
161+
containers, err := g.getContainers(cfg)
163162
if err != nil {
164163
log.Printf("Error listing containers: %s\n", err)
165164
continue
@@ -205,7 +204,7 @@ func (g *generator) generateFromEvents() {
205204
defer g.wg.Done()
206205
debouncedChan := newDebounceChannel(watcher, cfg.Wait)
207206
for range debouncedChan {
208-
containers, err := g.getContainers()
207+
containers, err := g.getContainers(cfg)
209208
if err != nil {
210209
log.Printf("Error listing containers: %s\n", err)
211210
continue
@@ -389,7 +388,7 @@ func (g *generator) sendSignalToFilteredContainers(config config.Config) {
389388
}
390389
}
391390

392-
func (g *generator) getContainers() ([]*context.RuntimeContainer, error) {
391+
func (g *generator) getContainers(config config.Config) ([]*context.RuntimeContainer, error) {
393392
apiInfo, err := g.Client.Info()
394393
if err != nil {
395394
log.Printf("Error retrieving docker server info: %s\n", err)
@@ -398,8 +397,9 @@ func (g *generator) getContainers() ([]*context.RuntimeContainer, error) {
398397
}
399398

400399
apiContainers, err := g.Client.ListContainers(docker.ListContainersOptions{
401-
All: g.All,
402-
Size: false,
400+
All: true,
401+
Size: false,
402+
Filters: config.ContainerFilter,
403403
})
404404
if err != nil {
405405
return nil, err

internal/generator/generator_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,14 @@ func TestGenerateFromEvents(t *testing.T) {
199199
// init 150ms 200ms 250ms 300ms 350ms 400ms 450ms 500ms 550ms 600ms 650ms 700ms
200200
// ├──────╫──────┼──────┼──────╫──────┼──────┼──────╫──────┼──────┼──────┼──────┼──────┤
201201
// File0 ├─ 1 ║ ║ ║
202-
// File1 ├─ 1 ╟─ 2 ╟─ 3 ╟─ 5
203-
// File2 ├─ 1 ╟───── max (250ms) ──║───────────> 4 ╟─────── min (200ms) ─────> 6
204-
// File3 └─ 1 ╟──────────────────> ╟──────────────────> ╟─────────── min (250ms) ────────> 7
202+
// File1 ├─ 2 ╟─ 5 ╟─ 6 ╟─ 8
203+
// File2 ├─ 3 ╟───── max (250ms) ──║───────────> 7 ╟─────── min (200ms) ─────> 9
204+
// File3 └─ 4 ╟──────────────────> ╟──────────────────> ╟─────────── min (250ms) ────────> 10
205205
// ┌───╨───┐ ┌───╨──┐ ┌───╨───┐
206206
// │ start │ │ stop │ │ start │
207207
// └───────┘ └──────┘ └───────┘
208208

209-
expectedCounters := []int{1, 5, 6, 7}
209+
expectedCounters := []int{1, 8, 9, 10}
210210

211211
for i, counter := range expectedCounters {
212212
value, _ = os.ReadFile(destFiles[i].Name())

0 commit comments

Comments
 (0)