Skip to content

Commit bd2f656

Browse files
committed
Integrate changes from #137
1 parent 9f2a20c commit bd2f656

15 files changed

+389
-157
lines changed

cli/config.go

+152-68
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
package cli
22

33
import (
4-
"os"
4+
"fmt"
55

6-
docker "github.com/fsouza/go-dockerclient"
76
"github.com/mcuadros/ofelia/core"
87
"github.com/mcuadros/ofelia/middlewares"
9-
logging "github.com/op/go-logging"
108

119
defaults "github.com/mcuadros/go-defaults"
1210
gcfg "gopkg.in/gcfg.v1"
1311
)
1412

1513
const (
16-
logFormat = "%{time} %{color} %{shortfile} ▶ %{level}%{color:reset} %{message}"
1714
jobExec = "job-exec"
1815
jobRun = "job-run"
1916
jobServiceRun = "job-service-run"
2017
jobLocal = "job-local"
2118
)
2219

23-
var IsDockerEnv bool
24-
2520
// Config contains the configuration
2621
type Config struct {
2722
Global struct {
@@ -33,113 +28,93 @@ type Config struct {
3328
RunJobs map[string]*RunJobConfig `gcfg:"job-run" mapstructure:"job-run,squash"`
3429
ServiceJobs map[string]*RunServiceConfig `gcfg:"job-service-run" mapstructure:"job-service-run,squash"`
3530
LocalJobs map[string]*LocalJobConfig `gcfg:"job-local" mapstructure:"job-local,squash"`
31+
32+
sh *core.Scheduler
33+
dockerHandler *DockerHandler
34+
logger core.Logger
3635
}
3736

38-
// BuildFromDockerLabels builds a scheduler using the config from a docker labels
39-
func BuildFromDockerLabels(filterFlags ...string) (*core.Scheduler, error) {
37+
func NewConfig(logger core.Logger) *Config {
38+
// Initialize
4039
c := &Config{}
41-
42-
d, err := c.buildDockerClient()
43-
if err != nil {
44-
return nil, err
45-
}
46-
47-
labels, err := getLabels(d, filterFlags)
48-
if err != nil {
49-
return nil, err
50-
}
51-
52-
if err := c.buildFromDockerLabels(labels); err != nil {
53-
return nil, err
54-
}
55-
56-
return c.build()
40+
c.ExecJobs = make(map[string]*ExecJobConfig)
41+
c.RunJobs = make(map[string]*RunJobConfig)
42+
c.ServiceJobs = make(map[string]*RunServiceConfig)
43+
c.LocalJobs = make(map[string]*LocalJobConfig)
44+
c.logger = logger
45+
defaults.SetDefaults(c)
46+
return c
5747
}
5848

5949
// BuildFromFile builds a scheduler using the config from a file
60-
func BuildFromFile(filename string) (*core.Scheduler, error) {
61-
c := &Config{}
62-
if err := gcfg.ReadFileInto(c, filename); err != nil {
63-
return nil, err
64-
}
65-
66-
return c.build()
50+
func BuildFromFile(filename string, logger core.Logger) (*Config, error) {
51+
c := NewConfig(logger)
52+
err := gcfg.ReadFileInto(c, filename)
53+
return c, err
6754
}
6855

6956
// BuildFromString builds a scheduler using the config from a string
70-
func BuildFromString(config string) (*core.Scheduler, error) {
71-
c := &Config{}
57+
func BuildFromString(config string, logger core.Logger) (*Config, error) {
58+
c := NewConfig(logger)
7259
if err := gcfg.ReadStringInto(c, config); err != nil {
7360
return nil, err
7461
}
75-
76-
return c.build()
62+
return c, nil
7763
}
7864

79-
func (c *Config) build() (*core.Scheduler, error) {
80-
defaults.SetDefaults(c)
65+
// Call this only once at app init
66+
func (c *Config) InitializeApp() error {
67+
if c.sh == nil {
68+
return fmt.Errorf("scheduler is not initialized yet")
69+
}
8170

82-
d, err := c.buildDockerClient()
71+
// In order to support non dynamic job types such as Local or Run using labels
72+
// lets parse the labels and merge the job lists
73+
dockerLabels, err := c.dockerHandler.GetDockerLabels()
8374
if err != nil {
84-
return nil, err
75+
return err
8576
}
8677

87-
sh := core.NewScheduler(c.buildLogger())
88-
c.buildSchedulerMiddlewares(sh)
78+
if err := c.buildFromDockerLabels(dockerLabels); err != nil {
79+
return err
80+
}
8981

9082
for name, j := range c.ExecJobs {
9183
defaults.SetDefaults(j)
92-
93-
j.Client = d
84+
j.Client = c.dockerHandler.GetInternalDockerClient()
9485
j.Name = name
9586
j.buildMiddlewares()
96-
sh.AddJob(j)
87+
c.sh.AddJob(j)
9788
}
9889

9990
for name, j := range c.RunJobs {
10091
defaults.SetDefaults(j)
101-
102-
j.Client = d
92+
j.Client = c.dockerHandler.GetInternalDockerClient()
10393
j.Name = name
10494
j.buildMiddlewares()
105-
sh.AddJob(j)
95+
c.sh.AddJob(j)
10696
}
10797

10898
for name, j := range c.LocalJobs {
10999
defaults.SetDefaults(j)
110-
111100
j.Name = name
112101
j.buildMiddlewares()
113-
sh.AddJob(j)
102+
c.sh.AddJob(j)
114103
}
115104

116105
for name, j := range c.ServiceJobs {
117106
defaults.SetDefaults(j)
118107
j.Name = name
119-
j.Client = d
108+
j.Client = c.dockerHandler.GetInternalDockerClient()
120109
j.buildMiddlewares()
121-
sh.AddJob(j)
122-
}
123-
124-
return sh, nil
125-
}
126-
127-
func (c *Config) buildDockerClient() (*docker.Client, error) {
128-
d, err := docker.NewClientFromEnv()
129-
if err != nil {
130-
return nil, err
110+
c.sh.AddJob(j)
131111
}
132112

133-
return d, nil
113+
return nil
134114
}
135115

136-
func (c *Config) buildLogger() core.Logger {
137-
stdout := logging.NewLogBackend(os.Stdout, "", 0)
138-
// Set the backends to be used.
139-
logging.SetBackend(stdout)
140-
logging.SetFormatter(logging.MustStringFormatter(logFormat))
141-
142-
return logging.MustGetLogger("ofelia")
116+
func (c *Config) JobsCount() int {
117+
return len(c.ExecJobs) + len(c.RunJobs) + len(c.LocalJobs) + len(c.ServiceJobs)
143118
}
144119

145120
func (c *Config) buildSchedulerMiddlewares(sh *core.Scheduler) {
@@ -148,6 +123,115 @@ func (c *Config) buildSchedulerMiddlewares(sh *core.Scheduler) {
148123
sh.Use(middlewares.NewMail(&c.Global.MailConfig))
149124
}
150125

126+
func (c *Config) dockerLabelsUpdate(labels map[string]map[string]string) {
127+
// Get the current labels
128+
var parsedLabelConfig Config
129+
parsedLabelConfig.buildFromDockerLabels(labels)
130+
131+
// Calculate the delta execJobs
132+
for name, j := range c.ExecJobs {
133+
found := false
134+
for newJobsName, newJob := range parsedLabelConfig.ExecJobs {
135+
// Check if the schedule has changed
136+
if name == newJobsName {
137+
found = true
138+
// There is a slight race condition were a job can be canceled / restarted with different params
139+
// so, lets take care of it by simply restarting
140+
// For the hash to work properly, we must fill the fields before calling it
141+
defaults.SetDefaults(newJob)
142+
newJob.Client = c.dockerHandler.GetInternalDockerClient()
143+
newJob.Name = newJobsName
144+
if newJob.Hash() != j.Hash() {
145+
c.logger.Debugf("Job %s has changed, restarting", name)
146+
// Remove from the scheduler
147+
c.sh.RemoveJob(j)
148+
// Add the job back to the scheduler
149+
newJob.buildMiddlewares()
150+
c.sh.AddJob(newJob)
151+
// Update the job config
152+
c.ExecJobs[name] = newJob
153+
}
154+
break
155+
}
156+
}
157+
if !found {
158+
c.logger.Debugf("Job %s is not found, Removing", name)
159+
// Remove the job
160+
c.sh.RemoveJob(j)
161+
delete(c.ExecJobs, name)
162+
}
163+
}
164+
165+
// Check for aditions
166+
for newJobsName, newJob := range parsedLabelConfig.ExecJobs {
167+
found := false
168+
for name := range c.ExecJobs {
169+
if name == newJobsName {
170+
found = true
171+
break
172+
}
173+
}
174+
if !found {
175+
defaults.SetDefaults(newJob)
176+
newJob.Client = c.dockerHandler.GetInternalDockerClient()
177+
newJob.Name = newJobsName
178+
newJob.buildMiddlewares()
179+
c.sh.AddJob(newJob)
180+
c.ExecJobs[newJobsName] = newJob
181+
}
182+
}
183+
184+
for name, j := range c.RunJobs {
185+
found := false
186+
for newJobsName, newJob := range parsedLabelConfig.RunJobs {
187+
// Check if the schedule has changed
188+
if name == newJobsName {
189+
found = true
190+
// There is a slight race condition were a job can be canceled / restarted with different params
191+
// so, lets take care of it by simply restarting
192+
// For the hash to work properly, we must fill the fields before calling it
193+
defaults.SetDefaults(newJob)
194+
newJob.Client = c.dockerHandler.GetInternalDockerClient()
195+
newJob.Name = newJobsName
196+
if newJob.Hash() != j.Hash() {
197+
// Remove from the scheduler
198+
c.sh.RemoveJob(j)
199+
// Add the job back to the scheduler
200+
newJob.buildMiddlewares()
201+
c.sh.AddJob(newJob)
202+
// Update the job config
203+
c.RunJobs[name] = newJob
204+
}
205+
break
206+
}
207+
}
208+
if !found {
209+
// Remove the job
210+
c.sh.RemoveJob(j)
211+
delete(c.RunJobs, name)
212+
}
213+
}
214+
215+
// Check for aditions
216+
for newJobsName, newJob := range parsedLabelConfig.RunJobs {
217+
found := false
218+
for name := range c.RunJobs {
219+
if name == newJobsName {
220+
found = true
221+
break
222+
}
223+
}
224+
if !found {
225+
defaults.SetDefaults(newJob)
226+
newJob.Client = c.dockerHandler.GetInternalDockerClient()
227+
newJob.Name = newJobsName
228+
newJob.buildMiddlewares()
229+
c.sh.AddJob(newJob)
230+
c.RunJobs[newJobsName] = newJob
231+
}
232+
}
233+
}
234+
151235
// ExecJobConfig contains all configuration params needed to build a ExecJob
152236
type ExecJobConfig struct {
153237
core.ExecJob `mapstructure:",squash"`

cli/config_test.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,16 @@ type SuiteConfig struct{}
1717

1818
var _ = Suite(&SuiteConfig{})
1919

20+
type TestLogger struct{}
21+
22+
func (*TestLogger) Criticalf(format string, args ...interface{}) {}
23+
func (*TestLogger) Debugf(format string, args ...interface{}) {}
24+
func (*TestLogger) Errorf(format string, args ...interface{}) {}
25+
func (*TestLogger) Noticef(format string, args ...interface{}) {}
26+
func (*TestLogger) Warningf(format string, args ...interface{}) {}
27+
2028
func (s *SuiteConfig) TestBuildFromString(c *C) {
21-
sh, err := BuildFromString(`
29+
conf, err := BuildFromString(`
2230
[job-exec "foo"]
2331
schedule = @every 10s
2432
@@ -33,10 +41,10 @@ func (s *SuiteConfig) TestBuildFromString(c *C) {
3341
3442
[job-service-run "bob"]
3543
schedule = @every 10s
36-
`)
44+
`, &TestLogger{})
3745

3846
c.Assert(err, IsNil)
39-
c.Assert(sh.Jobs, HasLen, 5)
47+
c.Assert(conf.JobsCount(), Equals, 5)
4048
}
4149

4250
func (s *SuiteConfig) TestJobDefaultsSet(c *C) {

0 commit comments

Comments
 (0)