msmg-hiera-templater 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 93f1bedbdcc88d9f3ae602533735b89d35c99b52525536a63b83bc982278541b
4
+ data.tar.gz: d078a5d8fd9adc3c32c2380320cf504fc1f9547524689b2a5624bbfc0eb0d6c4
5
+ SHA512:
6
+ metadata.gz: 1d91190aba1abd9d9946d41899e3a0e63a87135ab6f2d388b214e676a0f646fa2d2c2cbd042d2d145a11483b4d941f712c421b3971d6ac895df7b71f87fe26cb
7
+ data.tar.gz: 215c48e206f3ebc32701e12e622f46ae9ab4eb7d4b9b3c9f31db95071ab212ae4ef9b63960270c7ae74dc427dc6ddff3cd36a94f79425817ac1833ae95a8ecfc
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'optparse'
5
+ require_relative '../lib/hiera_template'
6
+
7
+ args = { config: HieraTemplate::DEFAULT_CONFIG }
8
+ opt_parser = OptionParser.new do |opts|
9
+ opts.banner = <<~USAGE
10
+ Usage: #{File.basename(__FILE__)} [options] <file/folder>
11
+
12
+ Processes a specific .erb template or a folder hierarchy for .erb files.
13
+ Note that only files named with an .erb suffix will be processed and the output file will match the
14
+ name but with the suffix removed.
15
+
16
+ The erb files in the folder must use hiera('key') to retrieve a data item.
17
+ USAGE
18
+
19
+ opts.on('-c CONFIG_FILE', '--config=CONFIG_FILE', String,
20
+ "hiera configuration file (default #{args[:config]})") do |c|
21
+ args[:config] = c
22
+ end
23
+
24
+ opts.on('-r', '--recurse', TrueClass, 'Recurse a specified folder rather than specify a file name') do |n|
25
+ args[:recurse] = n
26
+ end
27
+
28
+ opts.on('-f', '--force', TrueClass, 'Force overwrite if output file exists') do |f|
29
+ args[:overwrite] = f
30
+ end
31
+
32
+ opts.on('-h', '--help', 'Prints this help') do
33
+ puts opts
34
+ exit
35
+ end
36
+ end
37
+ opt_parser.parse!
38
+ name = ARGV.shift
39
+ unless name && ((File.directory?(name) && args[:recurse]) || (File.exist?(name) && File.extname(name) == '.erb'))
40
+ puts opt_parser.help
41
+ exit 1
42
+ end
43
+ templater = HieraTemplate.new(args[:config])
44
+
45
+ def strip_suffix(file, args)
46
+ new_file = File.join(File.dirname(file), File.basename(file, '.erb'))
47
+ return new_file if args[:overwrite] || !File.exist?(new_file)
48
+
49
+ abort "#{new_file} already exists (based on template #{file})"
50
+ end
51
+
52
+ def process_file(templater, file, args)
53
+ content = File.read(file)
54
+ output = templater.render(content)
55
+ return unless output.is_a?(String) # Detect custom output
56
+
57
+ out_file = strip_suffix(file, args)
58
+ File.write(out_file, output)
59
+ end
60
+
61
+ if args[:recurse]
62
+ Dir.glob(File.join(name, '**', '*.erb')) do |file|
63
+ process_file(templater, file, args)
64
+ end
65
+ else
66
+ process_file(templater, name, args)
67
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+
5
+ # Override some ERB behaviour to make it reflect methods supported
6
+ # by an object that is passed to it
7
+ class ERBReflective < ERB
8
+ # Simply store the object reference passed in
9
+ # then call the superclass behaviour with our binding instead
10
+ def result(other_object)
11
+ @other_object = other_object
12
+ super(binding)
13
+ end
14
+
15
+ def method_missing(meth, *args, &block)
16
+ super unless respond_to_missing? meth
17
+ @other_object&.send(meth, *args, &block)
18
+ end
19
+
20
+ def respond_to_missing?(meth)
21
+ @other_object&.respond_to? meth
22
+ end
23
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'facter'
5
+ require 'hiera'
6
+
7
+ require_relative 'erb_reflective'
8
+
9
+ # Templating handler object used by ERBReflective
10
+ class HieraTemplate
11
+ DEFAULT_CONFIG = '/etc/puppet/hiera.yaml'
12
+
13
+ def initialize(hiera_config = DEFAULT_CONFIG)
14
+ @scope = Facter.to_hash # Time consuming, do it once if possible!
15
+ # Just so we can handle old facter references starting ::
16
+ legacy = @scope.map { |k, v| ["::#{k}", v] }
17
+ @scope.merge!(Hash[legacy])
18
+ @hiera = Hiera.new(config: hiera_config)
19
+ end
20
+
21
+ # Template render
22
+ def render(content)
23
+ @blocks = []
24
+ rendered = ERBReflective.new(content, nil, '-').result(self)
25
+ return rendered unless @blocks.any?
26
+
27
+ # See custom_out
28
+ @blocks.each { |block| block.call(rendered) }
29
+ true
30
+ end
31
+
32
+ # Methods used by ERBReflective to access hiera data
33
+ # See above, full content is not available until the template is rendered so
34
+ # we only stash the block references that we are given and call it later
35
+ def custom_out(&block)
36
+ raise StandardError, 'custom_out requires a block argument' unless block_given?
37
+
38
+ @blocks << block
39
+ end
40
+
41
+ # The DSL
42
+
43
+ # If hiera does not exist we will try facter data in scope to unify the API
44
+ def hiera(key, default = nil)
45
+ @hiera.lookup(key, nil, @scope) || @scope.dig(*key.split('.')) || default
46
+ end
47
+
48
+ def hiera_hash(key, default = nil)
49
+ @hiera.lookup(key, default, @scope, nil, :hash)
50
+ end
51
+
52
+ def hiera_array(key, default = nil)
53
+ @hiera.lookup(key, default, @scope, nil, :array)
54
+ end
55
+
56
+ def mkdir_p(path, owner: nil, group: nil, mode: nil)
57
+ custom_out do |_|
58
+ FileUtils.mkdir_p(path)
59
+ FileUtils.chown_R(owner, group, path) if owner || group
60
+ FileUtils.chmod_R(mode, path) if mode
61
+ end
62
+ end
63
+
64
+ def custom_file(path, owner: nil, group: nil, mode: nil)
65
+ custom_out do |content|
66
+ File.write(path, content)
67
+ FileUtils.chown(owner, group, path) if owner || group
68
+ FileUtils.chmod(mode, path) if mode
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'msmg-hiera-templater'
5
+ s.version = '0.0.1'
6
+ s.authors = ['Andrew Smith']
7
+ s.email = ['andrew.smith at moneysupermarket.com']
8
+ s.required_ruby_version = '>=2.5'
9
+ s.summary = 'Render ERB templates from heira without requiring puppet'
10
+ s.description = 'MSM pubicly available Ruby'
11
+ s.homepage = 'https://github.com/MSMFG/msmg-hiera-templater'
12
+ s.license = 'Apache-2.0'
13
+ s.files = `git ls-files -z`.split("\x0")
14
+ s.add_runtime_dependency 'facter', ['~>4.2']
15
+ s.add_runtime_dependency 'hiera', ['~>3.7']
16
+ s.bindir = 'bin'
17
+ s.executables << 'hiera-templater'
18
+ end
data/readme.md ADDED
@@ -0,0 +1,111 @@
1
+ # Hiera templater without puppet
2
+
3
+ Requires facter and hiera gems, compatible with latest gems and versions installed on MSM instances.
4
+
5
+ ## Background
6
+
7
+ The idea behind this prohect is to allow the power of hiera to be extended to folks that wish to use it
8
+ in scenarios where they don't want to use puppet.
9
+
10
+ ## Operation - basics
11
+
12
+ The CLI has only one required option, a file or a path to be parsed, path parsing must be enabled by passing the -r (recursive flag) and
13
+ the CLI will then look for any file suffixed in .erb.
14
+
15
+ In basic mode of operation the templater has a safeguard blocking the overwrite of existing files (possibly from previous runs) unless the
16
+ -f flag (force) is also provided.
17
+
18
+ Syntax as per help
19
+
20
+ ```
21
+ andrew.smith@C02Z2A0SLVCG hiera-templater % ruby hiera_templater.rb
22
+ Usage: hiera_templater.rb [options] <file/folder>
23
+
24
+ Processes a specific .erb template or a folder hierarchy for .erb files.
25
+ Note that only files named with an .erb suffix will be processed and the output file will match the
26
+ name but with the suffix removed.
27
+
28
+ The erb files in the folder must use hiera('key') to retrieve a data item.
29
+ -c, --config=CONFIG_FILE hiera configuration file (default /etc/puppet/hiera.yaml)
30
+ -r, --recurse Recurse a specified folder rather than specify a file name
31
+ -f, --force Force overwrite if output file exists
32
+ -h, --help Prints this help
33
+ andrew.smith@C02Z2A0SLVCG hiera-templater %
34
+ ```
35
+
36
+ NOTE: In all modes of operation please attempt to utilise the recurse feature to process all your files, do NOT wrap this in another script as you will pay a 0.6 second penalty for each time the Facter library is initialised!!
37
+
38
+ In basic operation the files are delivered to the same folder as the erb template with the erb suffix removed. All template files must have the trailing .erb suffix.
39
+
40
+ ERB templates are processed with one of 3 hiera DSL lookup functions, these are..
41
+
42
+ ### hiera(<hey>, \[default\])
43
+
44
+ A key, regardless of type is retrieved, this form will return any type including hash and array and can be given an optional default value for
45
+ values that cannot be found.
46
+
47
+ The hiera function ALSO includes a lookup for facter keys if not found in hiera, neither of the two other lookup functions do this.
48
+
49
+ ### hiera_array(<key>, \[default\])
50
+
51
+ Type checked version of the above that will fail if a non array type is found.
52
+
53
+ ### hiera_hash(<key>, \[default\])
54
+
55
+ Type checked version of the above that will fail if a non hash type is found.
56
+
57
+ ## Operation - More DSL usage
58
+
59
+ If one wishes to output files to a specific folder rather than have then land automatically as placed by the script one can use one of 2 high level DSL
60
+ functions.
61
+
62
+ Noting that as soon as either of these or the more advanced custom_out function is used one is entirely responsible for the output to file, the tool will
63
+ no longer automatically do that for you in these cases.
64
+
65
+ ### mkdir_p(\<path\>, \[owner: \<owner\>\], \[group: \<group\>\], \[mode: \<mode\>\]))
66
+
67
+ This DSL function calls custom_out and is a helper to create a folder structure recursively with optional ownership and mode specifications.
68
+
69
+ ### custom_file(\<path\>, \[owner: \<owner\>\], \[group: \<group\>\], \[mode: \<mode\>\])
70
+
71
+ This DSL function calls custom_out with a preset block to ensure the output file is written with the path and optionally ownership and mode specified.
72
+
73
+ Note that custom_out blocks are performed in the order specified but following rendering so please ensure that any dependent file strictures are made (mkdir_p) before custome_file is used if it is dependent upon that path.
74
+
75
+ ## Operation - Advanced usage
76
+
77
+ The templater implements custom_out as a function which takes a block of Ruby code as a parameter, this block should implement a parameter which will receive the rendered content (whether it uses it or not) and is free to utilise that content for more advanced purposes.
78
+
79
+ ## DSL Example
80
+ ```
81
+ [root@ip-10-96-3-54 ~]# cat testing/dsl.erb
82
+ [Facter only]
83
+ My IP is: <%= hiera('ipaddress_primary') %>
84
+
85
+ [Something missing]
86
+ Non existant: <%= hiera('clearly::not_existing', 'but has default') %>
87
+
88
+ <% mkdir_p('/tmp/test')
89
+ mkdir_p('/tmp/test2', owner: 'sysadmin', mode: 0500)
90
+ mkdir_p('/tmp/test3', group: 'sysadmin', mode: 0550)
91
+ mkdir_p('/tmp/test4', owner: 'root', group: 'sysadmin', mode: 0750)
92
+ custom_file('/tmp/test4/testfile', owner: 'root', group: 'sysadmin', mode: 0640) -%>
93
+ ```
94
+
95
+ ## Advanced example
96
+ ```
97
+ [root@ip-10-96-3-54 ~]# cat testing/custom_out.erb
98
+ [Facter only]
99
+ My IP is: <%= hiera('ipaddress_primary') %>
100
+
101
+ [Something missing]
102
+ Non existant: <%= hiera('clearly::not_existing', 'but has default') %>
103
+
104
+ <% custom_out do |content|
105
+ File.write('/tmp/footest', content)
106
+ FileUtils.chmod(0640, '/tmp/footest')
107
+ FileUtils.chown('root', 'sysadmin', '/tmp/footest')
108
+ end -%>
109
+ <% custom_out { |content| puts content } -%>
110
+ [root@ip-10-96-3-54 ~]#
111
+ ```
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: msmg-hiera-templater
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-09-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: facter
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: hiera
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.7'
41
+ description: MSM pubicly available Ruby
42
+ email:
43
+ - andrew.smith at moneysupermarket.com
44
+ executables:
45
+ - hiera-templater
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - bin/hiera-templater
50
+ - lib/erb_reflective.rb
51
+ - lib/hiera_template.rb
52
+ - msmg-hiera-templater.gemspec
53
+ - readme.md
54
+ homepage: https://github.com/MSMFG/msmg-hiera-templater
55
+ licenses:
56
+ - Apache-2.0
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '2.5'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.1.4
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Render ERB templates from heira without requiring puppet
77
+ test_files: []