maintenance_tasks 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +316 -0
- data/Rakefile +29 -0
- data/app/controllers/maintenance_tasks/application_controller.rb +26 -0
- data/app/controllers/maintenance_tasks/runs_controller.rb +44 -0
- data/app/controllers/maintenance_tasks/tasks_controller.rb +42 -0
- data/app/helpers/maintenance_tasks/application_helper.rb +67 -0
- data/app/helpers/maintenance_tasks/task_helper.rb +110 -0
- data/app/jobs/maintenance_tasks/task_job.rb +98 -0
- data/app/models/maintenance_tasks/application_record.rb +10 -0
- data/app/models/maintenance_tasks/progress.rb +74 -0
- data/app/models/maintenance_tasks/run.rb +180 -0
- data/app/models/maintenance_tasks/runner.rb +52 -0
- data/app/models/maintenance_tasks/task_data.rb +137 -0
- data/app/models/maintenance_tasks/ticker.rb +58 -0
- data/app/tasks/maintenance_tasks/task.rb +83 -0
- data/app/validators/maintenance_tasks/run_status_validator.rb +86 -0
- data/app/views/layouts/maintenance_tasks/_navbar.html.erb +11 -0
- data/app/views/layouts/maintenance_tasks/application.html.erb +54 -0
- data/app/views/maintenance_tasks/runs/_info.html.erb +10 -0
- data/app/views/maintenance_tasks/runs/_run.html.erb +11 -0
- data/app/views/maintenance_tasks/runs/index.html.erb +15 -0
- data/app/views/maintenance_tasks/runs/info/_cancelled.html.erb +4 -0
- data/app/views/maintenance_tasks/runs/info/_cancelling.html.erb +1 -0
- data/app/views/maintenance_tasks/runs/info/_enqueued.html.erb +1 -0
- data/app/views/maintenance_tasks/runs/info/_errored.html.erb +25 -0
- data/app/views/maintenance_tasks/runs/info/_interrupted.html.erb +1 -0
- data/app/views/maintenance_tasks/runs/info/_paused.html.erb +8 -0
- data/app/views/maintenance_tasks/runs/info/_pausing.html.erb +1 -0
- data/app/views/maintenance_tasks/runs/info/_running.html.erb +7 -0
- data/app/views/maintenance_tasks/runs/info/_succeeded.html.erb +5 -0
- data/app/views/maintenance_tasks/tasks/_task.html.erb +8 -0
- data/app/views/maintenance_tasks/tasks/actions/_cancelled.html.erb +1 -0
- data/app/views/maintenance_tasks/tasks/actions/_cancelling.html.erb +4 -0
- data/app/views/maintenance_tasks/tasks/actions/_enqueued.html.erb +2 -0
- data/app/views/maintenance_tasks/tasks/actions/_errored.html.erb +1 -0
- data/app/views/maintenance_tasks/tasks/actions/_interrupted.html.erb +2 -0
- data/app/views/maintenance_tasks/tasks/actions/_new.html.erb +1 -0
- data/app/views/maintenance_tasks/tasks/actions/_paused.html.erb +2 -0
- data/app/views/maintenance_tasks/tasks/actions/_pausing.html.erb +2 -0
- data/app/views/maintenance_tasks/tasks/actions/_running.html.erb +2 -0
- data/app/views/maintenance_tasks/tasks/actions/_succeeded.html.erb +1 -0
- data/app/views/maintenance_tasks/tasks/index.html.erb +22 -0
- data/app/views/maintenance_tasks/tasks/show.html.erb +25 -0
- data/config/routes.rb +19 -0
- data/db/migrate/20201211151756_create_maintenance_tasks_runs.rb +22 -0
- data/exe/maintenance_tasks +13 -0
- data/lib/generators/maintenance_tasks/install_generator.rb +22 -0
- data/lib/generators/maintenance_tasks/task_generator.rb +74 -0
- data/lib/generators/maintenance_tasks/templates/task.rb.tt +21 -0
- data/lib/generators/maintenance_tasks/templates/task_spec.rb.tt +14 -0
- data/lib/generators/maintenance_tasks/templates/task_test.rb.tt +12 -0
- data/lib/maintenance_tasks.rb +58 -0
- data/lib/maintenance_tasks/cli.rb +40 -0
- data/lib/maintenance_tasks/engine.rb +28 -0
- data/lib/maintenance_tasks/integrations/bugsnag_handler.rb +4 -0
- data/lib/tasks/maintenance_tasks_tasks.rake +5 -0
- metadata +187 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d591c21fb76efef72185bf183b0f4b0bc0a5b97a2606e083f6fbfc11325e029b
|
4
|
+
data.tar.gz: 6195855524490ebc663f1970134b6aed2abe7887345b6c17d2d0dccea9d81fb4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3fa1e498d50830521cabeb8babda612a9762438cd1fdccf5cecf776ae069ec85cdbaedbded208bdfc1e72479b69ffb76a39b999e4c2f8d810811dc8672f0b5cc
|
7
|
+
data.tar.gz: 2d9aa8e0c134baa3864f2cd5e9fe53944a5d13a89c21caab26dcd394b3eb8845af62397f2d1be880c9f27d71cbe77db3865d16c7da53f0b70cc64cc7bf4153a4
|
data/README.md
ADDED
@@ -0,0 +1,316 @@
|
|
1
|
+
# MaintenanceTasks
|
2
|
+
|
3
|
+
A Rails engine for queuing and managing maintenance tasks.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
* [Installation](#installation)
|
7
|
+
* [Usage](#usage)
|
8
|
+
* [Creating a Task](#creating-a-task)
|
9
|
+
* [Considerations when writing Tasks](#considerations-when-writing-tasks)
|
10
|
+
* [Writing tests for a Task](#writing-tests-for-a-task)
|
11
|
+
* [Running a Task](#running-a-task)
|
12
|
+
* [Monitoring your Task's status](#monitoring-your-tasks-status)
|
13
|
+
* [How Maintenance Tasks runs a Task](#how-maintenance-tasks-runs-a-task)
|
14
|
+
* [Help! My Task is stuck](#help-my-task-is-stuck)
|
15
|
+
* [Configuring the gem](#configuring-the-gem)
|
16
|
+
* [Customizing the error handler](#customizing-the-error-handler)
|
17
|
+
* [Customizing the maintenance tasks module](#customizing-the-maintenance-tasks-module)
|
18
|
+
* [Customizing the underlying job class](#customizing-the-underlying-job-class)
|
19
|
+
* [Customizing the rate at which task progress gets updated](#customizing-the-rate-at-which-task-progress-gets-updated)
|
20
|
+
* [Upgrading](#upgrading)
|
21
|
+
* [Contributing](#contributing)
|
22
|
+
* [Releasing new versions](#releasing-new-versions)
|
23
|
+
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
gem 'maintenance_tasks'
|
30
|
+
```
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
```bash
|
35
|
+
$ bundle
|
36
|
+
$ rails generate maintenance_tasks:install
|
37
|
+
```
|
38
|
+
|
39
|
+
The generator creates and runs a migration to add the necessary table to your
|
40
|
+
database. It also mounts Maintenance Tasks in your `config/routes.rb`. By
|
41
|
+
default the web UI can be accessed in the new `/maintenance_tasks` path.
|
42
|
+
|
43
|
+
## Usage
|
44
|
+
|
45
|
+
### Creating a Task
|
46
|
+
|
47
|
+
A generator is provided to create tasks. Generate a new task by running:
|
48
|
+
|
49
|
+
```bash
|
50
|
+
$ rails generate maintenance_tasks:task update_posts
|
51
|
+
```
|
52
|
+
|
53
|
+
This creates the task file `app/tasks/maintenance/update_posts_task.rb`.
|
54
|
+
|
55
|
+
The generated task is a subclass of `MaintenanceTasks::Task` that implements:
|
56
|
+
|
57
|
+
* `collection`: return an Active Record Relation or an Array to be iterated
|
58
|
+
over.
|
59
|
+
* `process`: do the work of your maintenance task on a single record
|
60
|
+
* `count`: return the number of rows that will be iterated over (optional, to be
|
61
|
+
able to show progress)
|
62
|
+
|
63
|
+
Example:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# app/tasks/maintenance/update_posts_task.rb
|
67
|
+
module Maintenance
|
68
|
+
class UpdatePostsTask < MaintenanceTasks::Task
|
69
|
+
def collection
|
70
|
+
Post.all
|
71
|
+
end
|
72
|
+
|
73
|
+
def count
|
74
|
+
collection.count
|
75
|
+
end
|
76
|
+
|
77
|
+
def process(post)
|
78
|
+
post.update!(content: 'New content!')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
```
|
83
|
+
|
84
|
+
#### Considerations when writing Tasks
|
85
|
+
|
86
|
+
MaintenanceTasks relies on the queue adapter configured for your application to
|
87
|
+
run the job which is processing your Task. The guidelines for writing Task may
|
88
|
+
depend on the queue adapter but in general, you should follow these rules:
|
89
|
+
|
90
|
+
* Duration of `Task#process`: processing a single element of the collection
|
91
|
+
should take less than 25 seconds, or the duration set as a timeout for Sidekiq
|
92
|
+
or the queue adapter configured in your application. It allows the Task to be
|
93
|
+
safely interrupted and resumed.
|
94
|
+
* Idempotency of `Task#process`: it should be safe to run `process` multiple
|
95
|
+
times for the same element of the collection. Read more in [this Sidekiq best
|
96
|
+
practice][sidekiq-idempotent]. It's important if the Task errors and you run
|
97
|
+
it again, because the same element that errored the Task may well be processed
|
98
|
+
again. It especially matters in the situation described above, when the
|
99
|
+
iteration duration exceeds the timeout: if the job is re-enqueued, multiple
|
100
|
+
elements may be processed again.
|
101
|
+
|
102
|
+
[sidekiq-idempotent]: https://github.com/mperham/sidekiq/wiki/Best-Practices#2-make-your-job-idempotent-and-transactional
|
103
|
+
|
104
|
+
### Writing tests for a Task
|
105
|
+
|
106
|
+
The task generator will also create a test file for your task in the folder
|
107
|
+
`test/tasks/maintenance/`. At a minimum, it's recommended that the `#process`
|
108
|
+
method in your task be tested. You may also want to test the `#collection` and
|
109
|
+
`#count` methods for your task if they are sufficiently complex.
|
110
|
+
|
111
|
+
Example:
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# test/tasks/maintenance/update_posts_task_test.rb
|
115
|
+
|
116
|
+
require 'test_helper'
|
117
|
+
|
118
|
+
module Maintenance
|
119
|
+
class UpdatePostsTaskTest < ActiveSupport::TestCase
|
120
|
+
test "#process performs a task iteration" do
|
121
|
+
post = Post.new
|
122
|
+
|
123
|
+
Maintenance::UpdatePostsTask.new.process(post)
|
124
|
+
|
125
|
+
assert_equal 'New content!', post.content
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
### Running a Task
|
132
|
+
|
133
|
+
You can run your new Task by accessing the Web UI and clicking on "Run".
|
134
|
+
|
135
|
+
Alternatively, you can run your Task in the command line:
|
136
|
+
|
137
|
+
```bash
|
138
|
+
$ bundle exec maintenance_tasks perform Maintenance::UpdatePostsTask
|
139
|
+
```
|
140
|
+
|
141
|
+
You can also run a Task in Ruby by sending `run` with a Task name to a Runner
|
142
|
+
instance:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
MaintenanceTasks::Runner.new.run('Maintenance::UpdatePostsTask')
|
146
|
+
```
|
147
|
+
|
148
|
+
### Monitoring your Task's status
|
149
|
+
|
150
|
+
The web UI will provide updates on the status of your Task. Here are the states
|
151
|
+
a Task can be in:
|
152
|
+
|
153
|
+
* **new**: A Task that has not yet been run.
|
154
|
+
* **enqueued**: A Task that is waiting to be performed after a user has
|
155
|
+
instructed it to run.
|
156
|
+
* **running**: A Task that is currently being performed by a job worker.
|
157
|
+
* **pausing**: A Task that was paused by a user, but needs to finish work
|
158
|
+
before stopping.
|
159
|
+
* **paused**: A Task that was paused by a user and is not performing. It can be
|
160
|
+
resumed.
|
161
|
+
* **interrupted**: A Task that has been momentarily interrupted by the job
|
162
|
+
infrastructure.
|
163
|
+
* **cancelling**: A Task that was cancelled by a user, but needs to finish work
|
164
|
+
before stopping.
|
165
|
+
* **cancelled**: A Task that was cancelled by a user and is not performing. It
|
166
|
+
cannot be resumed.
|
167
|
+
* **succeeded**: A Task that finished successfully.
|
168
|
+
* **errored**: A Task that encountered an unhandled exception while performing.
|
169
|
+
|
170
|
+
### How Maintenance Tasks runs a Task
|
171
|
+
|
172
|
+
Maintenance tasks can be running for a long time, and the purpose of the gem is
|
173
|
+
to make it easy to continue running tasks through deploys, [Kubernetes Pod
|
174
|
+
scheduling][k8s-scheduling], [Heroku dyno restarts][heroku-cycles] or other
|
175
|
+
infrastructure or code changes.
|
176
|
+
|
177
|
+
[k8s-scheduling]: https://kubernetes.io/docs/concepts/scheduling-eviction/
|
178
|
+
[heroku-cycles]: https://www.heroku.com/dynos/lifecycle
|
179
|
+
|
180
|
+
This means a Task can safely be interrupted, re-enqueued and resumed without any
|
181
|
+
intervention at the end of an iteration, after the `process` method returns.
|
182
|
+
|
183
|
+
By default, a running Task will be interrupted after running for more 5 minutes.
|
184
|
+
This is [configured in the `job-iteration` gem][max-job-runtime] and can be
|
185
|
+
tweaked in an initializer if necessary.
|
186
|
+
|
187
|
+
[max-job-runtime]: https://github.com/Shopify/job-iteration/blob/master/guides/best-practices.md#max-job-runtime
|
188
|
+
|
189
|
+
Running tasks will also be interrupted and re-enqueued when needed. For example
|
190
|
+
[when Sidekiq workers shuts down for a deploy][sidekiq-deploy]:
|
191
|
+
|
192
|
+
[sidekiq-deploy]: https://github.com/mperham/sidekiq/wiki/Deployment
|
193
|
+
|
194
|
+
* When Sidekiq receives a TSTP or TERM signal, it will consider itself to be
|
195
|
+
stopping.
|
196
|
+
* When Sidekiq is stopping, JobIteration stops iterating over the enumerator.
|
197
|
+
The position in the iteration is saved, a new job is enqueued to resume work,
|
198
|
+
and the Task is marked as interrupted.
|
199
|
+
|
200
|
+
When Sidekiq is stopping, it will give workers 25 seconds to finish before
|
201
|
+
forcefully terminating them (this is the default but can be configured with the
|
202
|
+
`--timeout` option). Before the worker threads are terminated, Sidekiq will try
|
203
|
+
to re-enqueue the job so your Task will be resumed. However, the position in the
|
204
|
+
collection won't be persisted so at least one iteration may run again.
|
205
|
+
|
206
|
+
#### Help! My Task is stuck
|
207
|
+
|
208
|
+
Finally, if the queue adapter configured for your application doesn't have this
|
209
|
+
property, or if Sidekiq crashes, is forcefully terminated, or is unable to
|
210
|
+
re-enqueue the jobs that were in progress, the Task may be in a seemingly stuck
|
211
|
+
situation where it appears to be running but is not. In that situation, pausing
|
212
|
+
or cancelling it will not result in the Task being paused or cancelled, as the
|
213
|
+
Task will get stuck in a state of `pausing` or `cancelling`. As a work-around,
|
214
|
+
if a Task is `cancelling` for more than 5 minutes, you will be able to cancel it
|
215
|
+
for good, which will just mark it as cancelled, allowing you to run it again.
|
216
|
+
|
217
|
+
### Configuring the gem
|
218
|
+
|
219
|
+
There are a few configurable options for the gem. Custom configurations should
|
220
|
+
be placed in a `maintenance_tasks.rb` initializer.
|
221
|
+
|
222
|
+
#### Customizing the error handler
|
223
|
+
|
224
|
+
Exceptions raised while a Task is performing are rescued and information about
|
225
|
+
the error is persisted and visible in the UI.
|
226
|
+
|
227
|
+
If your application uses Bugsnag to monitor errors, the gem will automatically
|
228
|
+
notify Bugsnag of any errors raised while a Task is performing.
|
229
|
+
|
230
|
+
If you want to integrate with another exception monitoring service or customize
|
231
|
+
error handling, a callback can be defined:
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
# config/initializers/maintenance_tasks.rb
|
235
|
+
MaintenanceTasks.error_handler = ->(error) { MyErrorMonitor.notify(error) }
|
236
|
+
```
|
237
|
+
|
238
|
+
#### Customizing the maintenance tasks module
|
239
|
+
|
240
|
+
`MaintenanceTasks.tasks_module` can be configured to define the module in which
|
241
|
+
tasks will be placed.
|
242
|
+
|
243
|
+
```ruby
|
244
|
+
# config/initializers/maintenance_tasks.rb
|
245
|
+
MaintenanceTasks.tasks_module = 'TaskModule'
|
246
|
+
```
|
247
|
+
|
248
|
+
If no value is specified, it will default to `Maintenance`.
|
249
|
+
|
250
|
+
#### Customizing the underlying job class
|
251
|
+
|
252
|
+
`MaintenanceTasks.job` can be configured to define a Job class for your tasks to
|
253
|
+
use. This is a global configuration, so this Job class will be used across all
|
254
|
+
maintenance tasks in your application.
|
255
|
+
|
256
|
+
```ruby
|
257
|
+
# config/initializers/maintenance_tasks.rb
|
258
|
+
MaintenanceTasks.job = 'CustomTaskJob'
|
259
|
+
|
260
|
+
# app/jobs/custom_task_job.rb
|
261
|
+
class CustomTaskJob < MaintenanceTasks::TaskJob
|
262
|
+
queue_as :low_priority
|
263
|
+
end
|
264
|
+
```
|
265
|
+
|
266
|
+
The Job class **must inherit** from `MaintenanceTasks::TaskJob`.
|
267
|
+
|
268
|
+
Note that `retry_on` is not supported for custom Job
|
269
|
+
classes, so failed jobs cannot be retried.
|
270
|
+
|
271
|
+
#### Customizing the rate at which task progress gets updated
|
272
|
+
|
273
|
+
`MaintenanceTasks.ticker_delay` can be configured to customize how frequently
|
274
|
+
task progress gets persisted to the database. It can be a `Numeric` value or an
|
275
|
+
`ActiveSupport::Duration` value.
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
# config/initializers/maintenance_tasks.rb
|
279
|
+
MaintenanceTasks.ticker_delay = 2.seconds
|
280
|
+
```
|
281
|
+
|
282
|
+
If no value is specified, it will default to 1 second.
|
283
|
+
|
284
|
+
## Upgrading
|
285
|
+
|
286
|
+
Use bundler to check for and upgrade to newer versions. After installing a new
|
287
|
+
version, re-run the install command:
|
288
|
+
|
289
|
+
```bash
|
290
|
+
$ rails generate maintenance_tasks:install
|
291
|
+
```
|
292
|
+
|
293
|
+
This ensures that new migrations are installed and run as well.
|
294
|
+
|
295
|
+
## Contributing
|
296
|
+
|
297
|
+
Would you like to report an issue or contribute with code? We accept issues and
|
298
|
+
pull requests. You can find the contribution guidelines on
|
299
|
+
[CONTRIBUTING.md][contributing].
|
300
|
+
|
301
|
+
[contributing]: https://github.com/Shopify/maintenance_tasks/blob/main/.github/CONTRIBUTING.md
|
302
|
+
|
303
|
+
## Releasing new versions
|
304
|
+
|
305
|
+
This gem is published to packagecloud. The procedure to publish a new version:
|
306
|
+
|
307
|
+
* Update `spec.version` in `maintenance_tasks.gemspec`.
|
308
|
+
* Run `bundle install` to bump the `Gemfile.lock` version of the gem.
|
309
|
+
* Open a PR and merge on approval.
|
310
|
+
* Create a [release on GitHub][release] with a version number that matches the
|
311
|
+
version defined in the gemspec.
|
312
|
+
* Deploy via [Shipit][shipit] and see the new version on
|
313
|
+
<https://rubygems.org/gems/maintenance_tasks>.
|
314
|
+
|
315
|
+
[release]: https://help.github.com/articles/creating-releases/
|
316
|
+
[shipit]: https://shipit.shopify.io/shopify/maintenance_tasks/rubygems
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'rdoc/task'
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'MaintenanceTasks'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
|
18
|
+
load('rails/tasks/engine.rake')
|
19
|
+
|
20
|
+
load('rails/tasks/statistics.rake')
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rubocop/rake_task'
|
25
|
+
RuboCop::RakeTask.new
|
26
|
+
|
27
|
+
task(test: 'app:test')
|
28
|
+
task('test:system' => 'app:test:system')
|
29
|
+
task(default: ['db:setup', 'test', 'test:system', 'rubocop'])
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaintenanceTasks
|
4
|
+
# Base class for all controllers used by this engine.
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class ApplicationController < ActionController::Base
|
8
|
+
include Pagy::Backend
|
9
|
+
|
10
|
+
BULMA_CDN = 'https://cdn.jsdelivr.net'
|
11
|
+
|
12
|
+
content_security_policy do |policy|
|
13
|
+
policy.style_src(BULMA_CDN)
|
14
|
+
policy.frame_ancestors(:self)
|
15
|
+
end
|
16
|
+
|
17
|
+
before_action do
|
18
|
+
request.content_security_policy_nonce_generator ||=
|
19
|
+
->(_request) { SecureRandom.base64(16) }
|
20
|
+
request.content_security_policy_nonce_directives = ['style-src']
|
21
|
+
end
|
22
|
+
|
23
|
+
protect_from_forgery with: :exception
|
24
|
+
end
|
25
|
+
private_constant :ApplicationController
|
26
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaintenanceTasks
|
4
|
+
# Class communicates with the Run model to persist info related to task runs.
|
5
|
+
# It defines actions for creating and pausing runs.
|
6
|
+
#
|
7
|
+
# @api private
|
8
|
+
class RunsController < ApplicationController
|
9
|
+
before_action :set_run, except: :index
|
10
|
+
|
11
|
+
# Shows a full list of Runs.
|
12
|
+
def index
|
13
|
+
query = Run.all.order(id: :desc)
|
14
|
+
if params[:task_name].present?
|
15
|
+
task_name = Run.sanitize_sql_like(params[:task_name])
|
16
|
+
query = query.where('task_name LIKE ?', "%#{task_name}%")
|
17
|
+
end
|
18
|
+
@pagy, @runs = pagy(query)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Updates a Run status to paused.
|
22
|
+
def pause
|
23
|
+
@run.pausing!
|
24
|
+
redirect_to(task_path(@run.task_name))
|
25
|
+
rescue ActiveRecord::RecordInvalid => error
|
26
|
+
redirect_to(task_path(@run.task_name), alert: error.message)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Updates a Run status to cancelling.
|
30
|
+
def cancel
|
31
|
+
@run.cancel
|
32
|
+
redirect_to(task_path(@run.task_name))
|
33
|
+
rescue ActiveRecord::RecordInvalid => error
|
34
|
+
redirect_to(task_path(@run.task_name), alert: error.message)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def set_run
|
40
|
+
@run = Run.find(params.fetch(:id))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
private_constant :RunsController
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MaintenanceTasks
|
4
|
+
# Class handles rendering the maintenance_tasks page in the host application.
|
5
|
+
# It makes data about available, enqueued, performing, and completed
|
6
|
+
# tasks accessible to the views so it can be displayed in the UI.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class TasksController < ApplicationController
|
10
|
+
before_action :set_refresh, only: [:index, :show]
|
11
|
+
|
12
|
+
# Renders the maintenance_tasks/tasks page, displaying
|
13
|
+
# available tasks to users, grouped by category.
|
14
|
+
def index
|
15
|
+
@available_tasks = TaskData.available_tasks.group_by(&:category)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Renders the page responsible for providing Task actions to users.
|
19
|
+
# Shows running and completed instances of the Task.
|
20
|
+
def show
|
21
|
+
@task = TaskData.find(params.fetch(:id))
|
22
|
+
@pagy, @previous_runs = pagy(@task.previous_runs)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Runs a given Task and redirects to the Task page.
|
26
|
+
def run
|
27
|
+
task = Runner.new.run(name: params.fetch(:id))
|
28
|
+
redirect_to(task_path(task))
|
29
|
+
rescue ActiveRecord::RecordInvalid => error
|
30
|
+
redirect_to(task_path(error.record.task_name), alert: error.message)
|
31
|
+
rescue Runner::EnqueuingError => error
|
32
|
+
redirect_to(task_path(error.run.task_name), alert: error.message)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def set_refresh
|
38
|
+
@refresh = 3
|
39
|
+
end
|
40
|
+
end
|
41
|
+
private_constant :TasksController
|
42
|
+
end
|