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.
- checksums.yaml +4 -4
- data/README.md +192 -0
- data/bin/kafo-configure +1 -4
- data/bin/kafo-export-params +92 -68
- data/bin/kafofy +1 -1
- data/lib/kafo/condition.rb +25 -0
- data/lib/kafo/configuration.rb +104 -102
- data/lib/kafo/doc_parser.rb +129 -0
- data/lib/kafo/exceptions.rb +14 -3
- data/lib/kafo/help_builder.rb +3 -0
- data/lib/kafo/help_builders/advanced.rb +19 -0
- data/lib/kafo/help_builders/base.rb +82 -0
- data/lib/kafo/help_builders/basic.rb +12 -0
- data/lib/kafo/hooking.rb +37 -0
- data/lib/kafo/kafo_configure.rb +270 -243
- data/lib/kafo/logger.rb +54 -52
- data/lib/kafo/param.rb +83 -60
- data/lib/kafo/param_builder.rb +69 -45
- data/lib/kafo/param_group.rb +27 -0
- data/lib/kafo/params/array.rb +14 -12
- data/lib/kafo/params/boolean.rb +17 -15
- data/lib/kafo/params/integer.rb +11 -9
- data/lib/kafo/params/password.rb +44 -38
- data/lib/kafo/params/string.rb +8 -2
- data/lib/kafo/password_manager.rb +34 -32
- data/lib/kafo/progress_bar.rb +51 -48
- data/lib/kafo/progress_bars/black_white.rb +11 -9
- data/lib/kafo/progress_bars/colored.rb +20 -18
- data/lib/kafo/puppet_command.rb +34 -36
- data/lib/kafo/puppet_module.rb +92 -68
- data/lib/kafo/puppet_module_parser.rb +63 -60
- data/lib/kafo/string_helper.rb +19 -15
- data/lib/kafo/system_checker.rb +20 -18
- data/lib/kafo/validator.rb +49 -47
- data/lib/kafo/version.rb +1 -1
- data/lib/kafo/wizard.rb +135 -95
- metadata +28 -6
data/lib/kafo/configuration.rb
CHANGED
@@ -3,127 +3,129 @@ require 'yaml'
|
|
3
3
|
require 'kafo/puppet_module'
|
4
4
|
require 'kafo/password_manager'
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
module Kafo
|
7
|
+
class Configuration
|
8
|
+
attr_reader :config_file, :answer_file
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
DEFAULT = {
|
14
|
-
:log_dir => '/var/log/kafo',
|
15
|
-
:log_level => 'info',
|
16
|
-
:no_prefix => false,
|
17
|
-
:mapping => {},
|
18
|
-
:answer_file => '/etc/kafo/kafo.yaml',
|
19
|
-
:installer_dir => '.',
|
20
|
-
:modules_dir => './modules',
|
21
|
-
:default_values_dir => '/tmp',
|
22
|
-
:colors => Configuration.colors_possible?
|
23
|
-
}
|
24
|
-
|
25
|
-
def initialize(file, persist = true)
|
26
|
-
@config_file = file
|
27
|
-
@persist = persist
|
28
|
-
configure_application
|
29
|
-
@logger = KafoConfigure.logger
|
30
|
-
|
31
|
-
@answer_file = app[:answer_file]
|
32
|
-
begin
|
33
|
-
@data = YAML.load_file(@answer_file)
|
34
|
-
rescue Errno::ENOENT => e
|
35
|
-
puts "No answers file at #{@answer_file} found, can not continue"
|
36
|
-
KafoConfigure.exit(:no_answer_file)
|
10
|
+
def self.colors_possible?
|
11
|
+
!`which tput 2> /dev/null`.empty? && `tput colors`.to_i > 0
|
37
12
|
end
|
38
13
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
14
|
+
DEFAULT = {
|
15
|
+
:log_dir => '/var/log/kafo',
|
16
|
+
:log_level => 'info',
|
17
|
+
:no_prefix => false,
|
18
|
+
:mapping => {},
|
19
|
+
:answer_file => '/etc/kafo/kafo.yaml',
|
20
|
+
:installer_dir => '.',
|
21
|
+
:modules_dir => './modules',
|
22
|
+
:default_values_dir => '/tmp',
|
23
|
+
:colors => Configuration.colors_possible?
|
24
|
+
}
|
25
|
+
|
26
|
+
def initialize(file, persist = true)
|
27
|
+
@config_file = file
|
28
|
+
@persist = persist
|
29
|
+
configure_application
|
30
|
+
@logger = KafoConfigure.logger
|
31
|
+
|
32
|
+
@answer_file = app[:answer_file]
|
57
33
|
begin
|
58
|
-
|
59
|
-
rescue => e
|
60
|
-
|
34
|
+
@data = YAML.load_file(@answer_file)
|
35
|
+
rescue Errno::ENOENT => e
|
36
|
+
puts "No answers file at #{@answer_file} found, can not continue"
|
37
|
+
KafoConfigure.exit(:no_answer_file)
|
61
38
|
end
|
62
39
|
|
63
|
-
|
64
|
-
|
40
|
+
@config_dir = File.dirname(@config_file)
|
41
|
+
end
|
42
|
+
|
43
|
+
def save_configuration(configuration)
|
44
|
+
return true unless @persist
|
45
|
+
FileUtils.touch @config_file
|
46
|
+
File.chmod 0600, @config_file
|
47
|
+
File.open(@config_file, 'w') { |file| file.write(format(YAML.dump(configuration))) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def configure_application
|
51
|
+
result = app
|
52
|
+
save_configuration(result)
|
65
53
|
result
|
66
54
|
end
|
67
|
-
end
|
68
55
|
|
69
|
-
|
70
|
-
|
71
|
-
|
56
|
+
def app
|
57
|
+
@app ||= begin
|
58
|
+
begin
|
59
|
+
configuration = YAML.load_file(@config_file)
|
60
|
+
rescue => e
|
61
|
+
configuration = {}
|
62
|
+
end
|
63
|
+
|
64
|
+
result = DEFAULT.merge(configuration || {})
|
65
|
+
result[:password] ||= PasswordManager.new.password
|
66
|
+
result
|
67
|
+
end
|
68
|
+
end
|
72
69
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@logger.
|
82
|
-
|
70
|
+
def modules
|
71
|
+
@modules ||= @data.keys.map { |mod| PuppetModule.new(mod).parse }
|
72
|
+
end
|
73
|
+
|
74
|
+
def params_default_values
|
75
|
+
@params_default_values ||= begin
|
76
|
+
@logger.info "Parsing default values from puppet modules..."
|
77
|
+
command = PuppetCommand.new("#{includes} dump_values(#{params})").append('2>&1').command
|
78
|
+
@logger.debug `#{command}`
|
79
|
+
unless $?.exitstatus == 0
|
80
|
+
log = app[:log_dir] + '/' + app[:log_name]
|
81
|
+
puts "Could not get default values, check log file at #{log} for more information"
|
82
|
+
@logger.error "Could not get default values, cannot continue"
|
83
|
+
KafoConfigure.exit(:defaults_error)
|
84
|
+
end
|
85
|
+
@logger.info "... finished"
|
86
|
+
YAML.load_file(File.join(KafoConfigure.config.app[:default_values_dir], 'default_values.yaml'))
|
83
87
|
end
|
84
|
-
@logger.info "... finished"
|
85
|
-
YAML.load_file(File.join(KafoConfigure.config.app[:default_values_dir], 'default_values.yaml'))
|
86
88
|
end
|
87
|
-
end
|
88
89
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
# if a value is a true we return empty hash because we have no specific options for a
|
91
|
+
# particular puppet module
|
92
|
+
def [](key)
|
93
|
+
value = @data[key]
|
94
|
+
value.is_a?(Hash) ? value : {}
|
95
|
+
end
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
97
|
+
def module_enabled?(mod)
|
98
|
+
value = @data[mod.is_a?(String) ? mod : mod.name]
|
99
|
+
!!value || value.is_a?(Hash)
|
100
|
+
end
|
100
101
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
102
|
+
def config_header
|
103
|
+
files = [app[:config_header_file], File.join(KafoConfigure.gem_root, '/config/config_header.txt')].compact
|
104
|
+
file = files.select { |f| File.exists?(f) }.first
|
105
|
+
@config_header ||= file.nil? ? '' : File.read(file)
|
106
|
+
end
|
106
107
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
108
|
+
def store(data, file = nil)
|
109
|
+
filename = file || answer_file
|
110
|
+
FileUtils.touch filename
|
111
|
+
File.chmod 0600, filename
|
112
|
+
File.open(filename, 'w') { |file| file.write(config_header + format(YAML.dump(data))) }
|
113
|
+
end
|
113
114
|
|
114
|
-
|
115
|
+
private
|
115
116
|
|
116
|
-
|
117
|
-
|
118
|
-
|
117
|
+
def includes
|
118
|
+
modules.map { |mod| "include #{mod.dir_name}::params" }.join(' ')
|
119
|
+
end
|
119
120
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
121
|
+
def params
|
122
|
+
params = modules.map(&:params).flatten
|
123
|
+
params = params.select { |p| p.default != 'UNSET' }
|
124
|
+
params.map { |param| "#{param.default}" }.join(',')
|
125
|
+
end
|
125
126
|
|
126
|
-
|
127
|
-
|
127
|
+
def format(data)
|
128
|
+
data.gsub('!ruby/sym ', ':')
|
129
|
+
end
|
128
130
|
end
|
129
131
|
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rdoc'
|
3
|
+
require 'rdoc/markup' # required for RDoc < 0.9.5
|
4
|
+
require 'rdoc/markup/parser' # required for RDoc < 0.9.5
|
5
|
+
|
6
|
+
module Kafo
|
7
|
+
class DocParser
|
8
|
+
ATTRIBUTE_LINE = /^(condition|type)\s*:\s*(.*)/
|
9
|
+
HEADER_CONDITION = /\A(.+)\s*condition:\s*(.+)\Z/
|
10
|
+
|
11
|
+
def initialize(text)
|
12
|
+
@text = text
|
13
|
+
@nesting_buffer = []
|
14
|
+
@docs = {}
|
15
|
+
@groups = {}
|
16
|
+
@conditions = {}
|
17
|
+
@types = Hash.new('string')
|
18
|
+
@rdoc = rdoc_parser.parse(@text)
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :docs, :groups, :types, :conditions
|
22
|
+
|
23
|
+
# items is array of RDoc::Markup::* on one level
|
24
|
+
def parse(items = @rdoc.parts)
|
25
|
+
items.each do |item|
|
26
|
+
if item.is_a?(RDoc::Markup::Heading)
|
27
|
+
parse_header(item)
|
28
|
+
elsif item.is_a?(RDoc::Markup::List) && item.respond_to?(:items)
|
29
|
+
parse(item.items)
|
30
|
+
elsif item.is_a?(RDoc::Markup::ListItem)
|
31
|
+
parse_paragraph(item)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse_paragraph(para)
|
40
|
+
# Skip rdoc paras that aren't paragraphs
|
41
|
+
return unless (para.parts.to_s.scan("RDoc::Markup::Paragraph") == ["RDoc::Markup::Paragraph"])
|
42
|
+
# RDoc (>= 4) makes label an array
|
43
|
+
label = para.label.is_a?(Array) ? para.label.first : para.label
|
44
|
+
# Documentation must be a list - if there's no label then skip
|
45
|
+
return if label.nil?
|
46
|
+
key = label.gsub(/[^A-Za-z0-9_-]/, '')
|
47
|
+
@groups[key] = current_groups
|
48
|
+
text_parts = para.parts.first.parts.map!(&:strip)
|
49
|
+
attributes, docs = text_parts.partition { |line| line =~ ATTRIBUTE_LINE }
|
50
|
+
parse_attributes(key, attributes)
|
51
|
+
@docs[key] = docs
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_attributes(parameter, attributes)
|
55
|
+
condition = nil
|
56
|
+
attributes.each do |attribute|
|
57
|
+
data = attribute.match(ATTRIBUTE_LINE)
|
58
|
+
name, value = data[1], data[2]
|
59
|
+
|
60
|
+
case name
|
61
|
+
when 'type'
|
62
|
+
@types[parameter] = value
|
63
|
+
when 'condition'
|
64
|
+
if condition.nil?
|
65
|
+
condition = value
|
66
|
+
else
|
67
|
+
raise DocParseError, "Two or more conditions defined for #{name}"
|
68
|
+
end
|
69
|
+
else
|
70
|
+
raise DocParseError, "Unknown attribute #{name}"
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
condition = [current_condition, condition].compact.join(' && ')
|
75
|
+
@conditions[parameter] = condition.empty? ? nil : condition
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_header(heading)
|
79
|
+
if heading.level > current_level
|
80
|
+
@nesting_buffer.push nesting(heading)
|
81
|
+
elsif heading.level == current_level
|
82
|
+
@nesting_buffer.pop
|
83
|
+
@nesting_buffer.push nesting(heading)
|
84
|
+
else
|
85
|
+
while current_level >= heading.level do
|
86
|
+
@nesting_buffer.pop
|
87
|
+
end
|
88
|
+
@nesting_buffer.push nesting(heading)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def nesting(heading)
|
93
|
+
if heading.text =~ HEADER_CONDITION
|
94
|
+
text, condition = $1, $2
|
95
|
+
else
|
96
|
+
text, condition = heading.text, nil
|
97
|
+
end
|
98
|
+
Nesting.new(text.strip, heading.level, condition)
|
99
|
+
end
|
100
|
+
|
101
|
+
def current_groups
|
102
|
+
@nesting_buffer.map(&:name)
|
103
|
+
end
|
104
|
+
|
105
|
+
def current_level
|
106
|
+
current_nesting.nil? ? 0 : current_nesting.level
|
107
|
+
end
|
108
|
+
|
109
|
+
def current_condition
|
110
|
+
condition = @nesting_buffer.map(&:condition).select { |c| !c.nil? }.join(' && ')
|
111
|
+
condition.empty? ? nil : condition
|
112
|
+
end
|
113
|
+
|
114
|
+
def current_nesting
|
115
|
+
@nesting_buffer.last
|
116
|
+
end
|
117
|
+
|
118
|
+
def rdoc_parser
|
119
|
+
if RDoc::Markup.respond_to?(:parse)
|
120
|
+
RDoc::Markup
|
121
|
+
else # RDoc < 3.10.0
|
122
|
+
RDoc::Markup::Parser
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
class Nesting < Struct.new(:name, :level, :condition);
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/kafo/exceptions.rb
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
-
|
3
|
-
|
2
|
+
module Kafo
|
3
|
+
class ConfigurationException < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class ModuleName < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
class DocParseError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class ConditionError < StandardError
|
13
|
+
end
|
4
14
|
|
5
|
-
class
|
15
|
+
class TypeError < StandardError
|
16
|
+
end
|
6
17
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Kafo
|
4
|
+
module HelpBuilders
|
5
|
+
class Advanced < Base
|
6
|
+
def add_module(name, items)
|
7
|
+
data = by_parameter_groups(items)
|
8
|
+
if data.keys.size > 1
|
9
|
+
puts module_header(name + ':')
|
10
|
+
data.keys.each do |group|
|
11
|
+
add_list(header(2, group), data[group])
|
12
|
+
end
|
13
|
+
else
|
14
|
+
add_list(module_header(name), data[data.keys.first])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Kafo
|
4
|
+
module HelpBuilders
|
5
|
+
DEFAULT_GROUP_NAME = 'Basic'
|
6
|
+
DEFAULT_MODULE_NAME = 'Generic'
|
7
|
+
IGNORE_IN_GROUP_NAME = /\s*parameters:?/
|
8
|
+
|
9
|
+
class Base < Clamp::Help::Builder
|
10
|
+
include StringHelper
|
11
|
+
|
12
|
+
def initialize(params)
|
13
|
+
super()
|
14
|
+
@params = params
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_list(heading, items)
|
18
|
+
if heading == 'Options'
|
19
|
+
puts "\n#{heading}:"
|
20
|
+
|
21
|
+
data = by_module(items)
|
22
|
+
data.keys.each do |section|
|
23
|
+
if section == 'Generic'
|
24
|
+
add_list(header(1, section), data[section])
|
25
|
+
else
|
26
|
+
add_module(section, data[section])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def add_module(name, items)
|
37
|
+
raise NotImplementedError, 'add module not defined'
|
38
|
+
end
|
39
|
+
|
40
|
+
def header(level, text)
|
41
|
+
level(level) + ' ' + text
|
42
|
+
end
|
43
|
+
|
44
|
+
def module_header(name)
|
45
|
+
"\n" + header(1, 'Module ' + name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def level(n)
|
49
|
+
'=' * n
|
50
|
+
end
|
51
|
+
|
52
|
+
def by_parameter_groups(items)
|
53
|
+
data = Hash.new { |h, k| h[k] = [] }
|
54
|
+
params_mapping(items).each do |item, param|
|
55
|
+
data[group(param)].push item
|
56
|
+
end
|
57
|
+
data
|
58
|
+
end
|
59
|
+
|
60
|
+
def group(param)
|
61
|
+
name = param.groups.reverse.find { |group| group.include?('parameters') }
|
62
|
+
name.nil? ? DEFAULT_GROUP_NAME : name.sub(IGNORE_IN_GROUP_NAME, '')
|
63
|
+
end
|
64
|
+
|
65
|
+
def by_module(help_items)
|
66
|
+
data = Hash.new { |h, k| h[k] = [] }
|
67
|
+
params_mapping(help_items).each do |item, param|
|
68
|
+
data[param.nil? ? DEFAULT_MODULE_NAME : param.module_name].push item
|
69
|
+
end
|
70
|
+
data
|
71
|
+
end
|
72
|
+
|
73
|
+
def params_mapping(items)
|
74
|
+
items.map { |i| [i, parametrization[i.help.first.strip]] }
|
75
|
+
end
|
76
|
+
|
77
|
+
def parametrization
|
78
|
+
@parametrization ||= Hash[@params.map { |p| [parametrize(p), p] }]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|