kafo 0.0.1

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,5 @@
1
+ class ConfigurationException < StandardError
2
+ end
3
+
4
+ class ModuleName < StandardError
5
+ end
@@ -0,0 +1,180 @@
1
+ require 'pty'
2
+ require 'clamp'
3
+ require 'kafo/exceptions'
4
+ require 'kafo/configuration'
5
+ require 'kafo/logger'
6
+ require 'kafo/string_helper'
7
+ require 'kafo/wizard'
8
+ require 'kafo/system_checker'
9
+
10
+ class KafoConfigure < Clamp::Command
11
+ include StringHelper
12
+ attr_reader :logger
13
+
14
+ class << self
15
+ attr_accessor :config, :root_dir
16
+ end
17
+
18
+ def initialize(*args)
19
+ root_dir = Dir.pwd
20
+ @logger = Logging.logger.root
21
+ # TODO read also file from different places (aka foreman_install puppet)
22
+ config_file = File.join(root_dir, 'config', 'answers.yaml')
23
+ self.class.config = Configuration.new(config_file)
24
+ self.class.root_dir = root_dir
25
+ set_env
26
+ super
27
+ set_parameters
28
+ set_options
29
+ end
30
+
31
+ def config
32
+ self.class.config
33
+ end
34
+
35
+ def execute
36
+ parse_cli_arguments
37
+
38
+ if verbose?
39
+ logger.appenders = logger.appenders << ::Logging.appenders.stdout(:layout => COLOR_LAYOUT)
40
+ end
41
+
42
+ unless SystemChecker.check
43
+ puts "Your system does not meet configuration criteria"
44
+ exit(20)
45
+ end
46
+
47
+ if interactive?
48
+ wizard = Wizard.new
49
+ wizard.run
50
+ else
51
+ unless validate_all
52
+ puts "Error during configuration, exiting"
53
+ exit(21)
54
+ end
55
+ end
56
+
57
+ store_params
58
+ run_installation
59
+ end
60
+
61
+ private
62
+
63
+ def params
64
+ @params ||= config.modules.map(&:params).flatten
65
+ rescue ModuleName => e
66
+ puts e
67
+ exit(24)
68
+ end
69
+
70
+ def set_parameters
71
+ params.each do |param|
72
+ # set values based on default_values
73
+ param.set_default(config.params_default_values)
74
+ # set values based on YAML
75
+ param.set_value_by_config(config)
76
+ end
77
+ end
78
+
79
+ def set_options
80
+ self.class.option ['-i', '--interactive'], :flag, 'Run in interactive mode?'
81
+ self.class.option ['-v', '--verbose'], :flag, 'Display log on STDOUT instead of progressbar?'
82
+ self.class.option ['-n', '--noop'], :flag, 'Run puppet in noop mode?', :default => false
83
+
84
+ config.modules.each do |mod|
85
+ self.class.option d("--[no-]enable-#{mod.name}"),
86
+ :flag,
87
+ "Enable puppet module #{mod.name}?",
88
+ :default => mod.enabled?
89
+ end
90
+
91
+ params.each do |param|
92
+ doc = param.doc.nil? ? 'UNDOCUMENTED' : param.doc.join("\n")
93
+ self.class.option parametrize(param), '', doc,
94
+ :default => param.value, :multivalued => param.multivalued?
95
+ end
96
+ end
97
+
98
+ def parse_cli_arguments
99
+ # enable/disable modules according to CLI
100
+ config.modules.each { |mod| send("enable_#{mod.name}?") ? mod.enable : mod.disable }
101
+
102
+ # set values coming from CLI arguments
103
+ params.each do |param|
104
+ variable_name = u(with_prefix(param))
105
+ variable_name += '_list' if param.multivalued?
106
+ cli_value = instance_variable_get("@#{variable_name}")
107
+ param.value = cli_value unless cli_value.nil?
108
+ end
109
+ end
110
+
111
+ def store_params
112
+ data = Hash[config.modules.map { |mod| [mod.name, mod.enabled? ? mod.params_hash : false] }]
113
+ config.store(data)
114
+ end
115
+
116
+ def validate_all(logging = true)
117
+ logger.info 'Running validation checks'
118
+ results = params.map do |param|
119
+ result = param.valid?
120
+ logger.error "Parameter #{param.name} invalid" if logging && !result
121
+ result
122
+ end
123
+ results.all?
124
+ end
125
+
126
+ def run_installation
127
+ exit_code = 0
128
+ modules_path = "modules:#{File.join(File.dirname(__FILE__), '../../modules')}"
129
+ options = [
130
+ "--modulepath #{modules_path}",
131
+ '--verbose',
132
+ '--debug',
133
+ '--color=false',
134
+ '--show_diff',
135
+ '--detailed-exitcodes',
136
+ ]
137
+ options.push '--noop' if noop?
138
+ begin
139
+ PTY.spawn("echo include kafo_configure | puppet apply #{options.join(' ')}") do |stdin, stdout, pid|
140
+ begin
141
+ stdin.each { |line| puppet_log(line) }
142
+ rescue Errno::EIO
143
+ exit_code = PTY.check(pid).exitstatus
144
+ end
145
+ end
146
+ rescue PTY::ChildExited
147
+ end
148
+ logger.info "Puppet has finished, bye!"
149
+ exit(exit_code)
150
+ end
151
+
152
+ def puppet_log(line)
153
+ method, message = case
154
+ when line =~ /^Error:(.*)/i
155
+ [:error, $1]
156
+ when line =~ /^Warning:(.*)/i
157
+ [:warn, $1]
158
+ when line =~ /^Notice:(.*)/i
159
+ [:warn, $1]
160
+ when line =~ /^Info:(.*)/i
161
+ [:info, $1]
162
+ when line =~ /^Debug:(.*)/i
163
+ [:debug, $1]
164
+ else
165
+ [:info, line]
166
+ end
167
+ Logging.logger['puppet'].send(method, message.chomp)
168
+ end
169
+
170
+ def unset
171
+ params.select { |p| p.module.enabled? && p.value_set.nil? }
172
+ end
173
+
174
+ def set_env
175
+ # Puppet tries to determine FQDN from /etc/resolv.conf and we do NOT want this behavior
176
+ facter_hostname = Socket.gethostname
177
+ ENV['FACTER_fqdn'] = facter_hostname
178
+ end
179
+
180
+ end
@@ -0,0 +1,41 @@
1
+ require 'fileutils'
2
+ require 'logging'
3
+
4
+ Logging.color_scheme('bright',
5
+ :levels => {
6
+ :info => :green,
7
+ :warn => :yellow,
8
+ :error => :red,
9
+ :fatal => [:white, :on_red]
10
+ },
11
+ :date => :blue,
12
+ :logger => :cyan,
13
+ :line => :yellow,
14
+ :file => :yellow,
15
+ :method => :yellow
16
+ )
17
+ pattern = "[%5l %d %c] %m\n"
18
+ COLOR_LAYOUT = Logging::Layouts::Pattern.new(:pattern => pattern, :color_scheme => 'bright')
19
+ NOCOLOR_LAYOUT = Logging::Layouts::Pattern.new(:pattern => pattern, :color_scheme => nil)
20
+
21
+ begin
22
+ FileUtils.mkdir_p(Configuration::KAFO[:log_dir], :mode => 0750)
23
+ rescue Errno::EACCES => e
24
+ puts "No permissions to create log dir #{Configuration::KAFO[:log_dir]}"
25
+ end
26
+
27
+ logger = Logging.logger.root
28
+ filename = "#{Configuration::KAFO[:log_dir]}/configure.log"
29
+ begin
30
+ logger.appenders = ::Logging.appenders.rolling_file('configure',
31
+ :filename => filename,
32
+ :layout => NOCOLOR_LAYOUT,
33
+ :truncate => true
34
+ )
35
+ # set owner and group (it's ignored if attribute is nil)
36
+ FileUtils.chown Configuration::KAFO[:log_owner], Configuration::KAFO[:log_group], filename
37
+ rescue ArgumentError => e
38
+ puts "File #{filename} not writeable, won't log anything to file!"
39
+ end
40
+
41
+ logger.level = Configuration::KAFO[:log_level]
@@ -0,0 +1,65 @@
1
+ class Param
2
+ attr_reader :name, :module
3
+ attr_accessor :default, :doc, :value_set
4
+
5
+ def initialize(builder, name)
6
+ @name = name
7
+ @module = builder
8
+ end
9
+
10
+ # we use @value_set flag because even nil can be valid value
11
+ def value
12
+ @value_set ? @value : default
13
+ end
14
+
15
+ def value=(value)
16
+ @value_set = true
17
+ @value = value == 'UNDEF' ? nil : value
18
+ end
19
+
20
+ def module_name
21
+ self.module.name
22
+ end
23
+
24
+ def to_s
25
+ "#<#{self.class}:#{self.object_id} @name=#{name.inspect} @default=#{default.inspect} @value=#{value.inspect}>"
26
+ end
27
+
28
+ def set_default(defaults)
29
+ if default == 'UNSET'
30
+ self.value = nil
31
+ else
32
+ self.value = (value = defaults[default]) == :undef ? nil : value
33
+ end
34
+ end
35
+
36
+ def set_value_by_config(config)
37
+ base = config[module_name]
38
+ self.value = base[name] if base.has_key?(name)
39
+ end
40
+
41
+ def valid?
42
+ validations = self.module.validations(self)
43
+ # we get validations that can also run on other arguments, we need to take only current param
44
+ # also we want to clone validations so we don't interfere
45
+ validations.map! do |v|
46
+ v = v.clone
47
+ args = v.arguments.select { |a| a.to_s == "$#{self.name}" }
48
+ v.arguments = Puppet::Parser::AST::ASTArray.new :children => args
49
+ v
50
+ end
51
+
52
+ validator = Validator.new([self])
53
+ validations.map { |v| v.evaluate(validator) }.all?
54
+ end
55
+
56
+ # To be overwritten in children
57
+ def multivalued?
58
+ false
59
+ end
60
+ end
61
+
62
+ require 'kafo/params/boolean'
63
+ require 'kafo/params/string'
64
+ require 'kafo/params/array'
65
+ require 'kafo/params/integer'
@@ -0,0 +1,59 @@
1
+ class ParamBuilder
2
+ ATTRIBUTE_RE = /^(type):(.*)/
3
+
4
+ def initialize(mod, data)
5
+ @data = data
6
+ @module = mod
7
+ end
8
+
9
+ def validate
10
+ parameters = @data['parameters'].keys.sort
11
+ docs = @data['docs'].keys.sort
12
+ if parameters == docs
13
+ return true
14
+ else
15
+ undocumented = parameters - docs
16
+ raise ConfigurationException, "undocumented parameters in #{@module.name}: #{undocumented.join(', ')}" unless undocumented.empty?
17
+ deleted = docs - parameters
18
+ raise ConfigurationException, "documentation mentioned unknown parameters in #{@module.name}: #{deleted.join(', ')}" unless deleted.empty?
19
+ raise ConfigurationException, "unknown error in configuration in #{@module.name}"
20
+ end
21
+ end
22
+
23
+ def build_params
24
+ @data['parameters'].keys.map do |param_name|
25
+ build(param_name, @data['parameters'][param_name], @data['docs'][param_name])
26
+ end
27
+ end
28
+
29
+ def build(name, default, docs)
30
+ param = get_type(docs).new(@module, name)
31
+ param.default = default
32
+ param.doc = get_documentation(docs)
33
+ param
34
+ end
35
+
36
+ private
37
+
38
+ def get_documentation(docs)
39
+ return nil if docs.nil?
40
+ docs.select { |line| line !~ ATTRIBUTE_RE }
41
+ end
42
+
43
+ def get_type(docs)
44
+ type = (get_attributes(docs)[:type] || '').capitalize
45
+ type.empty? || !Params.const_defined?(type) ? Params::String : Params.const_get(type, false)
46
+ end
47
+
48
+ def get_attributes(docs)
49
+ data = {}
50
+ return data if docs.nil?
51
+
52
+ docs.each do |line|
53
+ if line =~ ATTRIBUTE_RE
54
+ data[$1.to_sym] = $2
55
+ end
56
+ end
57
+ data
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ module Params
2
+ class Array < Param
3
+ def value=(value)
4
+ super
5
+ @value = typecast(@value)
6
+ end
7
+
8
+ def multivalued?
9
+ true
10
+ end
11
+
12
+ private
13
+
14
+ def typecast(value)
15
+ value.nil? ? nil : [value].flatten
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Params
2
+ class Boolean < Param
3
+ def value=(value)
4
+ super
5
+ @value = typecast(@value)
6
+ end
7
+
8
+ private
9
+
10
+ def typecast(value)
11
+ case value
12
+ when '0', 'false', 'f', false
13
+ false
14
+ when '1', 'true', 't', true
15
+ true
16
+ else
17
+ value
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module Params
2
+ class Integer < Param
3
+ def value=(value)
4
+ super
5
+ @value = typecast(@value)
6
+ end
7
+
8
+ private
9
+
10
+ def typecast(value)
11
+ value.nil? ? nil : value.to_i
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Params
2
+ class String < Param; end
3
+ end
@@ -0,0 +1,93 @@
1
+ require 'kafo/param'
2
+ require 'kafo/param_builder'
3
+ require 'kafo/puppet_module_parser'
4
+ require 'kafo/validator'
5
+
6
+ class PuppetModule
7
+ attr_reader :name, :params, :dir_name, :class_name, :manifest_name, :manifest_path
8
+
9
+ def initialize(name, parser = PuppetModuleParser)
10
+ @name = name
11
+ @dir_name = get_dir_name
12
+ @manifest_name = get_manifest_name
13
+ @class_name = get_class_name
14
+ @params = []
15
+ @manifest_path = File.join(KafoConfigure.root_dir, '/modules/', module_manifest_path)
16
+ @parser = parser
17
+ @validations = []
18
+ @logger = Logging.logger.root
19
+ end
20
+
21
+ def enabled?
22
+ @enabled.nil? ? @enabled = KafoConfigure.config.module_enabled?(self) : @enabled
23
+ end
24
+
25
+ def disable
26
+ @enabled = false
27
+ end
28
+
29
+ def enable
30
+ @enabled = true
31
+ end
32
+
33
+ def parse(builder_klass = ParamBuilder)
34
+ @params = []
35
+ raw_data = @parser.parse(manifest_path)
36
+ builder = builder_klass.new(self, raw_data)
37
+ @validations = raw_data['validations']
38
+
39
+ builder.validate
40
+ @params = builder.build_params
41
+
42
+ self
43
+ rescue ConfigurationException => e
44
+ puts "Unable to continue because of:"
45
+ puts e.message
46
+ exit(22)
47
+ end
48
+
49
+ def validations(param = nil)
50
+ if param.nil?
51
+ @validations
52
+ else
53
+ @validations.select do |validation|
54
+ validation.arguments.map(&:to_s).include?("$#{param.name}")
55
+ end
56
+ end
57
+ end
58
+
59
+ def params_hash
60
+ Hash[params.map { |param| [param.name, param.value] }]
61
+ end
62
+
63
+ private
64
+
65
+ # customer module directory name
66
+ def get_dir_name
67
+ case name
68
+ when 'puppetmaster'
69
+ 'puppet'
70
+ else
71
+ name
72
+ end
73
+ end
74
+
75
+ # custom manifest filename without .pp extension
76
+ def get_manifest_name
77
+ case name
78
+ when 'puppetmaster'
79
+ 'server'
80
+ else
81
+ 'init'
82
+ end
83
+ end
84
+
85
+ def get_class_name
86
+ manifest_name == 'init' ? name : "#{dir_name}::#{manifest_name}"
87
+ end
88
+
89
+ def module_manifest_path
90
+ "#{dir_name}/manifests/#{manifest_name}.pp"
91
+ end
92
+
93
+ end