maintenance_on_steroids 0.2.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/CHANGELOG.md +267 -0
- data/LICENSE.txt +21 -0
- data/README.md +948 -0
- data/app/controllers/maintenance_on_steroids/application_controller.rb +66 -0
- data/app/controllers/maintenance_on_steroids/dashboard_controller.rb +18 -0
- data/app/controllers/maintenance_on_steroids/jobs_controller.rb +59 -0
- data/app/controllers/maintenance_on_steroids/runs_controller.rb +223 -0
- data/app/jobs/maintenance_on_steroids/run_job.rb +292 -0
- data/app/models/maintenance_on_steroids/application_record.rb +6 -0
- data/app/models/maintenance_on_steroids/artifact.rb +255 -0
- data/app/models/maintenance_on_steroids/run.rb +310 -0
- data/app/views/layouts/maintenance_on_steroids/application.html.erb +48 -0
- data/app/views/maintenance_on_steroids/dashboard/index.html.erb +163 -0
- data/app/views/maintenance_on_steroids/jobs/index.html.erb +35 -0
- data/app/views/maintenance_on_steroids/jobs/show.html.erb +107 -0
- data/app/views/maintenance_on_steroids/jobs/source.html.erb +79 -0
- data/app/views/maintenance_on_steroids/runs/new.html.erb +98 -0
- data/app/views/maintenance_on_steroids/runs/show.html.erb +410 -0
- data/app/views/maintenance_on_steroids/shared/_auto_refresh.html.erb +85 -0
- data/app/views/maintenance_on_steroids/shared/_javascript.html.erb +16 -0
- data/app/views/maintenance_on_steroids/shared/_pager.html.erb +20 -0
- data/app/views/maintenance_on_steroids/shared/_styles.html.erb +649 -0
- data/app/views/maintenance_on_steroids/shared/_task_list_item.html.erb +31 -0
- data/config/routes.rb +22 -0
- data/lib/generators/maintenance_on_steroids/install/install_generator.rb +56 -0
- data/lib/generators/maintenance_on_steroids/install/templates/create_maintenance_on_steroids_tables.rb.erb +56 -0
- data/lib/generators/maintenance_on_steroids/install/templates/initializer.rb +42 -0
- data/lib/generators/maintenance_on_steroids/job/job_generator.rb +17 -0
- data/lib/generators/maintenance_on_steroids/job/templates/job.rb.erb +30 -0
- data/lib/maintenance_on_steroids/about_dsl.rb +51 -0
- data/lib/maintenance_on_steroids/artifact_dsl.rb +82 -0
- data/lib/maintenance_on_steroids/artifacts_proxy.rb +205 -0
- data/lib/maintenance_on_steroids/callbacks_dsl.rb +61 -0
- data/lib/maintenance_on_steroids/csv_artifact.rb +88 -0
- data/lib/maintenance_on_steroids/engine.rb +26 -0
- data/lib/maintenance_on_steroids/form_dsl.rb +63 -0
- data/lib/maintenance_on_steroids/instrumentation.rb +33 -0
- data/lib/maintenance_on_steroids/job_dsl.rb +61 -0
- data/lib/maintenance_on_steroids/job_registry.rb +83 -0
- data/lib/maintenance_on_steroids/jsonb_artifact.rb +39 -0
- data/lib/maintenance_on_steroids/params_proxy.rb +93 -0
- data/lib/maintenance_on_steroids/task.rb +109 -0
- data/lib/maintenance_on_steroids/text_artifact.rb +74 -0
- data/lib/maintenance_on_steroids/version.rb +3 -0
- data/lib/maintenance_on_steroids.rb +187 -0
- metadata +125 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
require_relative "maintenance_on_steroids/version"
|
|
2
|
+
require_relative "maintenance_on_steroids/engine"
|
|
3
|
+
require_relative "maintenance_on_steroids/instrumentation"
|
|
4
|
+
require_relative "maintenance_on_steroids/job_registry"
|
|
5
|
+
require_relative "maintenance_on_steroids/form_dsl"
|
|
6
|
+
require_relative "maintenance_on_steroids/artifact_dsl"
|
|
7
|
+
require_relative "maintenance_on_steroids/job_dsl"
|
|
8
|
+
require_relative "maintenance_on_steroids/about_dsl"
|
|
9
|
+
require_relative "maintenance_on_steroids/callbacks_dsl"
|
|
10
|
+
require_relative "maintenance_on_steroids/params_proxy"
|
|
11
|
+
require_relative "maintenance_on_steroids/jsonb_artifact"
|
|
12
|
+
require_relative "maintenance_on_steroids/text_artifact"
|
|
13
|
+
require_relative "maintenance_on_steroids/csv_artifact"
|
|
14
|
+
require_relative "maintenance_on_steroids/artifacts_proxy"
|
|
15
|
+
require_relative "maintenance_on_steroids/task"
|
|
16
|
+
|
|
17
|
+
module MaintenanceOnSteroids
|
|
18
|
+
# Base controller class for the engine's controllers (resolved at load time).
|
|
19
|
+
# Set this in an initializer, before the engine's controllers are loaded.
|
|
20
|
+
mattr_accessor :parent_controller, default: "ActionController::Base"
|
|
21
|
+
|
|
22
|
+
# Maximum allowed size (in bytes) for file inputs uploaded when starting a run.
|
|
23
|
+
# Uploads are read into memory and stored in the artifacts table.
|
|
24
|
+
mattr_accessor :max_upload_size, default: 50 * 1024 * 1024
|
|
25
|
+
|
|
26
|
+
# Hard ceiling on a single stored artifact, in bytes.
|
|
27
|
+
#
|
|
28
|
+
# Artifacts are buffered in the worker's memory and stored in one database
|
|
29
|
+
# row. Save-time validation and text/CSV append checks reject oversized
|
|
30
|
+
# payloads, but this does not cap task allocations or aggregate process memory.
|
|
31
|
+
# For bulk export, write to object storage and keep only a reference here.
|
|
32
|
+
mattr_accessor :max_artifact_size, default: 64 * 1024 * 1024
|
|
33
|
+
|
|
34
|
+
# Proc to resolve the current user who triggered the run.
|
|
35
|
+
# Must return an object responding to #id (and optionally #email).
|
|
36
|
+
# Example:
|
|
37
|
+
# MaintenanceOnSteroids.current_user_resolver = -> { Current.user }
|
|
38
|
+
mattr_accessor :current_user_resolver, default: nil
|
|
39
|
+
|
|
40
|
+
# Proc to format user display in the UI.
|
|
41
|
+
# Receives the Run record and returns a string.
|
|
42
|
+
# Default shows email if present, otherwise "Type#ID".
|
|
43
|
+
# Examples:
|
|
44
|
+
# MaintenanceOnSteroids.user_display_formatter = ->(run) { run.user_email }
|
|
45
|
+
# MaintenanceOnSteroids.user_display_formatter = ->(run) {
|
|
46
|
+
# User.find(run.user_id).full_name rescue run.user_email
|
|
47
|
+
# }
|
|
48
|
+
mattr_accessor :user_display_formatter, default: nil
|
|
49
|
+
|
|
50
|
+
# --- Authentication / Authorization ---
|
|
51
|
+
|
|
52
|
+
# HTTP Basic Authentication.
|
|
53
|
+
# When enabled, users must provide credentials before any other checks run.
|
|
54
|
+
# Example:
|
|
55
|
+
# MaintenanceOnSteroids.http_basic_authentication_enabled = true
|
|
56
|
+
# MaintenanceOnSteroids.http_basic_authentication_user_name = "admin"
|
|
57
|
+
# MaintenanceOnSteroids.http_basic_authentication_password = Rails.application.credentials.maintenance_password
|
|
58
|
+
# Shipped placeholders. Leaving the password at its default is treated as
|
|
59
|
+
# *no* access control -- a dashboard behind "admin"/"secret" is not
|
|
60
|
+
# protected, and counting it as configured would make the most dangerous
|
|
61
|
+
# setup quieter than an unconfigured one.
|
|
62
|
+
DEFAULT_HTTP_BASIC_USER_NAME = "admin"
|
|
63
|
+
DEFAULT_HTTP_BASIC_PASSWORD = "secret"
|
|
64
|
+
|
|
65
|
+
mattr_accessor :http_basic_authentication_enabled, default: false
|
|
66
|
+
mattr_accessor :http_basic_authentication_user_name, default: DEFAULT_HTTP_BASIC_USER_NAME
|
|
67
|
+
mattr_accessor :http_basic_authentication_password, default: DEFAULT_HTTP_BASIC_PASSWORD
|
|
68
|
+
|
|
69
|
+
# Controller-based access verification.
|
|
70
|
+
# Receives the controller instance. Return true to allow, false to deny.
|
|
71
|
+
# Runs after HTTP Basic (if enabled). Denial renders 403.
|
|
72
|
+
# Example:
|
|
73
|
+
# MaintenanceOnSteroids.verify_access_proc = ->(controller) {
|
|
74
|
+
# controller.current_user&.admin?
|
|
75
|
+
# }
|
|
76
|
+
mattr_accessor :verify_access_proc, default: nil
|
|
77
|
+
|
|
78
|
+
# General-purpose authentication hook (instance_exec'd in controller context).
|
|
79
|
+
# Runs after HTTP Basic and before verify_access_proc. Use for custom auth flows.
|
|
80
|
+
# Example:
|
|
81
|
+
# MaintenanceOnSteroids.authentication = -> {
|
|
82
|
+
# authenticate_user!
|
|
83
|
+
# redirect_to main_app.root_path unless current_user.admin?
|
|
84
|
+
# }
|
|
85
|
+
mattr_accessor :authentication, default: nil
|
|
86
|
+
|
|
87
|
+
# Escape hatch for hosts that gate the dashboard somewhere this gem cannot
|
|
88
|
+
# see (reverse proxy, VPN, Rack middleware). Set it to boot in production
|
|
89
|
+
# without configuring any of the layers above -- an explicit, greppable
|
|
90
|
+
# statement that the exposure is intentional.
|
|
91
|
+
mattr_accessor :allow_insecure_dashboard, default: false
|
|
92
|
+
|
|
93
|
+
# Raised at boot when the dashboard would be reachable with no access
|
|
94
|
+
# control in production.
|
|
95
|
+
class InsecureDashboardError < StandardError; end
|
|
96
|
+
|
|
97
|
+
# Raised by Run#resume! when the job could not be put on the queue. Narrow
|
|
98
|
+
# on purpose: controllers catch this and show the operator why, while any
|
|
99
|
+
# other exception keeps propagating to the app's error reporting.
|
|
100
|
+
class EnqueueFailed < StandardError; end
|
|
101
|
+
|
|
102
|
+
# The run was reaped or claimed by a newer execution. The old worker must stop.
|
|
103
|
+
class ExecutionLost < StandardError; end
|
|
104
|
+
|
|
105
|
+
class << self
|
|
106
|
+
def configure
|
|
107
|
+
yield self
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# True when HTTP Basic is on *and* carries a password that actually
|
|
111
|
+
# protects anything. Blank counts as unprotected: the controller compares
|
|
112
|
+
# the supplied password against this value, so an empty one authenticates
|
|
113
|
+
# every request -- the common way to get there is a credentials key that
|
|
114
|
+
# is missing or misspelled and quietly resolves to nil.
|
|
115
|
+
def http_basic_authentication_configured?
|
|
116
|
+
http_basic_authentication_enabled && !http_basic_password_unsafe?
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The shipped placeholder, or blank -- neither is access control.
|
|
120
|
+
def http_basic_password_unsafe?
|
|
121
|
+
password = http_basic_authentication_password.to_s
|
|
122
|
+
|
|
123
|
+
password.empty? || password == DEFAULT_HTTP_BASIC_PASSWORD
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# True when at least one access-control layer is meaningfully configured.
|
|
127
|
+
def access_control_configured?
|
|
128
|
+
return true if authentication || verify_access_proc
|
|
129
|
+
|
|
130
|
+
http_basic_authentication_configured?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Called from the engine's after_initialize. Raises in production unless
|
|
134
|
+
# the host opted in via allow_insecure_dashboard; warns everywhere else.
|
|
135
|
+
def verify_access_control!(logger: Rails.logger, env: Rails.env)
|
|
136
|
+
if access_control_configured?
|
|
137
|
+
# An authentication hook alone only verifies *who* the user is --
|
|
138
|
+
# without verify_access_proc every authenticated user gets in.
|
|
139
|
+
if authentication && verify_access_proc.nil?
|
|
140
|
+
logger&.warn(
|
|
141
|
+
"[MaintenanceOnSteroids] `authentication` is configured without `verify_access_proc`: " \
|
|
142
|
+
"any authenticated user can access the maintenance dashboard. Set " \
|
|
143
|
+
"MaintenanceOnSteroids.verify_access_proc to restrict access (unless your " \
|
|
144
|
+
"authentication hook already enforces authorization)."
|
|
145
|
+
)
|
|
146
|
+
end
|
|
147
|
+
return
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
reason =
|
|
151
|
+
if !http_basic_authentication_enabled
|
|
152
|
+
"no access control is configured"
|
|
153
|
+
elsif http_basic_authentication_password.to_s.empty?
|
|
154
|
+
"HTTP Basic is enabled but its password is blank, which authenticates every request"
|
|
155
|
+
else
|
|
156
|
+
"HTTP Basic is enabled but still uses the shipped default password"
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Built once and prefixed per branch: deriving the warning by stripping a
|
|
160
|
+
# substring out of the raise message would silently start announcing
|
|
161
|
+
# "Refusing to boot" for boots that were never refused.
|
|
162
|
+
body =
|
|
163
|
+
"#{reason}. Anyone who can reach the mounted dashboard could view every task, read its " \
|
|
164
|
+
"source, and start runs against this database. Set http_basic_authentication_password " \
|
|
165
|
+
"(to something other than blank or the default), authentication, or verify_access_proc " \
|
|
166
|
+
"in config/initializers/maintenance_on_steroids.rb. If the dashboard is already protected " \
|
|
167
|
+
"elsewhere (reverse proxy, VPN, middleware), set " \
|
|
168
|
+
"MaintenanceOnSteroids.allow_insecure_dashboard = true to acknowledge that."
|
|
169
|
+
|
|
170
|
+
if env.production? && !allow_insecure_dashboard
|
|
171
|
+
raise InsecureDashboardError, "[MaintenanceOnSteroids] Refusing to boot: #{body}"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
logger&.warn("[MaintenanceOnSteroids] #{body}")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# Discover all task classes defined in the host app
|
|
178
|
+
def task_classes
|
|
179
|
+
load_all_tasks!
|
|
180
|
+
JobRegistry.tasks
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def load_all_tasks!
|
|
184
|
+
JobRegistry.load_all!
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: maintenance_on_steroids
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Igor Kasyanchuk
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 8.1.3.1
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: 8.1.3.1
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: csv
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '0'
|
|
39
|
+
type: :runtime
|
|
40
|
+
prerelease: false
|
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
description: 'A powerful maintenance task runner leveraging ActiveJob::Continuable.
|
|
47
|
+
Features include: form inputs with typed parameters, DB-stored artifacts, database
|
|
48
|
+
role switching, live progress UI, pause/resume/cancel, and more.'
|
|
49
|
+
email:
|
|
50
|
+
- igorkasyanchuk@gmail.com
|
|
51
|
+
executables: []
|
|
52
|
+
extensions: []
|
|
53
|
+
extra_rdoc_files: []
|
|
54
|
+
files:
|
|
55
|
+
- CHANGELOG.md
|
|
56
|
+
- LICENSE.txt
|
|
57
|
+
- README.md
|
|
58
|
+
- app/controllers/maintenance_on_steroids/application_controller.rb
|
|
59
|
+
- app/controllers/maintenance_on_steroids/dashboard_controller.rb
|
|
60
|
+
- app/controllers/maintenance_on_steroids/jobs_controller.rb
|
|
61
|
+
- app/controllers/maintenance_on_steroids/runs_controller.rb
|
|
62
|
+
- app/jobs/maintenance_on_steroids/run_job.rb
|
|
63
|
+
- app/models/maintenance_on_steroids/application_record.rb
|
|
64
|
+
- app/models/maintenance_on_steroids/artifact.rb
|
|
65
|
+
- app/models/maintenance_on_steroids/run.rb
|
|
66
|
+
- app/views/layouts/maintenance_on_steroids/application.html.erb
|
|
67
|
+
- app/views/maintenance_on_steroids/dashboard/index.html.erb
|
|
68
|
+
- app/views/maintenance_on_steroids/jobs/index.html.erb
|
|
69
|
+
- app/views/maintenance_on_steroids/jobs/show.html.erb
|
|
70
|
+
- app/views/maintenance_on_steroids/jobs/source.html.erb
|
|
71
|
+
- app/views/maintenance_on_steroids/runs/new.html.erb
|
|
72
|
+
- app/views/maintenance_on_steroids/runs/show.html.erb
|
|
73
|
+
- app/views/maintenance_on_steroids/shared/_auto_refresh.html.erb
|
|
74
|
+
- app/views/maintenance_on_steroids/shared/_javascript.html.erb
|
|
75
|
+
- app/views/maintenance_on_steroids/shared/_pager.html.erb
|
|
76
|
+
- app/views/maintenance_on_steroids/shared/_styles.html.erb
|
|
77
|
+
- app/views/maintenance_on_steroids/shared/_task_list_item.html.erb
|
|
78
|
+
- config/routes.rb
|
|
79
|
+
- lib/generators/maintenance_on_steroids/install/install_generator.rb
|
|
80
|
+
- lib/generators/maintenance_on_steroids/install/templates/create_maintenance_on_steroids_tables.rb.erb
|
|
81
|
+
- lib/generators/maintenance_on_steroids/install/templates/initializer.rb
|
|
82
|
+
- lib/generators/maintenance_on_steroids/job/job_generator.rb
|
|
83
|
+
- lib/generators/maintenance_on_steroids/job/templates/job.rb.erb
|
|
84
|
+
- lib/maintenance_on_steroids.rb
|
|
85
|
+
- lib/maintenance_on_steroids/about_dsl.rb
|
|
86
|
+
- lib/maintenance_on_steroids/artifact_dsl.rb
|
|
87
|
+
- lib/maintenance_on_steroids/artifacts_proxy.rb
|
|
88
|
+
- lib/maintenance_on_steroids/callbacks_dsl.rb
|
|
89
|
+
- lib/maintenance_on_steroids/csv_artifact.rb
|
|
90
|
+
- lib/maintenance_on_steroids/engine.rb
|
|
91
|
+
- lib/maintenance_on_steroids/form_dsl.rb
|
|
92
|
+
- lib/maintenance_on_steroids/instrumentation.rb
|
|
93
|
+
- lib/maintenance_on_steroids/job_dsl.rb
|
|
94
|
+
- lib/maintenance_on_steroids/job_registry.rb
|
|
95
|
+
- lib/maintenance_on_steroids/jsonb_artifact.rb
|
|
96
|
+
- lib/maintenance_on_steroids/params_proxy.rb
|
|
97
|
+
- lib/maintenance_on_steroids/task.rb
|
|
98
|
+
- lib/maintenance_on_steroids/text_artifact.rb
|
|
99
|
+
- lib/maintenance_on_steroids/version.rb
|
|
100
|
+
homepage: https://github.com/igorkasyanchuk/maintenance_on_steroids
|
|
101
|
+
licenses:
|
|
102
|
+
- MIT
|
|
103
|
+
metadata:
|
|
104
|
+
source_code_uri: https://github.com/igorkasyanchuk/maintenance_on_steroids
|
|
105
|
+
changelog_uri: https://github.com/igorkasyanchuk/maintenance_on_steroids/blob/main/CHANGELOG.md
|
|
106
|
+
bug_tracker_uri: https://github.com/igorkasyanchuk/maintenance_on_steroids/issues
|
|
107
|
+
rubygems_mfa_required: 'true'
|
|
108
|
+
rdoc_options: []
|
|
109
|
+
require_paths:
|
|
110
|
+
- lib
|
|
111
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - ">="
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: 3.2.0
|
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
|
+
requirements:
|
|
118
|
+
- - ">="
|
|
119
|
+
- !ruby/object:Gem::Version
|
|
120
|
+
version: '0'
|
|
121
|
+
requirements: []
|
|
122
|
+
rubygems_version: 3.7.2
|
|
123
|
+
specification_version: 4
|
|
124
|
+
summary: Maintenance tasks on steroids for Rails 8.1+
|
|
125
|
+
test_files: []
|