kafo 4.1.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -79,8 +79,8 @@ All configuration related files are to be found in the config directory.
79
79
 
80
80
  You can supply custom location for your scenario configuration and answer files
81
81
  and change configuration and answer files names using options:
82
- ```
83
- kafofy --help
82
+ ```console
83
+ $ kafofy --help
84
84
  Usage: kafofy [options]
85
85
  -c, --config_dir DIR location of the scenarios configuration directory [./config/installer-scenarios.d/]
86
86
  -s, --scenario SCENARIO scenario file name (without extension) [default]
@@ -736,6 +736,7 @@ We currently support the following hooks.
736
736
  * pre_commit - after validations or interactive wizard have completed, all parameter values are set but not yet stored in the answer file
737
737
  * pre - just before puppet is executed to converge system, after parameter values are stored in the answer file
738
738
  * post - just after puppet is executed to converge system
739
+ * pre_exit - happens during exit handling, before exit is completed
739
740
 
740
741
  For better understanding when the hooks are executed see the [diagram](doc/kafo_run.png).
741
742
 
Binary file
@@ -105,5 +105,7 @@
105
105
 
106
106
  #Greenyellow:**post**|
107
107
 
108
+ #Greenyellow:**pre_exit**|
109
+
108
110
  stop
109
111
  @enduml
Binary file
@@ -0,0 +1,19 @@
1
+ require_relative 'definition'
2
+
3
+ module Kafo
4
+ module AppOption
5
+ module Declaration
6
+
7
+ include Clamp::Option::Declaration
8
+
9
+ def app_option(switches, type, description, opts = {}, &block)
10
+ AppOption::Definition.new(switches, type, description, opts).tap do |option|
11
+ block ||= option.default_conversion_block
12
+ define_accessors_for(option, &block)
13
+ declared_options << option
14
+ end
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module Kafo
2
+ module AppOption
3
+ class Definition < Clamp::Option::Definition
4
+
5
+ def initialize(switches, type, description, options = {})
6
+ @advanced = options.fetch(:advanced, false)
7
+ super(switches, type, description, options)
8
+ end
9
+
10
+ def advanced?
11
+ @advanced
12
+ end
13
+
14
+ end
15
+ end
16
+ end
@@ -15,9 +15,11 @@ module Kafo
15
15
  :description => '',
16
16
  :enabled => true,
17
17
  :log_dir => '/var/log/kafo',
18
+ :log_owner => nil,
19
+ :log_group => nil,
18
20
  :store_dir => '',
19
21
  :log_name => 'configuration.log',
20
- :log_level => 'info',
22
+ :log_level => 'notice',
21
23
  :no_prefix => false,
22
24
  :mapping => {},
23
25
  :answer_file => './config/answers.yaml',
@@ -26,11 +28,20 @@ module Kafo
26
28
  :colors => Kafo::ColorScheme.colors_possible?,
27
29
  :color_of_background => :dark,
28
30
  :hook_dirs => [],
31
+ :check_dirs => nil,
29
32
  :custom => {},
30
33
  :facts => {},
31
34
  :low_priority_modules => [],
32
- :verbose_log_level => 'info',
33
- :skip_puppet_version_check => false
35
+ :verbose => false,
36
+ :verbose_log_level => 'notice',
37
+ :skip_puppet_version_check => false,
38
+ :parser_cache_path => nil,
39
+ :ignore_undocumented => nil,
40
+ :order => nil,
41
+ :hiera_config => nil,
42
+ :kafo_modules_dir => nil,
43
+ :config_header_file => nil,
44
+ :dont_save_answers => nil,
34
45
  }
35
46
 
36
47
  def self.get_scenario_id(filename)
@@ -57,10 +68,17 @@ module Kafo
57
68
 
58
69
  def save_configuration(configuration)
59
70
  return true unless @persist
71
+
72
+ trimmed = configuration.reject do |key, value|
73
+ !DEFAULT.key?(key) || value.nil?
74
+ end
75
+
60
76
  begin
61
77
  FileUtils.touch @config_file
62
78
  File.chmod 0600, @config_file
63
- File.open(@config_file, 'w') { |file| file.write(format(YAML.dump(configuration))) }
79
+ File.open(@config_file, 'w') do |file|
80
+ file.write(format(YAML.dump(trimmed.sort.to_h)))
81
+ end
64
82
  rescue Errno::EACCES
65
83
  puts "Insufficient permissions to write to #{@config_file}, can not continue"
66
84
  KafoConfigure.exit(:insufficient_permissions)
@@ -104,6 +122,10 @@ module Kafo
104
122
  custom_fact_storage[key.to_s] = value
105
123
  end
106
124
 
125
+ def has_custom_fact?(key)
126
+ custom_fact_storage.key?(key.to_s)
127
+ end
128
+
107
129
  def modules
108
130
  @modules ||= begin
109
131
  register_data_types
@@ -175,7 +197,7 @@ module Kafo
175
197
  }
176
198
  EOS
177
199
 
178
- @logger.info 'Loading default values from puppet modules...'
200
+ @logger.notice 'Loading default values from puppet modules...'
179
201
  command = PuppetCommand.new(dump_manifest, [], puppetconf, self).command
180
202
  stdout, stderr, status = Open3.capture3(*PuppetCommand.format_command(command))
181
203
 
@@ -200,7 +222,7 @@ EOS
200
222
  end
201
223
  end
202
224
 
203
- @logger.info "... finished"
225
+ @logger.notice "... finished"
204
226
 
205
227
  load_yaml_from_output(stdout.split($/))
206
228
  end
@@ -305,7 +327,7 @@ EOS
305
327
  save_configuration(app)
306
328
  store(answers)
307
329
  migrations.store_applied
308
- @logger.info("#{migrations.migrations.count} migration/s were applied. Updated configuration was saved.")
330
+ @logger.notice("#{migrations.migrations.count} migration/s were applied. Updated configuration was saved.")
309
331
  end
310
332
  migrations.migrations.count
311
333
  end
@@ -26,8 +26,8 @@ module Kafo
26
26
  def exit(code, &block)
27
27
  @exit_code = translate_exit_code(code)
28
28
  block.call if block
29
+ KafoConfigure.hooking.execute(:pre_exit, log_stage: false)
29
30
  logger.debug "Exit with status code: #{@exit_code} (signal was #{code})"
30
- logger.dump_errors unless KafoConfigure.verbose
31
31
  cleanup
32
32
  Kernel.exit(@exit_code)
33
33
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Kafo
4
4
  module HelpBuilders
5
- DEFAULT_GROUP_NAME = 'Basic'
6
- DEFAULT_MODULE_NAME = 'Generic'
5
+ DEFAULT_GROUP_NAME = 'Basic'
6
+ DEFAULT_MODULE_NAME = 'Generic'
7
7
  IGNORE_IN_GROUP_NAME = /\s*parameters:?/
8
8
 
9
9
  class Base < Clamp::Help::Builder
@@ -15,12 +15,14 @@ module Kafo
15
15
  end
16
16
 
17
17
  def add_list(heading, items)
18
+ return if items.empty?
18
19
  if heading == 'Options'
19
20
  puts "\n#{heading}:"
20
21
 
21
22
  data = by_module(items)
23
+
22
24
  sorted_keys(data).each do |section|
23
- if section == 'Generic'
25
+ if section == DEFAULT_MODULE_NAME
24
26
  add_list(header(1, section), data[section])
25
27
  else
26
28
  add_module(section, data[section])
@@ -4,8 +4,15 @@ module Kafo
4
4
  module HelpBuilders
5
5
  class Basic < Base
6
6
  def add_module(name, items)
7
- data = by_parameter_groups(except_resets(items))
8
- add_list(module_header(name), data['Basic'])
7
+ pruned = except_resets(items)
8
+ pruned = except_advanced(pruned)
9
+ data = by_parameter_groups(pruned)
10
+ add_list(module_header(name), data[DEFAULT_GROUP_NAME])
11
+ end
12
+
13
+ def add_list(heading, items)
14
+ pruned = except_advanced(items)
15
+ super(heading, pruned)
9
16
  end
10
17
 
11
18
  def string
@@ -17,6 +24,10 @@ module Kafo
17
24
  def except_resets(items)
18
25
  items.select { |i| !i.help.first.strip.start_with?('--reset-') || !i.help.last.include?('to the default value (') }
19
26
  end
27
+
28
+ def except_advanced(items)
29
+ items.reject { |item| item.respond_to?(:advanced?) && item.advanced? }
30
+ end
20
31
  end
21
32
  end
22
33
  end
@@ -3,49 +3,71 @@ require 'kafo/base_context'
3
3
 
4
4
  module Kafo
5
5
  class HookContext < BaseContext
6
+ # @return [Kafo::KafoConfigure]
6
7
  attr_reader :kafo
7
8
 
8
- def self.execute(kafo, &hook)
9
- # TODO can be removed in 0.6, is DEPRECATED since 0.5
10
- # instance_exec can be later changed to instance eval when people stop using |kafo| in their hooks
11
- # and rely only on hook context DSL
12
- if hook.arity > 0
13
- kafo.logger.warn "Hook '#{name}' is using block with arguments which is DEPRECATED, access to kafo instance is " +
14
- "provided by hook DSL, please remove |kafo| from your hook block"
15
- end
16
- new(kafo).instance_exec(kafo, &hook)
17
- end
18
-
19
- def initialize(kafo)
20
- @kafo = kafo
21
- end
22
-
23
9
  # some of hooks won't print any message because logger is not yet configured
24
10
  # configuration of logger depends on application configration (log level etc.)
25
- # examples:
11
+ #
12
+ # @return [Kafo::Logger]
13
+ #
14
+ # @example
26
15
  # logger.warn "this combindation of parameters is untested"
27
- def logger
28
- self.kafo.logger
16
+ attr_reader :logger
17
+
18
+ def self.execute(kafo, logger, &hook)
19
+ new(kafo, logger).instance_eval(&hook)
20
+ end
21
+
22
+ def initialize(kafo, logger)
23
+ @kafo = kafo
24
+ @logger = logger
29
25
  end
30
26
 
31
27
  # if you want to add new app_option be sure to do as soon as possible (usually boot hook)
32
- # otherwise it may be to late (e.g. when displaying help)
33
- # examples:
28
+ # otherwise it may be too late (e.g. when displaying help)
29
+ #
30
+ # @return [Clamp::Option]
31
+ #
32
+ # @example
34
33
  # app_option '--log-level', 'LEVEL', 'Log level for log file output', :default => config.app[:log_level]:
34
+ #
35
+ # @example
35
36
  # app_option ['-n', '--noop'], :flag, 'Run puppet in noop mode?', :default => false
36
37
  def app_option(*args)
37
38
  self.kafo.class.app_option(*args)
38
39
  end
39
40
 
40
- # examples:
41
+ # Returns whether the given app option exists. This is useful when there's a conditional option that is
42
+ # determined during boot; this helper can be used in later hooks to determine whether the option exists.
43
+ #
44
+ # @param [Symbol, String] option
45
+ def app_option?(option)
46
+ self.kafo.config.app.key?(option.to_sym)
47
+ end
48
+
49
+ # @param [Symbol, String] option
50
+ #
51
+ # @example
41
52
  # app_value(:log_level)
53
+ #
42
54
  # note the dash to underscore convention
43
55
  def app_value(option)
44
56
  self.kafo.config.app[option.to_sym]
45
57
  end
46
58
 
47
- # examples:
59
+ # Return the parameter of a module. Note that the module may not actually
60
+ # be enabled.
61
+ #
62
+ # @param [String] module_name
63
+ # @param [String] parameter_name
64
+ #
65
+ # @return [Kafo::Param, nil]
66
+ #
67
+ # @example
48
68
  # param('foreman', 'interface').value = 'eth0'
69
+ #
70
+ # @example
49
71
  # param('foreman', 'interface').value = app_option('bind_on_interface')
50
72
  def param(module_name, parameter_name)
51
73
  self.kafo.param(module_name, parameter_name)
@@ -56,8 +78,14 @@ module Kafo
56
78
  # part of answer file so it also preserves parameter values between runs. It also list
57
79
  # its options in help output. You can also specify mapping for this module as a second
58
80
  # parameter.
59
- # examples:
81
+ #
82
+ # @param [String] module_name
83
+ # @param [Hash, nil] mapping
84
+ #
85
+ # @example
60
86
  # add_module('my_module')
87
+ #
88
+ # @example
61
89
  # add_module('foreman::plugin::staypuft', {:dir_name => 'foreman', :manifest_name => 'plugin/staypuft'})
62
90
  def add_module(module_name, mapping = nil)
63
91
  self.kafo.config.add_mapping(module_name, mapping) if mapping
@@ -65,61 +93,108 @@ module Kafo
65
93
  end
66
94
 
67
95
  # Check if a module is enabled in the current configuration.
68
- # examples:
96
+ #
97
+ # @param [String] module_name
98
+ #
99
+ # @example
69
100
  # module_enabled?('example')
70
101
  def module_enabled?(module_name)
71
102
  mod = self.kafo.module(module_name)
72
103
  !mod.nil? && mod.enabled?
73
104
  end
74
105
 
106
+ # Check if a module is present in the current configuration.
107
+ #
108
+ # @param [String] module_name
109
+ #
110
+ # @example
111
+ # module_present?('example')
112
+ def module_present?(module_name)
113
+ mod = self.kafo.module(module_name)
114
+ !mod.nil?
115
+ end
116
+
75
117
  # You can trigger installer exit by this method. You must specify exit code as a first
76
118
  # argument. You can also specify a symbol alias which is built-in (see exit_handler.rb
77
119
  # for more details).
78
- # examples:
120
+ #
121
+ # @param [Integer, Symbol] code
122
+ #
123
+ # @example
79
124
  # exit(0)
125
+ #
126
+ # @example
80
127
  # exit(:manifest_error)
81
128
  def exit(code)
82
129
  self.kafo.class.exit(code)
83
130
  end
84
131
 
85
132
  # You can load a custom config value that has been saved using store_custom_config
133
+ #
134
+ # @param [Symbol] key
86
135
  def get_custom_config(key)
87
136
  self.kafo.config.get_custom(key)
88
137
  end
89
138
 
90
139
  # You can save any value into kafo configuration file, this is useful if you need to
91
140
  # share a value between more hooks and persist the values for next run
141
+ #
142
+ # @param [Symbol] key
143
+ # @param [Object] value
92
144
  def store_custom_config(key, value)
93
145
  self.kafo.config.set_custom(key, value)
94
146
  end
95
147
 
96
148
  # Load a custom fact from the custom fact storage as saved by store_custom_fact
149
+ #
150
+ # @param [Symbol] key
97
151
  def get_custom_fact(key)
98
152
  self.kafo.config.get_custom_fact(key)
99
153
  end
100
154
 
101
- # Store a any custom fact. This will show up as kafo.scenario.custom.your_fact.
155
+ # Store any custom fact. This will show up as kafo.scenario.custom.your_fact.
102
156
  # It is possible to use structures such as arrays and hashes besides the
103
157
  # obvious ones such as strings, integers, booleans.
104
158
  #
105
159
  # These facts can also be used in Hiera hierachy definitions.
160
+ #
161
+ # @param [Symbol] key
162
+ # @param [Object] value
106
163
  def store_custom_fact(key, value)
107
164
  self.kafo.config.set_custom_fact(key, value)
108
165
  end
109
166
 
167
+ # Check whether a custom fact exists, regardless of whether or not it has a value.
168
+ #
169
+ # @param [Symbol] key
170
+ def has_custom_fact?(key)
171
+ self.kafo.config.has_custom_fact?(key)
172
+ end
173
+
110
174
  # Return the id of the current scenario
175
+ #
176
+ # @return [String]
111
177
  def scenario_id
112
178
  self.kafo.config.scenario_id
113
179
  end
114
180
 
115
181
  # Return the path to the current scenario
182
+ #
183
+ # @return [String]
116
184
  def scenario_path
117
185
  self.kafo.config.config_file
118
186
  end
119
187
 
120
188
  # Return the actual data in the current scenario
189
+ #
190
+ # @return [Hash]
121
191
  def scenario_data
122
192
  self.kafo.config.app
123
193
  end
194
+
195
+ # Return the current exit code
196
+ def exit_code
197
+ self.kafo.exit_code
198
+ end
124
199
  end
125
200
  end
@@ -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)