karafka-web 0.1.2 → 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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +17 -1
- data/Gemfile +1 -0
- data/Gemfile.lock +23 -11
- data/bin/karafka-web +1 -26
- data/karafka-web.gemspec +2 -2
- data/lib/karafka/web/cli.rb +80 -0
- data/lib/karafka/web/config.rb +5 -0
- data/lib/karafka/web/installer.rb +65 -29
- data/lib/karafka/web/processing/consumer.rb +2 -1
- data/lib/karafka/web/tracking/consumers/contracts/job.rb +1 -0
- data/lib/karafka/web/tracking/consumers/contracts/report.rb +1 -0
- data/lib/karafka/web/tracking/consumers/listeners/errors.rb +1 -1
- data/lib/karafka/web/tracking/consumers/listeners/processing.rb +2 -1
- data/lib/karafka/web/tracking/consumers/sampler.rb +9 -3
- data/lib/karafka/web/tracking/reporter.rb +2 -1
- data/lib/karafka/web/ui/app.rb +5 -0
- data/lib/karafka/web/ui/controllers/status.rb +23 -0
- data/lib/karafka/web/ui/models/process.rb +7 -0
- data/lib/karafka/web/ui/models/status.rb +169 -0
- data/lib/karafka/web/ui/pro/app.rb +5 -0
- data/lib/karafka/web/ui/pro/controllers/status.rb +26 -0
- data/lib/karafka/web/ui/pro/views/consumers/_consumer.erb +15 -7
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_job.erb +4 -0
- data/lib/karafka/web/ui/pro/views/consumers/consumer/_metrics.erb +10 -0
- data/lib/karafka/web/ui/pro/views/jobs/_job.erb +4 -0
- data/lib/karafka/web/ui/pro/views/shared/_navigation.erb +5 -0
- data/lib/karafka/web/ui/views/consumers/_consumer.erb +15 -7
- data/lib/karafka/web/ui/views/jobs/_job.erb +4 -0
- data/lib/karafka/web/ui/views/shared/_navigation.erb +5 -0
- data/lib/karafka/web/ui/views/shared/exceptions/not_found.erb +2 -0
- data/lib/karafka/web/ui/views/status/_breadcrumbs.erb +5 -0
- data/lib/karafka/web/ui/views/status/_failure.erb +14 -0
- data/lib/karafka/web/ui/views/status/_halted.erb +11 -0
- data/lib/karafka/web/ui/views/status/_success.erb +11 -0
- data/lib/karafka/web/ui/views/status/failures/_connection.erb +7 -0
- data/lib/karafka/web/ui/views/status/failures/_initial_state.erb +20 -0
- data/lib/karafka/web/ui/views/status/failures/_live_reporting.erb +7 -0
- data/lib/karafka/web/ui/views/status/failures/_partitions.erb +19 -0
- data/lib/karafka/web/ui/views/status/failures/_state_calculation.erb +8 -0
- data/lib/karafka/web/ui/views/status/failures/_topics.erb +20 -0
- data/lib/karafka/web/ui/views/status/show.erb +94 -0
- data/lib/karafka/web/version.rb +1 -1
- data/lib/karafka/web.rb +0 -6
- data.tar.gz.sig +0 -0
- metadata +21 -6
- metadata.gz.sig +0 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Karafka
|
4
|
+
module Web
|
5
|
+
module Ui
|
6
|
+
module Models
|
7
|
+
# Model that represents the general status of the Web UI.
|
8
|
+
# We use this data to display a status page that helps with debugging on what is missing
|
9
|
+
# in the overall setup of the Web UI.
|
10
|
+
#
|
11
|
+
# People have various problems like too many partitions, not created topics, etc. and this
|
12
|
+
# data and view aims to help them with understanding the current status of the setup
|
13
|
+
class Status
|
14
|
+
# Status of a single step of setup
|
15
|
+
Step = Struct.new(:status, :details) do
|
16
|
+
# @return [Boolean] is the given step successfully configured and working
|
17
|
+
def success?
|
18
|
+
status == :success
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [String] stringified status
|
22
|
+
def to_s
|
23
|
+
status.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Initializes the status object and tries to connect to Kafka
|
28
|
+
def initialize
|
29
|
+
connect
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [Status::Step] were we able to connect to Kafka or not
|
33
|
+
def connection
|
34
|
+
Step.new(
|
35
|
+
@connected ? :success : :failure,
|
36
|
+
nil
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Status::Step] do all the needed topics exist
|
41
|
+
def topics
|
42
|
+
if connection.success?
|
43
|
+
details = topics_details
|
44
|
+
status = details.all? { |_, detail| detail[:present] } ? :success : :failure
|
45
|
+
else
|
46
|
+
status = :halted
|
47
|
+
details = {}
|
48
|
+
end
|
49
|
+
|
50
|
+
Step.new(
|
51
|
+
status,
|
52
|
+
details
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [Status::Step] do we have all topics with expected number of partitions
|
57
|
+
def partitions
|
58
|
+
if topics.success?
|
59
|
+
status = :success
|
60
|
+
status = :failure if topics_details[topics_consumers_states][:partitions] != 1
|
61
|
+
status = :failure if topics_details[topics_consumers_reports][:partitions] != 1
|
62
|
+
details = topics_details
|
63
|
+
else
|
64
|
+
status = :halted
|
65
|
+
details = {}
|
66
|
+
end
|
67
|
+
|
68
|
+
Step.new(
|
69
|
+
status,
|
70
|
+
details
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
# @return [Status::Step] Is the initial state present in the setup or not
|
75
|
+
def initial_state
|
76
|
+
if partitions.success?
|
77
|
+
@current_state ||= Models::State.current
|
78
|
+
status = @current_state ? :success : :failure
|
79
|
+
else
|
80
|
+
status = :halted
|
81
|
+
end
|
82
|
+
|
83
|
+
Step.new(
|
84
|
+
status,
|
85
|
+
nil
|
86
|
+
)
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Status::Step] Is there at least one active karafka server reporting to the
|
90
|
+
# Web UI
|
91
|
+
def live_reporting
|
92
|
+
if initial_state.success?
|
93
|
+
@processes ||= Models::Processes.active(@current_state)
|
94
|
+
status = @processes.empty? ? :failure : :success
|
95
|
+
else
|
96
|
+
status = :halted
|
97
|
+
end
|
98
|
+
|
99
|
+
Step.new(
|
100
|
+
status,
|
101
|
+
nil
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Status::Step] is there a subscription to our reports topic that is being
|
106
|
+
# consumed actively.
|
107
|
+
def state_calculation
|
108
|
+
if live_reporting.success?
|
109
|
+
@subscriptions ||= Models::Health.current(@current_state).values.flat_map(&:keys)
|
110
|
+
status = @subscriptions.include?(topics_consumers_reports) ? :success : :failure
|
111
|
+
else
|
112
|
+
status = :halted
|
113
|
+
end
|
114
|
+
|
115
|
+
Step.new(
|
116
|
+
status,
|
117
|
+
nil
|
118
|
+
)
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
# @return [String] consumers states topic name
|
124
|
+
def topics_consumers_states
|
125
|
+
::Karafka::Web.config.topics.consumers.states.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
# @return [String] consumers reports topic name
|
129
|
+
def topics_consumers_reports
|
130
|
+
::Karafka::Web.config.topics.consumers.reports.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
# @return [String] errors topic name
|
134
|
+
def topics_errors
|
135
|
+
::Karafka::Web.config.topics.errors
|
136
|
+
end
|
137
|
+
|
138
|
+
# @return [Hash] hash with topics with which we work details (even if don't exist)
|
139
|
+
def topics_details
|
140
|
+
topics = {
|
141
|
+
topics_consumers_states => { present: false, partitions: 0 },
|
142
|
+
topics_consumers_reports => { present: false, partitions: 0 },
|
143
|
+
topics_errors => { present: false, partitions: 0 }
|
144
|
+
}
|
145
|
+
|
146
|
+
@cluster_info.topics.each do |topic|
|
147
|
+
name = topic[:topic_name]
|
148
|
+
|
149
|
+
next unless topics.key?(name)
|
150
|
+
|
151
|
+
topics[name][:present] = true
|
152
|
+
topics[name][:partitions] = topic[:partition_count]
|
153
|
+
end
|
154
|
+
|
155
|
+
topics
|
156
|
+
end
|
157
|
+
|
158
|
+
# Tries connecting with the cluster and sets the connection state
|
159
|
+
def connect
|
160
|
+
@cluster_info = ::Karafka::Admin.cluster_info
|
161
|
+
@connected = true
|
162
|
+
rescue ::Rdkafka::RdkafkaError
|
163
|
+
@connected = false
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -112,6 +112,11 @@ module Karafka
|
|
112
112
|
controller = Controllers::Dlq.new(params)
|
113
113
|
render_response controller.index
|
114
114
|
end
|
115
|
+
|
116
|
+
r.get 'status' do
|
117
|
+
controller = Controllers::Status.new(params)
|
118
|
+
render_response controller.show
|
119
|
+
end
|
115
120
|
end
|
116
121
|
end
|
117
122
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This Karafka component is a Pro component under a commercial license.
|
4
|
+
# This Karafka component is NOT licensed under LGPL.
|
5
|
+
#
|
6
|
+
# All of the commercial components are present in the lib/karafka/pro directory of this
|
7
|
+
# repository and their usage requires commercial license agreement.
|
8
|
+
#
|
9
|
+
# Karafka has also commercial-friendly license, commercial support and commercial components.
|
10
|
+
#
|
11
|
+
# By sending a pull request to the pro components, you are agreeing to transfer the copyright of
|
12
|
+
# your code to Maciej Mensfeld.
|
13
|
+
|
14
|
+
module Karafka
|
15
|
+
module Web
|
16
|
+
module Ui
|
17
|
+
module Pro
|
18
|
+
module Controllers
|
19
|
+
# Status details - same as in OSS
|
20
|
+
class Status < Ui::Controllers::Status
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -8,16 +8,24 @@
|
|
8
8
|
<%= process.name %>
|
9
9
|
</a>
|
10
10
|
|
11
|
-
<
|
11
|
+
<p class="mt-0 mb-1">
|
12
|
+
<% process.consumer_groups.each do |consumer_group| %>
|
13
|
+
<% consumer_group.topics.each do |topic| %>
|
14
|
+
<span class="badge bg-secondary badge-topic" title="Consumer group: <%= consumer_group.id %>">
|
15
|
+
<%= topic.name %>:
|
16
|
+
<%= topic.partitions.map(&:id).join(',') %>
|
17
|
+
</span>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
</p>
|
12
21
|
|
13
|
-
|
14
|
-
<%
|
15
|
-
<span class="badge bg-
|
16
|
-
<%=
|
17
|
-
<%= topic.partitions.map(&:id).join(',') %>
|
22
|
+
<p class="mt-0 mb-1">
|
23
|
+
<% process.tags.each do |tag| %>
|
24
|
+
<span class="badge bg-info badge-topic">
|
25
|
+
<%= tag %>
|
18
26
|
</span>
|
19
27
|
<% end %>
|
20
|
-
|
28
|
+
</p>
|
21
29
|
</td>
|
22
30
|
|
23
31
|
<td>
|
@@ -27,6 +27,16 @@
|
|
27
27
|
</li>
|
28
28
|
</ul>
|
29
29
|
</p>
|
30
|
+
|
31
|
+
<% unless @process.tags.empty? %>
|
32
|
+
<p class="mb-0 text-end">
|
33
|
+
<% @process.tags.each do |tag| %>
|
34
|
+
<span class="badge bg-info badge-topic">
|
35
|
+
<%= tag %>
|
36
|
+
</span>
|
37
|
+
<% end %>
|
38
|
+
</p>
|
39
|
+
<% end %>
|
30
40
|
</div>
|
31
41
|
</div>
|
32
42
|
<div class="card">
|
@@ -8,16 +8,24 @@
|
|
8
8
|
<%= process.name %>
|
9
9
|
</a>
|
10
10
|
|
11
|
-
<
|
11
|
+
<p class="mt-0 mb-1">
|
12
|
+
<% process.consumer_groups.each do |consumer_group| %>
|
13
|
+
<% consumer_group.topics.each do |topic| %>
|
14
|
+
<span class="badge bg-secondary badge-topic" title="Consumer group: <%= consumer_group.id %>">
|
15
|
+
<%= topic.name %>:
|
16
|
+
<%= topic.partitions.map(&:id).join(',') %>
|
17
|
+
</span>
|
18
|
+
<% end %>
|
19
|
+
<% end %>
|
20
|
+
</p>
|
12
21
|
|
13
|
-
|
14
|
-
<%
|
15
|
-
<span class="badge bg-
|
16
|
-
<%=
|
17
|
-
<%= topic.partitions.map(&:id).join(',') %>
|
22
|
+
<p class="mt-0 mb-1">
|
23
|
+
<% process.tags.each do |tag| %>
|
24
|
+
<span class="badge bg-info badge-topic">
|
25
|
+
<%= tag %>
|
18
26
|
</span>
|
19
27
|
<% end %>
|
20
|
-
|
28
|
+
</p>
|
21
29
|
</td>
|
22
30
|
|
23
31
|
<td>
|
@@ -20,6 +20,7 @@
|
|
20
20
|
</p>
|
21
21
|
|
22
22
|
<ul class="mb-5 text-start">
|
23
|
+
<li>You have visited the <a href="<%= root_path('status') %>">Status</a> page to troubleshoot any potential issues</li>
|
23
24
|
<li>All the topics required by Karafka Web exist</li>
|
24
25
|
<li>You have used <code>bundle exec karafka-web install</code> to initialize the Web UI</li>
|
25
26
|
<li>You have a working connection with your Kafka cluster</li>
|
@@ -32,6 +33,7 @@
|
|
32
33
|
|
33
34
|
<p>
|
34
35
|
<a href="<%= root_path %>" class="btn btn-primary">Go Home</a>
|
36
|
+
<a href="<%= root_path('status') %>" class="btn btn-success">Status page</a>
|
35
37
|
</p>
|
36
38
|
</div>
|
37
39
|
</div>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<div class="card border-danger mb-3">
|
2
|
+
<div class="card-header text-bg-danger">
|
3
|
+
<span>
|
4
|
+
<%= title %>
|
5
|
+
</span>
|
6
|
+
|
7
|
+
<span class="float-end">
|
8
|
+
<span class="badge text-bg-light">Failure</span>
|
9
|
+
</span>
|
10
|
+
</div>
|
11
|
+
<div class="card-body">
|
12
|
+
<%== description %>
|
13
|
+
</div>
|
14
|
+
</div>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<p>
|
2
|
+
The initial state for the Web UI was not created.
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p>
|
6
|
+
It means that the <code>bundle exec karafka-web install</code> was not executed or failed.
|
7
|
+
</p>
|
8
|
+
|
9
|
+
<p>
|
10
|
+
To fix this, you can either:
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<ul>
|
14
|
+
<li>Run <code>bundle exec karafka-web install</code></li>
|
15
|
+
<li>Run at least one Karafka consumer process</li>
|
16
|
+
</ul>
|
17
|
+
|
18
|
+
<p class="mb-0">
|
19
|
+
Any of the above, when successful, will bootstrap the initial state.
|
20
|
+
</p>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<p>
|
2
|
+
There are no Karafka consumer processes actively reporting to the Web UI.
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<p class="mb-0">
|
6
|
+
If you are sure you are running at least one <code>karafka server</code> instance, please make sure that it can report to the <code><%= Karafka::Web.config.topics.consumers.reports %></code> topic.
|
7
|
+
</p>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<p>
|
2
|
+
Both
|
3
|
+
<code><%= Karafka::Web.config.topics.consumers.states %></code>
|
4
|
+
and
|
5
|
+
<code><%= Karafka::Web.config.topics.consumers.reports %></code>
|
6
|
+
topics need to be configured with <strong>exactly</strong> one partition.
|
7
|
+
</p>
|
8
|
+
|
9
|
+
<p>
|
10
|
+
Your current setup contains the following:
|
11
|
+
</p>
|
12
|
+
|
13
|
+
<ul class="mb-0">
|
14
|
+
<% details.each do |name, details| %>
|
15
|
+
<li>
|
16
|
+
<code><%= name %> </code> with <code><%= details[:partitions] %> </code> partitions.
|
17
|
+
</li>
|
18
|
+
<% end %>
|
19
|
+
</ul>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<p>
|
2
|
+
None of the <code>karafka server</code> processes is subscribed to handle the
|
3
|
+
<code><%= Karafka::Web.config.topics.consumers.reports %></code> topic data aggregations.
|
4
|
+
</p>
|
5
|
+
|
6
|
+
<p class="mb-0">
|
7
|
+
If you are limiting the topics you consume using the <a href="https://karafka.io/docs/CLI/#limiting-consumer-groups-used-per-process" target="_blank">limiting API</a>, please include the Karafka Web consumer group and the <code><%= Karafka::Web.config.topics.consumers.reports %></code> topic.
|
8
|
+
</p>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<p>
|
2
|
+
Web UI requires the following topics to exist:
|
3
|
+
</p>
|
4
|
+
|
5
|
+
<ul>
|
6
|
+
<% details.each do |name, details| %>
|
7
|
+
<li>
|
8
|
+
<code><%= name %></code> - <%= details[:present] ? 'Exists' : 'Missing' %>
|
9
|
+
</li>
|
10
|
+
<% end %>
|
11
|
+
</ul>
|
12
|
+
|
13
|
+
<p>
|
14
|
+
Please ensure all those topics exist and are accessible to your Karafka user.
|
15
|
+
</p>
|
16
|
+
|
17
|
+
<p class="mb-0">
|
18
|
+
You can read more about their setup and configuration
|
19
|
+
<a href="https://karafka.io/docs/Web-UI-Getting-Started/" target="_blank">here</a>.
|
20
|
+
</p>
|
@@ -0,0 +1,94 @@
|
|
1
|
+
<%== view_title('Web UI status details') %>
|
2
|
+
|
3
|
+
<div class="container mb-5">
|
4
|
+
<div class="row">
|
5
|
+
<div class="col-lg-10 offset-md-1">
|
6
|
+
|
7
|
+
<%==
|
8
|
+
partial(
|
9
|
+
"status/#{@status.connection.to_s}",
|
10
|
+
locals: {
|
11
|
+
title: 'Connection to Kafka',
|
12
|
+
description: partial('status/failures/connection')
|
13
|
+
}
|
14
|
+
)
|
15
|
+
%>
|
16
|
+
|
17
|
+
<%==
|
18
|
+
partial(
|
19
|
+
"status/#{@status.topics.to_s}",
|
20
|
+
locals: {
|
21
|
+
title: 'Topics presence',
|
22
|
+
description: partial(
|
23
|
+
'status/failures/topics',
|
24
|
+
locals: {
|
25
|
+
details: @status.topics.details
|
26
|
+
}
|
27
|
+
)
|
28
|
+
}
|
29
|
+
)
|
30
|
+
%>
|
31
|
+
|
32
|
+
<%==
|
33
|
+
partial(
|
34
|
+
"status/#{@status.partitions.to_s}",
|
35
|
+
locals: {
|
36
|
+
title: 'Partitions count',
|
37
|
+
description: partial(
|
38
|
+
'status/failures/partitions',
|
39
|
+
locals: {
|
40
|
+
details: @status.partitions.details
|
41
|
+
}
|
42
|
+
)
|
43
|
+
}
|
44
|
+
)
|
45
|
+
%>
|
46
|
+
|
47
|
+
<%==
|
48
|
+
partial(
|
49
|
+
"status/#{@status.initial_state.to_s}",
|
50
|
+
locals: {
|
51
|
+
title: 'Initial state presence',
|
52
|
+
description: partial(
|
53
|
+
'status/failures/initial_state',
|
54
|
+
locals: {
|
55
|
+
details: @status.initial_state.details
|
56
|
+
}
|
57
|
+
)
|
58
|
+
}
|
59
|
+
)
|
60
|
+
%>
|
61
|
+
|
62
|
+
<%==
|
63
|
+
partial(
|
64
|
+
"status/#{@status.live_reporting.to_s}",
|
65
|
+
locals: {
|
66
|
+
title: 'Live reporting',
|
67
|
+
description: partial(
|
68
|
+
'status/failures/live_reporting',
|
69
|
+
locals: {
|
70
|
+
details: @status.live_reporting.details
|
71
|
+
}
|
72
|
+
)
|
73
|
+
}
|
74
|
+
)
|
75
|
+
%>
|
76
|
+
|
77
|
+
<%==
|
78
|
+
partial(
|
79
|
+
"status/#{@status.state_calculation.to_s}",
|
80
|
+
locals: {
|
81
|
+
title: 'State calculation subscription',
|
82
|
+
description: partial(
|
83
|
+
'status/failures/state_calculation',
|
84
|
+
locals: {
|
85
|
+
details: @status.state_calculation.details
|
86
|
+
}
|
87
|
+
)
|
88
|
+
}
|
89
|
+
)
|
90
|
+
%>
|
91
|
+
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
</div>
|
data/lib/karafka/web/version.rb
CHANGED
data/lib/karafka/web.rb
CHANGED
@@ -27,12 +27,6 @@ module Karafka
|
|
27
27
|
Config.config
|
28
28
|
end
|
29
29
|
|
30
|
-
# Creates all the needed topics for the admin UI to work and populates initial (empty) set
|
31
|
-
# of data, so the UI will work even when no Karafka servers are started
|
32
|
-
def bootstrap!
|
33
|
-
Installer.new.bootstrap!
|
34
|
-
end
|
35
|
-
|
36
30
|
# Activates all the needed routing and sets up listener, etc
|
37
31
|
# This needs to run **after** the optional configuration of the web component
|
38
32
|
def enable!
|
data.tar.gz.sig
CHANGED
Binary file
|