ruby-grafana-reporter 0.1.7 → 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
- data/README.md +166 -339
- data/bin/ruby-grafana-reporter +5 -4
- data/lib/VERSION.rb +5 -3
- data/lib/grafana/abstract_panel_query.rb +22 -20
- data/lib/grafana/abstract_query.rb +132 -127
- data/lib/grafana/abstract_sql_query.rb +51 -42
- data/lib/grafana/dashboard.rb +77 -66
- data/lib/grafana/errors.rb +66 -61
- data/lib/grafana/grafana.rb +130 -131
- data/lib/grafana/panel.rb +41 -39
- data/lib/grafana/panel_image_query.rb +52 -49
- data/lib/grafana/variable.rb +217 -259
- data/lib/grafana_reporter/abstract_report.rb +112 -109
- data/lib/grafana_reporter/application/application.rb +404 -229
- data/lib/grafana_reporter/application/errors.rb +33 -30
- data/lib/grafana_reporter/application/webservice.rb +231 -0
- data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +104 -99
- data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +99 -96
- data/lib/grafana_reporter/asciidoctor/errors.rb +40 -37
- data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +92 -86
- data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +91 -86
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +69 -67
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +68 -65
- data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +61 -58
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +78 -75
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +73 -70
- data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +20 -18
- data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +43 -41
- data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +70 -67
- data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +66 -65
- data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +61 -57
- data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +34 -32
- data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +25 -23
- data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +44 -43
- data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +38 -36
- data/lib/grafana_reporter/asciidoctor/query_mixin.rb +310 -309
- data/lib/grafana_reporter/asciidoctor/report.rb +177 -159
- data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +37 -34
- data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +39 -32
- data/lib/grafana_reporter/configuration.rb +257 -326
- data/lib/grafana_reporter/errors.rb +48 -38
- data/lib/grafana_reporter/logger/two_way_logger.rb +58 -52
- data/lib/ruby-grafana-reporter.rb +29 -27
- metadata +10 -23
@@ -1,109 +1,112 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
class
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
# @param
|
31
|
-
# @param
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@destination_file_or_path.
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
return '
|
87
|
-
|
88
|
-
'
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
# @abstract Override {#create_report} and {#progress}.
|
5
|
+
#
|
6
|
+
# This class is used to build a report on basis of a given configuration and
|
7
|
+
# template.
|
8
|
+
#
|
9
|
+
# Objects of this class are also stored in {Application::Application}, unless
|
10
|
+
# the retention time is over.
|
11
|
+
class AbstractReport
|
12
|
+
# @return [String] path to the template
|
13
|
+
attr_reader :template
|
14
|
+
|
15
|
+
# @return [Time] time, when the report generation started
|
16
|
+
attr_reader :start_time
|
17
|
+
|
18
|
+
# @return [Time] time, when the report generation ended
|
19
|
+
attr_reader :end_time
|
20
|
+
|
21
|
+
# @return [Logger] logger object used during report generation
|
22
|
+
attr_reader :logger
|
23
|
+
|
24
|
+
# @return [Boolean] true, if the report is or shall be cancelled
|
25
|
+
attr_reader :cancel
|
26
|
+
|
27
|
+
# @return [Boolen] true, if the report generation is finished (successfull or not)
|
28
|
+
attr_reader :done
|
29
|
+
|
30
|
+
# @param config [Configuration] configuration object
|
31
|
+
# @param template [String] path to the template to be used
|
32
|
+
# @param destination_file_or_path [String or File] path to the destination report or file object to use
|
33
|
+
# @param custom_attributes [Hash] custom attributes, which shall be merged with priority over the configuration
|
34
|
+
def initialize(config, template, destination_file_or_path = nil, custom_attributes = {})
|
35
|
+
@config = config
|
36
|
+
@logger = Logger::TwoWayDelegateLogger.new
|
37
|
+
@logger.additional_logger = @config.logger
|
38
|
+
@done = false
|
39
|
+
@template = template
|
40
|
+
@destination_file_or_path = destination_file_or_path
|
41
|
+
@custom_attributes = custom_attributes
|
42
|
+
@start_time = nil
|
43
|
+
@end_time = nil
|
44
|
+
@cancel = false
|
45
|
+
raise MissingTemplateError, @template.to_s unless File.exist?(@template.to_s)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Call to request cancelling the report generation.
|
49
|
+
# @return [void]
|
50
|
+
def cancel!
|
51
|
+
@cancel = true
|
52
|
+
logger.info('Cancelling report generation invoked.')
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return [String] path to the report destination file
|
56
|
+
def path
|
57
|
+
@destination_file_or_path.respond_to?(:path) ? @destination_file_or_path.path : @destination_file_or_path
|
58
|
+
end
|
59
|
+
|
60
|
+
# Deletes the report file object.
|
61
|
+
# @return [void]
|
62
|
+
def delete_file
|
63
|
+
if @destination_file_or_path.is_a?(Tempfile)
|
64
|
+
@destination_file_or_path.unlink
|
65
|
+
elsif @destination_file_or_path.is_a?(File)
|
66
|
+
@destination_file_or_path.delete
|
67
|
+
end
|
68
|
+
@destination_file_or_path = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# @return [Float] time in seconds, that the report generation took
|
72
|
+
def execution_time
|
73
|
+
return nil if start_time.nil?
|
74
|
+
return end_time - start_time unless end_time.nil?
|
75
|
+
|
76
|
+
Time.now - start_time
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Array] error messages during report generation.
|
80
|
+
def error
|
81
|
+
@error || []
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [String] status of the report, one of 'in progress', 'cancelled', 'died' or 'finished'.
|
85
|
+
def status
|
86
|
+
return 'cancelled' if done && cancel
|
87
|
+
return 'finished' if done && error.empty?
|
88
|
+
return 'died' if done && !error.empty?
|
89
|
+
|
90
|
+
'in progress'
|
91
|
+
end
|
92
|
+
|
93
|
+
# @return [String] string containing all messages ([Logger::Severity::DEBUG]) of the logger during report
|
94
|
+
# generation.
|
95
|
+
def full_log
|
96
|
+
logger.internal_messages
|
97
|
+
end
|
98
|
+
|
99
|
+
# @abstract
|
100
|
+
# Is being called to start the report generation.
|
101
|
+
# @return [void]
|
102
|
+
def create_report
|
103
|
+
raise NotImplementedError
|
104
|
+
end
|
105
|
+
|
106
|
+
# @abstract
|
107
|
+
# @return [Integer] number between 0 and 100, representing the current progress of the report creation.
|
108
|
+
def progress
|
109
|
+
raise NotImplementedError
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -1,229 +1,404 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
#
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
#
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
if
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
# This module contains all classes, which are used by the grafana reporter
|
5
|
+
# application. The application is a set of classes, which allows to run the
|
6
|
+
# reporter in several ways.
|
7
|
+
#
|
8
|
+
# If you intend to use the reporter functionality, without the application,
|
9
|
+
# it might be helpful to not use the classes from here.
|
10
|
+
module Application
|
11
|
+
# This class contains the main application to run the grafana reporter.
|
12
|
+
#
|
13
|
+
# It can be run to test the grafana connection, render a single template
|
14
|
+
# or run as a service.
|
15
|
+
class Application
|
16
|
+
# Default file name for grafana reporter configuration file
|
17
|
+
CONFIG_FILE = 'grafana_reporter.config'
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@logger = ::Logger.new($stdout, level: :unknown)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Contains the {Configuration} object of the application.
|
24
|
+
attr_accessor :config
|
25
|
+
|
26
|
+
# This is the main method, which is called, if the application is
|
27
|
+
# run in standalone mode.
|
28
|
+
# @param params [Array<String>] command line parameters, mainly ARGV can be used.
|
29
|
+
# @return [Integer] 0 if everything is fine, -1 if execution aborted.
|
30
|
+
def configure_and_run(params = [])
|
31
|
+
config_file = CONFIG_FILE
|
32
|
+
cli_config = {}
|
33
|
+
cli_config ['grafana-reporter'] = {}
|
34
|
+
cli_config ['default-document-attributes'] = {}
|
35
|
+
|
36
|
+
parser = OptionParser.new do |opts|
|
37
|
+
opts.banner = "Usage: ruby #{$PROGRAM_NAME} [options]"
|
38
|
+
|
39
|
+
opts.on('-c', '--config CONFIG_FILE_NAME', 'Specify custom configuration file,'\
|
40
|
+
" instead of #{CONFIG_FILE}.") do |file_name|
|
41
|
+
config_file = file_name
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on('-d', '--debug LEVEL', 'Specify detail level: FATAL, ERROR, WARN, INFO, DEBUG.') do |level|
|
45
|
+
if level =~ /(?:FATAL|ERROR|WARN|INFO|DEBUG)/
|
46
|
+
@logger.level = Object.const_get("::Logger::Severity::#{level}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on('-o', '--output FILE', 'Output filename if only a single file is rendered') do |file|
|
51
|
+
cli_config['to_file'] = file
|
52
|
+
end
|
53
|
+
|
54
|
+
opts.on('-s', '--set VARIABLE,VALUE', Array, 'Set a variable value, which will be passed to the rendering') do |list|
|
55
|
+
raise ParameterValueError.new(list.length) unless list.length == 2
|
56
|
+
cli_config['default-document-attributes'][list[0]] = list[1]
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('--test GRAFANA_INSTANCE', 'test current configuration against given GRAFANA_INSTANCE') do |instance|
|
60
|
+
cli_config['grafana-reporter']['run-mode'] = 'test'
|
61
|
+
cli_config['grafana-reporter']['test-instance'] = instance
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('-t', '--template TEMPLATE', 'Render a single ASCIIDOC template to PDF and exit') do |template|
|
65
|
+
cli_config['grafana-reporter']['run-mode'] = 'single-render'
|
66
|
+
cli_config['default-document-attributes']['var-template'] = template
|
67
|
+
end
|
68
|
+
|
69
|
+
opts.on('-w', '--wizard', 'Configuration wizard to prepare environment for the reporter.') do
|
70
|
+
return config_wizard
|
71
|
+
end
|
72
|
+
|
73
|
+
opts.on('-v', '--version', 'Version information') do
|
74
|
+
puts GRAFANA_REPORTER_VERSION.join('.')
|
75
|
+
return -1
|
76
|
+
end
|
77
|
+
|
78
|
+
opts.on('-h', '--help', 'Show this message') do
|
79
|
+
puts opts
|
80
|
+
return -1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
begin
|
85
|
+
parser.parse!(params)
|
86
|
+
rescue ApplicationError => e
|
87
|
+
puts e.message
|
88
|
+
return -1
|
89
|
+
end
|
90
|
+
|
91
|
+
# abort if config file does not exist
|
92
|
+
unless File.exist?(config_file)
|
93
|
+
puts "Config file '#{config_file}' does not exist. Consider calling the configuration wizard"\
|
94
|
+
' with option \'-w\' or use \'-h\' to see help message. Aborting.'
|
95
|
+
return -1
|
96
|
+
end
|
97
|
+
|
98
|
+
# read config file
|
99
|
+
@config = GrafanaReporter::Configuration.new
|
100
|
+
config.logger = @logger
|
101
|
+
config_hash = nil
|
102
|
+
begin
|
103
|
+
config_hash = YAML.load_file(config_file)
|
104
|
+
rescue StandardError => e
|
105
|
+
raise ConfigurationError, "Could not read config file '#{config_file}' (Error: #{e.message})"
|
106
|
+
end
|
107
|
+
|
108
|
+
# merge command line configuration with read config file
|
109
|
+
config_hash.merge!(cli_config) { |_key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2) : v2 }
|
110
|
+
config.config = config_hash
|
111
|
+
|
112
|
+
run
|
113
|
+
end
|
114
|
+
|
115
|
+
# Runs the application with the current set {Configuration} object.
|
116
|
+
# @return [Integer] value smaller than 0, if error. 0 if successfull
|
117
|
+
def run
|
118
|
+
begin
|
119
|
+
config.validate
|
120
|
+
rescue ConfigurationError => e
|
121
|
+
puts e.message
|
122
|
+
return -2
|
123
|
+
end
|
124
|
+
|
125
|
+
case config.mode
|
126
|
+
when Configuration::MODE_CONNECTION_TEST
|
127
|
+
res = Grafana::Grafana.new(config.grafana_host(config.test_instance),
|
128
|
+
config.grafana_api_key(config.test_instance),
|
129
|
+
logger: config.logger).test_connection
|
130
|
+
puts res
|
131
|
+
|
132
|
+
when Configuration::MODE_SINGLE_RENDER
|
133
|
+
begin
|
134
|
+
config.report_class.new(config, config.template, config.to_file).create_report
|
135
|
+
rescue StandardError => e
|
136
|
+
puts e.message
|
137
|
+
end
|
138
|
+
|
139
|
+
when Configuration::MODE_SERVICE
|
140
|
+
Webservice.new(config).run
|
141
|
+
end
|
142
|
+
|
143
|
+
0
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
# Provides a command line configuration wizard for setting up the necessary configuration
|
149
|
+
# file.
|
150
|
+
def config_wizard
|
151
|
+
if File.exist?(CONFIG_FILE)
|
152
|
+
input = nil
|
153
|
+
until input
|
154
|
+
input = user_input("Configuration file '#{CONFIG_FILE}' already exists. Do you want to overwrite it?", 'yN')
|
155
|
+
return if input =~ /^(?:n|N|yN)$/
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
puts 'This wizard will guide you through an initial configuration for'\
|
160
|
+
' the ruby-grafana-reporter. The configuration file will be created'\
|
161
|
+
' in the current folder. Please make sure to specify necessary paths'\
|
162
|
+
' either with a relative or an absolute path properly.'
|
163
|
+
puts
|
164
|
+
port = ui_config_port
|
165
|
+
grafana = ui_config_grafana
|
166
|
+
templates = ui_config_templates_folder
|
167
|
+
reports = ui_config_reports_folder
|
168
|
+
images = ui_config_images_folder(templates)
|
169
|
+
retention = ui_config_retention
|
170
|
+
|
171
|
+
config_yaml = %(# This configuration has been built with the configuration wizard.
|
172
|
+
|
173
|
+
#{grafana}
|
174
|
+
|
175
|
+
grafana-reporter:
|
176
|
+
templates-folder: #{templates}
|
177
|
+
reports-folder: #{reports}
|
178
|
+
report-retention: #{retention}
|
179
|
+
webservice-port: #{port}
|
180
|
+
|
181
|
+
default-document-attributes:
|
182
|
+
imagesdir: #{images}
|
183
|
+
# feel free to add here additional asciidoctor document attributes which are applied to all your templates
|
184
|
+
)
|
185
|
+
|
186
|
+
begin
|
187
|
+
File.write(CONFIG_FILE, config_yaml, mode: 'w')
|
188
|
+
puts 'Configuration file successfully created.'
|
189
|
+
rescue StandardError => e
|
190
|
+
raise e
|
191
|
+
end
|
192
|
+
|
193
|
+
config = Configuration.new
|
194
|
+
begin
|
195
|
+
config.config = YAML.load_file(CONFIG_FILE)
|
196
|
+
puts 'Configuration file validated successfully.'
|
197
|
+
rescue StandardError => e
|
198
|
+
raise ConfigurationError, "Could not read config file '#{CONFIG_FILE}' (Error: #{e.message})\n"\
|
199
|
+
"Source:\n#{File.read(CONFIG_FILE)}"
|
200
|
+
end
|
201
|
+
|
202
|
+
# create a demo report
|
203
|
+
unless Dir.exist?(config.templates_folder)
|
204
|
+
puts "Skip creation of DEMO template, as folder '#{config.templates_folder}' does not exist."
|
205
|
+
return
|
206
|
+
end
|
207
|
+
demo_report = %(= First Grafana Report Template
|
208
|
+
|
209
|
+
include::grafana_help[]
|
210
|
+
|
211
|
+
include::grafana_environment[])
|
212
|
+
|
213
|
+
demo_report_file = "#{config.templates_folder}demo_report.adoc"
|
214
|
+
if File.exist?(demo_report_file)
|
215
|
+
puts "Skip creation of DEMO template, as file '#{demo_report_file}' already exists."
|
216
|
+
else
|
217
|
+
begin
|
218
|
+
File.write(demo_report_file, demo_report, mode: 'w')
|
219
|
+
puts "DEMO template '#{demo_report_file}' successfully created."
|
220
|
+
rescue StandardError => e
|
221
|
+
raise e
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
puts
|
226
|
+
puts 'Now everything is setup properly. Run the grafana reporter without any command to start the service.'
|
227
|
+
puts
|
228
|
+
puts ' ruby-grafana-reporter'
|
229
|
+
puts
|
230
|
+
puts "Open 'http://localhost:#{config.webserver_port}/render?var-template=demo_report' in a webbrowser to"
|
231
|
+
puts 'verify your configuration.'
|
232
|
+
end
|
233
|
+
|
234
|
+
def ui_config_grafana
|
235
|
+
valid = false
|
236
|
+
url = nil
|
237
|
+
api_key = nil
|
238
|
+
datasources = ''
|
239
|
+
until valid
|
240
|
+
url ||= user_input('Specify grafana host', 'http://localhost:3000')
|
241
|
+
print "Testing connection to '#{url}' #{api_key ? '_with_' : '_without_'} API key..."
|
242
|
+
begin
|
243
|
+
res = Grafana::Grafana.new(url,
|
244
|
+
api_key,
|
245
|
+
logger: @logger).test_connection
|
246
|
+
rescue StandardError => e
|
247
|
+
puts
|
248
|
+
puts e.message
|
249
|
+
end
|
250
|
+
puts 'done.'
|
251
|
+
|
252
|
+
case res
|
253
|
+
when 'Admin'
|
254
|
+
valid = true
|
255
|
+
|
256
|
+
when 'NON-Admin'
|
257
|
+
print 'Access to grafana is permitted as NON-Admin. Do you want to use an [a]pi key,'\
|
258
|
+
' configure [d]atasource manually, [r]e-enter api key or [i]gnore? [adRi]: '
|
259
|
+
|
260
|
+
case gets
|
261
|
+
when /(?:i|I)$/
|
262
|
+
valid = true
|
263
|
+
|
264
|
+
when /(?:a|A)$/
|
265
|
+
print 'Enter API key: '
|
266
|
+
api_key = gets.sub(/\n$/, '')
|
267
|
+
|
268
|
+
when /(?:r|R|adRi)$/
|
269
|
+
api_key = nil
|
270
|
+
|
271
|
+
when /(?:d|D)$/
|
272
|
+
valid = true
|
273
|
+
datasources = ui_config_datasources
|
274
|
+
|
275
|
+
end
|
276
|
+
|
277
|
+
else
|
278
|
+
print "Grafana could not be accessed at '#{url}'. Do you want do [r]e-enter url, or"\
|
279
|
+
' [i]gnore and proceed? [Ri]: '
|
280
|
+
|
281
|
+
case gets
|
282
|
+
when /(?:i|I)$/
|
283
|
+
valid = true
|
284
|
+
|
285
|
+
else
|
286
|
+
url = nil
|
287
|
+
api_key = nil
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
end
|
292
|
+
end
|
293
|
+
%(grafana:
|
294
|
+
default:
|
295
|
+
host: #{url}#{api_key ? "\n api_key: #{api_key}" : ''}#{datasources ? "\n#{datasources}" : ''}
|
296
|
+
)
|
297
|
+
end
|
298
|
+
|
299
|
+
def ui_config_datasources
|
300
|
+
finished = false
|
301
|
+
datasources = []
|
302
|
+
until finished
|
303
|
+
item = {}
|
304
|
+
print "Datasource ###{datasources.length + 1}) Enter datasource name as configured in grafana: "
|
305
|
+
item[:ds_name] = gets.sub(/\n$/, '')
|
306
|
+
print "Datasource ###{datasources.length + 1}) Enter datasource id: "
|
307
|
+
item[:ds_id] = gets.sub(/\n$/, '')
|
308
|
+
|
309
|
+
puts
|
310
|
+
selection = user_input("Datasource name: '#{item[:ds_name]}', Datasource id: '#{item[:ds_id]}'."\
|
311
|
+
' [A]ccept, [r]etry or [c]ancel?', 'Arc')
|
312
|
+
|
313
|
+
case selection
|
314
|
+
when /(?:Arc|A|a)$/
|
315
|
+
datasources << item
|
316
|
+
another = user_input('Add [a]nother datasource or [d]one?', 'aD')
|
317
|
+
finished = true if another =~ /(?:d|D)$/
|
318
|
+
|
319
|
+
when /(?:c|C)$/
|
320
|
+
finished = true
|
321
|
+
|
322
|
+
end
|
323
|
+
end
|
324
|
+
" datasources:\n#{datasources.collect { |el| " #{el[:ds_name]}: #{el[:ds_id]}" }.join('\n')}"
|
325
|
+
end
|
326
|
+
|
327
|
+
def ui_config_port
|
328
|
+
input = nil
|
329
|
+
until input
|
330
|
+
input = user_input('Specify port on which reporter shall run', '8815')
|
331
|
+
input = nil unless input =~ /[0-9]+/
|
332
|
+
end
|
333
|
+
input
|
334
|
+
end
|
335
|
+
|
336
|
+
def ui_config_templates_folder
|
337
|
+
input = nil
|
338
|
+
until input
|
339
|
+
input = user_input('Specify path where templates shall be stored', './templates')
|
340
|
+
input = nil unless validate_config_folder(input)
|
341
|
+
end
|
342
|
+
input
|
343
|
+
end
|
344
|
+
|
345
|
+
def ui_config_reports_folder
|
346
|
+
input = nil
|
347
|
+
until input
|
348
|
+
input = user_input('Specify path where created reports shall be stored', './reports')
|
349
|
+
input = nil unless validate_config_folder(input)
|
350
|
+
end
|
351
|
+
input
|
352
|
+
end
|
353
|
+
|
354
|
+
def ui_config_images_folder(parent)
|
355
|
+
input = nil
|
356
|
+
until input
|
357
|
+
input = user_input('Specify path where rendered images shall be stored (relative to templates folder)',
|
358
|
+
'./images')
|
359
|
+
input = nil unless validate_config_folder(File.join(parent, input))
|
360
|
+
end
|
361
|
+
input
|
362
|
+
end
|
363
|
+
|
364
|
+
def ui_config_retention
|
365
|
+
input = nil
|
366
|
+
until input
|
367
|
+
input = user_input('Specify report retention duration in hours', '24')
|
368
|
+
input = nil unless input =~ /[0-9]+/
|
369
|
+
end
|
370
|
+
input
|
371
|
+
end
|
372
|
+
|
373
|
+
def user_input(text, default)
|
374
|
+
print "#{text} [#{default}]: "
|
375
|
+
input = gets.gsub(/\n$/, '')
|
376
|
+
input = default if input.empty?
|
377
|
+
input
|
378
|
+
end
|
379
|
+
|
380
|
+
def validate_config_folder(folder)
|
381
|
+
return true if Dir.exist?(folder)
|
382
|
+
|
383
|
+
print "Directory '#{folder} does not exist: [c]reate, [r]e-enter path or [i]gnore? [cRi]: "
|
384
|
+
case gets
|
385
|
+
when /^(?:c|C)$/
|
386
|
+
begin
|
387
|
+
Dir.mkdir(folder)
|
388
|
+
puts "Directory '#{folder}' successfully created."
|
389
|
+
return true
|
390
|
+
rescue StandardError => e
|
391
|
+
puts "WARN: Directory '#{folder}' could not be created. Please create it manually."
|
392
|
+
puts e.message
|
393
|
+
end
|
394
|
+
|
395
|
+
when /^(?:i|I)$/
|
396
|
+
puts "WARN: Directory '#{folder}' does not exist. Please create manually."
|
397
|
+
return true
|
398
|
+
end
|
399
|
+
|
400
|
+
false
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|