1
1
package cli
2
2
3
3
import (
4
- "os "
4
+ "fmt "
5
5
6
- docker "github.com/fsouza/go-dockerclient"
7
6
"github.com/mcuadros/ofelia/core"
8
7
"github.com/mcuadros/ofelia/middlewares"
9
- logging "github.com/op/go-logging"
10
8
11
9
defaults "github.com/mcuadros/go-defaults"
12
10
gcfg "gopkg.in/gcfg.v1"
13
11
)
14
12
15
13
const (
16
- logFormat = "%{time} %{color} %{shortfile} ▶ %{level}%{color:reset} %{message}"
17
14
jobExec = "job-exec"
18
15
jobRun = "job-run"
19
16
jobServiceRun = "job-service-run"
20
17
jobLocal = "job-local"
21
18
)
22
19
23
- var IsDockerEnv bool
24
-
25
20
// Config contains the configuration
26
21
type Config struct {
27
22
Global struct {
@@ -33,113 +28,93 @@ type Config struct {
33
28
RunJobs map [string ]* RunJobConfig `gcfg:"job-run" mapstructure:"job-run,squash"`
34
29
ServiceJobs map [string ]* RunServiceConfig `gcfg:"job-service-run" mapstructure:"job-service-run,squash"`
35
30
LocalJobs map [string ]* LocalJobConfig `gcfg:"job-local" mapstructure:"job-local,squash"`
31
+
32
+ sh * core.Scheduler
33
+ dockerHandler * DockerHandler
34
+ logger core.Logger
36
35
}
37
36
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
40
39
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
57
47
}
58
48
59
49
// 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
67
54
}
68
55
69
56
// 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 )
72
59
if err := gcfg .ReadStringInto (c , config ); err != nil {
73
60
return nil , err
74
61
}
75
-
76
- return c .build ()
62
+ return c , nil
77
63
}
78
64
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
+ }
81
70
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 ()
83
74
if err != nil {
84
- return nil , err
75
+ return err
85
76
}
86
77
87
- sh := core .NewScheduler (c .buildLogger ())
88
- c .buildSchedulerMiddlewares (sh )
78
+ if err := c .buildFromDockerLabels (dockerLabels ); err != nil {
79
+ return err
80
+ }
89
81
90
82
for name , j := range c .ExecJobs {
91
83
defaults .SetDefaults (j )
92
-
93
- j .Client = d
84
+ j .Client = c .dockerHandler .GetInternalDockerClient ()
94
85
j .Name = name
95
86
j .buildMiddlewares ()
96
- sh .AddJob (j )
87
+ c . sh .AddJob (j )
97
88
}
98
89
99
90
for name , j := range c .RunJobs {
100
91
defaults .SetDefaults (j )
101
-
102
- j .Client = d
92
+ j .Client = c .dockerHandler .GetInternalDockerClient ()
103
93
j .Name = name
104
94
j .buildMiddlewares ()
105
- sh .AddJob (j )
95
+ c . sh .AddJob (j )
106
96
}
107
97
108
98
for name , j := range c .LocalJobs {
109
99
defaults .SetDefaults (j )
110
-
111
100
j .Name = name
112
101
j .buildMiddlewares ()
113
- sh .AddJob (j )
102
+ c . sh .AddJob (j )
114
103
}
115
104
116
105
for name , j := range c .ServiceJobs {
117
106
defaults .SetDefaults (j )
118
107
j .Name = name
119
- j .Client = d
108
+ j .Client = c . dockerHandler . GetInternalDockerClient ()
120
109
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 )
131
111
}
132
112
133
- return d , nil
113
+ return nil
134
114
}
135
115
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 )
143
118
}
144
119
145
120
func (c * Config ) buildSchedulerMiddlewares (sh * core.Scheduler ) {
@@ -148,6 +123,115 @@ func (c *Config) buildSchedulerMiddlewares(sh *core.Scheduler) {
148
123
sh .Use (middlewares .NewMail (& c .Global .MailConfig ))
149
124
}
150
125
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
+
151
235
// ExecJobConfig contains all configuration params needed to build a ExecJob
152
236
type ExecJobConfig struct {
153
237
core.ExecJob `mapstructure:",squash"`
0 commit comments