kafo 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of kafo might be problematic. Click here for more details.

@@ -0,0 +1,12 @@
1
+ # encoding: UTF-8
2
+
3
+ module Kafo
4
+ module HelpBuilders
5
+ class Basic < Base
6
+ def add_module(name, items)
7
+ data = by_parameter_groups(items)
8
+ add_list(module_header(name), data['Basic'])
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,37 @@
1
+ module Kafo
2
+ class Hooking
3
+
4
+ attr_accessor :hooks, :kafo
5
+
6
+ def initialize
7
+ self.hooks = Hash.new { |h, k| h[k] = {} }
8
+ end
9
+
10
+ def logger
11
+ KafoConfigure.logger
12
+ end
13
+
14
+ def execute(group)
15
+ logger.info "Executing hooks in group #{group}"
16
+ self.hooks[group].each_pair do |name, hook|
17
+ result = hook.yield(kafo)
18
+ logger.debug "Hook #{name} returned #{result.inspect}"
19
+ end
20
+ logger.info "All hooks in group #{group} finished"
21
+ end
22
+
23
+ def register_pre(name, &block)
24
+ register(:pre, name, &block)
25
+ end
26
+
27
+ def register_post(name, &block)
28
+ register(:post, name, &block)
29
+ end
30
+
31
+ private
32
+
33
+ def register(group, name, &block)
34
+ self.hooks[group][name] = block
35
+ end
36
+ end
37
+ end
@@ -6,306 +6,333 @@ require 'kafo/exceptions'
6
6
  require 'kafo/configuration'
7
7
  require 'kafo/logger'
8
8
  require 'kafo/string_helper'
9
+ require 'kafo/help_builder'
9
10
  require 'kafo/wizard'
10
11
  require 'kafo/system_checker'
11
12
  require 'kafo/puppet_command'
12
13
  require 'kafo/progress_bar'
14
+ require 'kafo/hooking'
13
15
 
14
- class KafoConfigure < Clamp::Command
15
- include StringHelper
16
+ module Kafo
17
+ class KafoConfigure < Clamp::Command
18
+ include StringHelper
16
19
 
17
- class << self
18
- attr_accessor :config, :root_dir, :config_file, :gem_root, :temp_config_file,
19
- :modules_dir, :kafo_modules_dir, :verbose, :app_options, :logger
20
- end
21
20
 
22
- def initialize(*args)
23
- self.class.logger = Logger.new
24
- self.class.config_file = config_file
25
- self.class.config = Configuration.new(self.class.config_file)
26
- self.class.root_dir = File.expand_path(self.class.config.app[:installer_dir])
27
- modules_dir = self.class.config.app[:module_dir] || (self.class.config.app[:installer_dir] + '/modules')
28
- self.class.modules_dir = File.expand_path(modules_dir)
29
- self.class.gem_root = File.join(File.dirname(__FILE__), '../../')
30
- self.class.kafo_modules_dir = self.class.config.app[:kafo_modules_dir] || (self.class.gem_root + '/modules')
31
- @progress_bar = nil
32
-
33
- super
34
-
35
- set_app_options
36
- allowed = self.class.app_options.map(&:switches).flatten
37
- allowed.map! { |s| s.include?('[no-]') ? [s.sub('[no-]', ''), s.sub('[no-]', 'no-')] : s }.flatten!
38
- # we need to parse app config params using clamp even before run method does it
39
- # so we limit parsing only to app config options (because of --help and later defined params)
40
- parse ARGV.select { |a| a =~ /([a-zA-Z0-9_-]*)([= ].*)?/ && allowed.include?($1) }
41
- parse_app_arguments
42
- Logger.setup
43
-
44
- set_parameters # here the params gets parsed and we need app config populated
45
- set_options
46
- end
21
+ class << self
22
+ attr_accessor :config, :root_dir, :config_file, :gem_root, :temp_config_file,
23
+ :modules_dir, :kafo_modules_dir, :verbose, :app_options, :logger
24
+ attr_writer :hooking
47
25
 
48
- def config
49
- self.class.config
50
- end
26
+ def hooking
27
+ @hooking ||= Hooking.new
28
+ end
29
+ end
51
30
 
52
- def execute
53
- catch :exit do
54
- parse_cli_arguments
31
+ def initialize(*args)
32
+ self.class.logger = Logger.new
33
+ self.class.config_file = config_file
34
+ self.class.config = Configuration.new(self.class.config_file)
35
+ self.class.root_dir = File.expand_path(self.class.config.app[:installer_dir])
36
+ modules_dir = self.class.config.app[:module_dir] || (self.class.config.app[:installer_dir] + '/modules')
37
+ self.class.modules_dir = File.expand_path(modules_dir)
38
+ self.class.gem_root = File.join(File.dirname(__FILE__), '../../')
39
+ self.class.kafo_modules_dir = self.class.config.app[:kafo_modules_dir] || (self.class.gem_root + '/modules')
40
+ @progress_bar = nil
41
+ self.class.hooking.kafo = self
42
+
43
+ super
44
+
45
+ set_app_options
46
+ allowed = self.class.app_options.map(&:switches).flatten
47
+ allowed.map! { |s| s.include?('[no-]') ? [s.sub('[no-]', ''), s.sub('[no-]', 'no-')] : s }.flatten!
48
+ # we need to parse app config params using clamp even before run method does it
49
+ # so we limit parsing only to app config options (because of --help and later defined params)
50
+ parse ARGV.select { |a| a =~ /([a-zA-Z0-9_-]*)([= ].*)?/ && allowed.include?($1) }
51
+ parse_app_arguments
52
+ Logger.setup
53
+
54
+ set_parameters # here the params gets parsed and we need app config populated
55
+ set_options
56
+ end
55
57
 
56
- if (self.class.verbose = verbose?)
57
- Logger.setup_verbose
58
- else
59
- @progress_bar = self.class.config.app[:colors] ? ProgressBars::Colored.new : ProgressBars::BlackWhite.new
60
- end
61
58
 
62
- unless SystemChecker.check
63
- puts "Your system does not meet configuration criteria"
64
- exit(:invalid_system)
65
- end
59
+ def config
60
+ self.class.config
61
+ end
66
62
 
67
- if interactive?
68
- wizard = Wizard.new
69
- wizard.run
70
- else
71
- unless validate_all
72
- puts "Error during configuration, exiting"
73
- exit(:invalid_values)
63
+ def execute
64
+ catch :exit do
65
+ parse_cli_arguments
66
+
67
+ if (self.class.verbose = verbose?)
68
+ Logger.setup_verbose
69
+ else
70
+ @progress_bar = self.class.config.app[:colors] ? ProgressBars::Colored.new : ProgressBars::BlackWhite.new
71
+ end
72
+
73
+ unless SystemChecker.check
74
+ puts "Your system does not meet configuration criteria"
75
+ exit(:invalid_system)
76
+ end
77
+
78
+ if interactive?
79
+ wizard = Wizard.new(self)
80
+ wizard.run
81
+ else
82
+ unless validate_all
83
+ puts "Error during configuration, exiting"
84
+ exit(:invalid_values)
85
+ end
86
+ end
87
+
88
+ if dont_save_answers?
89
+ self.class.temp_config_file = temp_config_file
90
+ store_params(temp_config_file)
91
+ else
92
+ store_params
74
93
  end
94
+ run_installation
75
95
  end
96
+ return self
97
+ end
76
98
 
77
- if dont_save_answers?
78
- self.class.temp_config_file = temp_config_file
79
- store_params(temp_config_file)
80
- else
81
- store_params
99
+ def self.run
100
+ catch :exit do
101
+ return super
82
102
  end
83
- run_installation
103
+ Kernel.exit(self.exit_code) # fail in initialize
84
104
  end
85
- return self
86
- end
87
105
 
88
- def self.run
89
- catch :exit do
90
- return super
106
+ def exit_code
107
+ self.class.exit_code
91
108
  end
92
- Kernel.exit(self.exit_code) # fail in initialize
93
- end
94
109
 
95
- def exit_code
96
- self.class.exit_code
97
- end
110
+ def self.exit(code)
111
+ @exit_code = translate_exit_code(code)
112
+ throw :exit
113
+ end
98
114
 
99
- def self.exit(code)
100
- @exit_code = translate_exit_code(code)
101
- throw :exit
102
- end
115
+ def self.exit_code
116
+ @exit_code ||= 0
117
+ end
103
118
 
104
- def self.exit_code
105
- @exit_code ||= 0
106
- end
119
+ def self.translate_exit_code(code)
120
+ return code if code.is_a? Fixnum
121
+ error_codes = { :invalid_system => 20,
122
+ :invalid_values => 21,
123
+ :manifest_error => 22,
124
+ :no_answer_file => 23,
125
+ :unknown_module => 24,
126
+ :defaults_error => 25 }
127
+ if error_codes.has_key? code
128
+ return error_codes[code]
129
+ else
130
+ raise "Unknown code #{code}"
131
+ end
132
+ end
107
133
 
108
- def self.translate_exit_code(code)
109
- return code if code.is_a? Fixnum
110
- error_codes = { :invalid_system => 20,
111
- :invalid_values => 21,
112
- :manifest_error => 22,
113
- :no_answer_file => 23,
114
- :unknown_module => 24,
115
- :defaults_error => 25}
116
- if error_codes.has_key? code
117
- return error_codes[code]
118
- else
119
- raise "Unknown code #{code}"
134
+ def help
135
+ self.class.help(invocation_path, self)
120
136
  end
121
- end
122
137
 
123
- def self.app_option(*args, &block)
124
- self.app_options ||= []
125
- self.app_options.push self.option(*args, &block)
126
- self.app_options.last
127
- end
138
+ def self.help(*args)
139
+ kafo = args.pop
140
+ builder_class = kafo.full_help? ? HelpBuilders::Advanced : HelpBuilders::Basic
141
+ args.push builder_class.new(kafo.params)
142
+ super(*args)
143
+ end
128
144
 
129
- def params
130
- @params ||= modules.map(&:params).flatten
131
- rescue ModuleName => e
132
- puts e
133
- exit(:unknown_module)
134
- end
145
+ def self.app_option(*args, &block)
146
+ self.app_options ||= []
147
+ self.app_options.push self.option(*args, &block)
148
+ self.app_options.last
149
+ end
135
150
 
136
- def modules
137
- config.modules.sort
138
- end
151
+ def params
152
+ @params ||= modules.map(&:params).flatten
153
+ rescue ModuleName => e
154
+ puts e
155
+ exit(:unknown_module)
156
+ end
139
157
 
140
- def module(name)
141
- modules.detect { |m| m.name == name}
142
- end
158
+ def modules
159
+ config.modules.sort
160
+ end
143
161
 
144
- def param(mod, name)
145
- params.detect { |p| p.name == name && p.module.name == mod }
146
- end
162
+ def module(name)
163
+ modules.detect { |m| m.name == name }
164
+ end
147
165
 
148
- private
166
+ def param(mod, name)
167
+ params.detect { |p| p.name == name && p.module.name == mod }
168
+ end
149
169
 
150
- def logger
151
- self.class.logger
152
- end
170
+ private
153
171
 
154
- def exit(code)
155
- self.class.exit(code)
156
- end
172
+ def logger
173
+ self.class.logger
174
+ end
157
175
 
158
- def set_parameters
159
- params.each do |param|
160
- # set values based on default_values
161
- param.set_default(config.params_default_values)
162
- # set values based on YAML
163
- param.set_value_by_config(config)
176
+ def exit(code)
177
+ self.class.exit(code)
164
178
  end
165
- end
166
179
 
167
- def set_app_options
168
- self.class.app_option ['--[no-]colors'], :flag, 'Use color output on STDOUT',
169
- :default => !!config.app[:colors]
170
- self.class.app_option ['-d', '--dont-save-answers'], :flag, 'Skip saving answers to answers.yaml?',
171
- :default => !!config.app[:dont_save_answers]
172
- self.class.app_option '--ignore-undocumented', :flag, 'Ignore inconsistent parameters documentation',
173
- :default => false
174
- self.class.app_option ['-i', '--interactive'], :flag, 'Run in interactive mode'
175
- self.class.app_option '--log-level', 'LEVEL', 'Log level for log file output',
176
- :default => config.app[:log_level]
177
- self.class.app_option ['-n', '--noop'], :flag, 'Run puppet in noop mode?',
178
- :default => false
179
- self.class.app_option ['-v', '--verbose'], :flag, 'Display log on STDOUT instead of progressbar'
180
- self.class.app_option ['-l', '--verbose-log-level'], 'LEVEL', 'Log level for verbose mode output',
181
- :default => 'info'
182
- end
180
+ def set_parameters
181
+ params.each do |param|
182
+ # set values based on default_values
183
+ param.set_default(config.params_default_values)
184
+ # set values based on YAML
185
+ param.set_value_by_config(config)
186
+ end
187
+ end
183
188
 
184
- def set_options
185
- modules.each do |mod|
186
- self.class.option d("--[no-]enable-#{mod.name}"),
187
- :flag,
188
- "Enable puppet module #{mod.name}?",
189
- :default => mod.enabled?
189
+ def set_app_options
190
+ self.class.app_option ['--[no-]colors'], :flag, 'Use color output on STDOUT',
191
+ :default => !!config.app[:colors]
192
+ self.class.app_option ['-d', '--dont-save-answers'], :flag, 'Skip saving answers to answers.yaml?',
193
+ :default => !!config.app[:dont_save_answers]
194
+ self.class.app_option '--ignore-undocumented', :flag, 'Ignore inconsistent parameters documentation',
195
+ :default => false
196
+ self.class.app_option ['-i', '--interactive'], :flag, 'Run in interactive mode'
197
+ self.class.app_option '--log-level', 'LEVEL', 'Log level for log file output',
198
+ :default => config.app[:log_level]
199
+ self.class.app_option ['-n', '--noop'], :flag, 'Run puppet in noop mode?',
200
+ :default => false
201
+ self.class.app_option ['-v', '--verbose'], :flag, 'Display log on STDOUT instead of progressbar'
202
+ self.class.app_option ['-l', '--verbose-log-level'], 'LEVEL', 'Log level for verbose mode output',
203
+ :default => 'info'
190
204
  end
191
205
 
192
- params.sort.each do |param|
193
- doc = param.doc.nil? ? 'UNDOCUMENTED' : param.doc.join("\n")
194
- self.class.option parametrize(param), '', doc,
195
- :default => param.value, :multivalued => param.multivalued?
206
+ def set_options
207
+ self.class.option '--full-help', :flag, "print complete help" do
208
+ @full_help = true
209
+ request_help
210
+ end
211
+
212
+ modules.each do |mod|
213
+ self.class.option d("--[no-]enable-#{mod.name}"),
214
+ :flag,
215
+ "Enable puppet module #{mod.name}?",
216
+ :default => mod.enabled?
217
+ end
218
+
219
+ params.sort.each do |param|
220
+ doc = param.doc.nil? ? 'UNDOCUMENTED' : param.doc.join("\n")
221
+ self.class.option parametrize(param), '', doc,
222
+ :default => param.value, :multivalued => param.multivalued?
223
+ end
196
224
  end
197
- end
198
225
 
199
- def parse_app_arguments
200
- self.class.app_options.each do |option|
201
- name = option.attribute_name
202
- value = send(option.flag? ? "#{name}?" : name)
203
- config.app[name.to_sym] = value.nil? ? option.default_value : value
226
+ def parse_app_arguments
227
+ self.class.app_options.each do |option|
228
+ name = option.attribute_name
229
+ value = send(option.flag? ? "#{name}?" : name)
230
+ config.app[name.to_sym] = value.nil? ? option.default_value : value
231
+ end
204
232
  end
205
- end
206
233
 
207
- def parse_cli_arguments
208
- # enable/disable modules according to CLI
209
- config.modules.each { |mod| send("enable_#{mod.name}?") ? mod.enable : mod.disable }
234
+ def parse_cli_arguments
235
+ # enable/disable modules according to CLI
236
+ config.modules.each { |mod| send("enable_#{mod.name}?") ? mod.enable : mod.disable }
210
237
 
211
- # set values coming from CLI arguments
212
- params.each do |param|
213
- variable_name = u(with_prefix(param))
214
- variable_name += '_list' if param.multivalued?
215
- cli_value = instance_variable_get("@#{variable_name}")
216
- param.value = cli_value unless cli_value.nil?
238
+ # set values coming from CLI arguments
239
+ params.each do |param|
240
+ variable_name = u(with_prefix(param))
241
+ variable_name += '_list' if param.multivalued?
242
+ cli_value = instance_variable_get("@#{variable_name}")
243
+ param.value = cli_value unless cli_value.nil?
244
+ end
217
245
  end
218
- end
219
246
 
220
- def store_params(file = nil)
221
- data = Hash[config.modules.map { |mod| [mod.name, mod.enabled? ? mod.params_hash : false] }]
222
- config.store(data, file)
223
- end
247
+ def store_params(file = nil)
248
+ data = Hash[config.modules.map { |mod| [mod.name, mod.enabled? ? mod.params_hash : false] }]
249
+ config.store(data, file)
250
+ end
224
251
 
225
- def validate_all(logging = true)
226
- logger.info 'Running validation checks'
227
- results = params.map do |param|
228
- result = param.valid?
229
- progress_log(:error, "Parameter #{with_prefix(param)} invalid") if logging && !result
230
- result
252
+ def validate_all(logging = true)
253
+ logger.info 'Running validation checks'
254
+ results = params.map do |param|
255
+ result = param.valid?
256
+ progress_log(:error, "Parameter #{with_prefix(param)} invalid") if logging && !result
257
+ result
258
+ end
259
+ results.all?
231
260
  end
232
- results.all?
233
- end
234
261
 
235
- def run_installation
236
- exit_code = 0
237
- exit_status = nil
238
- options = [
239
- '--verbose',
240
- '--debug',
241
- '--color=false',
242
- '--show_diff',
243
- '--detailed-exitcodes',
244
- ]
245
- options.push '--noop' if noop?
246
- begin
247
- command = PuppetCommand.new('include kafo_configure', options).command
248
- PTY.spawn(command) do |stdin, stdout, pid|
249
- begin
250
- stdin.each do |line|
251
- progress_log(*puppet_parse(line))
252
- @progress_bar.update(line) if @progress_bar
253
- end
254
- rescue Errno::EIO # we reach end of input
255
- exit_status = PTY.check(pid, true) if PTY.respond_to?(:check) # ruby >= 1.9.2
256
- if exit_status.nil? # process is still running or we have old ruby so we don't know
257
- begin
258
- Process.wait(pid)
259
- rescue Errno::ECHILD # process could exit meanwhile so we rescue
262
+ def run_installation
263
+ self.class.hooking.execute(:pre) if self.class.hooking
264
+ exit_code = 0
265
+ exit_status = nil
266
+ options = [
267
+ '--verbose',
268
+ '--debug',
269
+ '--color=false',
270
+ '--show_diff',
271
+ '--detailed-exitcodes',
272
+ ]
273
+ options.push '--noop' if noop?
274
+ begin
275
+ command = PuppetCommand.new('include kafo_configure', options).command
276
+ PTY.spawn(command) do |stdin, stdout, pid|
277
+ begin
278
+ stdin.each do |line|
279
+ progress_log(*puppet_parse(line))
280
+ @progress_bar.update(line) if @progress_bar
281
+ end
282
+ rescue Errno::EIO # we reach end of input
283
+ exit_status = PTY.check(pid, true) if PTY.respond_to?(:check) # ruby >= 1.9.2
284
+ if exit_status.nil? # process is still running or we have old ruby so we don't know
285
+ begin
286
+ Process.wait(pid)
287
+ rescue Errno::ECHILD # process could exit meanwhile so we rescue
288
+ end
260
289
  end
261
- exit_code = $?.exitstatus # after check $? should be set correctly
262
- else # this should never happen (unless they change PTY.check behavior) in new ruby
263
- exit_code = exit_code.exitstatus
264
290
  end
265
291
  end
292
+ rescue PTY::ChildExited => e # could be raised by Process.wait on older ruby or by PTY.check
293
+ exit_code = e.status.exitstatus
266
294
  end
267
- rescue PTY::ChildExited => e # could be raised by Process.wait on older ruby or by PTY.check
268
- exit_code = e.status.exitstatus
295
+ @progress_bar.close if @progress_bar
296
+ logger.info "Puppet has finished, bye!"
297
+ FileUtils.rm(temp_config_file, :force => true)
298
+ self.class.hooking.execute(:post) if self.class.hooking
299
+ exit(exit_code)
269
300
  end
270
- @progress_bar.close if @progress_bar
271
- logger.info "Puppet has finished, bye!"
272
- FileUtils.rm(temp_config_file, :force => true)
273
- exit(exit_code)
274
- end
275
301
 
276
- def progress_log(method, message)
277
- @progress_bar.print_error(message + "\n") if method == :error && @progress_bar
278
- logger.send(method, message)
279
- end
302
+ def progress_log(method, message)
303
+ @progress_bar.print_error(message + "\n") if method == :error && @progress_bar
304
+ logger.send(method, message)
305
+ end
280
306
 
281
- def puppet_parse(line)
282
- method, message = case
283
- when line =~ /^Error:(.*)/i || line =~ /^Err:(.*)/i
284
- [:error, $1]
285
- when line =~ /^Warning:(.*)/i || line =~ /^Notice:(.*)/i
286
- [:warn, $1]
287
- when line =~ /^Info:(.*)/i
288
- [:info, $1]
289
- when line =~ /^Debug:(.*)/i
290
- [:debug, $1]
291
- else
292
- [:info, line]
293
- end
294
-
295
- return [method, message.chomp]
296
- end
307
+ def puppet_parse(line)
308
+ method, message = case
309
+ when line =~ /^Error:(.*)/i || line =~ /^Err:(.*)/i
310
+ [:error, $1]
311
+ when line =~ /^Warning:(.*)/i || line =~ /^Notice:(.*)/i
312
+ [:warn, $1]
313
+ when line =~ /^Info:(.*)/i
314
+ [:info, $1]
315
+ when line =~ /^Debug:(.*)/i
316
+ [:debug, $1]
317
+ else
318
+ [:info, line]
319
+ end
320
+
321
+ return [method, message.chomp]
322
+ end
297
323
 
298
- def unset
299
- params.select { |p| p.module.enabled? && p.value_set.nil? }
300
- end
324
+ def unset
325
+ params.select { |p| p.module.enabled? && p.value_set.nil? }
326
+ end
301
327
 
302
- def config_file
303
- return CONFIG_FILE if defined?(CONFIG_FILE) && File.exists?(CONFIG_FILE)
304
- return '/etc/kafo/kafo.yaml' if File.exists?('/etc/kafo/kafo.yaml')
305
- File.join(Dir.pwd, 'config', 'kafo.yaml')
306
- end
328
+ def config_file
329
+ return CONFIG_FILE if defined?(CONFIG_FILE) && File.exists?(CONFIG_FILE)
330
+ return '/etc/kafo/kafo.yaml' if File.exists?('/etc/kafo/kafo.yaml')
331
+ File.join(Dir.pwd, 'config', 'kafo.yaml')
332
+ end
307
333
 
308
- def temp_config_file
309
- @temp_config_file ||= "/tmp/kafo_answers_#{rand(1_000_000)}.yaml"
334
+ def temp_config_file
335
+ @temp_config_file ||= "/tmp/kafo_answers_#{rand(1_000_000)}.yaml"
336
+ end
310
337
  end
311
338
  end