karafka-web 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|