ruby-grafana-reporter 0.3.0 → 0.4.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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +81 -71
  3. data/bin/ruby-grafana-reporter +5 -5
  4. data/lib/VERSION.rb +3 -2
  5. data/lib/grafana/abstract_datasource.rb +116 -0
  6. data/lib/grafana/dashboard.rb +1 -3
  7. data/lib/grafana/errors.rb +15 -0
  8. data/lib/grafana/grafana.rb +53 -56
  9. data/lib/grafana/grafana_alerts_datasource.rb +57 -0
  10. data/lib/grafana/grafana_annotations_datasource.rb +56 -0
  11. data/lib/grafana/grafana_property_datasource.rb +25 -0
  12. data/lib/grafana/graphite_datasource.rb +44 -0
  13. data/lib/grafana/image_rendering_datasource.rb +44 -0
  14. data/lib/grafana/panel.rb +9 -3
  15. data/lib/grafana/prometheus_datasource.rb +39 -0
  16. data/lib/grafana/sql_datasource.rb +65 -0
  17. data/lib/grafana/variable.rb +1 -0
  18. data/lib/grafana/webrequest.rb +71 -0
  19. data/lib/grafana_reporter/abstract_query.rb +401 -0
  20. data/lib/grafana_reporter/abstract_report.rb +54 -3
  21. data/lib/grafana_reporter/alerts_table_query.rb +44 -0
  22. data/lib/grafana_reporter/annotations_table_query.rb +43 -0
  23. data/lib/grafana_reporter/application/application.rb +12 -8
  24. data/lib/grafana_reporter/application/webservice.rb +18 -6
  25. data/lib/grafana_reporter/asciidoctor/alerts_table_include_processor.rb +90 -0
  26. data/lib/grafana_reporter/asciidoctor/annotations_table_include_processor.rb +89 -0
  27. data/lib/grafana_reporter/asciidoctor/panel_image_block_macro.rb +76 -0
  28. data/lib/grafana_reporter/asciidoctor/panel_image_inline_macro.rb +77 -0
  29. data/lib/grafana_reporter/asciidoctor/panel_property_inline_macro.rb +72 -0
  30. data/lib/grafana_reporter/asciidoctor/panel_query_table_include_processor.rb +98 -0
  31. data/lib/grafana_reporter/asciidoctor/panel_query_value_inline_macro.rb +93 -0
  32. data/lib/grafana_reporter/asciidoctor/processor_mixin.rb +23 -0
  33. data/lib/grafana_reporter/asciidoctor/report.rb +24 -31
  34. data/lib/grafana_reporter/asciidoctor/show_environment_include_processor.rb +46 -0
  35. data/lib/grafana_reporter/asciidoctor/show_help_include_processor.rb +35 -0
  36. data/lib/grafana_reporter/asciidoctor/sql_table_include_processor.rb +92 -0
  37. data/lib/grafana_reporter/asciidoctor/sql_value_inline_macro.rb +88 -0
  38. data/lib/grafana_reporter/asciidoctor/value_as_variable_include_processor.rb +90 -0
  39. data/lib/grafana_reporter/configuration.rb +12 -6
  40. data/lib/grafana_reporter/console_configuration_wizard.rb +115 -65
  41. data/lib/grafana_reporter/demo_report_wizard.rb +87 -0
  42. data/lib/grafana_reporter/errors.rb +33 -0
  43. data/lib/grafana_reporter/help.rb +447 -0
  44. data/lib/grafana_reporter/logger/two_way_logger.rb +1 -1
  45. data/lib/grafana_reporter/panel_image_query.rb +29 -0
  46. data/lib/grafana_reporter/panel_property_query.rb +22 -0
  47. data/lib/grafana_reporter/query_value_query.rb +79 -0
  48. data/lib/grafana_reporter/report_webhook.rb +35 -0
  49. data/lib/{ruby-grafana-reporter.rb → ruby_grafana_reporter.rb} +0 -3
  50. metadata +37 -35
  51. data/lib/grafana/abstract_panel_query.rb +0 -22
  52. data/lib/grafana/abstract_query.rb +0 -132
  53. data/lib/grafana/abstract_sql_query.rb +0 -51
  54. data/lib/grafana/panel_image_query.rb +0 -52
  55. data/lib/grafana_reporter/asciidoctor/alerts_table_query.rb +0 -101
  56. data/lib/grafana_reporter/asciidoctor/annotations_table_query.rb +0 -96
  57. data/lib/grafana_reporter/asciidoctor/errors.rb +0 -40
  58. data/lib/grafana_reporter/asciidoctor/extensions/alerts_table_include_processor.rb +0 -92
  59. data/lib/grafana_reporter/asciidoctor/extensions/annotations_table_include_processor.rb +0 -91
  60. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_block_macro.rb +0 -69
  61. data/lib/grafana_reporter/asciidoctor/extensions/panel_image_inline_macro.rb +0 -68
  62. data/lib/grafana_reporter/asciidoctor/extensions/panel_property_inline_macro.rb +0 -61
  63. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_table_include_processor.rb +0 -78
  64. data/lib/grafana_reporter/asciidoctor/extensions/panel_query_value_inline_macro.rb +0 -73
  65. data/lib/grafana_reporter/asciidoctor/extensions/processor_mixin.rb +0 -20
  66. data/lib/grafana_reporter/asciidoctor/extensions/show_environment_include_processor.rb +0 -43
  67. data/lib/grafana_reporter/asciidoctor/extensions/show_help_include_processor.rb +0 -30
  68. data/lib/grafana_reporter/asciidoctor/extensions/sql_table_include_processor.rb +0 -70
  69. data/lib/grafana_reporter/asciidoctor/extensions/sql_value_inline_macro.rb +0 -66
  70. data/lib/grafana_reporter/asciidoctor/extensions/value_as_variable_include_processor.rb +0 -88
  71. data/lib/grafana_reporter/asciidoctor/help.rb +0 -435
  72. data/lib/grafana_reporter/asciidoctor/panel_first_value_query.rb +0 -36
  73. data/lib/grafana_reporter/asciidoctor/panel_image_query.rb +0 -28
  74. data/lib/grafana_reporter/asciidoctor/panel_property_query.rb +0 -44
  75. data/lib/grafana_reporter/asciidoctor/panel_table_query.rb +0 -40
  76. data/lib/grafana_reporter/asciidoctor/query_mixin.rb +0 -312
  77. data/lib/grafana_reporter/asciidoctor/sql_first_value_query.rb +0 -42
  78. data/lib/grafana_reporter/asciidoctor/sql_table_query.rb +0 -44
@@ -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 {QueryMixin#format_columns}
30
+ #
31
+ # +replace_values+ - see {QueryMixin#replace_values}
32
+ #
33
+ # +filter_columns+ - see {QueryMixin#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
+ query.merge_hash_variables(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 {QueryMixin#format_columns}
30
+ #
31
+ # +replace_values+ - see {QueryMixin#replace_values}
32
+ #
33
+ # +filter_columns+ - see {QueryMixin#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
+ query.merge_hash_variables(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
@@ -56,11 +56,6 @@ module GrafanaReporter
56
56
  "#{templates_folder}#{get_config('default-document-attributes:var-template')}.adoc"
57
57
  end
58
58
 
59
- # @return [String] path to ssl certificate file, if manually specified, or nil
60
- def ssl_cert
61
- get_config('grafana-reporter:ssl-cert')
62
- end
63
-
64
59
  # @return [String] destination filename for the report in {MODE_SINGLE_RENDER}.
65
60
  def to_file
66
61
  return get_config('to_file') || true if mode == MODE_SINGLE_RENDER
@@ -209,6 +204,15 @@ module GrafanaReporter
209
204
  @logger.level = Object.const_get("::Logger::Severity::#{debug_level}") if debug_level =~ /DEBUG|INFO|WARN|
210
205
  ERROR|FATAL|UNKNOWN/x
211
206
  self.report_class = Object.const_get(rep_class) if rep_class
207
+ ::Grafana::WebRequest.ssl_cert = get_config('grafana-reporter:ssl-cert')
208
+
209
+ # register callbacks
210
+ callbacks = get_config('grafana-reporter:callbacks')
211
+ return unless callbacks
212
+
213
+ callbacks.each do |url, event|
214
+ AbstractReport.add_event_listener(event.to_sym, ReportWebhook.new(url))
215
+ end
212
216
  end
213
217
 
214
218
  def get_config(path)
@@ -283,6 +287,7 @@ module GrafanaReporter
283
287
  }
284
288
  ],
285
289
  'default-document-attributes' => [Hash, explicit ? 1 : 0],
290
+ 'to_file' => [String, 0],
286
291
  'grafana-reporter' =>
287
292
  [
288
293
  Hash, 1,
@@ -295,7 +300,8 @@ module GrafanaReporter
295
300
  'reports-folder' => [String, explicit ? 1 : 0],
296
301
  'report-retention' => [Integer, explicit ? 1 : 0],
297
302
  'ssl-cert' => [String, 0],
298
- 'webservice-port' => [Integer, explicit ? 1 : 0]
303
+ 'webservice-port' => [Integer, explicit ? 1 : 0],
304
+ 'callbacks' => [Hash, 0, { nil => [String, 1] }]
299
305
  }
300
306
  ]
301
307
  }
@@ -1,14 +1,58 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GrafanaReporter
4
+ # This class provides a console configuration wizard, to reduce the manual efforts that have
5
+ # to be spent for that action and to reduce mistakes as good as possible.
4
6
  class ConsoleConfigurationWizard
5
7
  # Provides a command line configuration wizard for setting up the necessary configuration
6
8
  # file.
7
9
  # TODO: refactor class
8
10
  def start_wizard(config_file, console_config)
9
- config = Configuration.new
11
+ action = overwrite_or_use_config_file(config_file)
12
+ return if action == 'abort'
13
+
14
+ config = create_config_wizard(config_file, console_config) if action == 'overwrite'
15
+ config ||= Configuration.new
16
+
17
+ begin
18
+ config.config = YAML.load_file(config_file)
19
+ rescue StandardError => e
20
+ raise ConfigurationError, "Could not read config file '#{config_file}' (Error: #{e.message})\n"\
21
+ "Source:\n#{File.read(config_file)}"
22
+ end
23
+
24
+ begin
25
+ config.validate(true)
26
+ puts 'Configuration file validated successfully.'
27
+ rescue ConfigurationError => e
28
+ raise e
29
+ end
30
+
31
+ demo_report = create_demo_report(config)
32
+
33
+ demo_report ||= '<<your_report_name>>'
34
+ config_param = config_file == Application::Application::CONFIG_FILE ? '' : " -c #{config_file}"
35
+ program_call = "#{Gem.ruby} #{$PROGRAM_NAME}"
36
+ program_call = ENV['OCRA_EXECUTABLE'].gsub("#{Dir.pwd}/".gsub('/', '\\'), '') if ENV['OCRA_EXECUTABLE']
10
37
 
11
- return unless overwrite_file(config_file)
38
+ puts
39
+ puts 'Now everything is setup properly. Create your reports as required in the templates '\
40
+ 'folder and run the reporter either standalone with e.g. the following command:'
41
+ puts
42
+ puts " #{program_call}#{config_param} -t #{demo_report} -o demo_report_with_help.pdf"
43
+ puts
44
+ puts 'or run it as a service using the following command:'
45
+ puts
46
+ puts " #{program_call}#{config_param}"
47
+ puts
48
+ puts "Open 'http://localhost:#{config.webserver_port}/render?var-template=#{demo_report}' in a webbrowser to"\
49
+ ' test your configuration.'
50
+ end
51
+
52
+ private
53
+
54
+ def create_config_wizard(config_file, console_config)
55
+ config = Configuration.new
12
56
 
13
57
  puts 'This wizard will guide you through an initial configuration for'\
14
58
  ' the ruby-grafana-reporter. The configuration file will be created'\
@@ -34,6 +78,20 @@ grafana-reporter:
34
78
  reports-folder: #{reports}
35
79
  report-retention: #{retention}
36
80
  webservice-port: #{port}
81
+ # you may want to configure the following webhook callbacks to get informed on certain events
82
+ # callbacks:
83
+ # all:
84
+ # - <<your_callback_url>>
85
+ # - ...
86
+ # on_before_create:
87
+ # - <<your_callback_url>>
88
+ # - ...
89
+ # on_after_cancel:
90
+ # - <<your_callback_url>>
91
+ # - ...
92
+ # on_after_finish:
93
+ # - <<your_callback_url>>
94
+ # - ...
37
95
 
38
96
  default-document-attributes:
39
97
  imagesdir: #{images}
@@ -47,65 +105,51 @@ default-document-attributes:
47
105
  raise e
48
106
  end
49
107
 
50
- begin
51
- config.config = YAML.load_file(config_file)
52
- rescue StandardError => e
53
- raise ConfigurationError, "Could not read config file '#{config_file}' (Error: #{e.message})\n"\
54
- "Source:\n#{File.read(config_file)}"
55
- end
56
-
57
- begin
58
- config.validate(true)
59
- puts 'Configuration file validated successfully.'
60
- rescue ConfigurationError => e
61
- raise e
62
- end
63
-
64
- demo_report = create_demo_report(config)
65
-
66
- demo_report ||= '<<your_report_name>>'
67
- config_param = config_file == Application::Application::CONFIG_FILE ? '' : " -c #{config_file}"
68
- program_call = "#{Gem.ruby} #{$PROGRAM_NAME}"
69
- program_call = ENV['OCRA_EXECUTABLE'].gsub("#{Dir.pwd}/".gsub('/', '\\'), '') if ENV['OCRA_EXECUTABLE']
70
-
71
- puts
72
- puts 'Now everything is setup properly. Create your reports as required in the templates '\
73
- 'folder and run the reporter either standalone with e.g. the following command:'
74
- puts
75
- puts " #{program_call}#{config_param} -t #{demo_report} -o demo_report_with_help.pdf"
76
- puts
77
- puts 'or run it as a service using the following command:'
78
- puts
79
- puts " #{program_call}#{config_param}"
80
- puts
81
- puts "Open 'http://localhost:#{config.webserver_port}/render?var-template=#{demo_report}' in a webbrowser to"\
82
- ' test your configuration.'
108
+ config
83
109
  end
84
110
 
85
- private
86
-
87
111
  def create_demo_report(config)
88
112
  unless Dir.exist?(config.templates_folder)
89
113
  puts "Skip creation of DEMO template, as folder '#{config.templates_folder}' does not exist."
90
114
  return nil
91
115
  end
92
116
 
117
+ create = user_input('Shall I create a demo report for your new configuration file? Please note '\
118
+ 'that this report might contain confidential information, depending on the '\
119
+ 'confidentiality of the information stored in your dashboard.', 'yN')
120
+ return nil unless create =~ /^(?:y|Y)$/
121
+
93
122
  demo_report = 'demo_report'
94
123
  demo_report_file = "#{config.templates_folder}#{demo_report}.adoc"
95
124
 
96
- # TODO: add question to overwrite file
125
+ # ask to overwrite file
97
126
  if File.exist?(demo_report_file)
98
- puts "Skip creation of DEMO template, as file '#{demo_report_file}' already exists."
99
- return demo_report
127
+ input = user_input("Demo template '#{demo_report_file}' does already exist. Do you want to "\
128
+ 'overwrite it?', 'yN')
129
+
130
+ case input
131
+ when /^(?:y|Y)$/
132
+ puts 'Overwriting existing DEMO template.'
133
+
134
+ else
135
+ puts 'Skip creation of DEMO template.'
136
+ return demo_report
137
+ end
100
138
  end
101
139
 
102
- demo_report = %(= First Grafana Report Template
140
+ # TODO: move this to Asciidoctor::Report class
141
+ classes = [Asciidoctor::AlertsTableIncludeProcessor, Asciidoctor::AnnotationsTableIncludeProcessor,
142
+ Asciidoctor::PanelImageBlockMacro, Asciidoctor::PanelImageInlineMacro,
143
+ Asciidoctor::PanelPropertyInlineMacro, Asciidoctor::PanelQueryTableIncludeProcessor,
144
+ Asciidoctor::PanelQueryValueInlineMacro, Asciidoctor::SqlTableIncludeProcessor,
145
+ Asciidoctor::SqlValueInlineMacro, Asciidoctor::ShowHelpIncludeProcessor,
146
+ Asciidoctor::ShowEnvironmentIncludeProcessor]
103
147
 
104
- include::grafana_help[]
148
+ grafana = ::Grafana::Grafana.new(config.grafana_host, config.grafana_api_key)
149
+ demo_report_content = DemoReportWizard.new(classes).build(grafana)
105
150
 
106
- include::grafana_environment[])
107
151
  begin
108
- File.write(demo_report_file, demo_report, mode: 'w')
152
+ File.write(demo_report_file, demo_report_content, mode: 'w')
109
153
  puts "DEMO template '#{demo_report_file}' successfully created."
110
154
  rescue StandardError => e
111
155
  puts e.message
@@ -123,10 +167,9 @@ include::grafana_environment[])
123
167
  url ||= user_input('Specify grafana host', 'http://localhost:3000')
124
168
  print "Testing connection to '#{url}' #{api_key ? '_with_' : '_without_'} API key..."
125
169
  begin
126
- # TODO: how to handle if ssl access if not working properly?
127
170
  res = Grafana::Grafana.new(url,
128
171
  api_key,
129
- logger: config.logger, ssl_cert: config.ssl_cert).test_connection
172
+ logger: config.logger).test_connection
130
173
  rescue StandardError => e
131
174
  puts
132
175
  puts e.message
@@ -135,35 +178,39 @@ include::grafana_environment[])
135
178
 
136
179
  case res
137
180
  when 'Admin'
138
- valid = true
181
+ tmp = user_input('Access to grafana is permitted as Admin, which is a potential security risk.'\
182
+ ' Do you want to use another [a]pi key, [r]e-enter url key or [i]gnore?', 'aRi')
139
183
 
140
- when 'NON-Admin'
141
- print 'Access to grafana is permitted as NON-Admin. Do you want to use an [a]pi key,'\
142
- ' [r]e-enter api key or [i]gnore? [aRi]: '
143
-
144
- case gets
184
+ case tmp
145
185
  when /(?:i|I)$/
146
186
  valid = true
147
187
 
148
- # TODO: what is difference between 'a' and 'r'?
149
188
  when /(?:a|A)$/
150
189
  print 'Enter API key: '
151
- api_key = gets.sub(/\n$/, '')
190
+ api_key = gets.strip
152
191
 
153
- when /(?:r|R|adRi)$/
192
+ else
193
+ url = nil
154
194
  api_key = nil
155
195
 
156
196
  end
157
197
 
158
- # TODO: ask to enter API key, if grafana cannot be accessed without that
198
+ when 'NON-Admin'
199
+ print 'Access to grafana is permitted as NON-Admin.'
200
+ valid = true
201
+
159
202
  else
160
- print "Grafana could not be accessed at '#{url}'. Do you want do [r]e-enter url, or"\
161
- ' [i]gnore and proceed? [Ri]: '
203
+ tmp = user_input("Grafana could not be accessed at '#{url}'. Do you want to use an [a]pi key,"\
204
+ ' [r]e-enter url, or [i]gnore and proceed?', 'aRi')
162
205
 
163
- case gets
206
+ case tmp
164
207
  when /(?:i|I)$/
165
208
  valid = true
166
209
 
210
+ when /(?:a|A)$/
211
+ print 'Enter API key: '
212
+ api_key = gets.strip
213
+
167
214
  else
168
215
  url = nil
169
216
  api_key = nil
@@ -174,7 +221,7 @@ include::grafana_environment[])
174
221
  end
175
222
  %(grafana:
176
223
  default:
177
- host: #{url}#{api_key ? "\n api_key: #{api_key}" : ''}}
224
+ host: #{url}#{api_key ? "\n api_key: #{api_key}" : ''}
178
225
  )
179
226
  end
180
227
 
@@ -254,16 +301,19 @@ include::grafana_environment[])
254
301
  false
255
302
  end
256
303
 
257
- def overwrite_file(config_file)
258
- return true unless File.exist?(config_file)
304
+ def overwrite_or_use_config_file(config_file)
305
+ return 'overwrite' unless File.exist?(config_file)
259
306
 
260
307
  input = nil
261
308
  until input
262
- input = user_input("Configuration file '#{config_file}' already exists. Do you want to overwrite it?", 'yN')
263
- return false if input =~ /^(?:n|N|yN)$/
309
+ input = user_input("Configuration file '#{config_file}' already exists. Do you want to [o]verwrite it, "\
310
+ 'use it to for [d]emo report creation only, or [a]bort?', 'odA')
264
311
  end
265
312
 
266
- true
313
+ return 'demo_report' if input =~ /^(?:d|D)$/
314
+ return 'abort' if input =~ /^(?:A|a|odA)$/
315
+
316
+ 'overwrite'
267
317
  end
268
318
  end
269
319
  end