sidekiq-cron 1.12.0 → 2.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,22 +1,25 @@
1
1
  <header class='row'>
2
2
  <div class='col-sm-5 pull-left'>
3
- <h3><%= t('CronJobs') %></h3>
3
+ <h3>
4
+ <%= t('CronJobs') %>
5
+ <small><%= @current_namespace %></small>
6
+ </h3>
4
7
  </div>
5
8
  <div class='col-sm-7 pull-right' style="margin-top: 20px; margin-bottom: 10px;">
6
9
  <% if @cron_jobs.size > 0 %>
7
- <form action="<%= root_path %>cron/__all__/delete" method="post" class="pull-right">
10
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/delete" method="post" class="pull-right">
8
11
  <%= csrf_tag if respond_to?(:csrf_tag) %>
9
12
  <input class="btn btn-danger" type="submit" name="delete" value="<%= t('DeleteAll') %>" data-confirm="<%= t('AreYouSureDeleteCronJobs') %>" />
10
13
  </form>
11
- <form action="<%= root_path %>cron/__all__/disable" method="post" class="pull-right">
14
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/disable" method="post" class="pull-right">
12
15
  <%= csrf_tag if respond_to?(:csrf_tag) %>
13
16
  <input class="btn btn-warn" type="submit" name="enque" value="<%= t('DisableAll') %>" />
14
17
  </form>
15
- <form action="<%= root_path %>cron/__all__/enable" method="post" class="pull-right">
18
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/enable" method="post" class="pull-right">
16
19
  <%= csrf_tag if respond_to?(:csrf_tag) %>
17
20
  <input class="btn btn-warn" type="submit" name="enque" value="<%= t('EnableAll') %>" />
18
21
  </form>
19
- <form action="<%= root_path %>cron/__all__/enque" method="post" class="pull-right">
22
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/all/enque" method="post" class="pull-right">
20
23
  <%= csrf_tag if respond_to?(:csrf_tag) %>
21
24
  <input class="btn btn-warn" type="submit" name="enque" value="<%= t('EnqueueAll') %>" data-confirm="<%= t('AreYouSureEnqueueCronJobs') %>" />
22
25
  </form>
@@ -24,8 +27,24 @@
24
27
  </div>
25
28
  </header>
26
29
 
27
- <% if @cron_jobs.size > 0 %>
30
+ <!-- Namespaces -->
31
+ <div class='row'>
32
+ <div class="col-sm-12 summary_bar">
33
+ <ul class="list-unstyled summary row">
34
+ <% @namespaces.sort_by { |namespace| namespace[:name] } .each do |namespace| %>
35
+ <li class="col-sm-1">
36
+ <a href="<%= root_path %>cron/namespaces/<%= namespace[:name] %>">
37
+ <span class="count"><%= namespace[:count] %></span>
38
+ <span class="desc"><%= namespace[:name] %></span>
39
+ </a>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+ </div>
44
+ </div>
45
+ <!-- Namespaces -->
28
46
 
47
+ <% if @cron_jobs.size > 0 %>
29
48
  <table class="table table-hover table-bordered table-striped table-white">
30
49
  <thead>
31
50
  <th><%= t('Status') %></th>
@@ -41,7 +60,7 @@
41
60
  <tr>
42
61
  <td style="<%= style %>"><%= t job.status %></td>
43
62
  <td style="<%= style %>">
44
- <a href="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>">
63
+ <a href="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>" title="<%= job.description %>">
45
64
  <b style="<%= style %>"><%= job.name %></b>
46
65
  </a>
47
66
  <hr style="margin:3px;border:0;">
@@ -61,28 +80,28 @@
61
80
  <% end %>
62
81
  </small>
63
82
  </td>
64
- <td style="<%= style %>"><b><%= job.cron.gsub(" ", "&nbsp;") %></b></td>
83
+ <td style="<%= style %>"><b><%= job.human_cron %><br/><small><%= job.cron.gsub(" ", "&nbsp;") %></small></b></td>
65
84
  <td style="<%= style %>"><%= job.last_enqueue_time ? relative_time(job.last_enqueue_time) : "-" %></td>
66
85
  <td style="<%= style %>">
67
86
  <% if job.status == 'enabled' %>
68
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
87
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
69
88
  <%= csrf_tag if respond_to?(:csrf_tag) %>
70
89
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="enque" value="<%= t('EnqueueNow') %>" data-confirm="<%= t('AreYouSureEnqueueCronJob', :job => job.name) %>"/>
71
90
  </form>
72
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/disable" method="post">
91
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/disable" method="post">
73
92
  <%= csrf_tag if respond_to?(:csrf_tag) %>
74
93
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="disable" value="<%= t('Disable') %>"/>
75
94
  </form>
76
95
  <% else %>
77
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
96
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/enque" method="post">
78
97
  <%= csrf_tag if respond_to?(:csrf_tag) %>
79
98
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="enque" value="<%= t('EnqueueNow') %>"/>
80
99
  </form>
81
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/enable" method="post">
100
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/enable" method="post">
82
101
  <%= csrf_tag if respond_to?(:csrf_tag) %>
83
102
  <input class='btn btn-warn btn-xs pull-left' type="submit" name="enable" value="<%= t('Enable') %>"/>
84
103
  </form>
85
- <form action="<%= root_path %>cron/<%= CGI.escape(job.name).gsub('+', '%20') %>/delete" method="post">
104
+ <form action="<%= root_path %>cron/namespaces/<%= @current_namespace %>/jobs/<%= CGI.escape(job.name).gsub('+', '%20') %>/delete" method="post">
86
105
  <%= csrf_tag if respond_to?(:csrf_tag) %>
87
106
  <input class='btn btn-xs btn-danger pull-left' type="submit" name="delete" value="<%= t('Delete') %>" data-confirm="<%= t('AreYouSureDeleteCronJob', :job => job.name) %>"/>
88
107
  </form>
@@ -6,8 +6,8 @@
6
6
  </h3>
7
7
  </div>
8
8
  <div class="span col-sm-7 pull-right" style="margin-top: 20px; margin-bottom: 10px;">
9
- <% cron_job_path = "#{root_path}cron/#{CGI.escape(@job.name).gsub('+', '%20')}" %>
10
- <form action="<%= cron_job_path %>/enque?redirect=<%= cron_job_path %>" method="post">
9
+ <% cron_job_path = "#{root_path}cron/namespaces/#{@current_namespace}/jobs/#{CGI.escape(@job.name).gsub('+', '%20')}" %>
10
+ <form action="<%= cron_job_path %>/enque?redirect=<%= cron_job_path %>" class="pull-right" method="post">
11
11
  <%= csrf_tag if respond_to?(:csrf_tag) %>
12
12
  <input class="btn btn-warn pull-left" name="enque" type="submit" value="<%= t('EnqueueNow') %>" data-confirm="<%= t('AreYouSureEnqueueCronJob', :job => @job.name) %>" />
13
13
  </form>
@@ -39,6 +39,10 @@
39
39
  <th><%= t 'Name' %></th>
40
40
  <td><%= @job.name %></td>
41
41
  </tr>
42
+ <tr>
43
+ <th><%= t 'Namespace' %></th>
44
+ <td><%= @job.namespace %></td>
45
+ </tr>
42
46
  <tr>
43
47
  <th><%= t 'Description' %></th>
44
48
  <td><%= @job.description %></td>
@@ -6,60 +6,99 @@ module Sidekiq
6
6
 
7
7
  # Index page of cron jobs.
8
8
  app.get '/cron' do
9
- view_path = File.join(File.expand_path("..", __FILE__), "views")
9
+ view_path = File.join(File.expand_path("..", __FILE__), "views")
10
+
11
+ @current_namespace = 'default'
12
+
13
+ @namespaces = Sidekiq::Cron::Namespace.all_with_count
10
14
 
15
+ # Not passing namespace takes all the jobs from the default one.
11
16
  @cron_jobs = Sidekiq::Cron::Job.all
12
17
 
13
18
  render(:erb, File.read(File.join(view_path, "cron.erb")))
14
19
  end
15
20
 
21
+ app.get '/cron/namespaces/:name' do
22
+ view_path = File.join(File.expand_path("..", __FILE__), "views")
23
+
24
+ @current_namespace = route_params[:name]
25
+
26
+ @namespaces = Sidekiq::Cron::Namespace.all_with_count
27
+
28
+ @cron_jobs = Sidekiq::Cron::Job.all(@current_namespace)
29
+
30
+ render(:erb, File.read(File.join(view_path, "cron.erb")))
31
+ end
32
+
16
33
  # Display job detail + jid history.
17
- app.get '/cron/:name' do
34
+ app.get '/cron/namespaces/:namespace/jobs/:name' do
18
35
  view_path = File.join(File.expand_path("..", __FILE__), "views")
19
36
 
20
- @job = Sidekiq::Cron::Job.find(route_params[:name])
37
+ @current_namespace = route_params[:namespace]
38
+ @job_name = route_params[:name]
39
+
40
+ @namespaces = Sidekiq::Cron::Namespace.all_with_count
41
+
42
+ @job = Sidekiq::Cron::Job.find(@job_name, @current_namespace)
43
+
21
44
  if @job
22
45
  render(:erb, File.read(File.join(view_path, "cron_show.erb")))
23
46
  else
24
- redirect "#{root_path}cron"
47
+ redirect "#{root_path}cron/namespaces/#{route_params[:namespace]}"
25
48
  end
26
49
  end
27
50
 
51
+ # Enque all cron jobs.
52
+ app.post '/cron/namespaces/:namespace/all/enque' do
53
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:enque!)
54
+ redirect params['redirect'] || "#{root_path}cron/namespaces/#{route_params[:namespace]}"
55
+ end
56
+
28
57
  # Enqueue cron job.
29
- app.post '/cron/:name/enque' do
30
- if route_params[:name] === '__all__'
31
- Sidekiq::Cron::Job.all.each(&:enque!)
32
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
58
+ app.post '/cron/namespaces/:namespace/jobs/:name/enque' do
59
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
33
60
  job.enque!
34
61
  end
35
62
  redirect params['redirect'] || "#{root_path}cron"
36
63
  end
37
64
 
65
+ # Delete all schedules.
66
+ app.post '/cron/namespaces/:namespace/all/delete' do
67
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:destroy)
68
+ redirect "#{root_path}cron/namespaces/#{route_params[:namespace]}"
69
+ end
70
+
38
71
  # Delete schedule.
39
- app.post '/cron/:name/delete' do
40
- if route_params[:name] === '__all__'
41
- Sidekiq::Cron::Job.all.each(&:destroy)
42
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
72
+ app.post '/cron/namespaces/:namespace/jobs/:name/delete' do
73
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
43
74
  job.destroy
44
75
  end
45
76
  redirect "#{root_path}cron"
46
77
  end
47
78
 
79
+ # Enable all jobs.
80
+ app.post '/cron/namespaces/:namespace/all/enable' do
81
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:enable!)
82
+ redirect params['redirect'] || "#{root_path}cron/namespaces/#{route_params[:namespace]}"
83
+ end
84
+
48
85
  # Enable job.
49
- app.post '/cron/:name/enable' do
50
- if route_params[:name] === '__all__'
51
- Sidekiq::Cron::Job.all.each(&:enable!)
52
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
86
+ app.post '/cron/namespaces/:namespace/jobs/:name/enable' do
87
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
53
88
  job.enable!
54
89
  end
55
90
  redirect params['redirect'] || "#{root_path}cron"
56
91
  end
57
92
 
93
+ # Disable all jobs.
94
+ app.post '/cron/namespaces/:namespace/all/disable' do
95
+ Sidekiq::Cron::Job.all(route_params[:namespace]).each(&:disable!)
96
+ redirect params['redirect'] || "#{root_path}cron/namespaces/#{route_params[:namespace]}"
97
+ end
98
+
58
99
  # Disable job.
59
- app.post '/cron/:name/disable' do
60
- if route_params[:name] === '__all__'
61
- Sidekiq::Cron::Job.all.each(&:disable!)
62
- elsif job = Sidekiq::Cron::Job.find(route_params[:name])
100
+ app.post '/cron/namespaces/:namespace/jobs/:name/disable' do
101
+ if job = Sidekiq::Cron::Job.find(route_params[:name], route_params[:namespace])
63
102
  job.disable!
64
103
  end
65
104
  redirect params['redirect'] || "#{root_path}cron"
data/lib/sidekiq/cron.rb CHANGED
@@ -1,9 +1,53 @@
1
1
  require "sidekiq/cron/job"
2
+ require "sidekiq/cron/namespace"
2
3
  require "sidekiq/cron/poller"
3
4
  require "sidekiq/cron/launcher"
4
5
  require "sidekiq/cron/schedule_loader"
5
6
 
6
7
  module Sidekiq
7
8
  module Cron
9
+ class << self
10
+ attr_accessor :configuration
11
+ end
12
+
13
+ def self.configure
14
+ self.configuration ||= Configuration.new
15
+ yield(configuration) if block_given?
16
+ end
17
+
18
+ class Configuration
19
+ # The default namespace is used when no namespace is specified.
20
+ attr_accessor :default_namespace
21
+
22
+ # The parsing mode when using the natural language cron syntax from the `fugit` gem.
23
+ #
24
+ # :single -- use the first parsed cron line and ignore the rest (default)
25
+ # :strict -- raise an error if multiple cron lines are parsed from one string
26
+ attr_reader :natural_cron_parsing_mode
27
+
28
+ # The poller will not enqueue jobs that are late by more than this amount of seconds.
29
+ # Defaults to 60 seconds.
30
+ #
31
+ # This is useful when sidekiq (and sidekiq-cron) is not used in zero downtime deployments and
32
+ # when the deployment is done and sidekiq-cron starts to catch up, it will consider older
33
+ # jobs that missed their schedules during the deployment. E.g., jobs that run once a day.
34
+ attr_accessor :reschedule_grace_period
35
+
36
+ def initialize
37
+ @default_namespace = 'default'
38
+ @natural_cron_parsing_mode = :single
39
+ @reschedule_grace_period = 60
40
+ end
41
+
42
+ def natural_cron_parsing_mode=(mode)
43
+ unless %i[single strict].include?(mode)
44
+ raise ArgumentError, "Unknown natural cron parsing mode: #{mode.inspect}"
45
+ end
46
+
47
+ @natural_cron_parsing_mode = mode
48
+ end
49
+ end
8
50
  end
9
51
  end
52
+
53
+ Sidekiq::Cron.configure
data/sidekiq-cron.gemspec CHANGED
@@ -26,9 +26,10 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.required_ruby_version = ">= 2.7"
28
28
 
29
+ s.add_dependency("cronex", ">= 0.13.0")
29
30
  s.add_dependency("fugit", "~> 1.8")
30
- s.add_dependency("sidekiq", ">= 6")
31
31
  s.add_dependency("globalid", ">= 1.0.1")
32
+ s.add_dependency("sidekiq", ">= 6")
32
33
 
33
34
  s.add_development_dependency("minitest", "~> 5.15")
34
35
  s.add_development_dependency("mocha", "~> 2.1")
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-cron
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 2.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ondrej Bartas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-08 00:00:00.000000000 Z
11
+ date: 2024-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cronex
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.13.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: fugit
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -25,33 +39,33 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '1.8'
27
41
  - !ruby/object:Gem::Dependency
28
- name: sidekiq
42
+ name: globalid
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '6'
47
+ version: 1.0.1
34
48
  type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '6'
54
+ version: 1.0.1
41
55
  - !ruby/object:Gem::Dependency
42
- name: globalid
56
+ name: sidekiq
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
46
60
  - !ruby/object:Gem::Version
47
- version: 1.0.1
61
+ version: '6'
48
62
  type: :runtime
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - ">="
53
67
  - !ruby/object:Gem::Version
54
- version: 1.0.1
68
+ version: '6'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: minitest
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -170,11 +184,13 @@ files:
170
184
  - lib/sidekiq/cron/launcher.rb
171
185
  - lib/sidekiq/cron/locales/de.yml
172
186
  - lib/sidekiq/cron/locales/en.yml
187
+ - lib/sidekiq/cron/locales/id.yml
173
188
  - lib/sidekiq/cron/locales/it.yml
174
189
  - lib/sidekiq/cron/locales/ja.yml
175
190
  - lib/sidekiq/cron/locales/pt.yml
176
191
  - lib/sidekiq/cron/locales/ru.yml
177
192
  - lib/sidekiq/cron/locales/zh-CN.yml
193
+ - lib/sidekiq/cron/namespace.rb
178
194
  - lib/sidekiq/cron/poller.rb
179
195
  - lib/sidekiq/cron/schedule_loader.rb
180
196
  - lib/sidekiq/cron/support.rb
@@ -200,9 +216,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
200
216
  version: '2.7'
201
217
  required_rubygems_version: !ruby/object:Gem::Requirement
202
218
  requirements:
203
- - - ">="
219
+ - - ">"
204
220
  - !ruby/object:Gem::Version
205
- version: '0'
221
+ version: 1.3.1
206
222
  requirements: []
207
223
  rubygems_version: 3.4.10
208
224
  signing_key: