naf 2.0.4 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +28 -4
- data/RELEASE_NOTES.rdoc +8 -0
- data/app/assets/javascripts/dataTablesTemplates/application_schedules.js +72 -0
- data/app/assets/javascripts/dataTablesTemplates/applications.js +12 -21
- data/app/controllers/naf/application_schedule_affinity_tabs_controller.rb +5 -8
- data/app/controllers/naf/application_schedules_controller.rb +98 -0
- data/app/controllers/naf/applications_controller.rb +6 -98
- data/app/controllers/naf/historical_jobs_controller.rb +18 -8
- data/app/helpers/naf/application_helper.rb +47 -22
- data/app/models/logical/naf/application.rb +9 -126
- data/app/models/logical/naf/application_schedule.rb +186 -0
- data/app/models/logical/naf/construction_zone/boss.rb +4 -5
- data/app/models/logical/naf/construction_zone/foreman.rb +34 -13
- data/app/models/logical/naf/construction_zone/proletariat.rb +1 -0
- data/app/models/logical/naf/construction_zone/work_order.rb +11 -4
- data/app/models/logical/naf/job_fetcher.rb +1 -0
- data/app/models/naf/application.rb +5 -16
- data/app/models/naf/application_schedule.rb +117 -43
- data/app/models/naf/historical_job.rb +6 -3
- data/app/models/naf/queued_job.rb +3 -0
- data/app/models/naf/run_interval_style.rb +20 -0
- data/app/models/naf/running_job.rb +3 -0
- data/app/models/process/naf/data_migration/backfill_application_schedule_run_interval.rb +85 -0
- data/app/models/process/naf/log_archiver.rb +1 -1
- data/app/models/process/naf/log_reader.rb +141 -0
- data/app/models/process/naf/runner.rb +6 -49
- data/app/views/naf/application_schedule_affinity_tabs/_form.html.erb +1 -1
- data/app/views/naf/{applications → application_schedules}/_application_schedule_prerequisites.html.erb +1 -1
- data/app/views/naf/application_schedules/_form.html.erb +120 -0
- data/app/views/naf/application_schedules/edit.html.erb +11 -0
- data/app/views/naf/application_schedules/index.html.erb +47 -0
- data/app/views/naf/application_schedules/index.json.erb +8 -0
- data/app/views/naf/application_schedules/new.html.erb +11 -0
- data/app/views/naf/application_schedules/show.html.erb +130 -0
- data/app/views/naf/applications/_form.html.erb +44 -106
- data/app/views/naf/applications/_search_container.html.erb +29 -29
- data/app/views/naf/applications/index.html.erb +1 -10
- data/app/views/naf/applications/index.json.erb +22 -6
- data/app/views/naf/applications/show.html.erb +47 -97
- data/app/views/naf/logger_styles/_form.html.erb +0 -3
- data/app/views/naf/machines/_form.html.erb +3 -4
- data/config/routes.rb +3 -4
- data/db/migrate/20131219195439_add_run_interval_styles_table.rb +49 -0
- data/db/migrate/20140113183243_drop_run_start_minute_from_application_schedules.rb +18 -0
- data/lib/naf/version.rb +1 -1
- data/naf.gemspec +1 -1
- data/spec/controllers/naf/application_schedule_affinity_tabs_controller_spec.rb +34 -27
- data/spec/controllers/naf/applications_controller_spec.rb +0 -48
- data/spec/factories/naf.rb +14 -8
- data/spec/models/logical/naf/application_spec.rb +9 -37
- data/spec/models/logical/naf/machine_spec.rb +1 -1
- data/spec/models/naf/application_schedule_spec.rb +38 -50
- data/spec/models/naf/application_spec.rb +3 -3
- data/spec/models/naf/historical_job_spec.rb +4 -2
- data/spec/models/naf/queued_job_spec.rb +2 -0
- data/spec/models/naf/run_interval_style_spec.rb +28 -0
- data/spec/models/naf/running_job_spec.rb +2 -0
- metadata +19 -7
- data/app/models/logical/naf/job_creator.rb +0 -151
- data/app/views/naf/applications/_application_schedule.html.erb +0 -80
- data/spec/models/logical/naf/job_creator_spec.rb +0 -102
@@ -80,12 +80,11 @@ module Logical::Naf::ConstructionZone
|
|
80
80
|
def enqueue_n_commands_on_machines(parameters, number_of_jobs = :from_limit, machines = [])
|
81
81
|
logger.detail "enqueuing #{parameters[:command]} #{number_of_jobs} time(s) on #{machines.length} machine(s)"
|
82
82
|
machines.each do |machine|
|
83
|
-
number_of_jobs = (parameters[:
|
83
|
+
number_of_jobs = (parameters[:application_run_group_quantum] || 1) if number_of_jobs == :from_limit
|
84
84
|
logger.info "enqueuing #{parameters[:command]} #{number_of_jobs} time(s) on #{machine}"
|
85
85
|
(1..number_of_jobs).each do
|
86
86
|
machine_parameters = {
|
87
|
-
:
|
88
|
-
:application_run_group_restriction => ::Naf::ApplicationRunGroupRestriction.limited_per_machine
|
87
|
+
application_run_group_restriction: ::Naf::ApplicationRunGroupRestriction.limited_per_machine
|
89
88
|
}.merge(parameters)
|
90
89
|
machine_parameters[:affinities] =
|
91
90
|
[machine.affinity] + if machine_parameters[:affinities].nil?
|
@@ -102,10 +101,10 @@ module Logical::Naf::ConstructionZone
|
|
102
101
|
end
|
103
102
|
|
104
103
|
def enqueue_n_commands(parameters, number_of_jobs = :from_limit)
|
105
|
-
number_of_jobs = (parameters[:
|
104
|
+
number_of_jobs = (parameters[:application_run_group_quantum] || 1) if number_of_jobs == :from_limit
|
106
105
|
logger.info "enqueuing #{parameters[:command]} #{number_of_jobs} time(s) on #{machine}"
|
107
106
|
(1..number_of_jobs).each do
|
108
|
-
work_order = AdHocWorkOrder.new(
|
107
|
+
work_order = AdHocWorkOrder.new(parameters)
|
109
108
|
@foreman.enqueue(work_order)
|
110
109
|
end
|
111
110
|
end
|
@@ -11,7 +11,8 @@ module Logical::Naf::ConstructionZone
|
|
11
11
|
unless work_order.enqueue_backlogs
|
12
12
|
if limited_by_run_group?(work_order.application_run_group_restriction,
|
13
13
|
work_order.application_run_group_name,
|
14
|
-
work_order.application_run_group_limit
|
14
|
+
work_order.application_run_group_limit,
|
15
|
+
work_order.historical_job_affinity_tab_parameters)
|
15
16
|
logger.warn "work order limited by run queue limits #{work_order.inspect}"
|
16
17
|
return nil
|
17
18
|
end
|
@@ -21,26 +22,46 @@ module Logical::Naf::ConstructionZone
|
|
21
22
|
work_order.historical_job_prerequisite_historical_jobs)
|
22
23
|
end
|
23
24
|
|
24
|
-
def limited_by_run_group?(application_run_group_restriction, application_run_group_name, application_run_group_limit)
|
25
|
+
def limited_by_run_group?(application_run_group_restriction, application_run_group_name, application_run_group_limit, affinities)
|
25
26
|
if (application_run_group_restriction.id == ::Naf::ApplicationRunGroupRestriction.no_limit.id ||
|
26
27
|
application_run_group_limit.nil? ||
|
27
28
|
application_run_group_name.nil?)
|
28
29
|
false
|
29
30
|
elsif application_run_group_restriction.id == ::Naf::ApplicationRunGroupRestriction.limited_per_machine.id
|
30
|
-
#
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
# Retrieve the affinity associated to the machine
|
32
|
+
machine_affinity = nil
|
33
|
+
affinities.each do |affinity|
|
34
|
+
machine_affinity = ::Naf::Affinity.find_by_id(affinity[:affinity_id])
|
35
|
+
if machine_affinity.affinity_classification_name == 'machine'
|
36
|
+
break
|
37
|
+
end
|
38
|
+
end
|
34
39
|
|
35
|
-
#
|
36
|
-
|
37
|
-
|
40
|
+
# If affinity is present, check if the sum of jobs running on the machine
|
41
|
+
# and queued for the machine is less the application_run_group_limit.
|
42
|
+
# If affinity is not present, send a log warning the user that application schedule
|
43
|
+
# should have affinity associated to the machine in order to behave correctly, and
|
44
|
+
# queue the application.
|
45
|
+
if machine_affinity.present?
|
46
|
+
queued_jobs = ::Naf::QueuedJob.
|
47
|
+
joins(:historical_job).
|
48
|
+
joins("INNER JOIN #{Naf.schema_name}.historical_job_affinity_tabs AS hjat
|
49
|
+
ON hjat.historical_job_id = #{Naf.schema_name}.historical_jobs.id").
|
50
|
+
where("#{Naf.schema_name}.historical_jobs.application_run_group_name = ? AND hjat.affinity_id = ?",
|
51
|
+
application_run_group_name, machine_affinity.id).count
|
52
|
+
running_jobs = ::Naf::RunningJob.where(
|
53
|
+
application_run_group_name: application_run_group_name,
|
54
|
+
started_on_machine_id: machine_affinity.affinity_name
|
55
|
+
).count
|
38
56
|
|
39
|
-
|
40
|
-
|
57
|
+
queued_jobs + running_jobs >= application_run_group_limit
|
58
|
+
else
|
59
|
+
logger.warn "application schedule does not have affinity associated with a machine"
|
60
|
+
false
|
61
|
+
end
|
41
62
|
elsif application_run_group_restriction.id == ::Naf::ApplicationRunGroupRestriction.limited_per_all_machines.id
|
42
|
-
(::Naf::QueuedJob.where(:
|
43
|
-
::Naf::RunningJob.where(:
|
63
|
+
(::Naf::QueuedJob.where(application_run_group_name: application_run_group_name).count +
|
64
|
+
::Naf::RunningJob.where(application_run_group_name: application_run_group_name).count) >= application_run_group_limit
|
44
65
|
else
|
45
66
|
logger.warn "not limited by run group restriction but don't know why: #{application_run_group_restriction.inspect}"
|
46
67
|
true
|
@@ -27,6 +27,7 @@ module Logical::Naf::ConstructionZone
|
|
27
27
|
|
28
28
|
def create_queued_job(historical_job)
|
29
29
|
queued_job = ::Naf::QueuedJob.new(application_id: historical_job.application_id,
|
30
|
+
application_schedule_id: historical_job.application_schedule_id,
|
30
31
|
application_type_id: historical_job.application_type_id,
|
31
32
|
command: historical_job.command,
|
32
33
|
application_run_group_restriction_id: historical_job.application_run_group_restriction_id,
|
@@ -1,8 +1,14 @@
|
|
1
1
|
module Logical::Naf::ConstructionZone
|
2
2
|
class WorkOrder
|
3
|
-
attr_reader :command,
|
4
|
-
|
5
|
-
|
3
|
+
attr_reader :command,
|
4
|
+
:application_type,
|
5
|
+
:application_run_group_restriction,
|
6
|
+
:application_run_group_name,
|
7
|
+
:application_run_group_limit,
|
8
|
+
:priority,
|
9
|
+
:enqueue_backlogs,
|
10
|
+
:application,
|
11
|
+
:application_schedule
|
6
12
|
|
7
13
|
def initialize(command,
|
8
14
|
application_type = ::Naf::ApplicationType.rails,
|
@@ -42,7 +48,8 @@ module Logical::Naf::ConstructionZone
|
|
42
48
|
application_run_group_name: application_run_group_name,
|
43
49
|
application_run_group_limit: application_run_group_limit,
|
44
50
|
priority: priority,
|
45
|
-
application_id: application.try(:id)
|
51
|
+
application_id: application.try(:id),
|
52
|
+
application_schedule_id: application_schedule.try(:id)
|
46
53
|
}
|
47
54
|
end
|
48
55
|
|
@@ -117,6 +117,7 @@ module Logical
|
|
117
117
|
if historical_job.present?
|
118
118
|
::Naf::QueuedJob.delete(historical_job.id)
|
119
119
|
running_job = ::Naf::RunningJob.new(application_id: historical_job.application_id,
|
120
|
+
application_schedule_id: historical_job.application_schedule_id,
|
120
121
|
application_type_id: historical_job.application_type_id,
|
121
122
|
command: historical_job.command,
|
122
123
|
application_run_group_restriction_id: historical_job.application_run_group_restriction_id,
|
@@ -7,8 +7,8 @@ module Naf
|
|
7
7
|
:log_level,
|
8
8
|
:short_name,
|
9
9
|
:deleted,
|
10
|
-
:
|
11
|
-
:
|
10
|
+
:application_schedules,
|
11
|
+
:application_schedules_attributes
|
12
12
|
|
13
13
|
#---------------------
|
14
14
|
# *** Associations ***
|
@@ -16,7 +16,7 @@ module Naf
|
|
16
16
|
|
17
17
|
belongs_to :application_type,
|
18
18
|
class_name: '::Naf::ApplicationType'
|
19
|
-
|
19
|
+
has_many :application_schedules,
|
20
20
|
class_name: '::Naf::ApplicationSchedule',
|
21
21
|
dependent: :destroy
|
22
22
|
has_many :historical_jobs,
|
@@ -35,12 +35,11 @@ module Naf
|
|
35
35
|
allow_nil: true,
|
36
36
|
format: {
|
37
37
|
with: /^[a-zA-Z_][a-zA-Z0-9_]*$/,
|
38
|
-
message: "letters
|
38
|
+
message: "short name consists of only letters/numbers/underscores, and it needs to start with a letter/underscore"
|
39
39
|
}
|
40
40
|
|
41
|
-
validate :check_references_with_application_schedule_prerequisites
|
42
41
|
before_save :check_blank_values
|
43
|
-
accepts_nested_attributes_for :
|
42
|
+
accepts_nested_attributes_for :application_schedules, allow_destroy: true
|
44
43
|
|
45
44
|
#--------------------
|
46
45
|
# *** Delegations ***
|
@@ -85,16 +84,6 @@ module Naf
|
|
85
84
|
self.log_level = nil if self.log_level.blank?
|
86
85
|
end
|
87
86
|
|
88
|
-
def check_references_with_application_schedule_prerequisites
|
89
|
-
if application_schedule.try(:marked_for_destruction?)
|
90
|
-
prerequisites = Naf::ApplicationSchedulePrerequisite.
|
91
|
-
where(prerequisite_application_schedule_id: application_schedule.id).all
|
92
|
-
unless prerequisites.blank?
|
93
|
-
errors.add(:base, "Cannot delete scheduler, because the following applications are referenced to it: " +
|
94
|
-
"#{prerequisites.map{ |pre| pre.application_schedule.title }.join(', ') }")
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
87
|
|
99
88
|
end
|
100
89
|
end
|
@@ -10,10 +10,14 @@ module Naf
|
|
10
10
|
:priority,
|
11
11
|
:visible,
|
12
12
|
:enabled,
|
13
|
-
:run_start_minute,
|
14
13
|
:application_run_group_limit,
|
14
|
+
:application_run_group_quantum,
|
15
15
|
:application_schedule_prerequisites_attributes,
|
16
|
-
:enqueue_backlogs
|
16
|
+
:enqueue_backlogs,
|
17
|
+
:run_interval_style_id,
|
18
|
+
:application,
|
19
|
+
:run_interval_style,
|
20
|
+
:application_run_group_restriction
|
17
21
|
|
18
22
|
SCHEDULES_LOCK_ID = 0
|
19
23
|
|
@@ -25,6 +29,8 @@ module Naf
|
|
25
29
|
class_name: '::Naf::Application'
|
26
30
|
belongs_to :application_run_group_restriction,
|
27
31
|
class_name: '::Naf::ApplicationRunGroupRestriction'
|
32
|
+
belongs_to :run_interval_style,
|
33
|
+
class_name: '::Naf::RunIntervalStyle'
|
28
34
|
has_many :application_schedule_affinity_tabs,
|
29
35
|
class_name: '::Naf::ApplicationScheduleAffinityTab',
|
30
36
|
dependent: :destroy
|
@@ -44,7 +50,10 @@ module Naf
|
|
44
50
|
# *** Validations ***
|
45
51
|
#++++++++++++++++++++
|
46
52
|
|
47
|
-
validates :application_run_group_restriction_id,
|
53
|
+
validates :application_run_group_restriction_id,
|
54
|
+
:run_interval_style_id,
|
55
|
+
:application_id,
|
56
|
+
:priority, presence: true
|
48
57
|
validates :priority, numericality: {
|
49
58
|
only_integer: true,
|
50
59
|
greater_than: -2147483648,
|
@@ -56,12 +65,6 @@ module Naf
|
|
56
65
|
less_than: 2147483647,
|
57
66
|
allow_blank: true
|
58
67
|
}
|
59
|
-
validates :run_start_minute, numericality: {
|
60
|
-
only_integer: true,
|
61
|
-
greater_than_or_equal_to: 0,
|
62
|
-
less_than: 24*60,
|
63
|
-
allow_blank: true
|
64
|
-
}
|
65
68
|
validates :run_interval, numericality: {
|
66
69
|
only_integer: true,
|
67
70
|
greater_than_or_equal_to: 0,
|
@@ -71,9 +74,8 @@ module Naf
|
|
71
74
|
|
72
75
|
before_save :check_blank_values
|
73
76
|
validate :visible_enabled_check
|
74
|
-
validate :run_interval_at_time_check
|
75
|
-
validate :enabled_application_id_unique
|
76
77
|
validate :prerequisite_application_schedule_id_uniqueness
|
78
|
+
validate :run_interval_check
|
77
79
|
|
78
80
|
#--------------------
|
79
81
|
# *** Delegations ***
|
@@ -94,17 +96,102 @@ module Naf
|
|
94
96
|
unlock_record(SCHEDULES_LOCK_ID)
|
95
97
|
end
|
96
98
|
|
97
|
-
|
98
|
-
|
99
|
+
# find the exact based schedules that should be queued
|
100
|
+
# select anything that
|
101
|
+
# isn't currently running (or queued) AND
|
102
|
+
# hasn't run since run_interval AND
|
103
|
+
# should have been run by now
|
104
|
+
def self.exact_schedules(time, not_finished_applications, application_last_runs)
|
105
|
+
custom_current_time = time.to_date + time.strftime('%H').to_i.hours + time.strftime('%M').to_i.minutes
|
106
|
+
schedules = ::Naf::ApplicationSchedule.
|
107
|
+
joins(:run_interval_style).
|
108
|
+
where("#{Naf.schema_name}.run_interval_styles.name IN (?)", ['at beginning of day', 'at beginning of hour']).
|
109
|
+
enabled.select do |schedule|
|
110
|
+
|
111
|
+
interval_time = time.to_date
|
112
|
+
if schedule.run_interval_style.name == 'at beginning of day'
|
113
|
+
interval_time += schedule.run_interval.minutes
|
114
|
+
elsif schedule.run_interval_style.name == 'at beginning of hour'
|
115
|
+
interval_time += time.strftime('%H').to_i.hours + schedule.run_interval.minutes
|
116
|
+
end
|
117
|
+
|
118
|
+
(not_finished_applications[schedule.id].nil? &&
|
119
|
+
(application_last_runs[schedule.id].nil? ||
|
120
|
+
(interval_time >= application_last_runs[schedule.id].finished_at)
|
121
|
+
) &&
|
122
|
+
(custom_current_time - interval_time) == 0.seconds
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
schedules
|
99
127
|
end
|
100
128
|
|
101
|
-
|
102
|
-
|
129
|
+
# find the interval based schedules that should be queued
|
130
|
+
# select anything that isn't currently running and completed
|
131
|
+
# running more than run_interval minutes ago
|
132
|
+
def self.relative_schedules(time, not_finished_applications, application_last_runs)
|
133
|
+
schedules = ::Naf::ApplicationSchedule.
|
134
|
+
joins(:run_interval_style).
|
135
|
+
where("#{Naf.schema_name}.run_interval_styles.name = ?", 'after previous run').
|
136
|
+
enabled.select do |schedule|
|
137
|
+
|
138
|
+
(not_finished_applications[schedule.id].nil? &&
|
139
|
+
(application_last_runs[schedule.id].nil? ||
|
140
|
+
(time - application_last_runs[schedule.id].finished_at) > schedule.run_interval.minutes
|
141
|
+
)
|
142
|
+
)
|
143
|
+
end
|
144
|
+
|
145
|
+
schedules
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.constant_schedules
|
149
|
+
::Naf::ApplicationSchedule.
|
150
|
+
joins(:run_interval_style).
|
151
|
+
where("#{Naf.schema_name}.run_interval_styles.name = ?", 'keep running').
|
152
|
+
enabled
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.enabled
|
156
|
+
where(enabled: true)
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.should_be_queued
|
160
|
+
current_time = Time.zone.now
|
161
|
+
# Applications that are still running
|
162
|
+
not_finished_applications = ::Naf::HistoricalJob.
|
163
|
+
queued_between(current_time - Naf::HistoricalJob::JOB_STALE_TIME, current_time).
|
164
|
+
where("finished_at IS NULL AND request_to_terminate = false").
|
165
|
+
find_all{ |job| job.application_schedule_id.present? }.
|
166
|
+
index_by{ |job| job.application_schedule_id }
|
167
|
+
|
168
|
+
# Last ran job for each application
|
169
|
+
application_last_runs = ::Naf::HistoricalJob.application_last_runs.
|
170
|
+
index_by{ |job| job.application_schedule_id }
|
171
|
+
|
172
|
+
relative_schedules = ::Naf::ApplicationSchedule.
|
173
|
+
relative_schedules(current_time, not_finished_applications, application_last_runs)
|
174
|
+
exact_schedules = ::Naf::ApplicationSchedule.
|
175
|
+
exact_schedules(current_time, not_finished_applications, application_last_runs)
|
176
|
+
constant_schedules = ::Naf::ApplicationSchedule.constant_schedules
|
177
|
+
|
178
|
+
foreman = ::Logical::Naf::ConstructionZone::Foreman.new
|
179
|
+
return (relative_schedules + exact_schedules + constant_schedules).select do |schedule|
|
180
|
+
affinities = []
|
181
|
+
schedule.affinities.each do |affinity|
|
182
|
+
affinities << { affinity_id: affinity.id }
|
183
|
+
end
|
184
|
+
|
185
|
+
schedule.enqueue_backlogs || !foreman.limited_by_run_group?(schedule.application_run_group_restriction,
|
186
|
+
schedule.application_run_group_name,
|
187
|
+
schedule.application_run_group_limit,
|
188
|
+
affinities)
|
189
|
+
end
|
103
190
|
end
|
104
191
|
|
105
|
-
def self.
|
192
|
+
def self.pickleables
|
106
193
|
# check the application is deleted
|
107
|
-
self.where(:
|
194
|
+
self.where(deleted: false)
|
108
195
|
end
|
109
196
|
|
110
197
|
#-------------------------
|
@@ -124,11 +211,7 @@ module Naf
|
|
124
211
|
end
|
125
212
|
components << "id: #{id}"
|
126
213
|
components << "\"#{application.title}\""
|
127
|
-
|
128
|
-
components << "start at: #{"%02d" % (run_start_minute/60)}:#{"%02d" % (run_start_minute%60)}"
|
129
|
-
else
|
130
|
-
components << "start every: #{run_interval} minutes"
|
131
|
-
end
|
214
|
+
components << ::Logical::Naf::ApplicationSchedule.new(self).display
|
132
215
|
|
133
216
|
return "::Naf::ApplicationSchedule<#{components.join(', ')}>"
|
134
217
|
end
|
@@ -140,31 +223,22 @@ module Naf
|
|
140
223
|
end
|
141
224
|
end
|
142
225
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
226
|
+
# When rolling back from Naf v2.1 to v2.0, check whether run_interval
|
227
|
+
# or run_start_minute is nil. Otherwise, just check the presence of
|
228
|
+
# run_interval.
|
229
|
+
def run_interval_check
|
230
|
+
if self.attributes.keys.include?('run_start_minute')
|
231
|
+
if !run_start_minute.present? && !run_interval.present?
|
232
|
+
errors.add(:run_interval, "or run_start_minute must be nil")
|
233
|
+
errors.add(:run_start_minute, "or run_interval must be nil")
|
234
|
+
end
|
148
235
|
else
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
num_collisions = self.class.count(conditions: conditions)
|
153
|
-
errors.add(:application_id, "is enabled and has already been taken") if num_collisions > 0
|
154
|
-
end
|
155
|
-
|
156
|
-
def run_interval_at_time_check
|
157
|
-
unless (run_start_minute.blank? || run_interval.blank?)
|
158
|
-
errors.add(:run_interval, "or Run start minute must be nil")
|
159
|
-
errors.add(:run_start_minute, "or Run interval must be nil")
|
236
|
+
if !run_interval.present?
|
237
|
+
errors.add(:run_interval, "must be present")
|
238
|
+
end
|
160
239
|
end
|
161
240
|
end
|
162
241
|
|
163
|
-
def self.pickleables(pickler)
|
164
|
-
self.joins(:application).
|
165
|
-
where("#{::Naf.schema_name}.applications.deleted IS FALSE")
|
166
|
-
end
|
167
|
-
|
168
242
|
private
|
169
243
|
|
170
244
|
def prerequisite_application_schedule_id_uniqueness
|
@@ -15,6 +15,7 @@ module Naf
|
|
15
15
|
|
16
16
|
# Protect from mass-assignment issue
|
17
17
|
attr_accessible :application_id,
|
18
|
+
:application_schedule_id,
|
18
19
|
:application_type_id,
|
19
20
|
:command,
|
20
21
|
:application_run_group_restriction_id,
|
@@ -44,6 +45,8 @@ module Naf
|
|
44
45
|
# *** Associations ***
|
45
46
|
#+++++++++++++++++++++
|
46
47
|
|
48
|
+
belongs_to :application_schedule,
|
49
|
+
class_name: '::Naf::ApplicationSchedule'
|
47
50
|
belongs_to :application_type,
|
48
51
|
class_name: '::Naf::ApplicationType'
|
49
52
|
belongs_to :started_on_machine,
|
@@ -175,9 +178,9 @@ module Naf
|
|
175
178
|
end
|
176
179
|
|
177
180
|
def self.application_last_runs
|
178
|
-
where("
|
179
|
-
group("
|
180
|
-
select("
|
181
|
+
where("application_schedule_id IS NOT NULL").
|
182
|
+
group("application_schedule_id").
|
183
|
+
select("application_schedule_id, MAX(finished_at) AS finished_at").
|
181
184
|
reject{ |job| job.finished_at.nil? }
|
182
185
|
end
|
183
186
|
|