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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sktop
|
|
4
|
+
module JobActions
|
|
5
|
+
class << self
|
|
6
|
+
def retry_job(jid, source)
|
|
7
|
+
job = find_job(jid, source)
|
|
8
|
+
raise "Job not found (JID: #{jid})" unless job
|
|
9
|
+
|
|
10
|
+
job.retry
|
|
11
|
+
true
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def delete_job(jid, source)
|
|
15
|
+
job = find_job(jid, source)
|
|
16
|
+
raise "Job not found (JID: #{jid})" unless job
|
|
17
|
+
|
|
18
|
+
job.delete
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def kill_job(jid, source)
|
|
23
|
+
job = find_job(jid, source)
|
|
24
|
+
raise "Job not found (JID: #{jid})" unless job
|
|
25
|
+
|
|
26
|
+
job.kill
|
|
27
|
+
true
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def retry_all(source)
|
|
31
|
+
set = get_set(source)
|
|
32
|
+
count = set.size
|
|
33
|
+
set.retry_all
|
|
34
|
+
count
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def delete_all(source)
|
|
38
|
+
set = get_set(source)
|
|
39
|
+
count = set.size
|
|
40
|
+
set.clear
|
|
41
|
+
count
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def quiet_process(identity)
|
|
45
|
+
process = find_process(identity)
|
|
46
|
+
raise "Process not found (identity: #{identity})" unless process
|
|
47
|
+
|
|
48
|
+
process.quiet!
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def stop_process(identity)
|
|
53
|
+
process = find_process(identity)
|
|
54
|
+
raise "Process not found (identity: #{identity})" unless process
|
|
55
|
+
|
|
56
|
+
process.stop!
|
|
57
|
+
true
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
def get_set(source)
|
|
63
|
+
case source
|
|
64
|
+
when :retry
|
|
65
|
+
Sidekiq::RetrySet.new
|
|
66
|
+
when :dead
|
|
67
|
+
Sidekiq::DeadSet.new
|
|
68
|
+
when :scheduled
|
|
69
|
+
Sidekiq::ScheduledSet.new
|
|
70
|
+
else
|
|
71
|
+
raise "Unknown source: #{source}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def find_job(jid, source)
|
|
76
|
+
set = get_set(source)
|
|
77
|
+
|
|
78
|
+
# Try find_job first (available in newer Sidekiq versions)
|
|
79
|
+
if set.respond_to?(:find_job)
|
|
80
|
+
job = set.find_job(jid)
|
|
81
|
+
return job if job
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Fall back to iterating through the set
|
|
85
|
+
set.each do |entry|
|
|
86
|
+
return entry if entry.jid == jid
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def find_process(identity)
|
|
93
|
+
Sidekiq::ProcessSet.new.find { |p| p["identity"] == identity }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Sktop
|
|
4
|
+
class StatsCollector
|
|
5
|
+
def initialize
|
|
6
|
+
@stats = Sidekiq::Stats.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def refresh!
|
|
10
|
+
@stats = Sidekiq::Stats.new
|
|
11
|
+
self
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def overview
|
|
15
|
+
{
|
|
16
|
+
processed: @stats.processed,
|
|
17
|
+
failed: @stats.failed,
|
|
18
|
+
scheduled_size: @stats.scheduled_size,
|
|
19
|
+
retry_size: @stats.retry_size,
|
|
20
|
+
dead_size: @stats.dead_size,
|
|
21
|
+
enqueued: @stats.enqueued,
|
|
22
|
+
default_queue_latency: @stats.default_queue_latency
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def queues
|
|
27
|
+
Sidekiq::Queue.all.map do |queue|
|
|
28
|
+
{
|
|
29
|
+
name: queue.name,
|
|
30
|
+
size: queue.size,
|
|
31
|
+
latency: queue.latency,
|
|
32
|
+
paused: queue.paused?
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def processes
|
|
38
|
+
Sidekiq::ProcessSet.new.map do |process|
|
|
39
|
+
# Use direct hash access for status flags - the method accessors
|
|
40
|
+
# like stopping? can have broader semantics in some Sidekiq versions
|
|
41
|
+
quiet_val = process["quiet"]
|
|
42
|
+
stopping_val = process["stopping"]
|
|
43
|
+
|
|
44
|
+
quiet_flag = quiet_val == true || quiet_val == "true"
|
|
45
|
+
stopping_flag = stopping_val == true || stopping_val == "true"
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
identity: process["identity"],
|
|
49
|
+
hostname: process["hostname"],
|
|
50
|
+
pid: process["pid"],
|
|
51
|
+
tag: process["tag"],
|
|
52
|
+
started_at: Time.at(process["started_at"]),
|
|
53
|
+
concurrency: process["concurrency"],
|
|
54
|
+
busy: process["busy"],
|
|
55
|
+
queues: process["queues"],
|
|
56
|
+
labels: process["labels"] || [],
|
|
57
|
+
quiet: quiet_flag,
|
|
58
|
+
stopping: stopping_flag,
|
|
59
|
+
rss: process["rss"] # Memory in KB
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def workers
|
|
65
|
+
Sidekiq::Workers.new.map do |process_id, thread_id, work|
|
|
66
|
+
{
|
|
67
|
+
process_id: process_id,
|
|
68
|
+
thread_id: thread_id,
|
|
69
|
+
queue: work["queue"],
|
|
70
|
+
class: work["payload"]["class"],
|
|
71
|
+
args: work["payload"]["args"],
|
|
72
|
+
run_at: Time.at(work["run_at"]),
|
|
73
|
+
elapsed: Time.now - Time.at(work["run_at"])
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def scheduled_jobs(limit: 10)
|
|
79
|
+
Sidekiq::ScheduledSet.new.first(limit).map do |job|
|
|
80
|
+
{
|
|
81
|
+
class: job.klass,
|
|
82
|
+
queue: job.queue,
|
|
83
|
+
args: job.args,
|
|
84
|
+
scheduled_at: job.at,
|
|
85
|
+
created_at: job.created_at
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def retry_jobs(limit: 10)
|
|
91
|
+
Sidekiq::RetrySet.new.first(limit).map do |job|
|
|
92
|
+
{
|
|
93
|
+
jid: job.jid,
|
|
94
|
+
class: job.klass,
|
|
95
|
+
queue: job.queue,
|
|
96
|
+
args: job.args,
|
|
97
|
+
failed_at: job["failed_at"] ? Time.at(job["failed_at"]) : nil,
|
|
98
|
+
retry_count: job["retry_count"],
|
|
99
|
+
error_class: job["error_class"],
|
|
100
|
+
error_message: job["error_message"]
|
|
101
|
+
}
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def dead_jobs(limit: 10)
|
|
106
|
+
Sidekiq::DeadSet.new.first(limit).map do |job|
|
|
107
|
+
{
|
|
108
|
+
jid: job.jid,
|
|
109
|
+
class: job.klass,
|
|
110
|
+
queue: job.queue,
|
|
111
|
+
args: job.args,
|
|
112
|
+
failed_at: job["failed_at"] ? Time.at(job["failed_at"]) : nil,
|
|
113
|
+
error_class: job["error_class"],
|
|
114
|
+
error_message: job["error_message"]
|
|
115
|
+
}
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def history(days: 7)
|
|
120
|
+
stats_history = Sidekiq::Stats::History.new(days)
|
|
121
|
+
{
|
|
122
|
+
processed: stats_history.processed,
|
|
123
|
+
failed: stats_history.failed
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
data/lib/sktop.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "sidekiq"
|
|
4
|
+
require "sidekiq/api"
|
|
5
|
+
require "terminal-table"
|
|
6
|
+
require "pastel"
|
|
7
|
+
require "tty-cursor"
|
|
8
|
+
require "tty-screen"
|
|
9
|
+
|
|
10
|
+
require_relative "sktop/version"
|
|
11
|
+
require_relative "sktop/stats_collector"
|
|
12
|
+
require_relative "sktop/job_actions"
|
|
13
|
+
require_relative "sktop/display"
|
|
14
|
+
require_relative "sktop/cli"
|
|
15
|
+
|
|
16
|
+
module Sktop
|
|
17
|
+
class Error < StandardError; end
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def configure_redis(url: nil, namespace: nil)
|
|
21
|
+
options = {}
|
|
22
|
+
options[:url] = url if url
|
|
23
|
+
|
|
24
|
+
Sidekiq.configure_client do |config|
|
|
25
|
+
config.redis = options
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sktop
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- James
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-17 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: sidekiq
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: redis
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '4.0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '4.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: terminal-table
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: pastel
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0.8'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0.8'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: tty-cursor
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0.7'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0.7'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: tty-screen
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0.8'
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0.8'
|
|
97
|
+
description: A terminal-based dashboard for monitoring Sidekiq, similar to the web
|
|
98
|
+
UI but for the command line
|
|
99
|
+
email:
|
|
100
|
+
- james@example.com
|
|
101
|
+
executables:
|
|
102
|
+
- sktop
|
|
103
|
+
extensions: []
|
|
104
|
+
extra_rdoc_files: []
|
|
105
|
+
files:
|
|
106
|
+
- README.md
|
|
107
|
+
- bin/sktop
|
|
108
|
+
- lib/sktop.rb
|
|
109
|
+
- lib/sktop/cli.rb
|
|
110
|
+
- lib/sktop/display.rb
|
|
111
|
+
- lib/sktop/job_actions.rb
|
|
112
|
+
- lib/sktop/stats_collector.rb
|
|
113
|
+
- lib/sktop/version.rb
|
|
114
|
+
homepage: https://github.com/james/sktop
|
|
115
|
+
licenses:
|
|
116
|
+
- MIT
|
|
117
|
+
metadata: {}
|
|
118
|
+
post_install_message:
|
|
119
|
+
rdoc_options: []
|
|
120
|
+
require_paths:
|
|
121
|
+
- lib
|
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
|
+
requirements:
|
|
124
|
+
- - ">="
|
|
125
|
+
- !ruby/object:Gem::Version
|
|
126
|
+
version: 2.7.0
|
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
requirements: []
|
|
133
|
+
rubygems_version: 3.5.16
|
|
134
|
+
signing_key:
|
|
135
|
+
specification_version: 4
|
|
136
|
+
summary: CLI tool to monitor Sidekiq queues and processes
|
|
137
|
+
test_files: []
|