kafo 5.0.1 → 6.1.2

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.
@@ -2,15 +2,16 @@ require 'kafo/hook_context'
2
2
 
3
3
  module Kafo
4
4
  class Hooking
5
- # pre_migrations - just after kafo reads its configuration - useful for config file updates. Only in this stage it is posible to request config reload (`Kafo.request_config_reload`) to get in our changes
6
- # boot - before kafo is ready to work, useful for adding new app arguments, logger won't work yet
7
- # init - just after hooking is initialized and kafo is configured, parameters have no values yet
8
- # pre_values - just before value from CLI is set to parameters (they already have default values)
9
- # pre_validations - just after system checks and before validations are executed (and before interactive wizard is started), at this point all parameter values are already set but not yet stored in answer file
10
- # pre_commit - after validations or interactive wizard have completed, all parameter values are set but not yet stored in the answer file
11
- # pre - just before puppet is executed to converge system
12
- # post - just after puppet is executed to converge system
13
- TYPES = [:pre_migrations, :boot, :init, :pre, :post, :pre_values, :pre_validations, :pre_commit]
5
+ # * pre_migrations - just after kafo reads its configuration - useful for config file updates. Only in this stage it is posible to request config reload (`Kafo.request_config_reload`) to get in our changes
6
+ # * boot - before kafo is ready to work, useful for adding new app arguments, logger won't work yet
7
+ # * init - just after hooking is initialized and kafo is configured, parameters have no values yet
8
+ # * pre_values - just before value from CLI is set to parameters (they already have default values)
9
+ # * pre_validations - just after system checks and before validations are executed (and before interactive wizard is started), at this point all parameter values are already set but not yet stored in answer file
10
+ # * pre_commit - after validations or interactive wizard have completed, all parameter values are set but not yet stored in the answer file
11
+ # * pre - just before puppet is executed to converge system
12
+ # * post - just after puppet is executed to converge system
13
+ # * pre_exit - happens during exit handling, before exit is completed
14
+ TYPES = [:pre_migrations, :boot, :init, :pre_values, :pre_validations, :pre_commit, :pre, :post, :pre_exit]
14
15
 
15
16
  attr_accessor :hooks, :kafo
16
17
 
@@ -44,14 +45,16 @@ module Kafo
44
45
  @loaded
45
46
  end
46
47
 
47
- def execute(group)
48
- logger.info "Executing hooks in group #{group}"
48
+ def execute(group, log_stage: true)
49
+ logger = Logger.new(group)
50
+ logger.notice "Executing hooks in group #{group}" if log_stage
49
51
  self.hooks[group].keys.sort_by(&:to_s).each do |name|
50
52
  hook = self.hooks[group][name]
51
- result = HookContext.execute(self.kafo, &hook)
53
+ result = HookContext.execute(self.kafo, logger, &hook)
52
54
  logger.debug "Hook #{name} returned #{result.inspect}"
53
55
  end
54
- logger.info "All hooks in group #{group} finished"
56
+ logger.notice "All hooks in group #{group} finished" if log_stage
57
+ @group = nil
55
58
  end
56
59
 
57
60
  def register_pre_migrations(name, &block)
@@ -86,6 +89,10 @@ module Kafo
86
89
  register(:post, name, &block)
87
90
  end
88
91
 
92
+ def register_pre_exit(name, &block)
93
+ register(:pre_exit, name, &block)
94
+ end
95
+
89
96
  private
90
97
 
91
98
  def register(group, name, &block)
@@ -27,20 +27,71 @@ require 'kafo/hooking'
27
27
  require 'kafo/exit_handler'
28
28
  require 'kafo/scenario_manager'
29
29
  require 'kafo/execution_environment'
30
+ require 'kafo/logging'
31
+ require 'kafo/app_option/declaration'
30
32
 
31
33
  module Kafo
32
34
  class KafoConfigure < Clamp::Command
33
35
  include StringHelper
34
36
 
35
37
  class << self
38
+ include AppOption::Declaration
39
+
36
40
  attr_accessor :config, :root_dir, :config_file, :gem_root,
37
- :module_dirs, :kafo_modules_dir, :verbose, :app_options, :logger,
41
+ :module_dirs, :kafo_modules_dir, :verbose, :logger,
38
42
  :check_dirs, :exit_handler, :scenario_manager, :store
39
43
  attr_writer :hooking
40
44
 
41
45
  def hooking
42
46
  @hooking ||= Hooking.new
43
47
  end
48
+
49
+ def run
50
+ return super
51
+ rescue SystemExit
52
+ self.exit_handler.exit(self.exit_code) # fail in initialize
53
+ end
54
+
55
+ def exit(code, &block)
56
+ exit_handler.exit(code, &block)
57
+ end
58
+
59
+ def exit_code
60
+ self.exit_handler.exit_code
61
+ end
62
+
63
+ def in_help_mode?
64
+ ARGV.include?('--help') || ARGV.include?('--full-help') || ARGV.include?('-h')
65
+ end
66
+
67
+ def help(*args)
68
+ kafo = args.pop
69
+ builder_class = kafo.full_help? ? HelpBuilders::Advanced : HelpBuilders::Basic
70
+ args.push builder_class.new(kafo.params)
71
+ super(*args)
72
+ end
73
+
74
+ def use_colors?
75
+ if config
76
+ colors = config.app[:colors]
77
+ else
78
+ colors = ARGV.include?('--no-colors') ? false : nil
79
+ colors = ARGV.include?('--colors') ? true : nil if colors.nil?
80
+ end
81
+ colors
82
+ end
83
+
84
+ def preset_color_scheme
85
+ match = ARGV.join(' ').match(/--color-of-background[ =](\w+)/)
86
+ background = match && match[1]
87
+ ColorScheme.new(:background => background, :colors => use_colors?).setup
88
+ end
89
+
90
+ def set_color_scheme
91
+ ColorScheme.new(
92
+ :background => config.app[:color_of_background],
93
+ :colors => use_colors?).setup
94
+ end
44
95
  end
45
96
 
46
97
  def initialize(*args)
@@ -65,9 +116,9 @@ module Kafo
65
116
  request_config_reload if applied_total > 0
66
117
 
67
118
  if ARGV.include?('--migrations-only')
68
- self.class.verbose = (ARGV.include?('--verbose') || ARGV.include?('-v'))
69
- Logger.setup
70
- self.class.logger.info('Log buffers flushed')
119
+ verbose = (ARGV.include?('--verbose') || ARGV.include?('-v'))
120
+ Logging.setup(verbose: verbose)
121
+ self.class.logger.notice('Log buffers flushed')
71
122
  self.class.exit(0)
72
123
  end
73
124
 
@@ -80,7 +131,7 @@ module Kafo
80
131
  prev_config.run_migrations
81
132
  self.class.config.migrate_configuration(prev_config, :skip => [:log_name])
82
133
  setup_config(self.class.config_file)
83
- self.class.logger.info("Due to scenario change the configuration (#{self.class.config_file}) was updated with #{scenario_manager.previous_scenario} and reloaded.")
134
+ self.class.logger.notice("Due to scenario change the configuration (#{self.class.config_file}) was updated with #{scenario_manager.previous_scenario} and reloaded.")
84
135
  end
85
136
  end
86
137
 
@@ -92,7 +143,7 @@ module Kafo
92
143
  # so we limit parsing only to app config options (because of --help and later defined params)
93
144
  parse clamp_app_arguments
94
145
  parse_app_arguments # set values from ARGS to config.app
95
- Logger.setup
146
+ Logging.setup(verbose: config.app[:verbose]) unless ARGV.any? { |option| ['--help', '--full-help'].include? option }
96
147
  self.class.set_color_scheme
97
148
 
98
149
  self.class.hooking.execute(:init)
@@ -114,19 +165,17 @@ module Kafo
114
165
 
115
166
  def run(*args)
116
167
  started_at = Time.now
117
- logger.info("Running installer with args #{args.inspect}")
168
+ logger.debug("Running installer with args #{args.inspect}")
118
169
  super
119
170
  ensure
120
- logger.info("Installer finished in #{Time.now - started_at} seconds")
171
+ logger.debug("Installer finished in #{Time.now - started_at} seconds")
121
172
  end
122
173
 
123
174
  def execute
124
175
  parse_cli_arguments
125
176
 
126
- if (self.class.verbose = !!verbose?)
127
- Logger.setup_verbose
128
- else
129
- @progress_bar = self.class.config.app[:colors] ? ProgressBars::Colored.new : ProgressBars::BlackWhite.new
177
+ if !config.app[:verbose]
178
+ @progress_bar = config.app[:colors] ? ProgressBars::Colored.new : ProgressBars::BlackWhite.new
130
179
  end
131
180
 
132
181
  unless skip_checks_i_know_better?
@@ -149,6 +198,7 @@ module Kafo
149
198
 
150
199
  self.class.hooking.execute(:pre_commit)
151
200
  unless dont_save_answers? || noop?
201
+ config.configure_application
152
202
  store_params
153
203
  self.class.scenario_manager.link_last_scenario(self.class.config_file) if self.class.scenario_manager.configured?
154
204
  end
@@ -158,46 +208,14 @@ module Kafo
158
208
  return self
159
209
  end
160
210
 
161
- def self.run
162
- return super
163
- rescue SystemExit
164
- self.exit_handler.exit(self.exit_code) # fail in initialize
165
- end
166
-
167
- def self.exit(code, &block)
168
- exit_handler.exit(code, &block)
169
- end
170
-
171
- def self.exit_code
172
- self.exit_handler.exit_code
173
- end
174
-
175
- def self.in_help_mode?
176
- ARGV.include?('--help') || ARGV.include?('--full-help') || ARGV.include?('-h')
177
- end
178
-
179
211
  def exit_code
180
212
  self.class.exit_code
181
213
  end
182
214
 
183
-
184
215
  def help
185
216
  self.class.help(invocation_path, self)
186
217
  end
187
218
 
188
- def self.help(*args)
189
- kafo = args.pop
190
- builder_class = kafo.full_help? ? HelpBuilders::Advanced : HelpBuilders::Basic
191
- args.push builder_class.new(kafo.params)
192
- super(*args)
193
- end
194
-
195
- def self.app_option(*args, &block)
196
- self.app_options ||= []
197
- self.app_options.push self.option(*args, &block)
198
- self.app_options.last
199
- end
200
-
201
219
  def params
202
220
  @params ||= modules.map(&:params).flatten
203
221
  rescue KafoParsers::ModuleName => e
@@ -236,7 +254,6 @@ module Kafo
236
254
  @config_reload_requested = true
237
255
  end
238
256
 
239
-
240
257
  private
241
258
 
242
259
  def setup_config(conf_file)
@@ -263,7 +280,7 @@ module Kafo
263
280
  scenario_manager = setup_scenario_manager
264
281
  self.class.scenario_manager = scenario_manager
265
282
  setup_config(self.class.config_file)
266
- self.class.logger.info('Installer configuration was reloaded')
283
+ self.class.logger.notice('Installer configuration was reloaded')
267
284
  @config_reload_requested = false
268
285
  end
269
286
  end
@@ -290,56 +307,67 @@ module Kafo
290
307
  end
291
308
  end
292
309
 
310
+ def app_option(*args, &block)
311
+ self.class.app_option(*args, &block)
312
+ end
313
+
293
314
  def set_app_options
294
- self.class.app_option ['--[no-]colors'], :flag, 'Use color output on STDOUT',
295
- :default => !!config.app[:colors]
296
- self.class.app_option ['--color-of-background'], 'COLOR', 'Your terminal background is :bright or :dark',
297
- :default => config.app[:color_of_background]
298
- self.class.app_option ['--dont-save-answers'], :flag, "Skip saving answers to '#{self.class.config.answer_file}'?",
299
- :default => !!config.app[:dont_save_answers]
300
- self.class.app_option '--ignore-undocumented', :flag, 'Ignore inconsistent parameter documentation',
301
- :default => false
302
- self.class.app_option ['-i', '--interactive'], :flag, 'Run in interactive mode'
303
- self.class.app_option '--log-level', 'LEVEL', 'Log level for log file output',
304
- :default => config.app[:log_level]
305
- self.class.app_option ['-n', '--noop'], :flag, 'Run puppet in noop mode?',
306
- :default => false
307
- self.class.app_option ['-p', '--profile'], :flag, 'Run puppet in profile mode?',
308
- :default => false
309
- self.class.app_option ['-s', '--skip-checks-i-know-better'], :flag, 'Skip all system checks', :default => false
310
- self.class.app_option ['--skip-puppet-version-check'], :flag, 'Skip check for compatible Puppet versions', :default => false
311
- self.class.app_option ['-v', '--verbose'], :flag, 'Display log on STDOUT instead of progressbar'
312
- self.class.app_option ['-l', '--verbose-log-level'], 'LEVEL', 'Log level for verbose mode output',
313
- :default => 'info'
314
- self.class.app_option ['-S', '--scenario'], 'SCENARIO', 'Use installation scenario'
315
- self.class.app_option ['--disable-scenario'], 'SCENARIO', 'Disable installation scenario'
316
- self.class.app_option ['--enable-scenario'], 'SCENARIO', 'Enable installation scenario'
317
- self.class.app_option ['--list-scenarios'], :flag, 'List available installation scenarios'
318
- self.class.app_option ['--force'], :flag, 'Force change of installation scenario'
319
- self.class.app_option ['--compare-scenarios'], :flag, 'Show changes between last used scenario and the scenario specified with -S or --scenario argument'
320
- self.class.app_option ['--migrations-only'], :flag, 'Apply migrations to a selected scenario and exit'
321
- self.class.app_option ['--[no-]parser-cache'], :flag, 'Force use or bypass of Puppet module parser cache'
315
+ app_option ['--[no-]colors'], :flag, 'Use color output on STDOUT',
316
+ :default => config.app[:colors], :advanced => true
317
+ app_option ['--color-of-background'], 'COLOR', 'Your terminal background is :bright or :dark',
318
+ :default => config.app[:color_of_background], :advanced => true
319
+ app_option ['--dont-save-answers'], :flag, "Skip saving answers to '#{self.class.config.answer_file}'?",
320
+ :default => config.app[:dont_save_answers], :advanced => true
321
+ app_option '--ignore-undocumented', :flag, 'Ignore inconsistent parameter documentation',
322
+ :default => config.app[:ignore_undocumented], :advanced => true
323
+ app_option ['-i', '--interactive'], :flag, 'Run in interactive mode'
324
+ app_option '--log-level', 'LEVEL', 'Log level for log file output',
325
+ :default => config.app[:log_level], :advanced => true
326
+ app_option ['-n', '--noop'], :flag, 'Run puppet in noop mode?',
327
+ :default => false
328
+ app_option ['-p', '--profile'], :flag, 'Run puppet in profile mode?',
329
+ :default => false, :advanced => true
330
+ app_option ['-s', '--skip-checks-i-know-better'], :flag, 'Skip all system checks',
331
+ :default => false
332
+ app_option ['--skip-puppet-version-check'], :flag, 'Skip check for compatible Puppet versions',
333
+ :default => false, :advanced => true
334
+ app_option ['-v', '--[no-]verbose'], :flag, 'Display log on STDOUT instead of progressbar',
335
+ :default => config.app[:verbose]
336
+ app_option ['-l', '--verbose-log-level'], 'LEVEL', 'Log level for verbose mode output',
337
+ :default => 'notice'
338
+ app_option ['-S', '--scenario'], 'SCENARIO', 'Use installation scenario'
339
+ app_option ['--disable-scenario'], 'SCENARIO', 'Disable installation scenario',
340
+ :advanced => true
341
+ app_option ['--enable-scenario'], 'SCENARIO', 'Enable installation scenario',
342
+ :advanced => true
343
+ app_option ['--list-scenarios'], :flag, 'List available installation scenarios'
344
+ app_option ['--force'], :flag, 'Force change of installation scenario',
345
+ :advanced => true
346
+ app_option ['--compare-scenarios'], :flag, 'Show changes between last used scenario and the scenario specified with -S or --scenario argument',
347
+ :advanced => true
348
+ app_option ['--migrations-only'], :flag, 'Apply migrations to a selected scenario and exit',
349
+ :advanced => true
350
+ app_option ['--[no-]parser-cache'], :flag, 'Force use or bypass of Puppet module parser cache',
351
+ :advanced => true
322
352
  end
323
353
 
324
354
  def set_options
325
- self.class.option '--full-help', :flag, "print complete help" do
355
+ app_option '--full-help', :flag, "print complete help" do
326
356
  @full_help = true
327
357
  request_help
328
358
  end
329
359
 
330
360
  modules.each do |mod|
331
- self.class.option d("--[no-]enable-#{mod.name}"),
332
- :flag,
333
- "Enable '#{mod.name}' puppet module",
334
- :default => mod.enabled?
361
+ app_option d("--[no-]enable-#{mod.name}"), :flag, "Enable '#{mod.name}' puppet module",
362
+ :default => mod.enabled?
335
363
  end
336
364
 
337
365
  params.sort.each do |param|
338
366
  doc = param.doc.nil? ? 'UNDOCUMENTED' : param.doc.join("\n")
339
- self.class.option parametrize(param), '', doc + " (current: #{param.value_to_s})",
340
- :multivalued => param.multivalued?
341
- self.class.option parametrize(param, 'reset-'), :flag,
342
- "Reset #{param.name} to the default value (#{param.default_to_s})"
367
+ app_option parametrize(param), '', doc + " (current: #{param.value_to_s})",
368
+ :multivalued => param.multivalued?
369
+ app_option parametrize(param, 'reset-'), :flag,
370
+ "Reset #{param.name} to the default value (#{param.default_to_s})"
343
371
  end
344
372
  end
345
373
 
@@ -347,13 +375,15 @@ module Kafo
347
375
  # so we accept either allowed args or those that does not start with '-' and are right after
348
376
  # accepted argument
349
377
  def clamp_app_arguments
350
- @allowed_clamp_app_arguments = self.class.app_options.map do |option|
378
+ @allowed_clamp_app_arguments = self.class.declared_options.map do |option|
351
379
  option.switches.map { |s| is_yes_no_flag?(s) ? build_yes_no_variants(s) : s }
352
380
  end
353
381
  @allowed_clamp_app_arguments.flatten!
354
382
 
355
383
  last_was_accepted = false
356
- ARGV.select { |arg| last_was_accepted = is_allowed_attribute_name?(arg) || (last_was_accepted && is_value?(arg)) }
384
+ ARGV.select do |arg|
385
+ last_was_accepted = is_allowed_attribute_name?(arg) || (last_was_accepted && is_value?(arg))
386
+ end
357
387
  end
358
388
 
359
389
  def is_yes_no_flag?(s)
@@ -373,7 +403,7 @@ module Kafo
373
403
  end
374
404
 
375
405
  def parse_app_arguments
376
- self.class.app_options.each do |option|
406
+ self.class.declared_options.each do |option|
377
407
  name = option.attribute_name
378
408
  value = send(option.flag? ? "#{name}?" : name)
379
409
  config.app[name.to_sym] = value.nil? ? option.default_value : value
@@ -410,11 +440,11 @@ module Kafo
410
440
  end
411
441
 
412
442
  def validate_all(logging = true)
413
- logger.info 'Running validation checks'
443
+ logger.notice 'Running validation checks'
414
444
  results = enabled_params.map do |param|
415
445
  result = param.valid?
416
446
  errors = param.validation_errors.join(', ')
417
- progress_log(:error, "Parameter #{with_prefix(param)} invalid: #{errors}") if logging && !result
447
+ progress_log(:error, "Parameter #{with_prefix(param)} invalid: #{errors}", logger) if logging && !result
418
448
  result
419
449
  end
420
450
  results.all?
@@ -429,13 +459,13 @@ module Kafo
429
459
  execution_env.store_answers
430
460
  puppetconf = execution_env.configure_puppet(
431
461
  'color' => false,
432
- 'evaltrace' => !!@progress_bar,
462
+ 'evaltrace' => true,
433
463
  'noop' => !!noop?,
434
464
  'profile' => !!profile?,
435
465
  'show_diff' => true,
436
466
  )
437
467
 
438
- exit_code = 0
468
+ self.class.exit_handler.exit_code = 0
439
469
  exit_status = nil
440
470
  options = [
441
471
  '--verbose',
@@ -445,11 +475,29 @@ module Kafo
445
475
  begin
446
476
  command = PuppetCommand.new('include kafo_configure', options, puppetconf).command
447
477
  log_parser = PuppetLogParser.new
478
+ logger = Logger.new('configure')
479
+
480
+ start_message = <<-HEREDOC
481
+ Starting system configuration.
482
+ The total number of configuration tasks may increase during the run.
483
+ Observe logs or specify --verbose-log-level to see individual configuration tasks.
484
+ HEREDOC
485
+
486
+ logger.notice(start_message.chomp)
487
+
448
488
  PTY.spawn(*PuppetCommand.format_command(command)) do |stdin, stdout, pid|
449
489
  begin
450
490
  stdin.each do |line|
451
491
  line = normalize_encoding(line)
452
- progress_log(*log_parser.parse(line))
492
+ method, message = log_parser.parse(line)
493
+ progress_log(method, message, logger)
494
+
495
+ if (output = line.match(%r{(.+\]): Starting to evaluate the resource( \((?<count>\d+) of (?<total>\d+)\))?}))
496
+ if (output[:count].to_i % 100) == 1 && output[:count].to_i != 1
497
+ logger.notice("#{output[:count].to_i - 1} out of #{output[:total]} done.")
498
+ end
499
+ end
500
+
453
501
  @progress_bar.update(line) if @progress_bar
454
502
  end
455
503
  rescue Errno::EIO # we reach end of input
@@ -459,21 +507,22 @@ module Kafo
459
507
  Process.wait(pid)
460
508
  rescue Errno::ECHILD # process could exit meanwhile so we rescue
461
509
  end
462
- exit_code = $?.exitstatus
510
+ self.class.exit_handler.exit_code = $?.exitstatus
463
511
  end
464
512
  end
465
513
  end
466
514
  rescue PTY::ChildExited => e # could be raised by Process.wait on older ruby or by PTY.check
467
- exit_code = e.status.exitstatus
515
+ self.class.exit_handler.exit_code = e.status.exitstatus
468
516
  end
517
+
469
518
  @progress_bar.close if @progress_bar
470
- logger.info "Puppet has finished, bye!"
471
- self.class.exit(exit_code) do
472
- self.class.hooking.execute(:post)
473
- end
519
+ logger.notice "System configuration has finished."
520
+
521
+ self.class.hooking.execute(:post)
522
+ self.class.exit(exit_code)
474
523
  end
475
524
 
476
- def progress_log(method, message)
525
+ def progress_log(method, message, logger)
477
526
  @progress_bar.print_error(message + "\n") if method == :error && @progress_bar
478
527
  logger.send(method, message)
479
528
  end
@@ -490,30 +539,6 @@ module Kafo
490
539
  File.join(Dir.pwd, 'config', 'kafo.yaml')
491
540
  end
492
541
 
493
- def self.use_colors?
494
- if config
495
- colors = config.app[:colors]
496
- else
497
- colors = ARGV.include?('--no-colors') ? false : nil
498
- colors = ARGV.include?('--colors') ? true : nil if colors.nil?
499
- end
500
- colors
501
- end
502
-
503
- def self.preset_color_scheme
504
- match = ARGV.join(' ').match(/--color-of-background[ =](\w+)/)
505
- background = match && match[1]
506
- ColorScheme.new(:background => background, :colors => use_colors?).setup
507
- end
508
-
509
- def self.set_color_scheme
510
- ColorScheme.new(
511
- :background => config.app[:color_of_background],
512
- :colors => use_colors?).setup
513
- end
514
-
515
- private
516
-
517
542
  def normalize_encoding(line)
518
543
  if line.respond_to?(:encode) && line.respond_to?(:valid_encoding?)
519
544
  line.valid_encoding? ? line : line.encode('UTF-16be', :invalid => :replace, :replace => '?').encode('UTF-8')