From 509f6ee5336eff54b17895758e069fb68055cd54 Mon Sep 17 00:00:00 2001 From: mmmohebi Date: Sun, 20 Jul 2025 15:29:38 +0330 Subject: [PATCH 1/7] chore: gitignore: Ignore .idea directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b317bdde8..fa06a790c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .vscode +.idea ofelia debug build From 45d0fa726d7094ee2f4bce2d195319da980651bb Mon Sep 17 00:00:00 2001 From: mmmohebi Date: Sun, 20 Jul 2025 15:30:01 +0330 Subject: [PATCH 2/7] feat(docker_handler): Listen for Docker events for hot reload Replaces polling with event listening for updating labels. --- cli/docker_handler.go | 53 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/cli/docker_handler.go b/cli/docker_handler.go index 2ae886a12..65bb3b49a 100644 --- a/cli/docker_handler.go +++ b/cli/docker_handler.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "os" + "slices" "strings" "time" @@ -86,18 +87,50 @@ func (c *DockerHandler) ConfigFromLabelsEnabled() bool { return c.configsFromLabels } +// Watch for Docker events and update the labels accordingly func (c *DockerHandler) watch() { - const pollInterval = 10 * time.Second - c.logger.Debugf("Watching for Docker labels changes every %s...", pollInterval) - ticker := time.NewTicker(pollInterval) - defer ticker.Stop() - for range ticker.C { - labels, err := c.GetDockerLabels() - // Do not print or care if there is no container up right now - if err != nil && !errors.Is(err, errNoContainersMatchingFilters) { - c.logger.Debugf("%v", err) + c.logger.Debugf("Listening for Docker events to hot reload configs...") + + events := make(chan *docker.APIEvents) + err := c.dockerClient.AddEventListener(events) + if err != nil { + c.logger.Errorf("Error adding event listener: %v", err) + } + defer func() { + // remove the listener when the program exits + err := c.dockerClient.RemoveEventListener(events) + if err != nil { + c.logger.Errorf("Error removing event listener: %v", err) + } + }() + + lifecycleEvents := []string{ + "create", + "start", + "restart", + "stop", + "kill", + "die", + "destroy", + } + + otherManagementEvents := []string{ + "pause", + "unpause", + "rename", + "update", + } + + for event := range events { + if event.Type == "container" && (slices.Contains(lifecycleEvents, event.Action) || slices.Contains(otherManagementEvents, event.Action)) { + c.logger.Debugf("Received Docker event: Type=%s, Action=%s, ID=%s, ContanerName=%s", event.Type, event.Action, event.ID[:8], event.Actor.Attributes["name"]) + labels, err := c.GetDockerLabels() + // Do not print or care if there is no container up right now + if err != nil && !errors.Is(err, errNoContainersMatchingFilters) { + c.logger.Debugf("%v", err) + } + c.notifier.dockerLabelsUpdate(labels) } - c.notifier.dockerLabelsUpdate(labels) } } From 02e6e625d48a9cc2e29f8236c75ecbe30e67eeac Mon Sep 17 00:00:00 2001 From: mmmohebi Date: Mon, 21 Jul 2025 09:39:56 +0330 Subject: [PATCH 3/7] fix(docker_handler): Remove debug log for Docker events --- cli/docker_handler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/docker_handler.go b/cli/docker_handler.go index 65bb3b49a..c12c30290 100644 --- a/cli/docker_handler.go +++ b/cli/docker_handler.go @@ -123,7 +123,6 @@ func (c *DockerHandler) watch() { for event := range events { if event.Type == "container" && (slices.Contains(lifecycleEvents, event.Action) || slices.Contains(otherManagementEvents, event.Action)) { - c.logger.Debugf("Received Docker event: Type=%s, Action=%s, ID=%s, ContanerName=%s", event.Type, event.Action, event.ID[:8], event.Actor.Attributes["name"]) labels, err := c.GetDockerLabels() // Do not print or care if there is no container up right now if err != nil && !errors.Is(err, errNoContainersMatchingFilters) { From 3c056505d9f687a997b739df49ea2b9e0ad95031 Mon Sep 17 00:00:00 2001 From: MMMohebi Date: Tue, 22 Jul 2025 14:56:11 +0330 Subject: [PATCH 4/7] Convert slice.Contains to swtich case Co-authored-by: Taras <9948629+taraspos@users.noreply.github.com> --- cli/docker_handler.go | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/cli/docker_handler.go b/cli/docker_handler.go index c12c30290..be4dda59b 100644 --- a/cli/docker_handler.go +++ b/cli/docker_handler.go @@ -104,25 +104,28 @@ func (c *DockerHandler) watch() { } }() - lifecycleEvents := []string{ - "create", - "start", - "restart", - "stop", - "kill", - "die", - "destroy", - } - otherManagementEvents := []string{ - "pause", - "unpause", - "rename", - "update", - } + shouldProcessEvent := func(event *docker.APIEvents) bool { + if event.Type != "container": + return false + } + + switch (event.Action) { + // lifecycle events + case "create", "start", "restart", "stop", "kill", "die", "destroy": + return true + // other management events + case "pause", "unpause", "rename", "update": + return true + default: + return false + } + } for event := range events { - if event.Type == "container" && (slices.Contains(lifecycleEvents, event.Action) || slices.Contains(otherManagementEvents, event.Action)) { + if !shouldProcessEvent(event) { + continue + } labels, err := c.GetDockerLabels() // Do not print or care if there is no container up right now if err != nil && !errors.Is(err, errNoContainersMatchingFilters) { From 1e42cdc6aea9522dba9be82a3aad8c38df9ecb3e Mon Sep 17 00:00:00 2001 From: mmmohebi Date: Tue, 22 Jul 2025 14:59:21 +0330 Subject: [PATCH 5/7] refactor(cli): Simplify docker event processing logic --- cli/docker_handler.go | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/cli/docker_handler.go b/cli/docker_handler.go index be4dda59b..bc86dbb72 100644 --- a/cli/docker_handler.go +++ b/cli/docker_handler.go @@ -5,14 +5,12 @@ import ( "encoding/json" "errors" "fmt" - "os" - "slices" - "strings" - "time" - docker "github.com/fsouza/go-dockerclient" "github.com/go-viper/mapstructure/v2" "github.com/mcuadros/ofelia/core" + "os" + "strings" + "time" ) const ( @@ -104,34 +102,22 @@ func (c *DockerHandler) watch() { } }() - - shouldProcessEvent := func(event *docker.APIEvents) bool { - if event.Type != "container": - return false - } - - switch (event.Action) { - // lifecycle events - case "create", "start", "restart", "stop", "kill", "die", "destroy": - return true - // other management events - case "pause", "unpause", "rename", "update": - return true - default: - return false - } - } - for event := range events { - if !shouldProcessEvent(event) { - continue + if event.Type != "container" { + continue } + + switch event.Action { + // |-----------------------lifecycle events---------------------| | ----- other management events -----| + case "create", "start", "restart", "stop", "kill", "die", "destroy", "pause", "unpause", "rename", "update": labels, err := c.GetDockerLabels() // Do not print or care if there is no container up right now if err != nil && !errors.Is(err, errNoContainersMatchingFilters) { c.logger.Debugf("%v", err) } c.notifier.dockerLabelsUpdate(labels) + default: + continue } } } From e18f11bef8bbd46777538179c14671a4cec5cc19 Mon Sep 17 00:00:00 2001 From: taraspos Date: Sat, 26 Jul 2025 12:56:05 +0100 Subject: [PATCH 6/7] Filter events by type and label --- cli/docker_handler.go | 66 +++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/cli/docker_handler.go b/cli/docker_handler.go index bc86dbb72..4a8b87aec 100644 --- a/cli/docker_handler.go +++ b/cli/docker_handler.go @@ -5,12 +5,13 @@ import ( "encoding/json" "errors" "fmt" - docker "github.com/fsouza/go-dockerclient" - "github.com/go-viper/mapstructure/v2" - "github.com/mcuadros/ofelia/core" "os" "strings" "time" + + docker "github.com/fsouza/go-dockerclient" + "github.com/go-viper/mapstructure/v2" + "github.com/mcuadros/ofelia/core" ) const ( @@ -32,7 +33,7 @@ type DockerHandler struct { notifier labelConfigUpdater configsFromLabels bool logger core.Logger - filters []string + filters map[string][]string } type labelConfigUpdater interface { @@ -58,8 +59,20 @@ func NewDockerHandler(config *Config, dockerFilters []string, configsFromLabels return nil, fmt.Errorf("docker filters can only be provided together with '--docker' flag") } + var filters = map[string][]string{ + "label": {requiredLabelFilter}, + } + + for _, f := range dockerFilters { + key, value, err := parseFilter(f) + if err != nil { + config.logger.Errorf("Error parsing filter '%s': %v", f, err) + } + filters[key] = append(filters[key], value) + } + c := &DockerHandler{ - filters: dockerFilters, + filters: filters, configsFromLabels: configsFromLabels, notifier: config, logger: logger, @@ -88,28 +101,32 @@ func (c *DockerHandler) ConfigFromLabelsEnabled() bool { // Watch for Docker events and update the labels accordingly func (c *DockerHandler) watch() { c.logger.Debugf("Listening for Docker events to hot reload configs...") - events := make(chan *docker.APIEvents) - err := c.dockerClient.AddEventListener(events) - if err != nil { + var filters = map[string][]string{ + "type": {"container"}, + "label": c.filters["label"], + } + + if err := c.dockerClient.AddEventListenerWithOptions(docker.EventsOptions{ + Filters: filters}, events); err != nil { c.logger.Errorf("Error adding event listener: %v", err) + return } + defer func() { - // remove the listener when the program exits - err := c.dockerClient.RemoveEventListener(events) - if err != nil { + if err := c.dockerClient.RemoveEventListener(events); err != nil { c.logger.Errorf("Error removing event listener: %v", err) } }() for event := range events { - if event.Type != "container" { - continue - } - switch event.Action { - // |-----------------------lifecycle events---------------------| | ----- other management events -----| - case "create", "start", "restart", "stop", "kill", "die", "destroy", "pause", "unpause", "rename", "update": + case + // lifecycle events + "create", "start", "restart", "stop", "kill", "die", "destroy", + // other management events + "pause", "unpause", "rename", "update": + c.logger.Debugf("Refreshing configurations. Docker event received - [%s] %s (%s)", event.Action, event.Actor.Attributes["name"], event.Actor.ID) labels, err := c.GetDockerLabels() // Do not print or care if there is no container up right now if err != nil && !errors.Is(err, errNoContainersMatchingFilters) { @@ -152,22 +169,11 @@ func (c *DockerHandler) WaitForLabels() { } func (c *DockerHandler) GetDockerLabels() (map[string]map[string]string, error) { - var filters = map[string][]string{ - "label": {requiredLabelFilter}, - } - for _, f := range c.filters { - key, value, err := parseFilter(f) - if err != nil { - return nil, fmt.Errorf("%w: %s", err, f) - } - filters[key] = append(filters[key], value) - } - - conts, err := c.dockerClient.ListContainers(docker.ListContainersOptions{Filters: filters}) + conts, err := c.dockerClient.ListContainers(docker.ListContainersOptions{Filters: c.filters}) if err != nil { return nil, fmt.Errorf("%w: %w", errFailedToListContainers, err) } else if len(conts) == 0 { - return nil, fmt.Errorf("%w: %v", errNoContainersMatchingFilters, filters) + return nil, fmt.Errorf("%w: %v", errNoContainersMatchingFilters, c.filters) } var labels = make(map[string]map[string]string) From b0bbaac3ae8d74b20c37b9864461d535a7069c63 Mon Sep 17 00:00:00 2001 From: taraspos Date: Sat, 26 Jul 2025 13:00:57 +0100 Subject: [PATCH 7/7] Log event filters --- cli/docker_handler.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cli/docker_handler.go b/cli/docker_handler.go index 4a8b87aec..d0640e1e3 100644 --- a/cli/docker_handler.go +++ b/cli/docker_handler.go @@ -66,7 +66,8 @@ func NewDockerHandler(config *Config, dockerFilters []string, configsFromLabels for _, f := range dockerFilters { key, value, err := parseFilter(f) if err != nil { - config.logger.Errorf("Error parsing filter '%s': %v", f, err) + logger.Errorf("Error parsing filter '%s': %v", f, err) + return nil, errInvalidDockerFilter } filters[key] = append(filters[key], value) } @@ -100,13 +101,14 @@ func (c *DockerHandler) ConfigFromLabelsEnabled() bool { // Watch for Docker events and update the labels accordingly func (c *DockerHandler) watch() { - c.logger.Debugf("Listening for Docker events to hot reload configs...") events := make(chan *docker.APIEvents) var filters = map[string][]string{ "type": {"container"}, "label": c.filters["label"], } + c.logger.Debugf("Listening for Docker events (with following filters %v) to hot reload configs...", filters) + if err := c.dockerClient.AddEventListenerWithOptions(docker.EventsOptions{ Filters: filters}, events); err != nil { c.logger.Errorf("Error adding event listener: %v", err)