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.

@@ -3,127 +3,129 @@ require 'yaml'
3
3
  require 'kafo/puppet_module'
4
4
  require 'kafo/password_manager'
5
5
 
6
- class Configuration
7
- attr_reader :config_file, :answer_file
6
+ module Kafo
7
+ class Configuration
8
+ attr_reader :config_file, :answer_file
8
9
 
9
- def self.colors_possible?
10
- !`which tput 2> /dev/null`.empty? && `tput colors`.to_i > 0
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
- @config_dir = File.dirname(@config_file)
40
- end
41
-
42
- def save_configuration(configuration)
43
- return true unless @persist
44
- FileUtils.touch @config_file
45
- File.chmod 0600, @config_file
46
- File.open(@config_file, 'w') { |file| file.write(format(YAML.dump(configuration))) }
47
- end
48
-
49
- def configure_application
50
- result = app
51
- save_configuration(result)
52
- result
53
- end
54
-
55
- def app
56
- @app ||= begin
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
- configuration = YAML.load_file(@config_file)
59
- rescue => e
60
- configuration = {}
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
- result = DEFAULT.merge(configuration || {})
64
- result[:password] ||= PasswordManager.new.password
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
- def modules
70
- @modules ||= @data.keys.map { |mod| PuppetModule.new(mod).parse }
71
- end
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
- def params_default_values
74
- @params_default_values ||= begin
75
- @logger.info "Parsing default values from puppet modules..."
76
- command = PuppetCommand.new("#{includes} dump_values(#{params})").append('2>&1').command
77
- @logger.debug `#{command}`
78
- unless $?.exitstatus == 0
79
- log = app[:log_dir] + '/' + app[:log_name]
80
- puts "Could not get default values, check log file at #{log} for more information"
81
- @logger.error "Could not get default values, cannot continue"
82
- KafoConfigure.exit(:defaults_error)
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
- # if a value is a true we return empty hash because we have no specific options for a
90
- # particular puppet module
91
- def [](key)
92
- value = @data[key]
93
- value.is_a?(Hash) ? value : {}
94
- end
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
- def module_enabled?(mod)
97
- value = @data[mod.is_a?(String) ? mod : mod.name]
98
- !!value || value.is_a?(Hash)
99
- end
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
- def config_header
102
- files = [ app[:config_header_file], File.join(KafoConfigure.gem_root, '/config/config_header.txt') ].compact
103
- file = files.select { |f| File.exists?(f) }.first
104
- @config_header ||= file.nil? ? '' : File.read(file)
105
- end
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
- def store(data, file = nil)
108
- filename = file || answer_file
109
- FileUtils.touch filename
110
- File.chmod 0600, filename
111
- File.open(filename, 'w') { |file| file.write(config_header + format(YAML.dump(data))) }
112
- end
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
- private
115
+ private
115
116
 
116
- def includes
117
- modules.map { |mod| "include #{mod.dir_name}::params" }.join(' ')
118
- end
117
+ def includes
118
+ modules.map { |mod| "include #{mod.dir_name}::params" }.join(' ')
119
+ end
119
120
 
120
- def params
121
- params = modules.map(&:params).flatten
122
- params = params.select { |p| p.default != 'UNSET' }
123
- params.map { |param| "#{param.default}" }.join(',')
124
- end
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
- def format(data)
127
- data.gsub('!ruby/sym ', ':')
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
@@ -1,6 +1,17 @@
1
1
  # encoding: UTF-8
2
- class ConfigurationException < StandardError
3
- end
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 ModuleName < StandardError
15
+ class TypeError < StandardError
16
+ end
6
17
  end
@@ -0,0 +1,3 @@
1
+ require 'kafo/help_builders/base'
2
+ require 'kafo/help_builders/basic'
3
+ require 'kafo/help_builders/advanced'
@@ -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