puppet-runner 0.0.20 → 0.0.27
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +5 -1
- data/README.md +272 -1
- data/bin/puppet-runner +168 -37
- data/puppet-runner.gemspec +2 -2
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0f8efcfb59ec97544f71bd48cef975038d26309d
|
4
|
+
data.tar.gz: c79062ae1be29f79cde741a014b8a66997ed1504
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9968eaf857daa85f81af9cd69bd76095a0845888787af4298383e6ed466d91c75d6d7e6e499ac56360d5d181d0daf8a0e121d9d8cafa7735b27b5c4d7f38eacd
|
7
|
+
data.tar.gz: c3d6892856cda4d15562deac8c53396694472185b8c2d17bdb9b8ea8a87c74026ffc28aa4546d679ffad2467826d398edb89fb557d948e497db010123177c7b7
|
data/.travis.yml
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
language: ruby
|
2
|
+
before_install:
|
3
|
+
# https://github.com/travis-ci/travis-rubies/issues/57#issuecomment-458981237
|
4
|
+
- "find /home/travis/.rvm/rubies -wholename '*default/bundler-*.gemspec' -delete"
|
5
|
+
- gem install bundler --version 2.2.10
|
2
6
|
notifications:
|
3
7
|
email: false
|
4
8
|
hipchat:
|
@@ -7,7 +11,7 @@ notifications:
|
|
7
11
|
template:
|
8
12
|
- 'Gem - %{repository} #%{build_number} (%{build_url}) by %{author}: %{message}'
|
9
13
|
rvm:
|
10
|
-
- 2.
|
14
|
+
- 2.4.0
|
11
15
|
deploy:
|
12
16
|
provider: rubygems
|
13
17
|
api_key:
|
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Executable gem that composes hiera configuration and facts and execute puppet ap
|
|
8
8
|
|
9
9
|
## Usage
|
10
10
|
|
11
|
-
* puppet-runner (prepare|all) [-c CONFIG_DIR] [-t TEMPLATES] [-d DESTINATION_DIR] [-f FACTS_DEST] [-s SERVERNAME] [-r PUPPETFILE_CONFIG] [-o PUPPETFILE_OUTPUT_PATH] [-e EYAML_KEY_PATH]
|
11
|
+
* puppet-runner (prepare|all) [-c CONFIG_DIR] [-t TEMPLATES] [-d DESTINATION_DIR] [-f FACTS_DEST] [-s SERVERNAME] [-r PUPPETFILE_CONFIG] [-o PUPPETFILE_OUTPUT_PATH] [-e EYAML_KEY_PATH] [-x CUSTOM_FACTS_DIR]
|
12
12
|
* puppet-runner start [-p PUPPET_APPLY]
|
13
13
|
* puppet-runner -h | --help
|
14
14
|
|
@@ -101,6 +101,271 @@ With this setup we define prefix -> substitution_string pair for each prefix in
|
|
101
101
|
|
102
102
|
"hostname"_facts.yaml - must contain all prefixed custom facts to customize the setup
|
103
103
|
|
104
|
+
### Fact Metadata
|
105
|
+
|
106
|
+
The defaults file contains all the facts (variables) that are needed to run the tempalte, the basic format of this is:
|
107
|
+
```
|
108
|
+
fact_name: 'fact_value'
|
109
|
+
```
|
110
|
+
|
111
|
+
So in this instance fact_name will have a default value of fact_value, which can be overiden by the user in their own facts file.
|
112
|
+
|
113
|
+
However it is also possible to add metadata to the facts by changing the format, the current metadata is `comment` and `type`, this extened format looks like:
|
114
|
+
|
115
|
+
```
|
116
|
+
fact_name:
|
117
|
+
value: 'fact_value'
|
118
|
+
comment: 'this is an important fact'
|
119
|
+
type: 'string'
|
120
|
+
```
|
121
|
+
|
122
|
+
The two metadata values are:
|
123
|
+
`comment` - This is a description for the fact to help identify what it is for, it is added above the fact in the reultant facts files written by puppet-runner
|
124
|
+
`type` - This identifies the data type of the fact, by default if this meatadata is not set the value defaults to `string`, current valid types are:
|
125
|
+
**string**
|
126
|
+
**boolean**
|
127
|
+
**nilable**
|
128
|
+
|
129
|
+
### Fact Types
|
130
|
+
|
131
|
+
As mentioned above it is possible to assign a "type" metadata element to each fact, the reasons is that facter delivers all its data as a string, this can cause issues so the "type" metadata allows puppet-runner to do something special for other data typs:
|
132
|
+
|
133
|
+
#### string
|
134
|
+
|
135
|
+
All data is passed by default as a string, setting the type to string does not do anythign special
|
136
|
+
|
137
|
+
#### boolean
|
138
|
+
|
139
|
+
If the type is set to boolean this tells puppet-runner that we want to pass in true boolean values, a conversion from the string value into true boolean is attempted. If the conversions is successfull the fact reference in the final compiled hiera document (/etc/puppet/hiera/<HOSTNAME>.eyaml) would be replaced with the actual boolean value so that puppet does not recive its string representation.
|
140
|
+
|
141
|
+
Example:
|
142
|
+
|
143
|
+
Template
|
144
|
+
```
|
145
|
+
---
|
146
|
+
|
147
|
+
prefixes:
|
148
|
+
- yum_
|
149
|
+
|
150
|
+
classes:
|
151
|
+
- yum
|
152
|
+
|
153
|
+
dependencies:
|
154
|
+
- yum
|
155
|
+
- puppi
|
156
|
+
|
157
|
+
yum::defaultrepo: "%{::yum_defaultrepo}"
|
158
|
+
```
|
159
|
+
|
160
|
+
If the defaults does not use metadata (or uses a type of string) and the facts are set as below
|
161
|
+
```
|
162
|
+
---
|
163
|
+
|
164
|
+
yum_defaultrepo: "true"
|
165
|
+
```
|
166
|
+
|
167
|
+
Then the value writen into /etc/puppet/hiera/<HOSTNAME>.eyaml would be
|
168
|
+
|
169
|
+
```
|
170
|
+
yum::defaultrepo: "%{::yum_defaultrepo}"
|
171
|
+
```
|
172
|
+
|
173
|
+
Whereas if the default was set to boolean as below:
|
174
|
+
```
|
175
|
+
---
|
176
|
+
|
177
|
+
yum_defaultrepo:
|
178
|
+
value: "true"
|
179
|
+
type: "boolean"
|
180
|
+
```
|
181
|
+
|
182
|
+
Then the value writen into /etc/puppet/hiera/<HOSTNAME>.eyaml would be
|
183
|
+
|
184
|
+
```
|
185
|
+
yum::defaultrepo: true
|
186
|
+
```
|
187
|
+
|
188
|
+
#### nilable
|
189
|
+
|
190
|
+
If the type is set to nilable this tells puppet-runner that we want to pass in a nil/undef (null) value instead of an empty string, if the fact value is blank '' it will be converted into a tilda (~) as this is the nil representation in hiera. If the conversions is successfull the fact reference in the final compiled hiera document (/etc/puppet/hiera/<HOSTNAME>.eyaml) would be replaced with a tilda. Please note this only works for puppet variables that are defaulted in the code to undef, if they have a value passing nil to them will result in the code default still being set
|
191
|
+
|
192
|
+
Example:
|
193
|
+
|
194
|
+
Template
|
195
|
+
```
|
196
|
+
---
|
197
|
+
classes:
|
198
|
+
- artifactory
|
199
|
+
|
200
|
+
|
201
|
+
artifactory::conf:
|
202
|
+
tarball_location_file: "%{::artifactory_file_location}"
|
203
|
+
tarball_location_url: "%{::artifactory_url_location}"
|
204
|
+
.........
|
205
|
+
```
|
206
|
+
|
207
|
+
If the defaults does not use metadata (or uses a type of string) and the facts are set as below
|
208
|
+
```
|
209
|
+
---
|
210
|
+
|
211
|
+
artifactory_file_location: '/tmp/file.zip'
|
212
|
+
artifactory_url_location:
|
213
|
+
```
|
214
|
+
|
215
|
+
They are therefore both mandatory fileds and puppet-runner will ask for you to give a value for both, however in reality these are mutually exclusive, one will take precident over the other so passing them both could have unforseen consequences.
|
216
|
+
|
217
|
+
The other otpion is to set the facts to be empty string, however this still passed an empty string into puppet which, unless the code has been written to discount that, could still cause issues:
|
218
|
+
```
|
219
|
+
artifactory_file_location: '/tmp/file.zip'
|
220
|
+
artifactory_url_location: ''
|
221
|
+
```
|
222
|
+
|
223
|
+
Then the value writen into /etc/puppet/hiera/<HOSTNAME>.eyaml would be
|
224
|
+
|
225
|
+
```
|
226
|
+
artifactory::conf:
|
227
|
+
tarball_location_file: "%{::artifactory_file_location}"
|
228
|
+
tarball_location_url: "%{::artifactory_url_location}"
|
229
|
+
```
|
230
|
+
|
231
|
+
Whereas if the metadata type was set to nilable and a value of '' (empty string is supplied) in the defaults
|
232
|
+
```
|
233
|
+
---
|
234
|
+
|
235
|
+
artifactory_file_location:
|
236
|
+
value: ''
|
237
|
+
comment: 'blah'
|
238
|
+
type: 'nilable'
|
239
|
+
artifactory_url_location:
|
240
|
+
value: ''
|
241
|
+
comment: 'other blah'
|
242
|
+
type: 'nilable'
|
243
|
+
```
|
244
|
+
|
245
|
+
And the facts were set with a value for one and empty string for the other as below:
|
246
|
+
```
|
247
|
+
artifactory_file_location: '/tmp/file.zip'
|
248
|
+
artifactory_url_location: ''
|
249
|
+
```
|
250
|
+
|
251
|
+
Then the value writen into /etc/puppet/hiera/<HOSTNAME>.eyaml would be
|
252
|
+
|
253
|
+
```
|
254
|
+
artifactory::conf:
|
255
|
+
tarball_location_file: "%{::artifactory_file_location}"
|
256
|
+
tarball_location_url: ~
|
257
|
+
```
|
258
|
+
|
259
|
+
### Inter fact references
|
260
|
+
|
261
|
+
There are a number of occasions where you may want one fact to point to the value of another, either because you want it to be exactly the same or you want your fact to be a superset of the other, puppet-runner will evaluate facts that reference another fact and present the last fact reference to puppet, the resolution will recursivly resolve facts down to their base fact to a max depth of 5, after which it will stop in order to prevent infinite loops, this will cause a failure of the fact lookup.
|
262
|
+
|
263
|
+
#### Example 1: Fact directly referneces another fact
|
264
|
+
|
265
|
+
In this example we want our fact to reference another, in this example we will point a custom fact at a system fact (although you can point it at any fact, system or custom)
|
266
|
+
|
267
|
+
**template:**
|
268
|
+
````
|
269
|
+
---
|
270
|
+
|
271
|
+
prefixes:
|
272
|
+
- vpn_snat_
|
273
|
+
|
274
|
+
classes:
|
275
|
+
- fw
|
276
|
+
|
277
|
+
dependencies:
|
278
|
+
- fw
|
279
|
+
- firewall
|
280
|
+
- stdlib
|
281
|
+
|
282
|
+
fw::rules: &fw_rules
|
283
|
+
"%{::vpn_snat_description}":
|
284
|
+
chain: "%{::vpn_snat_chain}"
|
285
|
+
tosource: "%{::vpn_snat_tosource}"
|
286
|
+
jump: "%{::vpn_snat_jump}"
|
287
|
+
source: "%{::vpn_snat_source}"
|
288
|
+
table: "%{::vpn_snat_table}"
|
289
|
+
proto: "%{::vpn_snat_proto}"
|
290
|
+
````
|
291
|
+
|
292
|
+
We want the `tosource` value to be set with the IP address for the servers eth0 adapter, there is already a system fact for this `ipaddress_eth0`
|
293
|
+
|
294
|
+
We set the facts as below:
|
295
|
+
|
296
|
+
````
|
297
|
+
vpn_snat_description: '000 VPN SNAT Configuration'
|
298
|
+
vpn_snat_chain: 'POSTROUTING'
|
299
|
+
vpn_snat_tosource: "%{::ipaddress_eth0}"
|
300
|
+
vpn_snat_jump: 'SNAT'
|
301
|
+
vpn_snat_source: '172.28.254.0/23'
|
302
|
+
vpn_snat_table: 'nat'
|
303
|
+
vpn_snat_proto: 'all'
|
304
|
+
````
|
305
|
+
|
306
|
+
Pupet-runner will resolve the `vpn_snat_tosource` faqt down to the first fact it references, which is `ipaddress_eth0`, as a result the value writen into /etc/puppet/hiera/<HOSTNAME>.eyaml would be
|
307
|
+
|
308
|
+
````
|
309
|
+
fw::rules: &fw_rules
|
310
|
+
"%{::vpn_snat_description}":
|
311
|
+
chain: "%{::vpn_snat_chain}"
|
312
|
+
tosource: "%{::ipaddress_eth0}"
|
313
|
+
jump: "%{::vpn_snat_jump}"
|
314
|
+
source: "%{::vpn_snat_source}"
|
315
|
+
table: "%{::vpn_snat_table}"
|
316
|
+
proto: "%{::vpn_snat_proto}"
|
317
|
+
````
|
318
|
+
|
319
|
+
#### Example 2: Fact includes another fact as part of its value
|
320
|
+
|
321
|
+
In this example we want our fact to be a superset of another fact.
|
322
|
+
|
323
|
+
**template:**
|
324
|
+
````
|
325
|
+
---
|
326
|
+
|
327
|
+
prefixes:
|
328
|
+
- tripwire_
|
329
|
+
|
330
|
+
classes:
|
331
|
+
- tripwire
|
332
|
+
|
333
|
+
dependencies:
|
334
|
+
- tripwire
|
335
|
+
- stdlib
|
336
|
+
- concat
|
337
|
+
|
338
|
+
tripwire::local_passphrase: '%{::tripwire_local_passphrase}'
|
339
|
+
tripwire::site_passphrase: '%{::tripwire_site_passphrase}'
|
340
|
+
tripwire::tripwire_email: '%{::tripwire_tripwire_email}'
|
341
|
+
tripwire::tripwire_policy_file: '%{::tripwire_tripwire_policy_file}'
|
342
|
+
````
|
343
|
+
|
344
|
+
We want the `site_passphrase` value to be the same as `local_passphrase` but with _LOCAL at the end
|
345
|
+
|
346
|
+
We set the facts as below:
|
347
|
+
|
348
|
+
````
|
349
|
+
tripwire_global_passphrase: 'super_secret'
|
350
|
+
tripwire_local_passphrase: "%{::tripwire_site_passphrase}_LOCAL"
|
351
|
+
tripwire_site_passphrase: "%{::tripwire_global_passphrase}
|
352
|
+
tripwire_tripwire_email: 'blackhole'
|
353
|
+
tripwire_tripwire_policy_file: 'false'
|
354
|
+
````
|
355
|
+
|
356
|
+
Note here we have added a custom fact that is not references in any template or default, this will still be avaliable via facter in the normal way.
|
357
|
+
|
358
|
+
The fact `tripwire_site_passphrase` will resolve down to `tripwire_global_passphrase` as in the previous example, however the fact `tripwire_local_passphrase` will be resolved twice (once to `tripwire_site_passphrase` and then again down to `tripwire_global_passphrase`)
|
359
|
+
|
360
|
+
As a result the value writen into /etc/puppet/hiera/<HOSTNAME>.eyaml would be
|
361
|
+
|
362
|
+
````
|
363
|
+
tripwire::local_passphrase: '%{::tripwire_global_passphrase}_LOCAL'
|
364
|
+
tripwire::site_passphrase: '%{::tripwire_global_passphrase}'
|
365
|
+
tripwire::tripwire_email: '%{::tripwire_tripwire_email}'
|
366
|
+
tripwire::tripwire_policy_file: '%{::tripwire_tripwire_policy_file}'
|
367
|
+
````
|
368
|
+
|
104
369
|
#### TEMPLATES
|
105
370
|
Must contain 2 subdirectories.
|
106
371
|
- templates - template yaml files
|
@@ -151,10 +416,16 @@ Path to output Puppetfile.
|
|
151
416
|
* -d DESTINATION_DIR --dest_dir DESTINATION_DIR Directory for result hiera config.
|
152
417
|
* -t TEMPLATES --templates TEMPLATES Directory containing templates and defaults folder with functionality templates and default facts
|
153
418
|
* -f FACTS_DEST --facts_dest_dir FACTS_DEST Destination directory to store result facts
|
419
|
+
* -x CUSTOM_FACTS_DIR --custom_facts_dir CUSTOM_FACTS_DIR Directory containing yaml files with custom facts that will be merged with ones from <hostname>_facts.yaml, custom facts can overwrite them
|
154
420
|
* -r PUPPETFILE_CONFIG --puppetfile_config puppetfile_config Puppetfile composition config file
|
155
421
|
* -o PUPPETFILE_OUTPUT_PATH --puppetfile_output_path PUPPETFILE_OUTPUT_PATH Result Puppetfile path
|
156
422
|
* -e EYAML_KEY_PATH --eyaml_key_pair EYAML_KEY_PATH Path to eyaml encryption key pair
|
157
423
|
* -p PUPPET_APPLY --puppet_apply PUPPET_APPLY Custom puppet apply command to run
|
424
|
+
* -k --keep-facts Flag to keep the encrypted facts file in /tmp for analysis
|
425
|
+
* -n --dry-run Flag to indicate puppet should run in dry run mode (--noop), this also sets the verbose flag to true
|
426
|
+
* -v --verbose Flag to indicate that all output from puppet apply should be displayed instead of just stdout
|
427
|
+
Commands:
|
428
|
+
|
158
429
|
|
159
430
|
Commands:
|
160
431
|
|
data/bin/puppet-runner
CHANGED
@@ -27,8 +27,8 @@ doc = <<DOCOPT
|
|
27
27
|
Adaptavist puppet runner
|
28
28
|
|
29
29
|
Usage:
|
30
|
-
puppet-runner (prepare|all) [-c CONFIG_DIR] [-t TEMPLATES] [-d DESTINATION_DIR] [-f FACTS_DEST] [-s SERVERNAME] [-p PUPPET_APPLY] [-r PUPPETFILE_CONFIG] [-o PUPPETFILE_OUTPUT_PATH] [-e EYAML_KEY_PATH] [-k]
|
31
|
-
puppet-runner start [-p PUPPET_APPLY]
|
30
|
+
puppet-runner (prepare|all) [-c CONFIG_DIR] [-t TEMPLATES] [-d DESTINATION_DIR] [-f FACTS_DEST] [-s SERVERNAME] [-p PUPPET_APPLY] [-r PUPPETFILE_CONFIG] [-o PUPPETFILE_OUTPUT_PATH] [-e EYAML_KEY_PATH] [-m MODULE_PATH] [-x CUSTOM_FACTS_DIR] [-k] [-n] [-v]
|
31
|
+
puppet-runner start [-p PUPPET_APPLY] [-m MODULE_PATH] [-n] [-v]
|
32
32
|
puppet-runner -h | --help
|
33
33
|
|
34
34
|
Options:
|
@@ -38,11 +38,15 @@ Options:
|
|
38
38
|
-d DESTINATION_DIR --dest_dir DESTINATION_DIR Directory for result hiera config.
|
39
39
|
-t TEMPLATES --templates TEMPLATES Directory containing templates and defaults folder with functionality templates and default facts
|
40
40
|
-f FACTS_DEST --facts_dest_dir FACTS_DEST Destination directory to store result facts
|
41
|
+
-x CUSTOM_FACTS_DIR --custom_facts_dir CUSTOM_FACTS_DIR Directory containing yaml files with custom facts that will be merged with ones from <hostname>_facts.yaml, custom facts can overwrite them
|
42
|
+
-m MODULE_PATH --module_path MODULE_PATH Path to find puppet modules, can be colon (:) delimited
|
41
43
|
-p PUPPET_APPLY --puppet_apply PUPPET_APPLY Custom puppet apply command to run
|
42
44
|
-r PUPPETFILE_CONFIG --puppetfile_config puppetfile_config Puppetfile composition config file
|
43
45
|
-o PUPPETFILE_OUTPUT_PATH --puppetfile_output_path PUPPETFILE_OUTPUT_PATH Result Puppetfile path
|
44
46
|
-e EYAML_KEY_PATH --eyaml_key_path EYAML_KEY_PATH Path to eyaml encryption key pair
|
45
47
|
-k --keep-facts Flag to keep the encrypted facts file in /tmp for analysis
|
48
|
+
-n --dry-run Flag to indicate puppet should run in dry run mode (--noop), this also sets the verbose flag to true
|
49
|
+
-v --verbose Flag to indicate that all output from puppet apply should be displayed instead of just stdout
|
46
50
|
Commands:
|
47
51
|
all Runs the following commands prepare, start
|
48
52
|
start Runs puppet apply
|
@@ -107,12 +111,36 @@ def extract_type_from_hash(input)
|
|
107
111
|
type = val["type"]
|
108
112
|
end
|
109
113
|
end
|
110
|
-
{
|
114
|
+
{type => key }
|
111
115
|
}
|
112
116
|
end
|
113
117
|
res
|
114
118
|
end
|
115
119
|
|
120
|
+
# function to resolve inter fact references, it should return the last fact name (not value)
|
121
|
+
def resolve_fact(fact_name, max_depth=5, current_depth=0, return_fact_value=false)
|
122
|
+
final_val = nil
|
123
|
+
fact_name = fact_name.sub(/%{::/,'').sub(/}/,'')
|
124
|
+
if max_depth == current_depth
|
125
|
+
warning "Fact resolution has reached a depth of 5 facts, aborting lookup"
|
126
|
+
else
|
127
|
+
# attempt to get value
|
128
|
+
val = $all_facts[fact_name] || Facter.value(fact_name) || nil
|
129
|
+
|
130
|
+
if val == "%{::#{fact_name}}"
|
131
|
+
warning "Fact resolves to itself, skipping"
|
132
|
+
elsif val.instance_of?(String) and val.match(/%{::.*.}/)
|
133
|
+
val = resolve_fact(val,max_depth, current_depth + 1)
|
134
|
+
else
|
135
|
+
# if we have not been told to return the fact value, then return the fact key, else the value wil be returned
|
136
|
+
val = "%{::#{fact_name}}" if return_fact_value == false
|
137
|
+
end
|
138
|
+
final_val = val
|
139
|
+
end
|
140
|
+
final_val
|
141
|
+
end
|
142
|
+
|
143
|
+
|
116
144
|
begin
|
117
145
|
options = Docopt::docopt(doc)
|
118
146
|
rescue Docopt::Exit => e
|
@@ -126,6 +154,7 @@ if options['all'] || options['prepare']
|
|
126
154
|
input_dir = options["--config_dir"] || options["-c"]
|
127
155
|
dest_dir = options["--dest_dir"] || options["-d"]
|
128
156
|
facts_dest_dir = options["--facts_dest_dir"] || options["-f"]
|
157
|
+
custom_facts_dir = options["--custom_facts_dir"] || options["-x"] || nil
|
129
158
|
templates = options["--templates"] || options["-t"]
|
130
159
|
puppetfile_config_path = options["--puppetfile_config"] || options["-r"]
|
131
160
|
puppetfile_output_path = options["--puppetfile_output_path"] || options["-o"]
|
@@ -133,6 +162,7 @@ if options['all'] || options['prepare']
|
|
133
162
|
hostname = options["--servername"] || options["-s"] || Facter.value("hostname")
|
134
163
|
puts "Hostname #{hostname}"
|
135
164
|
keep_facts = true if options["-k"] or options["--keep-facts"]
|
165
|
+
|
136
166
|
|
137
167
|
config_file_path = path_join_glob(input_dir, hostname+".yaml")
|
138
168
|
templates_dir = path_join_glob(templates, "templates")
|
@@ -170,7 +200,8 @@ if options['all'] || options['prepare']
|
|
170
200
|
prefixed_facts_comments = {}
|
171
201
|
puppetfile_config = YAML.load_file(puppetfile_config_path) || {}
|
172
202
|
puppetfile_dependencies = []
|
173
|
-
|
203
|
+
global_data_types = []
|
204
|
+
nil_transform_present = false
|
174
205
|
# functionalities:
|
175
206
|
# # In honor of Henry...
|
176
207
|
# 1_app:
|
@@ -211,17 +242,7 @@ if options['all'] || options['prepare']
|
|
211
242
|
fact_comments_as_string = extract_comment_from_hash(default_facts).to_s
|
212
243
|
|
213
244
|
# get a list of each fields type if set (if not they will report as strings)
|
214
|
-
|
215
|
-
|
216
|
-
# Check to see if any of the fields are booleans, if so add an entry to the transform_data array
|
217
|
-
transform_data = []
|
218
|
-
data_type.each do | field|
|
219
|
-
field.each do | field_name, field_type |
|
220
|
-
if field_type == 'boolean'
|
221
|
-
transform_data << field_name
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
245
|
+
data_types = extract_type_from_hash(default_facts)
|
225
246
|
|
226
247
|
if to_add.is_a?(Hash)
|
227
248
|
# if prefixes are not defined skip replacing
|
@@ -239,7 +260,13 @@ if options['all'] || options['prepare']
|
|
239
260
|
facts_as_string = facts_as_string.gsub(/#{prefix}/, "#{replace_prefixes_with}")
|
240
261
|
fact_comments_as_string = fact_comments_as_string.gsub(/#{prefix}/, "#{replace_prefixes_with}")
|
241
262
|
prefixed_required_facts = prefixed_required_facts.merge(required_facts.map! { |item| item.gsub(/#{prefix}/, "#{replace_prefixes_with}") })
|
242
|
-
|
263
|
+
if !data_types.empty?
|
264
|
+
data_types.each do | transform |
|
265
|
+
transform.each do|key, value|
|
266
|
+
transform[key] = value.gsub(/#{prefix}/, "#{replace_prefixes_with}")
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
243
270
|
end
|
244
271
|
end
|
245
272
|
end
|
@@ -252,7 +279,13 @@ if options['all'] || options['prepare']
|
|
252
279
|
facts_as_string = facts_as_string.gsub(/#{prefix}/, "#{replace_prefixes_with}")
|
253
280
|
fact_comments_as_string = fact_comments_as_string.gsub(/#{prefix}/, "#{replace_prefixes_with}")
|
254
281
|
prefixed_required_facts = prefixed_required_facts.merge(required_facts.map! { |item| item.gsub(/#{prefix}/, "#{replace_prefixes_with}") })
|
255
|
-
|
282
|
+
if !data_types.empty?
|
283
|
+
data_types.each do | transform |
|
284
|
+
transform.each do|key, value|
|
285
|
+
transform[key] = value.gsub(/#{prefix}/, "#{replace_prefixes_with}")
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
256
289
|
end
|
257
290
|
end
|
258
291
|
end
|
@@ -267,8 +300,8 @@ if options['all'] || options['prepare']
|
|
267
300
|
default_fact_comments = extract_comment_from_hash(plain_facts)
|
268
301
|
prefixed_required_facts = prefixed_required_facts.merge(required_facts)
|
269
302
|
end
|
270
|
-
# add the "local"
|
271
|
-
|
303
|
+
# add the "local" data type list to the global one
|
304
|
+
global_data_types.push(*data_types)
|
272
305
|
|
273
306
|
result_template.deep_merge!(template)
|
274
307
|
# default_facts_prefixed is Array of hashes as the result of map, this will create hash from it
|
@@ -287,37 +320,109 @@ if options['all'] || options['prepare']
|
|
287
320
|
|
288
321
|
File.open(output_encrypted_facts_file_path, 'w+') do |output_file|
|
289
322
|
output_result_default_facts = result_default_facts.deep_merge!(custom_facts, {:merge_hash_arrays => true}).to_yaml
|
323
|
+
|
324
|
+
# convert final facts to hash for inter fact resolution and also for potential transformations later
|
325
|
+
$all_facts = YAML.load(output_result_default_facts)
|
326
|
+
|
327
|
+
#convert result templat to string
|
328
|
+
result_template = result_template.to_s
|
329
|
+
|
330
|
+
# loop through facts looking for any that reference other facts
|
331
|
+
$all_facts.each do | fact_key, fact_val |
|
332
|
+
if fact_val.instance_of?(String) and fact_val.match(/%{::.*.}/)
|
333
|
+
debug "Fact #{fact_key} references another fact or facts"
|
334
|
+
# find each instance of a fact within the value (it may contain multiple facts
|
335
|
+
# i.e "TEST%{::fact1}TEST%{::fact2}"
|
336
|
+
fact_val.gsub(/}/,"}\n").scan(/%{::.*.}/).each do | fact |
|
337
|
+
# if the fact is part of a larger string set a flag to tell resolve_fact
|
338
|
+
# to return the final fact value instead of the finalfact key!
|
339
|
+
if fact.length != fact_val.length
|
340
|
+
return_fact_value = true
|
341
|
+
else
|
342
|
+
return_fact_value = false
|
343
|
+
end
|
344
|
+
|
345
|
+
# resolve the fact down to its last reference to another fact (not the end value) if return_fact_value is false
|
346
|
+
# or down to the end value if return_fact_value is true
|
347
|
+
resolved_fact = resolve_fact(fact, 5, 0, return_fact_value)
|
348
|
+
# if the resolved fact name is not the same as the original fact we found referenced
|
349
|
+
# then replace the value in fact_val
|
350
|
+
if !resolved_fact.nil? and resolved_fact != fact
|
351
|
+
fact_val = fact_val.gsub(/#{fact}/,"#{resolved_fact}")
|
352
|
+
end
|
353
|
+
end
|
354
|
+
debug "Attempting to replace fact '#{fact_key}' with value '#{fact_val}' in compiled template"
|
355
|
+
# replace the original fact reference in the template with the resovled value
|
356
|
+
# this is done before global teansformation as they may change the final value again
|
357
|
+
result_template = result_template.gsub(/\%{::#{fact_key}}/, "#{fact_val}")
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# add comments above any fact lines
|
290
362
|
prefixed_facts_comments.each do |pattern, replacement|
|
291
363
|
if replacement != nil
|
292
364
|
output_result_default_facts.gsub!(/^#{pattern}/, "\##{replacement}\n#{pattern}")
|
293
365
|
end
|
294
366
|
end
|
295
367
|
|
296
|
-
|
368
|
+
# merge custom facts if parameter provided
|
369
|
+
custom_facts_all = {}
|
370
|
+
if (custom_facts_dir)
|
371
|
+
Dir.glob("#{custom_facts_dir}/*.yaml").sort.each do |custom_facts_file|
|
372
|
+
custom_facts_from_file = YAML.load_file(custom_facts_file) || {}
|
373
|
+
custom_facts_all.merge!(custom_facts_from_file)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
# merge, prefer custom facts
|
377
|
+
merged_all_facts = YAML.load(output_result_default_facts).merge!(custom_facts_all)
|
378
|
+
|
379
|
+
# write the temp encrypted facts file
|
380
|
+
output_file.write(merged_all_facts.to_yaml)
|
297
381
|
|
298
382
|
# now that the merged final facts are present look for any global transformations to apply
|
299
|
-
# global transformations are currently
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
383
|
+
# global transformations are currently either booleans that need to be expressed directly in
|
384
|
+
# the output file or nilable values that need to be expressed as unquoted tildas (~)
|
385
|
+
if !global_data_types.empty?
|
386
|
+
global_data_types.each do | transform |
|
387
|
+
transform.each do|transform_type, transform_value|
|
388
|
+
begin
|
389
|
+
if transform_type == 'boolean'
|
390
|
+
debug "Attempting to replace boolean value for fact #{transform_value}"
|
391
|
+
# convert the fact to boolean and then back to string during the replace, this allows validation that the fact is actually a boolean
|
392
|
+
result_template = result_template.gsub(/\"\%{::#{transform_value}}\"/, $all_facts[transform_value].to_bool.to_s)
|
393
|
+
elsif transform_type == 'nilable'
|
394
|
+
# replace fact reference with tilda if the value is nil, empty or already a tilda
|
395
|
+
# due to issues with ruby hash values being unquoted we will quote it now and remove the quotes later
|
396
|
+
if $all_facts[transform_value].nil? or $all_facts[transform_value].empty? or $all_facts[transform_value] == "~"
|
397
|
+
debug "Attempting to replace nilable value for fact #{transform_value}"
|
398
|
+
# replace value with tilda
|
399
|
+
result_template = result_template.gsub(/\"\%{::#{transform_value}}\"/, '"~"')
|
400
|
+
# identify that we have made at least one nil transformation
|
401
|
+
nil_transform_present = true
|
402
|
+
end
|
403
|
+
end
|
404
|
+
rescue
|
405
|
+
warning "Unable to convert fact #{transform_value} with value #{all_facts[transform_value]} into #{transform_type}, conversion will be skipped"
|
406
|
+
end
|
311
407
|
end
|
312
408
|
end
|
313
|
-
result_template = eval(result_template)
|
314
409
|
end
|
315
410
|
end
|
316
411
|
|
412
|
+
# convert result_template back to hash
|
413
|
+
result_template = eval(result_template)
|
414
|
+
|
317
415
|
# Write results
|
318
416
|
File.open(output_file_path, 'w+') do |output_file|
|
319
417
|
YAML.dump(result_template, output_file)
|
320
418
|
end
|
419
|
+
|
420
|
+
# hack to get around the fact we have to pass tilda as quoted earlier
|
421
|
+
if nil_transform_present
|
422
|
+
compiled_hiera = File.read(output_file_path)
|
423
|
+
replaced_hiera = compiled_hiera.gsub('"~"', '~')
|
424
|
+
File.open(output_file_path, "w") {|new_hiera| new_hiera.puts replaced_hiera}
|
425
|
+
end
|
321
426
|
|
322
427
|
# decrypt facts file because Puppet doesn't appear to be able to read encrypted facts
|
323
428
|
require 'hiera/backend/eyaml/plugins'
|
@@ -359,7 +464,6 @@ if options['all'] || options['prepare']
|
|
359
464
|
end
|
360
465
|
}
|
361
466
|
end
|
362
|
-
|
363
467
|
output_file.write(decrypted.join)
|
364
468
|
end
|
365
469
|
|
@@ -414,10 +518,33 @@ end
|
|
414
518
|
# start puppet
|
415
519
|
if (options['start'] || options['all']) && !stop_apply
|
416
520
|
require 'puppet'
|
417
|
-
|
418
|
-
|
521
|
+
|
522
|
+
# set dry run option if the flag has been set
|
523
|
+
if options["-n"] or options["--dry-run"]
|
524
|
+
dry_run = '--noop'
|
525
|
+
verbose_output = true
|
526
|
+
else
|
527
|
+
dry_run = ''
|
528
|
+
end
|
529
|
+
|
530
|
+
# if the user has specified a module path pass it to puppet
|
531
|
+
if options["--module_path"]
|
532
|
+
modulefile_definition = "--modulepath #{options['--module_path']}"
|
533
|
+
elsif options["-m"]
|
534
|
+
modulefile_definition = "--modulepath #{options['-m']}"
|
535
|
+
# if no modulepath has been set default to /etc/puppet/modules for puppet 4 and above and blank for older versions
|
536
|
+
else
|
537
|
+
modulefile_definition = Gem::Version.new(Puppet.version) > Gem::Version.new('4.0.0') ? '--modulepath /etc/puppet/modules' : ''
|
538
|
+
end
|
539
|
+
|
540
|
+
# construct defaut puppet apply command
|
541
|
+
puppet_command = "sudo su -c 'source /usr/local/rvm/scripts/rvm; puppet apply #{dry_run} /etc/puppet/manifests/site.pp --confdir=/etc/puppet --verbose --detailed-exitcodes #{modulefile_definition}'"
|
542
|
+
|
543
|
+
# if a custom puppet apply command has been set use if, otherwise use the default generated above
|
419
544
|
to_execute = options["--puppet_apply"] || options["-p"] || puppet_command
|
420
545
|
debug "Running #{to_execute}"
|
546
|
+
|
547
|
+
# execute puppet apply and capture return code
|
421
548
|
`#{to_execute}`
|
422
549
|
exit_code = $?.exitstatus
|
423
550
|
|
@@ -436,7 +563,11 @@ if (options['start'] || options['all']) && !stop_apply
|
|
436
563
|
warning "Unable to locate Facts file, please urgently locate and remove this as it holds unencrypted values"
|
437
564
|
end
|
438
565
|
|
439
|
-
if
|
566
|
+
# if we see a bad exit code report it, for refrerence good codes are:
|
567
|
+
# 0: The run succeeded with no changes or failures; the system was already in the desired state.
|
568
|
+
# 2: The run succeeded, and some resources were changed.
|
569
|
+
if exit_code != 2 and exit_code != 0
|
440
570
|
raise "execute_puppet exit status: #{exit_code}"
|
441
571
|
end
|
442
572
|
end
|
573
|
+
|
data/puppet-runner.gemspec
CHANGED
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "puppet-runner"
|
7
|
-
spec.version = "0.0.
|
7
|
+
spec.version = "0.0.27"
|
8
8
|
spec.authors = ["Martin Brehovsky", "Matthew Hope"]
|
9
9
|
spec.email = ["mbrehovsky@adaptavist.com"]
|
10
10
|
spec.summary = %q{Preprocessor for hiera config}
|
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
|
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
18
|
spec.require_paths = ["lib"]
|
19
19
|
|
20
|
-
spec.add_development_dependency "bundler", "
|
20
|
+
spec.add_development_dependency "bundler", "2.2.10"
|
21
21
|
spec.add_development_dependency "rake"
|
22
22
|
spec.add_dependency "docopt", ">= 0.5.0"
|
23
23
|
spec.add_dependency "colorize", ">= 0.7.3"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puppet-runner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.27
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Brehovsky
|
@@ -9,22 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-02-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - '='
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
20
|
+
version: 2.2.10
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - '='
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version:
|
27
|
+
version: 2.2.10
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -173,7 +173,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
173
|
version: '0'
|
174
174
|
requirements: []
|
175
175
|
rubyforge_project:
|
176
|
-
rubygems_version: 2.
|
176
|
+
rubygems_version: 2.6.8
|
177
177
|
signing_key:
|
178
178
|
specification_version: 4
|
179
179
|
summary: Preprocessor for hiera config
|