From 59c27f4e5afa20dd964dbd9fe8cd6980ae1da747 Mon Sep 17 00:00:00 2001 From: DevMiner Date: Tue, 28 Mar 2023 11:10:16 +0200 Subject: [PATCH] impl: filtering based off of mcuadros/ofelia#212 --- README.md | 23 ++++++++++++++++++++++ cli/config.go | 32 ++++++++++++++++++++----------- cli/daemon.go | 12 +++++++----- cli/docker_config_handler.go | 37 +++++++++++++++++++++++++++--------- 4 files changed, 79 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 03f31430..ebb433e2 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,29 @@ docker run -it --rm \ nginx ``` +**Ofelia** reads labels of all Docker containers for configuration by default. To apply on a subset of containers only, use the flag `--docker-filter` (or `-f`) similar to the [filtering for `docker ps`](https://docs.docker.com/engine/reference/commandline/ps/#filter). E.g. to apply to current docker compose project only using `label` filter: + +```yaml +version: "3" +services: + ofelia: + image: mcuadros/ofelia:latest + depends_on: + - nginx + command: daemon --docker -f label=com.docker.compose.project=${COMPOSE_PROJECT_NAME} + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + ofelia.job-local.my-test-job.schedule: "@every 5s" + ofelia.job-local.my-test-job.command: "date" + nginx: + image: nginx + labels: + ofelia.enabled: "true" + ofelia.job-exec.datecron.schedule: "@every 5s" + ofelia.job-exec.datecron.command: "uname -a" +``` + ### Dynamic Docker configuration You can start Ofelia in its own container or on the host itself, and it will magically pick up any container that starts, stops or is modified on the fly. diff --git a/cli/config.go b/cli/config.go index ebeb4085..713bf25d 100644 --- a/cli/config.go +++ b/cli/config.go @@ -27,6 +27,7 @@ type Config struct { RunJobs map[string]*RunJobConfig `gcfg:"job-run" mapstructure:"job-run,squash"` ServiceJobs map[string]*RunServiceConfig `gcfg:"job-service-run" mapstructure:"job-service-run,squash"` LocalJobs map[string]*LocalJobConfig `gcfg:"job-local" mapstructure:"job-local,squash"` + Docker DockerConfig sh *core.Scheduler dockerHandler *DockerHandler logger core.Logger @@ -67,7 +68,7 @@ func (c *Config) InitializeApp() error { c.buildSchedulerMiddlewares(c.sh) var err error - c.dockerHandler, err = NewDockerHandler(c, c.logger) + c.dockerHandler, err = NewDockerHandler(c, c.logger, c.Docker.Filters) if err != nil { return err } @@ -75,16 +76,21 @@ func (c *Config) InitializeApp() error { // In order to support non dynamic job types such as Local or Run using labels // lets parse the labels and merge the job lists dockerLabels, err := c.dockerHandler.GetDockerLabels() - var parsedLabelConfig Config - parsedLabelConfig.buildFromDockerLabels(dockerLabels) - for name, j := range parsedLabelConfig.RunJobs { - c.RunJobs[name] = j - } - for name, j := range parsedLabelConfig.LocalJobs { - c.LocalJobs[name] = j - } - for name, j := range parsedLabelConfig.ServiceJobs { - c.ServiceJobs[name] = j + if err == nil { + parsedLabelConfig := Config{} + + parsedLabelConfig.buildFromDockerLabels(dockerLabels) + for name, j := range parsedLabelConfig.RunJobs { + c.RunJobs[name] = j + } + + for name, j := range parsedLabelConfig.LocalJobs { + c.LocalJobs[name] = j + } + + for name, j := range parsedLabelConfig.ServiceJobs { + c.ServiceJobs[name] = j + } } for name, j := range c.ExecJobs { @@ -297,3 +303,7 @@ func (c *RunServiceConfig) buildMiddlewares() { c.RunServiceJob.Use(middlewares.NewSave(&c.SaveConfig)) c.RunServiceJob.Use(middlewares.NewMail(&c.MailConfig)) } + +type DockerConfig struct { + Filters []string `mapstructure:"filters"` +} diff --git a/cli/daemon.go b/cli/daemon.go index 9471ec65..27ba9975 100644 --- a/cli/daemon.go +++ b/cli/daemon.go @@ -10,11 +10,13 @@ import ( // DaemonCommand daemon process type DaemonCommand struct { - ConfigFile string `long:"config" description:"configuration file" default:"/etc/ofelia.conf"` - scheduler *core.Scheduler - signals chan os.Signal - done chan bool - Logger core.Logger + ConfigFile string `long:"config" description:"configuration file" default:"/etc/ofelia.conf"` + DockerFilters []string `short:"f" long:"docker-filter" description:"Filter for docker containers"` + + scheduler *core.Scheduler + signals chan os.Signal + done chan bool + Logger core.Logger } // Execute runs the daemon diff --git a/cli/docker_config_handler.go b/cli/docker_config_handler.go index 0db135eb..b48ef4e5 100644 --- a/cli/docker_config_handler.go +++ b/cli/docker_config_handler.go @@ -12,6 +12,7 @@ import ( var ErrNoContainerWithOfeliaEnabled = errors.New("Couldn't find containers with label 'ofelia.enabled=true'") type DockerHandler struct { + filters []string dockerClient *docker.Client notifier dockerLabelsUpdate logger core.Logger @@ -35,18 +36,21 @@ func (c *DockerHandler) buildDockerClient() (*docker.Client, error) { return d, nil } -func NewDockerHandler(notifier dockerLabelsUpdate, logger core.Logger) (*DockerHandler, error) { - c := &DockerHandler{} +func NewDockerHandler(notifier dockerLabelsUpdate, logger core.Logger, filters []string) (*DockerHandler, error) { + c := &DockerHandler{ + filters: filters, + notifier: notifier, + logger: logger, + } + var err error c.dockerClient, err = c.buildDockerClient() - c.notifier = notifier - c.logger = logger if err != nil { return nil, err } + // Do a sanity check on docker - _, err = c.dockerClient.Info() - if err != nil { + if _, err = c.dockerClient.Info(); err != nil { return nil, err } @@ -71,10 +75,25 @@ func (c *DockerHandler) watch() { } func (c *DockerHandler) GetDockerLabels() (map[string]map[string]string, error) { + filters := map[string][]string{ + "label": {requiredLabelFilter}, + } + for _, f := range c.filters { + parts := strings.SplitN(f, "=", 2) + if len(parts) != 2 { + return nil, errors.New("invalid docker filter: " + f) + } + key, value := parts[0], parts[1] + values, ok := filters[key] + if ok { + filters[key] = append(values, value) + } else { + filters[key] = []string{value} + } + } + conts, err := c.dockerClient.ListContainers(docker.ListContainersOptions{ - Filters: map[string][]string{ - "label": {requiredLabelFilter}, - }, + Filters: filters, }) if err != nil { return nil, err