ruby-grafana-reporter 0.2.0 → 0.4.1
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 +105 -86
- data/bin/ruby-grafana-reporter +5 -5
- data/lib/VERSION.rb +3 -2
- data/lib/grafana/abstract_datasource.rb +136 -0
- data/lib/grafana/dashboard.rb +21 -23
- data/lib/grafana/errors.rb +8 -1
- data/lib/grafana/grafana.rb +61 -65
- data/lib/grafana/grafana_alerts_datasource.rb +57 -0
- data/lib/grafana/grafana_annotations_datasource.rb +56 -0
- data/lib/grafana/grafana_property_datasource.rb +25 -0
- data/lib/grafana/graphite_datasource.rb +50 -0
- data/lib/grafana/image_rendering_datasource.rb +44 -0
- data/lib/grafana/panel.rb +9 -3
- data/lib/grafana/prometheus_datasource.rb +45 -0
- data/lib/grafana/sql_datasource.rb +71 -0
- data/lib/grafana/unsupported_datasource.rb +7 -0
- data/lib/grafana/variable.rb +3 -2
- data/lib/grafana/webrequest.rb +71 -0
- data/lib/grafana_reporter/abstract_query.rb +359 -0
- data/lib/grafana_reporter/abstract_report.rb +119 -17
- data/lib/grafana_reporter/alerts_table_query.rb +44 -0
- data/lib/grafana_reporter/annotations_table_query.rb +43 -0
- data/lib/grafana_reporter/application/application.rb +49 -297
- data/lib/grafana_reporter/application/webservice.rb +49 -14
- data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +90 -0
- data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +89 -0
- data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +77 -0
- data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +79 -0
- data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +73 -0
- data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +99 -0
- data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +93 -0
- data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +64 -0
- data/lib/grafana_reporter/asciidoctor/report.rb +47 -76
- data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +46 -0
- data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +35 -0
- data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +92 -0
- data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +88 -0
- data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +90 -0
- data/lib/grafana_reporter/configuration.rb +108 -43
- data/lib/grafana_reporter/console_configuration_wizard.rb +311 -0
- data/lib/grafana_reporter/demo_report_wizard.rb +101 -0
- data/lib/grafana_reporter/erb/report.rb +43 -0
- data/lib/grafana_reporter/errors.rb +41 -0
- data/lib/grafana_reporter/help.rb +443 -0
- data/lib/grafana_reporter/logger/{two_way_logger.rb → two_way_delegate_logger.rb} +1 -1
- data/lib/grafana_reporter/panel_image_query.rb +29 -0
- data/lib/grafana_reporter/panel_property_query.rb +22 -0
- data/lib/grafana_reporter/query_value_query.rb +79 -0
- data/lib/grafana_reporter/report_webhook.rb +35 -0
- data/lib/ruby_grafana_extension.rb +8 -0
- data/lib/{ruby-grafana-reporter.rb → ruby_grafana_reporter.rb} +13 -0
- metadata +47 -43
- data/lib/grafana/abstract_panel_query.rb +0 -22
- data/lib/grafana/abstract_query.rb +0 -132
- data/lib/grafana/abstract_sql_query.rb +0 -51
- data/lib/grafana/panel_image_query.rb +0 -52
- data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +0 -104
- data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +0 -99
- data/lib/grafana_reporter/asciidoctor/errors.rb +0 -40
- data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +0 -92
- data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +0 -91
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +0 -69
- data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +0 -68
- data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +0 -61
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +0 -78
- data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +0 -73
- data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +0 -20
- data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +0 -43
- data/lib/grafana_reporter/asciidoctor/extensions/show_help_include_processor.rb +0 -202
- data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +0 -70
- data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +0 -66
- data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +0 -61
- data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +0 -34
- data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +0 -25
- data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +0 -44
- data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +0 -38
- data/lib/grafana_reporter/asciidoctor/query_mixin.rb +0 -310
- data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +0 -37
- data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +0 -39
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
module Asciidoctor
|
5
|
+
# Implements the hook
|
6
|
+
# include::grafana_environment[]
|
7
|
+
#
|
8
|
+
# Shows all available variables, which are accessible during this run of the asciidoctor
|
9
|
+
# grafana reporter in a asciidoctor readable form.
|
10
|
+
#
|
11
|
+
# This processor is very helpful during report template design, to find out the available
|
12
|
+
# variables, that can be accessed.
|
13
|
+
#
|
14
|
+
# == Used document parameters
|
15
|
+
# All, to be listed as the available environment.
|
16
|
+
class ShowEnvironmentIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
17
|
+
include ProcessorMixin
|
18
|
+
|
19
|
+
# :nodoc:
|
20
|
+
def handles?(target)
|
21
|
+
target.start_with? 'grafana_environment'
|
22
|
+
end
|
23
|
+
|
24
|
+
# :nodoc:
|
25
|
+
def process(doc, reader, _target, _attrs)
|
26
|
+
# return if @report.cancel
|
27
|
+
@report.next_step
|
28
|
+
@report.logger.debug('Processing ShowEnvironmentIncludeProcessor')
|
29
|
+
|
30
|
+
vars = ['== Accessible Variables',
|
31
|
+
'|===']
|
32
|
+
doc.attributes.sort.each do |k, v|
|
33
|
+
vars << "| `+{#{k}}+` | #{v}"
|
34
|
+
end
|
35
|
+
vars << '|==='
|
36
|
+
|
37
|
+
reader.unshift_lines vars
|
38
|
+
end
|
39
|
+
|
40
|
+
# @see ProcessorMixin#build_demo_entry
|
41
|
+
def build_demo_entry(_panel)
|
42
|
+
'include::grafana_environment[]'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
module Asciidoctor
|
5
|
+
# Implements the hook
|
6
|
+
# include::grafana_help[]
|
7
|
+
#
|
8
|
+
# Shows all available options for the asciidoctor grafana reporter in a asciidoctor readable form.
|
9
|
+
#
|
10
|
+
# == Used document parameters
|
11
|
+
# None
|
12
|
+
class ShowHelpIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
13
|
+
include ProcessorMixin
|
14
|
+
|
15
|
+
# :nodoc:
|
16
|
+
def handles?(target)
|
17
|
+
target.start_with? 'grafana_help'
|
18
|
+
end
|
19
|
+
|
20
|
+
# :nodoc:
|
21
|
+
def process(_doc, reader, _target, _attrs)
|
22
|
+
# return if @report.cancel
|
23
|
+
@report.next_step
|
24
|
+
@report.logger.debug('Processing ShowHelpIncludeProcessor')
|
25
|
+
|
26
|
+
reader.unshift_lines GrafanaReporter::Help.new.asciidoctor.split("\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
# @see ProcessorMixin#build_demo_entry
|
30
|
+
def build_demo_entry(_panel)
|
31
|
+
'include::grafana_help[]'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
module Asciidoctor
|
5
|
+
# Implements the hook
|
6
|
+
# include::grafana_sql_table:<datasource_id>[<options>]
|
7
|
+
#
|
8
|
+
# Returns the results of the SQL query as a asciidoctor table.
|
9
|
+
#
|
10
|
+
# == Used document parameters
|
11
|
+
# +grafana_default_instance+ - name of grafana instance, 'default' if not specified
|
12
|
+
#
|
13
|
+
# +from+ - 'from' time for the sql query
|
14
|
+
#
|
15
|
+
# +to+ - 'to' time for the sql query
|
16
|
+
#
|
17
|
+
# All other variables starting with +var-+ will be used to replace grafana templating strings
|
18
|
+
# in the given SQL query.
|
19
|
+
#
|
20
|
+
# == Supported options
|
21
|
+
# +sql+ - sql statement (*mandatory*)
|
22
|
+
#
|
23
|
+
# +instance+ - name of grafana instance, 'default' if not specified
|
24
|
+
#
|
25
|
+
# +from+ - 'from' time for the sql query
|
26
|
+
#
|
27
|
+
# +to+ - 'to' time for the sql query
|
28
|
+
#
|
29
|
+
# +format+ - see {AbstractQuery#format_columns}
|
30
|
+
#
|
31
|
+
# +replace_values+ - see {AbstractQuery#replace_values}
|
32
|
+
#
|
33
|
+
# +filter_columns+ - see {AbstractQuery#filter_columns}
|
34
|
+
class SqlTableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
35
|
+
include ProcessorMixin
|
36
|
+
|
37
|
+
# :nodoc:
|
38
|
+
def handles?(target)
|
39
|
+
target.start_with? 'grafana_sql_table:'
|
40
|
+
end
|
41
|
+
|
42
|
+
# :nodoc:
|
43
|
+
def process(doc, reader, target, attrs)
|
44
|
+
return if @report.cancel
|
45
|
+
|
46
|
+
@report.next_step
|
47
|
+
instance = attrs['instance'] || doc.attr('grafana_default_instance') || 'default'
|
48
|
+
attrs['result_type'] = 'sql_table'
|
49
|
+
@report.logger.debug("Processing SqlTableIncludeProcessor (instance: #{instance},"\
|
50
|
+
" datasource: #{target.split(':')[1]}, sql: #{attrs['sql']})")
|
51
|
+
|
52
|
+
begin
|
53
|
+
# catch properly if datasource could not be identified
|
54
|
+
query = QueryValueQuery.new(@report.grafana(instance))
|
55
|
+
query.datasource = @report.grafana(instance).datasource_by_id(target.split(':')[1].to_i)
|
56
|
+
query.raw_query = attrs['sql']
|
57
|
+
assign_doc_and_item_variables(query, doc.attributes, attrs)
|
58
|
+
@report.logger.debug("from: #{query.from}, to: #{query.to}")
|
59
|
+
|
60
|
+
reader.unshift_lines query.execute
|
61
|
+
rescue GrafanaReporterError => e
|
62
|
+
@report.logger.error(e.message)
|
63
|
+
reader.unshift_line "|#{e.message}"
|
64
|
+
rescue StandardError => e
|
65
|
+
@report.logger.fatal(e.message)
|
66
|
+
reader.unshift_line "|#{e.message}"
|
67
|
+
end
|
68
|
+
|
69
|
+
reader
|
70
|
+
end
|
71
|
+
|
72
|
+
# @see ProcessorMixin#build_demo_entry
|
73
|
+
def build_demo_entry(panel)
|
74
|
+
return nil unless panel
|
75
|
+
return nil unless panel.model['type'].include?('table')
|
76
|
+
|
77
|
+
ref_id = nil
|
78
|
+
panel.model['targets'].each do |item|
|
79
|
+
if !item['hide'] && !panel.query(item['refId']).to_s.empty?
|
80
|
+
ref_id = item['refId']
|
81
|
+
break
|
82
|
+
end
|
83
|
+
end
|
84
|
+
return nil unless ref_id
|
85
|
+
|
86
|
+
"|===\ninclude::grafana_sql_table:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
|
87
|
+
"[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",filter_columns=\"time\","\
|
88
|
+
"dashboard=\"#{panel.dashboard.id}\",from=\"now-1h\",to=\"now\"]\n|==="
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GrafanaReporter
|
4
|
+
module Asciidoctor
|
5
|
+
# Implements the hook
|
6
|
+
# grafana_sql_value:<datasource_id>[<options>]
|
7
|
+
#
|
8
|
+
# Returns the first value of the resulting SQL query.
|
9
|
+
#
|
10
|
+
# == Used document parameters
|
11
|
+
# +grafana_default_instance+ - name of grafana instance, 'default' if not specified
|
12
|
+
#
|
13
|
+
# +from+ - 'from' time for the sql query
|
14
|
+
#
|
15
|
+
# +to+ - 'to' time for the sql query
|
16
|
+
#
|
17
|
+
# All other variables starting with +var-+ will be used to replace grafana templating strings
|
18
|
+
# in the given SQL query.
|
19
|
+
#
|
20
|
+
# == Supported options
|
21
|
+
# +sql+ - sql statement (*mandatory*)
|
22
|
+
#
|
23
|
+
# +instance+ - name of grafana instance, 'default' if not specified
|
24
|
+
#
|
25
|
+
# +from+ - 'from' time for the sql query
|
26
|
+
#
|
27
|
+
# +to+ - 'to' time for the sql query
|
28
|
+
#
|
29
|
+
# +format+ - see {AbstractQuery#format_columns}
|
30
|
+
#
|
31
|
+
# +replace_values+ - see {AbstractQuery#replace_values}
|
32
|
+
#
|
33
|
+
# +filter_columns+ - see {AbstractQuery#filter_columns}
|
34
|
+
class SqlValueInlineMacro < ::Asciidoctor::Extensions::InlineMacroProcessor
|
35
|
+
include ProcessorMixin
|
36
|
+
use_dsl
|
37
|
+
|
38
|
+
named :grafana_sql_value
|
39
|
+
|
40
|
+
# @see GrafanaReporter::Asciidoctor::SqlFirstValueQuery
|
41
|
+
def process(parent, target, attrs)
|
42
|
+
return if @report.cancel
|
43
|
+
|
44
|
+
@report.next_step
|
45
|
+
instance = attrs['instance'] || parent.document.attr('grafana_default_instance') || 'default'
|
46
|
+
attrs['result_type'] = 'sql_value'
|
47
|
+
@report.logger.debug("Processing SqlValueInlineMacro (instance: #{instance}, datasource: #{target},"\
|
48
|
+
" sql: #{attrs['sql']})")
|
49
|
+
|
50
|
+
begin
|
51
|
+
# catch properly if datasource could not be identified
|
52
|
+
query = QueryValueQuery.new(@report.grafana(instance))
|
53
|
+
query.datasource = @report.grafana(instance).datasource_by_id(target)
|
54
|
+
query.raw_query = attrs['sql']
|
55
|
+
assign_doc_and_item_variables(query, parent.document.attributes, attrs)
|
56
|
+
@report.logger.debug("from: #{query.from}, to: #{query.to}")
|
57
|
+
|
58
|
+
create_inline(parent, :quoted, query.execute)
|
59
|
+
rescue GrafanaReporterError => e
|
60
|
+
@report.logger.error(e.message)
|
61
|
+
create_inline(parent, :quoted, e.message)
|
62
|
+
rescue StandardError => e
|
63
|
+
@report.logger.fatal(e.message)
|
64
|
+
create_inline(parent, :quoted, e.message)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @see ProcessorMixin#build_demo_entry
|
69
|
+
def build_demo_entry(panel)
|
70
|
+
return nil unless panel
|
71
|
+
return nil unless panel.model['type'] == 'singlestat'
|
72
|
+
|
73
|
+
ref_id = nil
|
74
|
+
panel.model['targets'].each do |item|
|
75
|
+
if !item['hide'] && !panel.query(item['refId']).to_s.empty?
|
76
|
+
ref_id = item['refId']
|
77
|
+
break
|
78
|
+
end
|
79
|
+
end
|
80
|
+
return nil unless ref_id
|
81
|
+
|
82
|
+
"grafana_sql_value:#{panel.dashboard.grafana.datasource_by_name(panel.model['datasource']).id}"\
|
83
|
+
"[sql=\"#{panel.query(ref_id).gsub(/"/, '\"').gsub("\n", ' ').gsub(/\\/, '\\\\')}\",from=\"now-1h\","\
|
84
|
+
'to="now"]'
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'processor_mixin'
|
4
|
+
|
5
|
+
module GrafanaReporter
|
6
|
+
module Asciidoctor
|
7
|
+
# Implements the hook
|
8
|
+
# include::grafana_value_as_variable[<options>]
|
9
|
+
#
|
10
|
+
# Returns an attribute definition in asciidoctor format. This is needed if you want to refer to values of
|
11
|
+
# a grafana query within a variable in asciidoctor. As this works without this function for the
|
12
|
+
# `IncludeProcessor`s values, it will not work for all the other processors.
|
13
|
+
#
|
14
|
+
# This method is just a proxy for all other hooks and will forward parameters accordingly.
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
#
|
18
|
+
# include:grafana_value_as_variable[call="grafana_sql_value:1",variable_name="my_variable",sql="SELECT 'looks good'",<any_other_option>]
|
19
|
+
#
|
20
|
+
# This will call the {SqlValueInlineMacro} with `datasource_id` set to `1` and store the result in the
|
21
|
+
# variable. The resulting asciidoctor variable definition will be created as:
|
22
|
+
#
|
23
|
+
# :my_variable: looks good
|
24
|
+
#
|
25
|
+
# and can be refered to in your document easily as
|
26
|
+
#
|
27
|
+
# {my_variable}
|
28
|
+
#
|
29
|
+
# == Supported options
|
30
|
+
# +call+ - regular call to the reporter hook (*mandatory*)
|
31
|
+
#
|
32
|
+
# +variable_name+ - name of the variable, to which the result shall be assigned (*mandatory*)
|
33
|
+
class ValueAsVariableIncludeProcessor < ::Asciidoctor::Extensions::IncludeProcessor
|
34
|
+
include ProcessorMixin
|
35
|
+
|
36
|
+
# :nodoc:
|
37
|
+
def handles?(target)
|
38
|
+
target.start_with? 'grafana_value_as_variable'
|
39
|
+
end
|
40
|
+
|
41
|
+
# :nodoc:
|
42
|
+
def process(doc, reader, target, attrs)
|
43
|
+
return if @report.cancel
|
44
|
+
|
45
|
+
# increase step for this processor as well as it is also counted in the step counter
|
46
|
+
@report.next_step
|
47
|
+
|
48
|
+
call_attr = attrs.delete('call')
|
49
|
+
call, target = call_attr.split(':') if call_attr
|
50
|
+
attribute = attrs.delete('variable_name')
|
51
|
+
@report.logger.debug("Processing ValueAsVariableIncludeProcessor (call: #{call}, target: #{target},"\
|
52
|
+
" variable_name: #{attribute}, attrs: #{attrs})")
|
53
|
+
if !call || !attribute
|
54
|
+
@report.logger.error('ValueAsVariableIncludeProcessor: Missing mandatory attribute \'call\' or '\
|
55
|
+
'\'variable_name\'.')
|
56
|
+
# increase counter, as error occured and no sub call is being processed
|
57
|
+
@report.next_step
|
58
|
+
return reader
|
59
|
+
end
|
60
|
+
|
61
|
+
# TODO: remove dirty hack to allow the document as parameter for other processors
|
62
|
+
def doc.document
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# TODO: properly show error messages also in document
|
67
|
+
ext = doc.extensions.find_inline_macro_extension(call) if doc.extensions.inline_macros?
|
68
|
+
if !ext
|
69
|
+
@report.logger.error('ValueAsVariableIncludeProcessor: Could not find inline macro extension for '\
|
70
|
+
"'#{call}'.")
|
71
|
+
# increase counter, as error occured and no sub call is being processed
|
72
|
+
@report.next_step
|
73
|
+
else
|
74
|
+
@report.logger.debug('ValueAsVariableIncludeProcessor: Calling sub-method.')
|
75
|
+
item = ext.process_method.call(doc, target, attrs)
|
76
|
+
if !item.text.to_s.empty?
|
77
|
+
result = ":#{attribute}: #{item.text}"
|
78
|
+
@report.logger.debug("ValueAsVariableIncludeProcessor: Adding '#{result}' to document.")
|
79
|
+
reader.unshift_line(result)
|
80
|
+
else
|
81
|
+
@report.logger.debug("ValueAsVariableIncludeProcessor: Not adding variable '#{attribute}'"\
|
82
|
+
' as query result was empty.')
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
reader
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -13,6 +13,10 @@ module GrafanaReporter
|
|
13
13
|
class Configuration
|
14
14
|
# @return [AbstractReport] specific report class, which should be used.
|
15
15
|
attr_accessor :report_class
|
16
|
+
attr_accessor :logger
|
17
|
+
|
18
|
+
# Default file name for grafana reporter configuration file
|
19
|
+
DEFAULT_CONFIG_FILE_NAME = 'grafana_reporter.config'
|
16
20
|
|
17
21
|
# Returned by {#mode} if only a connection test shall be executed.
|
18
22
|
MODE_CONNECTION_TEST = 'test'
|
@@ -23,18 +27,28 @@ module GrafanaReporter
|
|
23
27
|
|
24
28
|
# Used to access the configuration hash. To make sure, that the configuration is
|
25
29
|
# valid, call {#validate}.
|
26
|
-
|
27
|
-
# NOTE: This function overwrites all existing configurations
|
28
|
-
attr_accessor :config
|
30
|
+
attr_reader :config
|
29
31
|
|
30
32
|
def initialize
|
31
33
|
@config = {}
|
32
|
-
@logger = ::Logger.new($stderr, level: :
|
33
|
-
# TODO: set report class somewhere else, but make it known here
|
34
|
-
self.report_class = Asciidoctor::Report
|
34
|
+
@logger = ::Logger.new($stderr, level: :info)
|
35
35
|
end
|
36
36
|
|
37
|
-
|
37
|
+
# Reads a given configuration file.
|
38
|
+
# @param config_file [String] path to configuration file, defaults to DEFAULT_CONFIG_FILE_NAME
|
39
|
+
# @return [Hash] configuration hash to be set as {Configuration#config}
|
40
|
+
def load_config_from_file(config_file = nil)
|
41
|
+
config_file ||= DEFAULT_CONFIG_FILE_NAME
|
42
|
+
self.config = YAML.load_file(config_file)
|
43
|
+
rescue StandardError => e
|
44
|
+
raise ConfigurationError, "Could not read config file '#{config_file}' (Error: #{e.message})"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Used to overwrite the current configuration.
|
48
|
+
def config=(new_config)
|
49
|
+
@config = new_config
|
50
|
+
update_configuration
|
51
|
+
end
|
38
52
|
|
39
53
|
# @return [String] mode, in which the reporting shall be executed. One of {MODE_CONNECTION_TEST},
|
40
54
|
# {MODE_SINGLE_RENDER} and {MODE_SERVICE}.
|
@@ -50,7 +64,8 @@ module GrafanaReporter
|
|
50
64
|
# @return [String] full path of configured report template. Only needed in {MODE_SINGLE_RENDER}.
|
51
65
|
def template
|
52
66
|
return nil if get_config('default-document-attributes:var-template').nil?
|
53
|
-
|
67
|
+
|
68
|
+
"#{templates_folder}#{get_config('default-document-attributes:var-template')}"
|
54
69
|
end
|
55
70
|
|
56
71
|
# @return [String] destination filename for the report in {MODE_SINGLE_RENDER}.
|
@@ -81,16 +96,6 @@ module GrafanaReporter
|
|
81
96
|
get_config("grafana:#{instance}:api_key")
|
82
97
|
end
|
83
98
|
|
84
|
-
# @param instance [String] grafana instance name, for which the value shall be retrieved.
|
85
|
-
# @return [Hash<String,Integer>] configured datasources for the requested grafana instance. Name as key,
|
86
|
-
# ID as value.
|
87
|
-
def grafana_datasources(instance = 'default')
|
88
|
-
hash = get_config("grafana:#{instance}:datasources")
|
89
|
-
return nil if hash.nil?
|
90
|
-
|
91
|
-
hash.map { |k, v| [k, v] }.to_h
|
92
|
-
end
|
93
|
-
|
94
99
|
# @return [String] configured folder, in which the report templates are stored including trailing slash.
|
95
100
|
# By default: current folder.
|
96
101
|
def templates_folder
|
@@ -150,9 +155,11 @@ module GrafanaReporter
|
|
150
155
|
# This function shall be called, before the configuration object is used in the
|
151
156
|
# {Application::Application#run}. It ensures, that everything is setup properly
|
152
157
|
# and all necessary folders exist. Appropriate errors are raised in case of errors.
|
158
|
+
# @param explicit [Boolean] true, if validation shall expect explicit (wizard) configuration file
|
153
159
|
# @return [void]
|
154
|
-
def validate
|
155
|
-
|
160
|
+
def validate(explicit = false)
|
161
|
+
check_deprecation
|
162
|
+
validate_schema(schema(explicit), @config)
|
156
163
|
|
157
164
|
# check if set folders exist
|
158
165
|
raise FolderDoesNotExistError.new(reports_folder, 'reports-folder') unless File.directory?(reports_folder)
|
@@ -160,8 +167,66 @@ module GrafanaReporter
|
|
160
167
|
raise FolderDoesNotExistError.new(images_folder, 'images-folder') unless File.directory?(images_folder)
|
161
168
|
end
|
162
169
|
|
170
|
+
# Can be used to configure or overwrite single parameters.
|
171
|
+
#
|
172
|
+
# @param path [String] path of the paramter to set, e.g. +grafana-reporter:webservice-port+
|
173
|
+
# @param value [Object] value to set
|
174
|
+
def set_param(path, value)
|
175
|
+
return if path.nil?
|
176
|
+
|
177
|
+
levels = path.split(':')
|
178
|
+
last_level = levels.pop
|
179
|
+
|
180
|
+
cur_pos = @config
|
181
|
+
levels.each do |subpath|
|
182
|
+
cur_pos[subpath] = {} unless cur_pos[subpath]
|
183
|
+
cur_pos = cur_pos[subpath]
|
184
|
+
end
|
185
|
+
|
186
|
+
cur_pos[last_level] = value
|
187
|
+
update_configuration
|
188
|
+
end
|
189
|
+
|
190
|
+
# Merge the given configuration object settings with the current config, i.e. overwrite and add all
|
191
|
+
# settings from the given config, but keep the not specified configs from the current object.
|
192
|
+
#
|
193
|
+
# param other_config [Configuration] other configuration object
|
194
|
+
def merge!(other_config)
|
195
|
+
config.merge!(other_config.config) { |_key, v1, v2| v1.is_a?(Hash) && v2.is_a?(Hash) ? v1.merge(v2) : v2 }
|
196
|
+
update_configuration
|
197
|
+
end
|
198
|
+
|
163
199
|
private
|
164
200
|
|
201
|
+
def check_deprecation
|
202
|
+
return if report_class
|
203
|
+
|
204
|
+
logger.warn('DEPRECATION WARNING: Your configuration explicitly needs to specify the '\
|
205
|
+
'\'grafana-reporter:report-class\' value. Currently this defaults to '\
|
206
|
+
'\'GrafanaReporter::Asciidoctor::Report\'. You can get rid of this warning, if you '\
|
207
|
+
'explicitly set this configuration in your configuration file. Setting this default will be '\
|
208
|
+
'removed in a future version.')
|
209
|
+
set_param('grafana-reporter:report-class', 'GrafanaReporter::Asciidoctor::Report')
|
210
|
+
end
|
211
|
+
|
212
|
+
def update_configuration
|
213
|
+
debug_level = get_config('grafana-reporter:debug-level')
|
214
|
+
rep_class = get_config('grafana-reporter:report-class')
|
215
|
+
|
216
|
+
@logger.level = Object.const_get("::Logger::Severity::#{debug_level}") if debug_level =~ /DEBUG|INFO|WARN|
|
217
|
+
ERROR|FATAL|UNKNOWN/x
|
218
|
+
self.report_class = Object.const_get(rep_class) if rep_class
|
219
|
+
::Grafana::WebRequest.ssl_cert = get_config('grafana-reporter:ssl-cert')
|
220
|
+
|
221
|
+
# register callbacks
|
222
|
+
callbacks = get_config('grafana-reporter:callbacks')
|
223
|
+
return unless callbacks
|
224
|
+
|
225
|
+
callbacks.each do |url, event|
|
226
|
+
AbstractReport.add_event_listener(event.to_sym, ReportWebhook.new(url))
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
165
230
|
def get_config(path)
|
166
231
|
return if path.nil?
|
167
232
|
|
@@ -182,20 +247,16 @@ module GrafanaReporter
|
|
182
247
|
|
183
248
|
if key.nil?
|
184
249
|
# apply to all on this level
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
else
|
198
|
-
raise ConfigurationError, "Unhandled configuration data type '#{subject.class}'."
|
250
|
+
raise ConfigurationError, "Unhandled configuration data type '#{subject.class}'." unless subject.is_a?(Hash)
|
251
|
+
|
252
|
+
if subject.length < min_occurence
|
253
|
+
raise ConfigurationDoesNotMatchSchemaError.new(key, 'occur', min_occurence, subject.length)
|
254
|
+
end
|
255
|
+
|
256
|
+
subject.each do |k, _v|
|
257
|
+
sub_scheme = {}
|
258
|
+
sub_scheme[k] = schema[nil]
|
259
|
+
validate_schema(sub_scheme, subject)
|
199
260
|
end
|
200
261
|
|
201
262
|
# apply to single item
|
@@ -221,7 +282,7 @@ module GrafanaReporter
|
|
221
282
|
end
|
222
283
|
end
|
223
284
|
|
224
|
-
def schema
|
285
|
+
def schema(explicit)
|
225
286
|
{
|
226
287
|
'grafana' =>
|
227
288
|
[
|
@@ -232,23 +293,27 @@ module GrafanaReporter
|
|
232
293
|
Hash, 1,
|
233
294
|
{
|
234
295
|
'host' => [String, 1],
|
235
|
-
'api_key' => [String, 0]
|
236
|
-
'datasources' => [Hash, 0, { nil => [Integer, 1] }]
|
296
|
+
'api_key' => [String, 0]
|
237
297
|
}
|
238
298
|
]
|
239
299
|
}
|
240
300
|
],
|
241
|
-
'default-document-attributes' => [Hash, 0],
|
301
|
+
'default-document-attributes' => [Hash, explicit ? 1 : 0],
|
302
|
+
'to_file' => [String, 0],
|
242
303
|
'grafana-reporter' =>
|
243
304
|
[
|
244
|
-
Hash,
|
305
|
+
Hash, 1,
|
245
306
|
{
|
307
|
+
'debug-level' => [String, 0],
|
246
308
|
'run-mode' => [String, 0],
|
247
309
|
'test-instance' => [String, 0],
|
248
|
-
'templates-folder' => [String, 0],
|
249
|
-
'
|
250
|
-
'
|
251
|
-
'
|
310
|
+
'templates-folder' => [String, explicit ? 1 : 0],
|
311
|
+
'report-class' => [String, 1],
|
312
|
+
'reports-folder' => [String, explicit ? 1 : 0],
|
313
|
+
'report-retention' => [Integer, explicit ? 1 : 0],
|
314
|
+
'ssl-cert' => [String, 0],
|
315
|
+
'webservice-port' => [Integer, explicit ? 1 : 0],
|
316
|
+
'callbacks' => [Hash, 0, { nil => [String, 1] }]
|
252
317
|
}
|
253
318
|
]
|
254
319
|
}
|