sktop 0.1.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 +104 -0
- data/bin/sktop +6 -0
- data/lib/sktop/cli.rb +501 -0
- data/lib/sktop/display.rb +1150 -0
- data/lib/sktop/job_actions.rb +97 -0
- data/lib/sktop/stats_collector.rb +127 -0
- data/lib/sktop/version.rb +5 -0
- data/lib/sktop.rb +29 -0
- metadata +137 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c99f0dede27934a3422767109eb5d1870b61ee9d0131e83d9731335b5e968f29
|
|
4
|
+
data.tar.gz: 070bf9de8114f114555b841abf898864070aebdaefb899f4b97e04c62a2dae64
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ce99bdcc62ae52bbf105ae7c9fdeb7735d58e3ce354fb393e96b307d7f2c5d2a3db0672244b318c6dd0d6f4d496ba6134708c7ea6d8233eed93e829311ad0fe4
|
|
7
|
+
data.tar.gz: 62eb8486ef2c299d937268f26b9ce339119d628f15c0b5dbb33a911c3ed06e2c0a8f21149a3e942d7e4a953e70beed42ec9416872d38e4acd72503065bb36e16
|
data/README.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# sktop
|
|
2
|
+
|
|
3
|
+
A CLI tool to monitor Sidekiq queues and processes - like htop for Sidekiq.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'sktop'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or install directly:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
gem install sktop
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Basic usage (auto-refresh every 2 seconds)
|
|
23
|
+
sktop
|
|
24
|
+
|
|
25
|
+
# Connect to a specific Redis instance
|
|
26
|
+
sktop -r redis://myhost:6379/1
|
|
27
|
+
|
|
28
|
+
# Custom refresh interval
|
|
29
|
+
sktop -i 5
|
|
30
|
+
|
|
31
|
+
# Start with a specific view
|
|
32
|
+
sktop -w # Workers view
|
|
33
|
+
sktop -R # Retries view
|
|
34
|
+
sktop -d # Dead jobs view
|
|
35
|
+
|
|
36
|
+
# Display once and exit (no auto-refresh)
|
|
37
|
+
sktop -1
|
|
38
|
+
sktop -R -1 # Show retries view once and exit
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Options
|
|
42
|
+
|
|
43
|
+
| Option | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `-r, --redis URL` | Redis URL (default: redis://localhost:6379/0 or REDIS_URL env var) |
|
|
46
|
+
| `-n, --namespace NS` | Redis namespace (e.g., 'myapp') |
|
|
47
|
+
| `-i, --interval SECONDS` | Auto-refresh interval in seconds (default: 2) |
|
|
48
|
+
| `-1, --once` | Display once and exit (no auto-refresh) |
|
|
49
|
+
| `-v, --version` | Show version |
|
|
50
|
+
| `-h, --help` | Show help |
|
|
51
|
+
|
|
52
|
+
### View Options (set initial view)
|
|
53
|
+
|
|
54
|
+
| Option | Description |
|
|
55
|
+
|--------|-------------|
|
|
56
|
+
| `-m, --main` | Main view - processes + workers (default) |
|
|
57
|
+
| `-q, --queues` | Queues view |
|
|
58
|
+
| `-p, --processes` | Processes view |
|
|
59
|
+
| `-w, --workers` | Workers view |
|
|
60
|
+
| `-R, --retries` | Retries view |
|
|
61
|
+
| `-s, --scheduled` | Scheduled jobs view |
|
|
62
|
+
| `-d, --dead` | Dead jobs view |
|
|
63
|
+
|
|
64
|
+
## Keyboard Navigation
|
|
65
|
+
|
|
66
|
+
| Key | Action |
|
|
67
|
+
|-----|--------|
|
|
68
|
+
| `m` | Main view (processes + workers) |
|
|
69
|
+
| `q` | Queues view |
|
|
70
|
+
| `p` | Processes view |
|
|
71
|
+
| `w` | Workers view |
|
|
72
|
+
| `r` | Retry queue view |
|
|
73
|
+
| `s` | Scheduled jobs view |
|
|
74
|
+
| `d` | Dead jobs view |
|
|
75
|
+
| `↑/↓` | Scroll / select items |
|
|
76
|
+
| `PgUp/PgDn` | Scroll by page |
|
|
77
|
+
| `Esc` | Return to main view |
|
|
78
|
+
| `Ctrl+C` | Quit |
|
|
79
|
+
|
|
80
|
+
### Process Actions (in Processes view)
|
|
81
|
+
|
|
82
|
+
| Key | Action |
|
|
83
|
+
|-----|--------|
|
|
84
|
+
| `Ctrl+Q` | Quiet selected process |
|
|
85
|
+
| `Ctrl+K` | Stop/kill selected process |
|
|
86
|
+
|
|
87
|
+
### Job Actions (in Retry/Dead views)
|
|
88
|
+
|
|
89
|
+
| Key | Action |
|
|
90
|
+
|-----|--------|
|
|
91
|
+
| `Ctrl+R` | Retry selected job |
|
|
92
|
+
| `Ctrl+X` | Delete selected job |
|
|
93
|
+
| `Alt+R` | Retry all jobs |
|
|
94
|
+
| `Alt+X` | Delete all jobs |
|
|
95
|
+
|
|
96
|
+
## Environment Variables
|
|
97
|
+
|
|
98
|
+
- `REDIS_URL` - Default Redis connection URL
|
|
99
|
+
- `SIDEKIQ_NAMESPACE` - Default Redis namespace
|
|
100
|
+
- `DEBUG` - Show stack traces on errors
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
MIT License
|
data/bin/sktop
ADDED
data/lib/sktop/cli.rb
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "redis"
|
|
5
|
+
require "redis-namespace"
|
|
6
|
+
require "io/console"
|
|
7
|
+
require "io/wait"
|
|
8
|
+
|
|
9
|
+
module Sktop
|
|
10
|
+
class CLI
|
|
11
|
+
def initialize(args = ARGV)
|
|
12
|
+
@args = args
|
|
13
|
+
@options = {
|
|
14
|
+
redis_url: ENV["REDIS_URL"] || "redis://localhost:6379/0",
|
|
15
|
+
namespace: ENV["SIDEKIQ_NAMESPACE"],
|
|
16
|
+
refresh_interval: 2,
|
|
17
|
+
initial_view: :main,
|
|
18
|
+
once: false
|
|
19
|
+
}
|
|
20
|
+
@running = true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
parse_options!
|
|
25
|
+
configure_redis
|
|
26
|
+
start_watcher
|
|
27
|
+
rescue Interrupt
|
|
28
|
+
shutdown
|
|
29
|
+
rescue RedisClient::CannotConnectError, Redis::CannotConnectError => e
|
|
30
|
+
shutdown
|
|
31
|
+
puts "Error: Cannot connect to Redis at #{@options[:redis_url]}"
|
|
32
|
+
puts "Make sure Redis is running and the URL is correct."
|
|
33
|
+
puts "You can specify a different URL with: sktop -r redis://host:port/db"
|
|
34
|
+
exit 1
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
shutdown
|
|
37
|
+
puts "Error: #{e.message}"
|
|
38
|
+
puts e.backtrace.first(5).join("\n") if ENV["DEBUG"]
|
|
39
|
+
exit 1
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def shutdown
|
|
43
|
+
@running = false
|
|
44
|
+
print "\e[?25h" # Show cursor
|
|
45
|
+
print "\e[?1049l" # Restore main screen
|
|
46
|
+
$stdout.flush
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def parse_options!
|
|
52
|
+
parser = OptionParser.new do |opts|
|
|
53
|
+
opts.banner = "Usage: sktop [options]"
|
|
54
|
+
opts.separator ""
|
|
55
|
+
opts.separator "Options:"
|
|
56
|
+
|
|
57
|
+
opts.on("-r", "--redis URL", "Redis URL (default: redis://localhost:6379/0)") do |url|
|
|
58
|
+
@options[:redis_url] = url
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
opts.on("-n", "--namespace NS", "Redis namespace (e.g., 'myapp')") do |ns|
|
|
62
|
+
@options[:namespace] = ns
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
opts.on("-i", "--interval SECONDS", Integer, "Refresh interval in seconds (default: 2)") do |interval|
|
|
66
|
+
@options[:refresh_interval] = interval
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
opts.separator ""
|
|
70
|
+
opts.separator "Views (set initial view):"
|
|
71
|
+
|
|
72
|
+
opts.on("-m", "--main", "Main view (default)") do
|
|
73
|
+
@options[:initial_view] = :main
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
opts.on("-q", "--queues", "Queues view") do
|
|
77
|
+
@options[:initial_view] = :queues
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
opts.on("-p", "--processes", "Processes view") do
|
|
81
|
+
@options[:initial_view] = :processes
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
opts.on("-w", "--workers", "Workers view") do
|
|
85
|
+
@options[:initial_view] = :workers
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
opts.on("-R", "--retries", "Retries view") do
|
|
89
|
+
@options[:initial_view] = :retries
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
opts.on("-s", "--scheduled", "Scheduled jobs view") do
|
|
93
|
+
@options[:initial_view] = :scheduled
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
opts.on("-d", "--dead", "Dead jobs view") do
|
|
97
|
+
@options[:initial_view] = :dead
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
opts.separator ""
|
|
101
|
+
|
|
102
|
+
opts.on("-1", "--once", "Display once and exit (no auto-refresh)") do
|
|
103
|
+
@options[:once] = true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
opts.on("-v", "--version", "Show version") do
|
|
107
|
+
puts "sktop #{Sktop::VERSION}"
|
|
108
|
+
exit 0
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
opts.on("-h", "--help", "Show this help") do
|
|
112
|
+
puts opts
|
|
113
|
+
exit 0
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
parser.parse!(@args)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def configure_redis
|
|
121
|
+
Sidekiq.configure_client do |config|
|
|
122
|
+
if @options[:namespace]
|
|
123
|
+
config.redis = {
|
|
124
|
+
url: @options[:redis_url],
|
|
125
|
+
namespace: @options[:namespace]
|
|
126
|
+
}
|
|
127
|
+
else
|
|
128
|
+
config.redis = { url: @options[:redis_url] }
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def start_watcher
|
|
134
|
+
collector = StatsCollector.new
|
|
135
|
+
@display = Display.new
|
|
136
|
+
@display.current_view = @options[:initial_view]
|
|
137
|
+
|
|
138
|
+
if @options[:once]
|
|
139
|
+
# One-shot mode: just print and exit
|
|
140
|
+
puts @display.render(collector)
|
|
141
|
+
return
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Auto-refresh mode with keyboard input
|
|
145
|
+
$stdout.sync = true
|
|
146
|
+
|
|
147
|
+
# Get terminal size before entering raw mode
|
|
148
|
+
@display.update_terminal_size
|
|
149
|
+
|
|
150
|
+
# Switch to alternate screen buffer (like htop/vim)
|
|
151
|
+
print "\e[?1049h" # Enable alternate screen
|
|
152
|
+
print "\e[?25l" # Hide cursor
|
|
153
|
+
print "\e[2J" # Clear screen
|
|
154
|
+
$stdout.flush
|
|
155
|
+
|
|
156
|
+
# Thread-safe data cache
|
|
157
|
+
@cached_data = nil
|
|
158
|
+
@data_version = 0
|
|
159
|
+
@rendered_version = 0
|
|
160
|
+
@data_mutex = Mutex.new
|
|
161
|
+
@fetch_in_progress = false
|
|
162
|
+
@fetch_mutex = Mutex.new
|
|
163
|
+
|
|
164
|
+
# Set up signal handler for Ctrl+C (works even when blocked)
|
|
165
|
+
Signal.trap("INT") { @running = false }
|
|
166
|
+
|
|
167
|
+
# Start background thread for data fetching
|
|
168
|
+
fetch_thread = Thread.new do
|
|
169
|
+
while @running
|
|
170
|
+
# Skip if a fetch is already in progress (defensive guard)
|
|
171
|
+
can_fetch = @fetch_mutex.synchronize do
|
|
172
|
+
if @fetch_in_progress
|
|
173
|
+
false
|
|
174
|
+
else
|
|
175
|
+
@fetch_in_progress = true
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
next unless can_fetch
|
|
180
|
+
|
|
181
|
+
begin
|
|
182
|
+
collector.refresh!
|
|
183
|
+
# Cache a snapshot of the data
|
|
184
|
+
snapshot = {
|
|
185
|
+
overview: collector.overview,
|
|
186
|
+
queues: collector.queues,
|
|
187
|
+
processes: collector.processes,
|
|
188
|
+
workers: collector.workers,
|
|
189
|
+
retry_jobs: collector.retry_jobs(limit: 500),
|
|
190
|
+
scheduled_jobs: collector.scheduled_jobs(limit: 500),
|
|
191
|
+
dead_jobs: collector.dead_jobs(limit: 500)
|
|
192
|
+
}
|
|
193
|
+
@data_mutex.synchronize do
|
|
194
|
+
@cached_data = snapshot
|
|
195
|
+
@data_version += 1
|
|
196
|
+
end
|
|
197
|
+
rescue => e
|
|
198
|
+
# Ignore fetch errors, will retry next interval
|
|
199
|
+
ensure
|
|
200
|
+
@fetch_mutex.synchronize { @fetch_in_progress = false }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# Sleep in small increments so we can exit quickly
|
|
204
|
+
(@options[:refresh_interval] * 10).to_i.times do
|
|
205
|
+
break unless @running
|
|
206
|
+
sleep 0.1
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
begin
|
|
212
|
+
# Set up raw mode for keyboard input
|
|
213
|
+
STDIN.raw do |stdin|
|
|
214
|
+
# Wait for initial data (with timeout)
|
|
215
|
+
10.times do
|
|
216
|
+
break if @data_mutex.synchronize { @cached_data }
|
|
217
|
+
sleep 0.1
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Initial render
|
|
221
|
+
render_cached_data
|
|
222
|
+
|
|
223
|
+
while @running
|
|
224
|
+
# Wait for keyboard input with short timeout
|
|
225
|
+
ready = IO.select([stdin], nil, nil, 0.03)
|
|
226
|
+
|
|
227
|
+
if ready
|
|
228
|
+
key = stdin.read_nonblock(1) rescue nil
|
|
229
|
+
if key
|
|
230
|
+
handle_keypress(key, stdin)
|
|
231
|
+
# Immediate refresh on keypress
|
|
232
|
+
render_cached_data
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Check if background thread has new data
|
|
237
|
+
current_version = @data_mutex.synchronize { @data_version }
|
|
238
|
+
if current_version != @rendered_version
|
|
239
|
+
@rendered_version = current_version
|
|
240
|
+
render_cached_data
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
ensure
|
|
245
|
+
# Stop fetch thread
|
|
246
|
+
@running = false
|
|
247
|
+
fetch_thread.join(0.5) rescue nil
|
|
248
|
+
|
|
249
|
+
# Restore normal screen
|
|
250
|
+
print "\e[?25h" # Show cursor
|
|
251
|
+
print "\e[?1049l" # Disable alternate screen
|
|
252
|
+
$stdout.flush
|
|
253
|
+
|
|
254
|
+
# Reset signal handler
|
|
255
|
+
Signal.trap("INT", "DEFAULT")
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def render_cached_data
|
|
260
|
+
data = @data_mutex.synchronize { @cached_data }
|
|
261
|
+
return unless data
|
|
262
|
+
|
|
263
|
+
@display.render_refresh_from_cache(data)
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def handle_keypress(key, stdin)
|
|
267
|
+
case key
|
|
268
|
+
when 'q', 'Q'
|
|
269
|
+
@display.current_view = :queues
|
|
270
|
+
when 'p', 'P'
|
|
271
|
+
@display.current_view = :processes
|
|
272
|
+
when 'w', 'W'
|
|
273
|
+
@display.current_view = :workers
|
|
274
|
+
when 'r', 'R' # Retries view
|
|
275
|
+
@display.current_view = :retries
|
|
276
|
+
when 's', 'S'
|
|
277
|
+
@display.current_view = :scheduled
|
|
278
|
+
when 'd', 'D'
|
|
279
|
+
@display.current_view = :dead
|
|
280
|
+
when 'm', 'M'
|
|
281
|
+
@display.current_view = :main
|
|
282
|
+
when "\x12" # Ctrl+R - retry job
|
|
283
|
+
handle_retry_action
|
|
284
|
+
when "\x18" # Ctrl+X - delete job
|
|
285
|
+
handle_delete_action
|
|
286
|
+
when "\x11" # Ctrl+Q - quiet process
|
|
287
|
+
handle_quiet_process_action
|
|
288
|
+
when "\x0B" # Ctrl+K - stop/kill process
|
|
289
|
+
handle_stop_process_action
|
|
290
|
+
when "\e" # Escape sequence - could be arrow keys, Alt+key, or just Escape
|
|
291
|
+
# Try to read more characters (arrow keys send \e[A, \e[B, etc.)
|
|
292
|
+
if IO.select([stdin], nil, nil, 0.05)
|
|
293
|
+
seq = stdin.read_nonblock(10) rescue ""
|
|
294
|
+
case seq
|
|
295
|
+
when "[A" # Up arrow
|
|
296
|
+
@display.select_up
|
|
297
|
+
when "[B" # Down arrow
|
|
298
|
+
@display.select_down
|
|
299
|
+
when "[C" # Right arrow (unused for now)
|
|
300
|
+
when "[D" # Left arrow (unused for now)
|
|
301
|
+
when "[5~" # Page Up
|
|
302
|
+
@display.page_up
|
|
303
|
+
when "[6~" # Page Down
|
|
304
|
+
@display.page_down
|
|
305
|
+
when "r", "R" # Alt+R - Retry All
|
|
306
|
+
handle_retry_all_action
|
|
307
|
+
when "x", "X" # Alt+X - Delete All
|
|
308
|
+
handle_delete_all_action
|
|
309
|
+
else
|
|
310
|
+
# Just Escape key - go to main
|
|
311
|
+
@display.current_view = :main
|
|
312
|
+
end
|
|
313
|
+
else
|
|
314
|
+
# Just Escape key - go to main
|
|
315
|
+
@display.current_view = :main
|
|
316
|
+
end
|
|
317
|
+
when "\u0003" # Ctrl+C
|
|
318
|
+
raise Interrupt
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
def handle_retry_action
|
|
323
|
+
return unless [:retries, :dead].include?(@display.current_view)
|
|
324
|
+
|
|
325
|
+
data = @data_mutex.synchronize { @cached_data }
|
|
326
|
+
unless data
|
|
327
|
+
@display.set_status("No data available")
|
|
328
|
+
return
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
jobs = @display.current_view == :retries ? data[:retry_jobs] : data[:dead_jobs]
|
|
332
|
+
selected_idx = @display.selected_index
|
|
333
|
+
|
|
334
|
+
if jobs.empty?
|
|
335
|
+
@display.set_status("No jobs to retry")
|
|
336
|
+
return
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
if selected_idx >= jobs.length
|
|
340
|
+
@display.set_status("Invalid selection")
|
|
341
|
+
return
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
job = jobs[selected_idx]
|
|
345
|
+
unless job[:jid]
|
|
346
|
+
@display.set_status("Job has no JID")
|
|
347
|
+
return
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
begin
|
|
351
|
+
source = @display.current_view == :retries ? :retry : :dead
|
|
352
|
+
Sktop::JobActions.retry_job(job[:jid], source)
|
|
353
|
+
@display.set_status("Retrying #{job[:class]}")
|
|
354
|
+
# Force data refresh
|
|
355
|
+
@rendered_version = -1
|
|
356
|
+
rescue => e
|
|
357
|
+
@display.set_status("Error: #{e.message}")
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def handle_delete_action
|
|
362
|
+
return unless [:retries, :dead].include?(@display.current_view)
|
|
363
|
+
|
|
364
|
+
data = @data_mutex.synchronize { @cached_data }
|
|
365
|
+
unless data
|
|
366
|
+
@display.set_status("No data available")
|
|
367
|
+
return
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
jobs = @display.current_view == :retries ? data[:retry_jobs] : data[:dead_jobs]
|
|
371
|
+
selected_idx = @display.selected_index
|
|
372
|
+
|
|
373
|
+
if jobs.empty?
|
|
374
|
+
@display.set_status("No jobs to delete")
|
|
375
|
+
return
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
if selected_idx >= jobs.length
|
|
379
|
+
@display.set_status("Invalid selection")
|
|
380
|
+
return
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
job = jobs[selected_idx]
|
|
384
|
+
unless job[:jid]
|
|
385
|
+
@display.set_status("Job has no JID")
|
|
386
|
+
return
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
begin
|
|
390
|
+
source = @display.current_view == :retries ? :retry : :dead
|
|
391
|
+
Sktop::JobActions.delete_job(job[:jid], source)
|
|
392
|
+
@display.set_status("Deleted #{job[:class]}")
|
|
393
|
+
# Force data refresh
|
|
394
|
+
@rendered_version = -1
|
|
395
|
+
rescue => e
|
|
396
|
+
@display.set_status("Error: #{e.message}")
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
def handle_retry_all_action
|
|
401
|
+
return unless [:retries, :dead].include?(@display.current_view)
|
|
402
|
+
|
|
403
|
+
begin
|
|
404
|
+
source = @display.current_view == :retries ? :retry : :dead
|
|
405
|
+
count = Sktop::JobActions.retry_all(source)
|
|
406
|
+
@display.set_status("Retrying all #{count} jobs")
|
|
407
|
+
@rendered_version = -1
|
|
408
|
+
rescue => e
|
|
409
|
+
@display.set_status("Error: #{e.message}")
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def handle_delete_all_action
|
|
414
|
+
return unless [:retries, :dead].include?(@display.current_view)
|
|
415
|
+
|
|
416
|
+
begin
|
|
417
|
+
source = @display.current_view == :retries ? :retry : :dead
|
|
418
|
+
count = Sktop::JobActions.delete_all(source)
|
|
419
|
+
@display.set_status("Deleted all #{count} jobs")
|
|
420
|
+
@rendered_version = -1
|
|
421
|
+
rescue => e
|
|
422
|
+
@display.set_status("Error: #{e.message}")
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def handle_quiet_process_action
|
|
427
|
+
return unless @display.current_view == :processes
|
|
428
|
+
|
|
429
|
+
data = @data_mutex.synchronize { @cached_data }
|
|
430
|
+
unless data
|
|
431
|
+
@display.set_status("No data available")
|
|
432
|
+
return
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
processes = data[:processes]
|
|
436
|
+
selected_idx = @display.selected_index
|
|
437
|
+
|
|
438
|
+
if processes.empty?
|
|
439
|
+
@display.set_status("No processes")
|
|
440
|
+
return
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
if selected_idx >= processes.length
|
|
444
|
+
@display.set_status("Invalid selection")
|
|
445
|
+
return
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
process = processes[selected_idx]
|
|
449
|
+
unless process[:identity]
|
|
450
|
+
@display.set_status("Process has no identity")
|
|
451
|
+
return
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
begin
|
|
455
|
+
Sktop::JobActions.quiet_process(process[:identity])
|
|
456
|
+
@display.set_status("Quieting #{process[:hostname]}:#{process[:pid]}")
|
|
457
|
+
@rendered_version = -1
|
|
458
|
+
rescue => e
|
|
459
|
+
@display.set_status("Error: #{e.message}")
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
def handle_stop_process_action
|
|
464
|
+
return unless @display.current_view == :processes
|
|
465
|
+
|
|
466
|
+
data = @data_mutex.synchronize { @cached_data }
|
|
467
|
+
unless data
|
|
468
|
+
@display.set_status("No data available")
|
|
469
|
+
return
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
processes = data[:processes]
|
|
473
|
+
selected_idx = @display.selected_index
|
|
474
|
+
|
|
475
|
+
if processes.empty?
|
|
476
|
+
@display.set_status("No processes")
|
|
477
|
+
return
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
if selected_idx >= processes.length
|
|
481
|
+
@display.set_status("Invalid selection")
|
|
482
|
+
return
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
process = processes[selected_idx]
|
|
486
|
+
unless process[:identity]
|
|
487
|
+
@display.set_status("Process has no identity")
|
|
488
|
+
return
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
begin
|
|
492
|
+
Sktop::JobActions.stop_process(process[:identity])
|
|
493
|
+
@display.set_status("Stopping #{process[:hostname]}:#{process[:pid]}")
|
|
494
|
+
@rendered_version = -1
|
|
495
|
+
rescue => e
|
|
496
|
+
@display.set_status("Error: #{e.message}")
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
end
|
|
501
|
+
end
|