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,65 @@
|
|
1
|
+
require 'puppet'
|
2
|
+
require 'rdoc'
|
3
|
+
# Based on ideas from puppet-parse by Johan van den Dorpe
|
4
|
+
class PuppetModuleParser
|
5
|
+
def self.parse(file)
|
6
|
+
content = new(file)
|
7
|
+
|
8
|
+
{
|
9
|
+
'parameters' => content.parameters,
|
10
|
+
'docs' => content.docs,
|
11
|
+
'validations' => content.validations
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(file)
|
16
|
+
raise ModuleName, "File not found #{file}, check you answer file" unless File.exists?(file)
|
17
|
+
parser = Puppet::Parser::Parser.new('production')
|
18
|
+
values = Puppet.settings.instance_variable_get('@values')
|
19
|
+
values[:production][:confdir] ||= '/' # just some stubbing
|
20
|
+
parser.import(file)
|
21
|
+
|
22
|
+
# Find object in list of hostclasses
|
23
|
+
parser.environment.known_resource_types.hostclasses.each do |x|
|
24
|
+
@object = x.last if x.last.file == file
|
25
|
+
end
|
26
|
+
# Find object in list of definitions
|
27
|
+
parser.environment.known_resource_types.definitions.each do |x|
|
28
|
+
@object = x.last if x.last.file == file
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def parameters
|
33
|
+
parameters = {}
|
34
|
+
arguments = @object.respond_to?(:arguments) ? @object.arguments : {}
|
35
|
+
arguments.each { |k, v| parameters[k] = v.respond_to?(:value) ? v.value : nil }
|
36
|
+
parameters
|
37
|
+
end
|
38
|
+
|
39
|
+
def klass
|
40
|
+
@object.name if @object.class.respond_to?(:name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def validations(param = nil)
|
44
|
+
@object.code.select { |stmt| stmt.is_a?(Puppet::Parser::AST::Function) && stmt.name =~ /^validate_/ }
|
45
|
+
end
|
46
|
+
|
47
|
+
def docs
|
48
|
+
docs = {}
|
49
|
+
if !@object.doc.nil?
|
50
|
+
rdoc = RDoc::Markup.parse(@object.doc)
|
51
|
+
items = rdoc.parts.select { |part| part.respond_to?(:items) }.map(&:items).flatten
|
52
|
+
items.each do |item|
|
53
|
+
# Skip rdoc items that aren't paragraphs
|
54
|
+
next unless (item.parts.to_s.scan("RDoc::Markup::Paragraph") == ["RDoc::Markup::Paragraph"])
|
55
|
+
# RDoc (>= 4) makes label an array
|
56
|
+
label = item.label.is_a?(Array) ? item.label.first : item.label
|
57
|
+
# Documentation must be a list - if there's no label then skip
|
58
|
+
next if label.nil?
|
59
|
+
key = label.tr('^A-Za-z0-9_-', '')
|
60
|
+
docs[key] = item.parts.first.parts.map!(&:strip)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
docs
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module StringHelper
|
2
|
+
def dashize(string)
|
3
|
+
string.tr('_', '-')
|
4
|
+
end
|
5
|
+
alias :d :dashize
|
6
|
+
|
7
|
+
def underscore(string)
|
8
|
+
string.tr('-', '_')
|
9
|
+
end
|
10
|
+
alias :u :underscore
|
11
|
+
|
12
|
+
def with_prefix(param)
|
13
|
+
"#{d(param.module_name)}-#{d(param.name)}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def parametrize(param)
|
17
|
+
"--#{with_prefix(param)}"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# we require separate STDERR
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
class SystemChecker
|
5
|
+
def self.check
|
6
|
+
new(File.join(KafoConfigure.root_dir, 'checks', '*')).check
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(path)
|
10
|
+
@checkers = Dir.glob(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def logger
|
14
|
+
Logging::logger['checks']
|
15
|
+
end
|
16
|
+
|
17
|
+
def check
|
18
|
+
@checkers.map! do |checker|
|
19
|
+
logger.debug "Executing checker: #{checker}"
|
20
|
+
Open3.popen3(checker) { |stdin, stdout, stderr, wait_thr|
|
21
|
+
stdout = stdout.read
|
22
|
+
stderr = stderr.read
|
23
|
+
logger.debug stdout unless stdout.empty?
|
24
|
+
logger.error stderr unless stderr.empty?
|
25
|
+
wait_thr.value.success?
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
@checkers.all?
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Validator
|
2
|
+
|
3
|
+
def initialize(params)
|
4
|
+
files = KafoConfigure.root_dir + '/modules/stdlib/lib/puppet/parser/functions/validate_*.rb'
|
5
|
+
Dir.glob(files).each do |file|
|
6
|
+
require file
|
7
|
+
end
|
8
|
+
|
9
|
+
@params = params
|
10
|
+
@logger = Logging.logger.root
|
11
|
+
|
12
|
+
@cache ||= Hash.new do |hash, key|
|
13
|
+
@logger.debug "Looked for #{key}"
|
14
|
+
param = @params.select { |p| p.name == key.to_s }.first
|
15
|
+
hash[key] = param.nil? ? nil : param.value
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def lookupvar(name, options = {})
|
20
|
+
@cache[name]
|
21
|
+
end
|
22
|
+
|
23
|
+
# for puppet >= 3
|
24
|
+
def include?(value)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
# for puppet >= 3
|
29
|
+
def [](value, *args)
|
30
|
+
lookupvar(value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method, *args, &block)
|
34
|
+
method.to_s =~ /^function_(.*)$/
|
35
|
+
super unless $1
|
36
|
+
super unless Puppet::Parser::Functions.function($1)
|
37
|
+
# In odd circumstances, this might not end up defined by the previous
|
38
|
+
# method, so we might as well be certain.
|
39
|
+
if engine.respond_to? method
|
40
|
+
@logger.debug "calling #{method.inspect} with #{args.inspect}"
|
41
|
+
engine.send(method, *args)
|
42
|
+
else
|
43
|
+
raise Puppet::DevError, "Function #{$1} not defined despite being loaded!"
|
44
|
+
end
|
45
|
+
rescue Puppet::ParseError => e
|
46
|
+
@logger.error e.message
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def engine
|
53
|
+
@engine ||= begin
|
54
|
+
klass = Class.new
|
55
|
+
klass.send :include, Puppet::Parser::Functions.environment_module
|
56
|
+
klass.new
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/kafo/version.rb
ADDED
data/lib/kafo/wizard.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'highline/import'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
class Wizard
|
6
|
+
def initialize
|
7
|
+
setup_terminal
|
8
|
+
setup_colors
|
9
|
+
@name = 'Kafo'
|
10
|
+
@config = KafoConfigure.config
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
message = "Welcome to the #{@name} installer!"
|
15
|
+
say("<%= color('#{message}', :headline) %>")
|
16
|
+
say("<%= color('#{'-' * message.size}', :horizontal_line) %>")
|
17
|
+
say(<<END)
|
18
|
+
|
19
|
+
This wizard will gather all required information. You can change any parameter to your needs.
|
20
|
+
|
21
|
+
END
|
22
|
+
|
23
|
+
exit 0 unless agree("\n<%= color('Ready to start?', :question) %> (y/n)", false)
|
24
|
+
|
25
|
+
main_menu
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def main_menu
|
31
|
+
finished = false
|
32
|
+
until finished
|
33
|
+
say("\n<%= color('Main Config Menu', :headline) %>")
|
34
|
+
choose do |menu|
|
35
|
+
menu.prompt = 'Choose an option from the menu... '
|
36
|
+
@config.modules.each do |mod|
|
37
|
+
menu.choice "[#{mod.enabled? ? '✓' : '✗'}] Configure #{mod.name}" do
|
38
|
+
configure_module(mod)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
menu.choice "Display current config" do
|
42
|
+
display_hash
|
43
|
+
end
|
44
|
+
menu.choice "<%= color('Save and run', :run) %>" do
|
45
|
+
KafoConfigure.config
|
46
|
+
finished = true
|
47
|
+
end
|
48
|
+
menu.choice "<%= color('Cancel run without Saving', :cancel) %>" do
|
49
|
+
say("Bye!"); exit 0
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def display_hash
|
56
|
+
data = Hash[@config.modules.map { |mod| [mod.name, mod.enabled? ? mod.params_hash : false] }]
|
57
|
+
say "<%= color('#{YAML.dump data}', :info) %>"
|
58
|
+
end
|
59
|
+
|
60
|
+
def configure_module(mod)
|
61
|
+
go_back = false
|
62
|
+
until go_back
|
63
|
+
say("\n<%= color('Module #{mod.name} configuration', :headline) %>")
|
64
|
+
choose do |menu|
|
65
|
+
menu.prompt = 'Choose an option from the menu... '
|
66
|
+
menu.choice("Enable/disable #{mod.name} module, current value: <%= color('#{mod.enabled?}', :info) %>") { turn_module(mod) }
|
67
|
+
if mod.enabled?
|
68
|
+
mod.params.each do |param|
|
69
|
+
menu.choice "Set <%= color('#{param.name}', :important) %>, current value: <%= color('#{param.value}', :info) %>" do
|
70
|
+
configure(param)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
menu.choice("Back to main menu") { go_back = true }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def configure(param)
|
80
|
+
say "\n<%= color('Parameter #{param.name} (of module #{param.module.name})', :headline) %>"
|
81
|
+
say "<%= color(\"#{param.doc.join("\n")}\", :important) %>"
|
82
|
+
value = param.multivalued? ? configure_multi(param) : configure_single(param)
|
83
|
+
value_was = param.value
|
84
|
+
param.value = value unless value.empty?
|
85
|
+
|
86
|
+
until param.valid?
|
87
|
+
param.value = value_was
|
88
|
+
say "\n<%= color('Invalid value for #{param.name}', :important) %>"
|
89
|
+
value = param.multivalued? ? configure_multi(param) : configure_single(param)
|
90
|
+
param.value = value unless value.empty?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def configure_single(param)
|
95
|
+
say "\ncurrent value: <%= color('#{param.value}', :info) %>"
|
96
|
+
ask("new value:")
|
97
|
+
end
|
98
|
+
|
99
|
+
def configure_multi(param)
|
100
|
+
say "<%= color('every line is a separate value, blank line to quit', :info) %>"
|
101
|
+
say "\ncurrent value: <%= color('#{param.value}', :info) %>"
|
102
|
+
ask("new value:") do |q|
|
103
|
+
q.gather = ""
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
def turn_module(mod)
|
109
|
+
agree("Enable #{mod.name} module? (y/n) ") ? mod.enable : mod.disable
|
110
|
+
end
|
111
|
+
|
112
|
+
def setup_terminal
|
113
|
+
$terminal = HighLine.new
|
114
|
+
data = HighLine::SystemExtensions.terminal_size
|
115
|
+
$terminal.wrap_at = data.first > 80 ? 80 : data.first
|
116
|
+
$terminal.page_at = data.last
|
117
|
+
end
|
118
|
+
|
119
|
+
# setup colour scheme for prompts
|
120
|
+
def setup_colors
|
121
|
+
HighLine.color_scheme = HighLine::ColorScheme.new do |cs|
|
122
|
+
cs[:headline] = [:bold, :yellow, :on_black]
|
123
|
+
cs[:horizontal_line] = [:bold, :white, :on_black]
|
124
|
+
cs[:important] = [:bold, :white, :on_black]
|
125
|
+
cs[:question] = [:bold, :green, :on_black]
|
126
|
+
cs[:info] = [:bold, :cyan, :on_black]
|
127
|
+
cs[:cancel] = [:bold, :red, :on_black]
|
128
|
+
cs[:run] = [:bold, :green, :on_black]
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Translates module name to proper class name
|
2
|
+
# This is especially useful if you want to give a nice name to
|
3
|
+
# a configuration option and still want to use some class with
|
4
|
+
# less readable name e.g. puppetmaster -> puppet::server
|
5
|
+
# - if the argument matches known module name, it returns corresponding class name
|
6
|
+
# - otherwise it returns argument that was specified
|
7
|
+
#
|
8
|
+
module Puppet::Parser::Functions
|
9
|
+
newfunction(:class_name, :type => :rvalue) do |args|
|
10
|
+
case args[0]
|
11
|
+
when 'puppetmaster'
|
12
|
+
'puppet::server'
|
13
|
+
else
|
14
|
+
args[0]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Takes a hash to extract keys from
|
2
|
+
# - If the first argument is not Hash, print warning and return nil
|
3
|
+
# - Returns array of hash keys otherwise
|
4
|
+
#
|
5
|
+
module Puppet::Parser::Functions
|
6
|
+
newfunction(:hash_keys, :type => :rvalue) do |args|
|
7
|
+
unless args[0].is_a?(Hash)
|
8
|
+
Puppet.warning "hash_keys takes one argument, the input hash"
|
9
|
+
nil
|
10
|
+
else
|
11
|
+
args[0].keys
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
#
|
2
|
+
# is_hash.rb
|
3
|
+
#
|
4
|
+
|
5
|
+
module Puppet::Parser::Functions
|
6
|
+
newfunction(:is_hash, :type => :rvalue, :doc => <<-EOS
|
7
|
+
Returns true if the variable passed to this function is a hash.
|
8
|
+
EOS
|
9
|
+
) do |arguments|
|
10
|
+
|
11
|
+
raise(Puppet::ParseError, "is_hash(): Wrong number of arguments " +
|
12
|
+
"given (#{arguments.size} for 1)") if arguments.size != 1
|
13
|
+
|
14
|
+
type = arguments[0]
|
15
|
+
|
16
|
+
result = type.is_a?(Hash)
|
17
|
+
|
18
|
+
return result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# vim: set ts=2 sw=2 et :
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Puppet::Parser::Functions
|
2
|
+
|
3
|
+
# convert nil values to :undefined recursively
|
4
|
+
newfunction(:convert, :type => :rvalue) do |args|
|
5
|
+
hash = args[0]
|
6
|
+
data = {}
|
7
|
+
|
8
|
+
hash.each do |key, value|
|
9
|
+
if value.is_a?(Hash)
|
10
|
+
data[key] = function_convert([value])
|
11
|
+
else
|
12
|
+
data[key] = value.nil? ? :undef : value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
data
|
17
|
+
end
|
18
|
+
|
19
|
+
newfunction(:loadanyyaml, :type => :rvalue, :doc => <<-'ENDHEREDOC') do |args|
|
20
|
+
Load a YAML file containing an array, string, or hash, and return the data
|
21
|
+
in the corresponding native data type.
|
22
|
+
|
23
|
+
For example:
|
24
|
+
|
25
|
+
$myhash = loadanyyaml('/etc/puppet/data/myhash.yaml')
|
26
|
+
ENDHEREDOC
|
27
|
+
|
28
|
+
args.delete_if { |filename| not File.exist? filename }
|
29
|
+
|
30
|
+
if args.length == 0
|
31
|
+
raise Puppet::ParseError, ("loadanyyaml(): No files to load")
|
32
|
+
end
|
33
|
+
|
34
|
+
function_convert([YAML.load_file(args[0])])
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# This class is called from the kafo configure script
|
2
|
+
# and expects a yaml file to exist at either:
|
3
|
+
# optional $answers class parameter
|
4
|
+
# $modulepath/config/answers.yaml
|
5
|
+
# /etc/kafo-configure/answers.yaml
|
6
|
+
#
|
7
|
+
class kafo_configure(
|
8
|
+
$answers = undef
|
9
|
+
) {
|
10
|
+
|
11
|
+
$params = loadanyyaml($answers,
|
12
|
+
"/etc/kafo-configure/answers.yaml",
|
13
|
+
"config/answers.yaml")
|
14
|
+
$keys = hash_keys($params)
|
15
|
+
|
16
|
+
kafo_configure::yaml_to_class { $keys: }
|
17
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Takes a key to lookup in the installation answers file
|
2
|
+
# - If it's a hash, declare a class with those parameters
|
3
|
+
# - If it's true or "true" declare the default parameters for that class
|
4
|
+
# - If it's false or "false" ignore it
|
5
|
+
# - Otherwise fail with error
|
6
|
+
#
|
7
|
+
define kafo_configure::yaml_to_class {
|
8
|
+
|
9
|
+
$classname = class_name($name)
|
10
|
+
|
11
|
+
if is_hash($kafo_configure::params[$name]) {
|
12
|
+
# The quotes around $classname seem to matter to puppet's parser...
|
13
|
+
$params = { "${classname}" => $kafo_configure::params[$name] }
|
14
|
+
create_resources( 'class', $params )
|
15
|
+
} elsif $kafo_configure::params[$name] == true {
|
16
|
+
$params = { "${classname}" => {} }
|
17
|
+
create_resources( 'class', $params )
|
18
|
+
} elsif ! $kafo_configure::params[$name] or $kafo_configure::params[$name] == "false" {
|
19
|
+
debug("${::hostname}: not including $name")
|
20
|
+
} else {
|
21
|
+
fail("${::hostname}: unknown type of answers data for $name")
|
22
|
+
}
|
23
|
+
|
24
|
+
}
|