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,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in kafo.gemspec
4
+ gemspec
@@ -0,0 +1,11 @@
1
+ This program and entire repository is free software: you can redistribute it
2
+ and/or modify it under the terms of the GNU General Public License as
3
+ published by the Free Software Foundation, either version 3 of the License,
4
+ or any later version.
5
+
6
+ This program is distributed in the hope that it will be useful, but WITHOUT
7
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9
+
10
+ You should have received a copy of the GNU General Public License along with
11
+ this program. If not, see http://www.gnu.org/licenses/.
@@ -0,0 +1,269 @@
1
+ # Kafo
2
+
3
+ A puppet based installer and configurer (not-only) for Foreman and Katello
4
+ projects. Kafo is a ruby gem that allows you to create fancy user interfaces for
5
+ puppet modules. It's some kind of a nice frontend to a
6
+
7
+ ```bash
8
+ echo "include some_modules" | puppet apply
9
+ ```
10
+ ## Why should I care?
11
+
12
+ Suppose you work on a software which you want to distribute to a machine in
13
+ infrastructure managed by puppet. You write a puppet module for your app.
14
+ But you also want to be able to distribute this app to a machine outside of
15
+ puppet infrastructure (e.g. install it to your clients) or you want to install
16
+ it in order to create a puppet infrastructure itself (e.g. foreman or
17
+ foreman-proxy).
18
+
19
+ With kafo you can reuse your puppet modules for creating an installer. Even
20
+ better after the installation you can easily modify you configuration. All
21
+ using the very same puppet modules.
22
+
23
+ ## What it does, how does it work?
24
+
25
+ Kafo reads a config file to find out which modules should it use. Then it
26
+ loads parameters from puppet manifests and gives you a way to customize them.
27
+
28
+ There are three ways how you can set parameters. You can
29
+ * predefine them in configuration file
30
+ * specify them as CLI arguments
31
+ * you can use interactive mode which will ask you for all required parameters
32
+
33
+ Note that your answers (gathered from any mode) are saved for the next run
34
+ so you don't have to specify them again. Kafo also support default values of
35
+ parameters so you can set only those you want to change. Also you can combine
36
+ akk modes so you can create an answer file with default values easily
37
+ and then use it for unattended installs.
38
+
39
+ ## How do I use it?
40
+
41
+ First install kafo gem.
42
+
43
+ Using bundler - add kafo gem to your Gemfile and run
44
+ ```bash
45
+ bundle install
46
+ ```
47
+
48
+ or without bundler
49
+ ```bash
50
+ gem install kafo
51
+ ```
52
+
53
+ Create a directory for your installer. Let's say we want to create
54
+ foreman-installer.
55
+
56
+ ```bash
57
+ mkdir foreman-installer
58
+ cd foreman-installer
59
+ ```
60
+
61
+ Now we run +kafofy+ script which will prepare directory structure and
62
+ optionally create a bin script according to first parameter.
63
+
64
+ ```bash
65
+ kafofy foreman-installer
66
+ ```
67
+
68
+ You can see that it created modules directory where your puppet modules
69
+ should live. It also created config and bin directories. If you specified
70
+ argument (foreman-installer in this case) a script in bin was created.
71
+ It's the script you can use to run installer. If you did not specify any
72
+ you can run your installer by +kafo-configure+ which is provided by the gem.
73
+ All configuration related files are to be found in config directory.
74
+
75
+ So for example to install foreman you want to
76
+ ```bash
77
+ cd foreman-installer/modules
78
+ git clone https://github.com/theforeman/puppet-foreman/ foreman
79
+ ```
80
+ Currently you must also download any dependant modules.
81
+ Then you need to tell kafo it's going to use foreman module.
82
+ ```bash
83
+ cd ..
84
+ echo "foreman: true" > config/answers.yaml
85
+ ```
86
+ Fire it with -h
87
+ ```bash
88
+ bin/foreman-installer -h
89
+ ```
90
+
91
+ You will see all arguments that you can pass to kafo. Note that underscored
92
+ puppet parameters are automatically converted to dashed arguments. You can
93
+ also see a documentation extracted from foreman puppet module and default
94
+ value.
95
+
96
+ Now run it without -h argument. It will print you the puppet apply command
97
+ to execute. This will be automatized later. Look at config/answers.yaml, it
98
+ was populated with default values. To change those options you can use
99
+ arguments like this
100
+
101
+ ```bash
102
+ bin/foreman-installer --foreman-enc=false --foreman-db-type=sqlite
103
+ ```
104
+
105
+ or you can run interactive mode
106
+
107
+ ```bash
108
+ bin/foreman-installer --interactive
109
+ ```
110
+
111
+ Also every change made to config/answers.yaml persists and becomes new default
112
+ value for next run.
113
+
114
+ As you noticed there are several ways how to specify arguments. Here's the list
115
+ the lower the item is the higher precedence it has:
116
+ * default values from puppet modules
117
+ * values from answers.yaml
118
+ * values specified on CLI
119
+ * interactive mode arguments
120
+
121
+ # Advanced topics
122
+
123
+ ## Testing aka noop
124
+
125
+ You'll probably want to tweak your installer before so you may find --noop
126
+ argument handy. This will run puppet in noop so no change will be done to your
127
+ system. Default value is false!
128
+
129
+ ## Documentation
130
+
131
+ Every parameter that can be set by kafo *must* be documented. This means that
132
+ you must add documentation to your puppet class in init.pp. It's basically
133
+ rdoc formatted documentation that must be above class definitions. There can
134
+ be no space between doc block and class definition.
135
+
136
+ Example:
137
+ ```puppet
138
+ # Manage your foreman server
139
+ #
140
+ # This class ...
141
+ # ... does what it does.
142
+ #
143
+ # === Parameters:
144
+ #
145
+ # $foreman_url:: URL on which foreman is going to run
146
+ #
147
+ # $enc:: Should foreman act as an external node classifier (manage puppet class
148
+ # assignments)
149
+ # type:boolean
150
+ class foreman (
151
+ $foreman_url = $foreman::params::foreman_url,
152
+ $enc = $foreman::params::enc
153
+ ) {
154
+ class { 'foreman::install': }
155
+ }
156
+ ```
157
+
158
+ ## Argument types
159
+
160
+ By default all arguments that are parsed from puppet are treated as string.
161
+ If you want to indicate that a parameter has a particular type you can do it
162
+ in puppet manifest documentation like this
163
+
164
+ ```puppet
165
+ # $param:: Some documentation for param
166
+ type:boolean
167
+ ```
168
+
169
+ Supported types are: string, boolean, integer, array
170
+
171
+ Note that all arguments that are nil (have no value in answers.yaml or you
172
+ set them UNDEF (see below) are translated to ```undef``` in puppet.
173
+
174
+ ## Array arguments
175
+
176
+ Some arguments may be Arrays. If you want to specify array values you can
177
+ specify CLI argument multiple times e.g.
178
+ ```bash
179
+ bin/foreman-installer --puppetmaster-environments=development --puppetmaster-environments=production
180
+ ```
181
+
182
+ In interactive mode you'll be prompted for another value until you specify
183
+ blank line.
184
+
185
+ ## Validations
186
+
187
+ If you specify validations of parameters in you init.pp manifest they
188
+ will be executed for your values even before puppet is run. In order to do this
189
+ you must follow few rules however:
190
+
191
+ * you must use standard validation functions (e.g. validate_array, validate_re, ...)
192
+ * you must have stdlib in modules directory
193
+
194
+ ## Enabling or disabling module
195
+
196
+ You can enable or disable module specified in answers.yaml file. Every module
197
+ automatically adds two options to foreman-installer script. For module foreman
198
+ you have two flag options ```--enable-foreman``` and ```--no-enable-foreman```.
199
+
200
+ When you disable a module all its answers will be removed and module will be
201
+ set to false. When you reenable the module you'll end up with default values.
202
+
203
+ ## Special values for arguments
204
+
205
+ Sometimes you may want to enforce ```undef``` value for a particular parameter.
206
+ You can set this value by specifying UNDEF string e.g.
207
+
208
+ ```bash
209
+ bin/foreman-installer --foreman-db-password=UNDEF
210
+ ```
211
+
212
+ It also works in interactive mode.
213
+
214
+ ## Changing of log directory and user/group
215
+
216
+ By default kafo logs every run to a separate file in /var/log/kafo.
217
+ You probably want to put your installation logs alongside with other logs of
218
+ your application. That's why kafo has its own configuration file in which you
219
+ can tune details like this.
220
+
221
+ In order to do that create a configuration file in config/kafo.yaml. You can
222
+ use config/kafo.yaml.example as a template. If config/kafo.yaml does not exist
223
+ default values will be used.
224
+
225
+ As a developer you can appreciate more verbose log. You can set debug level
226
+ in config/kafo.yml. Also you can change a user or group that will own the
227
+ log file. This is usefull if your installer requires to be run under root
228
+ but you want the logs to be readable by specific users.
229
+
230
+ ## System checks
231
+
232
+ When you want to make sure that user has some software installed or has the
233
+ right version you can write a simple script and put it into checks directory.
234
+ All files found there will be ran and if any has non-zero exit code, kafo
235
+ wont execute puppet.
236
+
237
+ Everything on STDOUT is logged in debug level, everything on STDERR is logged
238
+ in error level.
239
+
240
+ Example shell script which checks java version
241
+
242
+ ```bash
243
+ #!/bin/sh
244
+ java -version 2>&1 | grep OpenJDK
245
+ exit $?
246
+ ```
247
+
248
+ ## Exit code
249
+
250
+ Kafo can terminate either before or after puppet is ran. Puppet is ran with
251
+ --detailed-exitcodes and Kafo returns the same exit code as puppet does. If
252
+ kafo terminates after puppet run exit codes are:
253
+ * '2' means there were changes,
254
+ * '4' means there were failures during the transaction,
255
+ * '6' means there were both changes and failures.
256
+
257
+ Other exit codes that can be returned:
258
+ * '0' means everything went fine no changes were made
259
+ * '20' means your system does not meet configuration criteria (system checks failed)
260
+ * '21' means your answer file contains invalid values
261
+ * '22' means that puppet modules contains some error (e.g. missing documentation)
262
+ * '23' means that you have no answer file
263
+ * '24' means that your answer file asks for puppet module that you did not provide
264
+ * '25' means that kafo could not get default values from puppet
265
+
266
+
267
+ # License
268
+
269
+ This project is licensed under the GPLv3+.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # TODO cover with tests
4
+ # TODO validations: "puppet master --compile <fqdn>" compiles a catalog for a given host but it uses the global manifest/modules rather than stdin.. but by specifying --manifestdir you could get it to read a site.pp that contains the "include kafo_configure"
5
+
6
+ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'kafo'))
7
+ require 'kafo_configure'
8
+ KafoConfigure.run
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ # Create directory structure
6
+ %w(bin config modules).each do |dir|
7
+ FileUtils.mkdir_p dir
8
+ end
9
+
10
+ # Copy config files
11
+ src = File.expand_path(File.join(File.dirname(__FILE__), '..'))
12
+ %w(config_header.txt kafo.yaml.example).each do |file|
13
+ FileUtils.cp src + "/config/#{file}", 'config/'
14
+ end
15
+
16
+ script_name = "kafo-configure"
17
+ # Optional configure script
18
+ if ARGV.size > 0
19
+ name = ARGV[0]
20
+ script_name = "bin/#{name}"
21
+ puts "... creating #{script_name}"
22
+ content = <<EOS
23
+ #!/usr/bin/env ruby
24
+ require 'kafo'
25
+ KafoConfigure.run
26
+ EOS
27
+ File.write script_name, content
28
+ FileUtils.chmod 0755, script_name
29
+ end
30
+
31
+ puts "Your directory was kafofied"
32
+
33
+ puts "Now you should:"
34
+ puts " 1. upload your puppet modules to modules directory"
35
+ puts " 2. create default config/answers.yaml"
36
+ puts " 3. run #{script_name}"
@@ -0,0 +1,8 @@
1
+ # Format:
2
+ # <classname>: false - don't include this class
3
+ # <classname>: true - include and use the defaults
4
+ # <classname>:
5
+ # <param>: <value> - include and override the default(s)
6
+ #
7
+ # See params.pp in each class for what options are available
8
+
@@ -0,0 +1,11 @@
1
+ # kafo main configuration file example
2
+ # you can rename it to kafo.yaml so it overwrite default kafo settings
3
+ # useful for development, e.g. when you want to move log files to local directory
4
+
5
+ # :log_dir: '/var/log/kafo'
6
+ # :log_level: :info
7
+
8
+ # Advanced configuration - if not set it's ignored
9
+ # :log_owner: root
10
+ # :log_group: root
11
+
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ $LOAD_PATH.unshift(lib + '/kafo')
5
+ $LOAD_PATH.unshift(lib + '/kafo/params')
6
+ require 'kafo/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = "kafo"
10
+ spec.version = Kafo::VERSION
11
+ spec.authors = ["Marek Hulan"]
12
+ spec.email = ["ares@igloonet.cz"]
13
+ spec.description = %q{A gem for making installations based on puppet user friendly}
14
+ spec.summary = %q{If you write puppet modules for installing your software, you can use kafo to create powerful installer}
15
+ spec.homepage = "https://github.com/theforeman/kafo-configure"
16
+ spec.license = "GPLv3+"
17
+
18
+ spec.files = `git ls-files`.split($/)
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+
26
+ # puppet manifests parsing
27
+ spec.add_dependency 'puppet'
28
+ spec.add_dependency 'rdoc', '~> 3.0'
29
+ # better logging
30
+ spec.add_dependency 'logging'
31
+ # CLI interface
32
+ spec.add_dependency 'clamp'
33
+ # interactive mode
34
+ spec.add_dependency 'highline'
35
+
36
+ end
@@ -0,0 +1,2 @@
1
+ require 'kafo/version'
2
+ require 'kafo/kafo_configure'
@@ -0,0 +1,87 @@
1
+ require 'yaml'
2
+ require 'kafo/puppet_module'
3
+
4
+ class Configuration
5
+ attr_reader :config_file
6
+
7
+
8
+ begin
9
+ default_hash = YAML.load_file(File.join(Dir.pwd, 'config/kafo.yaml'))
10
+ rescue => e
11
+ default_hash = {}
12
+ end
13
+ KAFO = {
14
+ :log_dir => '/var/log/kafo',
15
+ :log_level => :info,
16
+ }.merge(default_hash || {})
17
+
18
+ def initialize(file)
19
+ @logger = Logging.logger.root
20
+ @logger.info "Loading config file #{file}"
21
+
22
+ begin
23
+ @data = YAML.load_file file
24
+ rescue Errno::ENOENT => e
25
+ puts "No answers file at #{file} found, can not continue"
26
+ exit(23)
27
+ end
28
+
29
+ @config_file = file
30
+ @config_dir = File.join(Dir.pwd, 'config')
31
+ end
32
+
33
+ def modules
34
+ @modules ||= @data.keys.map { |mod| PuppetModule.new(mod).parse }
35
+ end
36
+
37
+ def params_default_values
38
+ @params_default_values ||= begin
39
+ @logger.info "Parsing default values from puppet modules..."
40
+ # TODO not dry, kafo_configure.rb does similar thing
41
+ modules_path = "modules:#{File.join(gem_root_path, '../../modules')}"
42
+ @logger.debug `echo '#{includes} dump_values(#{params})' | puppet apply --modulepath #{modules_path} 2>&1`
43
+ unless $?.exitstatus == 0
44
+ @logger.error "Could not get default values, cannot continue"
45
+ exit(25)
46
+ end
47
+ @logger.info "... finished"
48
+ YAML.load_file(File.join(@config_dir, 'default_values.yaml'))
49
+ end
50
+ end
51
+
52
+ # if a value is a true we return empty hash because we have no specific options for a
53
+ # particular puppet module
54
+ def [](key)
55
+ value = @data[key]
56
+ value.is_a?(Hash) ? value : {}
57
+ end
58
+
59
+ def module_enabled?(mod)
60
+ value = @data[mod.name]
61
+ !!value || value.is_a?(Hash)
62
+ end
63
+
64
+ def config_header
65
+ @config_header ||= File.read(File.join(gem_root_path, '/config/config_header.txt'))
66
+ end
67
+
68
+ def gem_root_path
69
+ @gem_root_path ||= File.join(File.dirname(__FILE__), '../../')
70
+ end
71
+
72
+ def store(data)
73
+ File.write(config_file, config_header + YAML.dump(data))
74
+ end
75
+
76
+ private
77
+
78
+ def includes
79
+ modules.map { |mod| "include #{mod.dir_name}::params" }.join(' ')
80
+ end
81
+
82
+ def params
83
+ params = modules.map(&:params).flatten
84
+ params = params.select { |p| p.default != 'UNSET' }
85
+ params.map { |param| "#{param.default}" }.join(',')
86
+ end
87
+ end