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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +11 -0
- data/README.md +269 -0
- data/Rakefile +1 -0
- data/bin/kafo-configure +8 -0
- data/bin/kafofy +36 -0
- data/config/config_header.txt +8 -0
- data/config/kafo.yaml.example +11 -0
- data/kafo.gemspec +36 -0
- data/lib/kafo.rb +2 -0
- data/lib/kafo/configuration.rb +87 -0
- data/lib/kafo/exceptions.rb +5 -0
- data/lib/kafo/kafo_configure.rb +180 -0
- data/lib/kafo/logger.rb +41 -0
- data/lib/kafo/param.rb +65 -0
- data/lib/kafo/param_builder.rb +59 -0
- data/lib/kafo/params/array.rb +18 -0
- data/lib/kafo/params/boolean.rb +21 -0
- data/lib/kafo/params/integer.rb +14 -0
- data/lib/kafo/params/string.rb +3 -0
- data/lib/kafo/puppet_module.rb +93 -0
- data/lib/kafo/puppet_module_parser.rb +65 -0
- data/lib/kafo/string_helper.rb +19 -0
- data/lib/kafo/system_checker.rb +31 -0
- data/lib/kafo/validator.rb +59 -0
- data/lib/kafo/version.rb +3 -0
- data/lib/kafo/wizard.rb +131 -0
- data/modules/kafo_configure/lib/puppet/parser/functions/class_name.rb +18 -0
- data/modules/kafo_configure/lib/puppet/parser/functions/dump_values.rb +9 -0
- data/modules/kafo_configure/lib/puppet/parser/functions/hash_keys.rb +15 -0
- data/modules/kafo_configure/lib/puppet/parser/functions/is_hash.rb +22 -0
- data/modules/kafo_configure/lib/puppet/parser/functions/loadanyyaml.rb +37 -0
- data/modules/kafo_configure/manifests/init.pp +17 -0
- data/modules/kafo_configure/manifests/yaml_to_class.pp +24 -0
- metadata +197 -0
@@ -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
|
data/lib/kafo/logger.rb
ADDED
@@ -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]
|
data/lib/kafo/param.rb
ADDED
@@ -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,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,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
|