Skip to content

Commit

Permalink
Reverts to when label discover worked.
Browse files Browse the repository at this point in the history
  • Loading branch information
maietta committed Jan 1, 2022
1 parent c6eebc3 commit 939d851
Show file tree
Hide file tree
Showing 32 changed files with 719 additions and 432 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ jobs:
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: amd64
goarch: amd64,arm64,ppc64le,armv7,armv6
goversion: 1.17
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: Test
on:
push:
branches:
- master
- main
pull_request:
branches:
- master
- main
jobs:
test:
strategy:
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ FROM golang:1.17.1-alpine AS builder

RUN apk --no-cache add gcc musl-dev

LABEL org.opencontainers.image.description Cron alternative for Docker Swarm enviornments.

WORKDIR ${GOPATH}/src/github.com/PremoWeb/Chadburn
COPY . ${GOPATH}/src/github.com/PremoWeb/Chadburn

Expand Down
19 changes: 16 additions & 3 deletions chadburn.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,29 @@ import (
"os"

"github.com/PremoWeb/Chadburn/cli"
"github.com/PremoWeb/Chadburn/core"
"github.com/jessevdk/go-flags"
"github.com/op/go-logging"
)

var version string
var build string

const logFormat = "%{color}%{shortfile} ▶ %{level}%{color:reset} %{message}"

func buildLogger() core.Logger {
stdout := logging.NewLogBackend(os.Stdout, "", 0)
// Set the backends to be used.
logging.SetBackend(stdout)
logging.SetFormatter(logging.MustStringFormatter(logFormat))
return logging.MustGetLogger("chadburn")
}

func main() {
parser := flags.NewNamedParser("ofelia", flags.Default)
parser.AddCommand("daemon", "daemon process", "", &cli.DaemonCommand{})
parser.AddCommand("validate", "validates the config file", "", &cli.ValidateCommand{})
logger := buildLogger()
parser := flags.NewNamedParser("chadburn", flags.Default)
parser.AddCommand("daemon", "daemon process", "", &cli.DaemonCommand{Logger: logger})
parser.AddCommand("validate", "validates the config file", "", &cli.ValidateCommand{Logger: logger})

if _, err := parser.Parse(); err != nil {
if _, ok := err.(*flags.Error); ok {
Expand Down
188 changes: 109 additions & 79 deletions cli/config.go
Original file line number Diff line number Diff line change
@@ -1,151 +1,173 @@
package cli

import (
"os"

"github.com/PremoWeb/Chadburn/core"
"github.com/PremoWeb/Chadburn/middlewares"
docker "github.com/fsouza/go-dockerclient"
logging "github.com/op/go-logging"

defaults "github.com/mcuadros/go-defaults"
gcfg "gopkg.in/gcfg.v1"
)

const (
logFormat = "%{time} %{color} %{shortfile} ▶ %{level}%{color:reset} %{message}"
jobExec = "job-exec"
jobRun = "job-run"
jobServiceRun = "job-service-run"
jobLocal = "job-local"
)

var IsDockerEnv bool

// Config contains the configuration
type Config struct {
Global struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}
ExecJobs map[string]*ExecJobConfig `gcfg:"job-exec" mapstructure:"job-exec,squash"`
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"`
ExecJobs map[string]*ExecJobConfig `gcfg:"job-exec" mapstructure:"job-exec,squash"`
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"`
sh *core.Scheduler
dockerHandler *DockerHandler
logger core.Logger
}

// BuildFromDockerLabels builds a scheduler using the config from a docker labels
func BuildFromDockerLabels() (*core.Scheduler, error) {
func NewConfig(logger core.Logger) *Config {
// Initialize
c := &Config{}

d, err := c.buildDockerClient()
if err != nil {
return nil, err
}

labels, err := getLabels(d)
if err != nil {
return nil, err
}

if err := c.buildFromDockerLabels(labels); err != nil {
return nil, err
}

return c.build()
c.ExecJobs = make(map[string]*ExecJobConfig)
c.RunJobs = make(map[string]*RunJobConfig)
c.ServiceJobs = make(map[string]*RunServiceConfig)
c.LocalJobs = make(map[string]*LocalJobConfig)
c.logger = logger
defaults.SetDefaults(c)
return c
}

// BuildFromFile builds a scheduler using the config from a file
func BuildFromFile(filename string) (*core.Scheduler, error) {
c := &Config{}
if err := gcfg.ReadFileInto(c, filename); err != nil {
return nil, err
}

return c.build()
func BuildFromFile(filename string, logger core.Logger) (*Config, error) {
c := NewConfig(logger)
err := gcfg.ReadFileInto(c, filename)
return c, err
}

// BuildFromString builds a scheduler using the config from a string
func BuildFromString(config string) (*core.Scheduler, error) {
c := &Config{}
func BuildFromString(config string, logger core.Logger) (*Config, error) {
c := NewConfig(logger)
if err := gcfg.ReadStringInto(c, config); err != nil {
return nil, err
}

return c.build()
return c, nil
}

func (c *Config) build() (*core.Scheduler, error) {
defaults.SetDefaults(c)
// Call this only once at app init
func (c *Config) InitializeApp() error {
c.sh = core.NewScheduler(c.logger)
c.buildSchedulerMiddlewares(c.sh)

d, err := c.buildDockerClient()
var err error
c.dockerHandler, err = NewDockerHandler(c, c.logger)
if err != nil {
return nil, err
return err
}

sh := core.NewScheduler(c.buildLogger())
c.buildSchedulerMiddlewares(sh)

for name, j := range c.ExecJobs {
defaults.SetDefaults(j)

j.Client = d
j.Client = c.dockerHandler.GetInternalDockerClient()
j.Name = name
j.buildMiddlewares()
sh.AddJob(j)
c.sh.AddJob(j)
}

for name, j := range c.RunJobs {
defaults.SetDefaults(j)

j.Client = d
j.Client = c.dockerHandler.GetInternalDockerClient()
j.Name = name
j.buildMiddlewares()
sh.AddJob(j)
c.sh.AddJob(j)
}

for name, j := range c.LocalJobs {
defaults.SetDefaults(j)

j.Name = name
j.buildMiddlewares()
sh.AddJob(j)
c.sh.AddJob(j)
}

for name, j := range c.ServiceJobs {
defaults.SetDefaults(j)
j.Name = name
j.Client = d
j.Client = c.dockerHandler.GetInternalDockerClient()
j.buildMiddlewares()
sh.AddJob(j)
}

return sh, nil
}

func (c *Config) buildDockerClient() (*docker.Client, error) {
d, err := docker.NewClientFromEnv()
if err != nil {
return nil, err
c.sh.AddJob(j)
}

return d, nil
}

func (c *Config) buildLogger() core.Logger {
stdout := logging.NewLogBackend(os.Stdout, "", 0)
// Set the backends to be used.
logging.SetBackend(stdout)
logging.SetFormatter(logging.MustStringFormatter(logFormat))

return logging.MustGetLogger("ofelia")
return nil
}

func (c *Config) buildSchedulerMiddlewares(sh *core.Scheduler) {
sh.Use(middlewares.NewSlack(&c.Global.SlackConfig))
sh.Use(middlewares.NewSave(&c.Global.SaveConfig))
sh.Use(middlewares.NewMail(&c.Global.MailConfig))
sh.Use(middlewares.NewGotify(&c.Global.GotifyConfig))
}

func (c *Config) dockerLabelsUpdate(labels map[string]map[string]string) {
// Get the current labels
var parsedLabelConfig Config
parsedLabelConfig.buildFromDockerLabels(labels)

// Calculate the delta
for name, j := range c.ExecJobs {
found := false
for newJobsName, newJob := range parsedLabelConfig.ExecJobs {
// Check if the schedule has changed
if name == newJobsName {
found = true
// There is a slight race condition were a job can be canceled / restarted with different params
// so, lets take care of it by simply restarting
// For the hash to work properly, we must fill the fields before calling it
defaults.SetDefaults(newJob)
newJob.Client = c.dockerHandler.GetInternalDockerClient()
newJob.Name = newJobsName
if newJob.Hash() != j.Hash() {
// Remove from the scheduler
c.sh.RemoveJob(j)
// Add the job back to the scheduler
newJob.buildMiddlewares()
c.sh.AddJob(newJob)
// Update the job config
c.ExecJobs[name] = newJob
}
break
}
}
if !found {
// Remove the job
c.sh.RemoveJob(j)
delete(c.ExecJobs, name)
}
}

// Check for aditions
for newJobsName, newJob := range parsedLabelConfig.ExecJobs {
found := false
for name := range c.ExecJobs {
if name == newJobsName {
found = true
break
}
}
if !found {
defaults.SetDefaults(newJob)
newJob.Client = c.dockerHandler.GetInternalDockerClient()
newJob.Name = newJobsName
newJob.buildMiddlewares()
c.sh.AddJob(newJob)
c.ExecJobs[newJobsName] = newJob
}
}

}

// ExecJobConfig contains all configuration params needed to build a ExecJob
Expand All @@ -155,13 +177,15 @@ type ExecJobConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

func (c *ExecJobConfig) buildMiddlewares() {
c.ExecJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.ExecJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.ExecJob.Use(middlewares.NewSave(&c.SaveConfig))
c.ExecJob.Use(middlewares.NewMail(&c.MailConfig))
c.ExecJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}

// RunServiceConfig contains all configuration params needed to build a RunJob
Expand All @@ -171,6 +195,7 @@ type RunServiceConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

type RunJobConfig struct {
Expand All @@ -179,13 +204,15 @@ type RunJobConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

func (c *RunJobConfig) buildMiddlewares() {
c.RunJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.RunJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.RunJob.Use(middlewares.NewSave(&c.SaveConfig))
c.RunJob.Use(middlewares.NewMail(&c.MailConfig))
c.RunJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}

// LocalJobConfig contains all configuration params needed to build a RunJob
Expand All @@ -195,18 +222,21 @@ type LocalJobConfig struct {
middlewares.SlackConfig `mapstructure:",squash"`
middlewares.SaveConfig `mapstructure:",squash"`
middlewares.MailConfig `mapstructure:",squash"`
middlewares.GotifyConfig `mapstructure:",squash"`
}

func (c *LocalJobConfig) buildMiddlewares() {
c.LocalJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.LocalJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.LocalJob.Use(middlewares.NewSave(&c.SaveConfig))
c.LocalJob.Use(middlewares.NewMail(&c.MailConfig))
c.LocalJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}

func (c *RunServiceConfig) buildMiddlewares() {
c.RunServiceJob.Use(middlewares.NewOverlap(&c.OverlapConfig))
c.RunServiceJob.Use(middlewares.NewSlack(&c.SlackConfig))
c.RunServiceJob.Use(middlewares.NewSave(&c.SaveConfig))
c.RunServiceJob.Use(middlewares.NewMail(&c.MailConfig))
c.RunServiceJob.Use(middlewares.NewGotify(&c.GotifyConfig))
}
Loading

0 comments on commit 939d851

Please sign in to comment.