msmg-hiera-templater 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []