petef-yamlenc 3.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f517b6886ecdde4ec6d220aaa3c388ef9768fa2c
4
+ data.tar.gz: 6861205e73a6e929cde66d7dd0010bab2613ddf2
5
+ SHA512:
6
+ metadata.gz: 196e581df3ce1f9622db9b7278b099bb917b1284511ddb7f57fcc4790dd673c54f1b285cff5c75685732a40499fabba0ddd5a53bd5ebcdf928835567a61c0c43
7
+ data.tar.gz: 76584558332c4697feeee2b1a7e4df66f673e79b3fddc9aaee1d90b2b420d75ad5234e3f458d7b0d5212965a12f0f39ce9ea19f9c3ac1feb9163e70567eef39b
@@ -0,0 +1,50 @@
1
+ # YAML ENC
2
+
3
+ [![Build Status](https://travis-ci.org/danzilio/yamlenc.svg?branch=master)](https://travis-ci.org/danzilio/yamlenc)
4
+
5
+ This is an external node classifier (ENC) for Puppet. This ENC reads from YAML
6
+ files and returns a node statement for Puppet to consume. There is a specific
7
+ schema for the YAML input.
8
+
9
+ # Basic example
10
+ Here's a basic example of a node file.
11
+
12
+ ---
13
+ node1.example.com:
14
+ environment: production
15
+ role: roles::puppet::master
16
+ classes:
17
+ - base::puppetmaster
18
+ parameters:
19
+ rack: R1
20
+ elevation: 23
21
+ node2.example.com:
22
+ classes:
23
+ - base::ntp
24
+ ^(dc1|dc2)-host.example.com$:
25
+ role: roles::puppet::puppetdb
26
+
27
+ # Node syntax reference
28
+ A node key can be a string literal or a regex pattern. All fields in a node
29
+ statement are optional.
30
+
31
+ - `environment` (string) - This is the Puppet environment that the node should run in.
32
+
33
+ - `role` (string) - This is the role class that should be included on the node. This class is automatically added to the node's `classes` section.
34
+
35
+ - `classes` (array of strings) - This is an array of classes to include on the node.
36
+
37
+ - `parameters` (hash of string keys and values) - This is a hash of parameters to pass to the node statements. Parameters must be strings and their values must also be strings.
38
+
39
+ # Configuration file
40
+ By default the command looks at `/etc/puppet/enc.yaml` for its configuration. You can override this by passing the `-c` or `--config` parameters at the command line. The configuration file only supports one configuration option at this time:
41
+
42
+ ---
43
+ nodes:
44
+ - /etc/enc/nodes.yaml
45
+ - /etc/enc/nodes2.yaml
46
+
47
+ The `nodes` option can be an array of strings or a single string. Each value must be a fully qualified path to node files. They will be parsed sequentially.
48
+
49
+ # Command line nodes
50
+ You can pass node files at the command line with the `-n` or `--nodes` option. The value must be a fully qualified path to a node file. These will be appended to the end of the `nodes` array and will be parsed in the order in which they're defined at the command line, after all of the nodes files defined in the configuration file.
data/bin/enc ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w(.. lib))
3
+ require 'enc'
4
+
5
+ lookup = Enc::Cli.new(ARGV)
6
+
7
+ if lookup.found
8
+ puts lookup.found.to_yaml
9
+ exit 0
10
+ elsif lookup.options[:fail]
11
+ STDERR.puts 'No node found.'
12
+ exit 1
13
+ else
14
+ puts '{}'
15
+ exit 0
16
+ end
@@ -0,0 +1,3 @@
1
+ require 'enc/cli'
2
+ require 'enc/node'
3
+ require 'enc/version'
@@ -0,0 +1,85 @@
1
+ require 'yaml'
2
+ require 'optparse'
3
+
4
+ module Enc
5
+ class Cli
6
+ attr_accessor :options
7
+ attr_reader :node_name, :found
8
+
9
+ def initialize(argv)
10
+ @options = { config_file: '/etc/puppet/enc.yaml', cli_nodes: [], fail: false }
11
+
12
+ optparse.parse!(argv)
13
+
14
+ if (@node_name = argv[0]).nil?
15
+ puts "ERROR: Didn't specify a node name to look up!"
16
+ puts ''
17
+ puts optparse.to_s
18
+ exit 1
19
+ end
20
+
21
+ @found = find
22
+ end
23
+
24
+ private
25
+
26
+ def find
27
+ Node.lookup(node_name, nodes) unless nodes.empty?
28
+ end
29
+
30
+ def optparse
31
+ OptionParser.new do |opt|
32
+ opt.banner = "Usage: #{$PROGRAM_NAME} <options> <hostname>"
33
+ opt.separator ''
34
+ opt.separator 'Available options:'
35
+ opt.on('-c', '--config [path to configuration file]', String, 'Path to configuration file (Default: /etc/puppet/enc.yaml).') { |c| options[:config_file] = c if c }
36
+ opt.on('-n', '--nodes [path to data file]', String, 'Path to a data file to add to the end of the "nodes" array') { |n| options[:cli_nodes] << n if n }
37
+ opt.on('-f', '--fail', 'Fail if no nodes are found.') { |f| options[:fail] = f }
38
+ opt.on('-v', '--version', 'Print the version') do
39
+ puts Enc::VERSION
40
+ exit 0
41
+ end
42
+ opt.on_tail('-h', '--help', 'Show this message') do
43
+ puts opt
44
+ exit 0
45
+ end
46
+ end
47
+ end
48
+
49
+ def config_file
50
+ options[:config_file]
51
+ end
52
+
53
+ def collect_nodes(nodes)
54
+ collection = []
55
+
56
+ nodes.each do |node|
57
+ collection << node if File.file?(node)
58
+ collection += Dir["#{node}/*.yaml"].sort if File.directory?(node)
59
+ end
60
+
61
+ collection
62
+ end
63
+
64
+ def cli_nodes
65
+ return [] if options[:cli_nodes].empty?
66
+ collect_nodes(options[:cli_nodes])
67
+ end
68
+
69
+ def config
70
+ return {} unless File.exist?(config_file)
71
+ Hash(YAML.load_file(config_file))
72
+ end
73
+
74
+ def config_nodes
75
+ return [] unless config.include?('nodes')
76
+ collect_nodes(Array(config['nodes']))
77
+ end
78
+
79
+ def nodes
80
+ fail('No node files specified!') if config['nodes'].nil? && cli_nodes.empty?
81
+
82
+ cli_nodes + config_nodes
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,114 @@
1
+ require 'yaml'
2
+
3
+ module Enc
4
+ class Node
5
+ attr_reader :node, :role, :environment
6
+
7
+ def initialize(node)
8
+ @node = node
9
+ @role = validate('role', String)
10
+ @environment = validate('environment', String)
11
+ @classes = validate('classes', Array)
12
+ @parameters = validate('parameters', Hash)
13
+ @node_hash = to_hash
14
+ end
15
+
16
+ # Search through the files in the `nodes` array until we find one that
17
+ # matches the node `name`. Set `@found` to the node hash returned by
18
+ # `.search_nodes`.
19
+ #
20
+ # @api public
21
+ # @param name [String] the name of the node to look for.
22
+ # @param nodes [Array] an array of node files to search.
23
+ def self.lookup(name, nodes)
24
+ found = nil
25
+ nodes.each do |c|
26
+ found = search(name, YAML.load_file(c))
27
+ break if found
28
+ end
29
+ Node.new(found) if found
30
+ end
31
+
32
+ # Return the hash version of the node statement in YAML
33
+ #
34
+ # @api public
35
+ # @return [String] the YAML representation of the node statement
36
+ def to_yaml
37
+ to_hash.to_yaml unless to_hash.empty?
38
+ end
39
+
40
+ # Return the classes array. Checks if a role class has been delcared via
41
+ # #role. If there's a role add it to the classes array.
42
+ #
43
+ # @api public
44
+ # @return [Array, nil] the array of classes, or nil
45
+ def classes
46
+ if role
47
+ return [role] unless @classes
48
+ @classes << role unless @classes.include?(role)
49
+ end
50
+
51
+ @classes
52
+ end
53
+
54
+ # Return the parameters hash. Checks if a role class has been delcared via
55
+ # #role. If there's a role, format it correctly (drop the 'roles' class,
56
+ # change the class separator :: to / for filesystem use) add it to the
57
+ # parameters hash as 'role' overriding anything else set there.
58
+ #
59
+ # @api public
60
+ # @return [Hash, nil] the Hash of parameters, or nil
61
+ def parameters
62
+ if role
63
+ role_parameter = role.gsub(/^roles::/, '').gsub(/::/, '/')
64
+ @parameters = { 'role' => role_parameter } unless @parameters
65
+ @parameters['role'] = role_parameter
66
+ end
67
+
68
+ @parameters
69
+ end
70
+
71
+ private
72
+
73
+ # Takes a node name and hash of nodes and return the matching node statement
74
+ # or nil if no node statement is found.
75
+ #
76
+ # @api private
77
+ # @param name [String] a node name to search for.
78
+ # @param nodes [Hash] a hash of node statements to search.
79
+ # @return [Hash, nil] the matching node statement or nil if none is found.
80
+ def self.search(name, nodes)
81
+ nodes.keys.each do |k|
82
+ return nodes[k] if name == k || name.match(k)
83
+ end
84
+ nil
85
+ end
86
+
87
+ # Takes a key name, and data type and ensures that the data in nodes[key] is
88
+ # the correct data type. If it's not the correct data type, we'll fail and
89
+ # print a message. If nodes[key] is the correct data type and is not empty,
90
+ # return it, otherwise we return nil.
91
+ #
92
+ # @api private
93
+ # @param key [String] a key name to search the nodes hash for
94
+ # @param type [Object] a data type to check for
95
+ # @return [Object, nil] returns the data of 'type' or nil if the value is empty
96
+ def validate(key, type)
97
+ return nil unless node && node.key?(key)
98
+ fail "#{key.capitalize} must be a #{type}!" unless node[key].is_a?(type)
99
+ return node[key] unless node[key].empty?
100
+ end
101
+
102
+ # A hash representation of the node statement
103
+ #
104
+ # @api private
105
+ # @return [Hash] the node statment in Hash form
106
+ def to_hash
107
+ node_hash = {}
108
+ node_hash['environment'] = environment if environment
109
+ node_hash['classes'] = classes if classes
110
+ node_hash['parameters'] = parameters if parameters
111
+ node_hash
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,3 @@
1
+ module Enc
2
+ VERSION = '3.1.1'
3
+ end
@@ -0,0 +1,10 @@
1
+ ---
2
+ dc1-server03:
3
+ environment: production
4
+ role: roles::puppet::master
5
+ classes:
6
+ - base
7
+ parameters:
8
+ rack: R5
9
+ ^(dc1|dc2)-server04:
10
+ role: roles::puppet::master
@@ -0,0 +1,10 @@
1
+ ---
2
+ dc4-server03:
3
+ environment: production
4
+ role: roles::puppet::master
5
+ classes:
6
+ - base
7
+ parameters:
8
+ rack: R5
9
+ ^(dc4|dc5)-server04:
10
+ role: roles::puppet::master
@@ -0,0 +1,10 @@
1
+ require 'enc'
2
+
3
+ def fixture(path)
4
+ File.expand_path(File.join(__FILE__, '..', 'fixtures', path))
5
+ end
6
+
7
+ RSpec.configure do |c|
8
+ c.formatter = 'documentation'
9
+ c.color = true
10
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Enc::Cli do
4
+ let(:query) { 'dc1-server03' }
5
+ let(:argv) { ['-n', data, query] }
6
+ subject { Enc::Cli.new(argv) }
7
+
8
+ context 'when passing a directory of nodes' do
9
+ let(:data) { fixture('data') }
10
+
11
+ context 'when not finding a node' do
12
+ let(:query) { 'dc1-server09' }
13
+
14
+ it { is_expected.to be_a Enc::Cli }
15
+ it { expect(subject.found).to be nil }
16
+ end
17
+
18
+ context 'when finding a node' do
19
+ it { is_expected.to be_a Enc::Cli }
20
+ it { expect(subject.found).not_to be nil }
21
+ it { expect(subject.found).to be_a Enc::Node }
22
+ it { expect(subject.found.classes.length).to eq 2 }
23
+ end
24
+ end
25
+
26
+ context 'when passing a single node file' do
27
+ let(:data) { fixture('data/nodes.yaml') }
28
+
29
+ context 'when not finding a node' do
30
+ let(:query) { 'dc1-server09' }
31
+
32
+ it { is_expected.to be_a Enc::Cli }
33
+ it { expect(subject.found).to be nil }
34
+ end
35
+
36
+ context 'when finding a node' do
37
+ it { is_expected.to be_a Enc::Cli }
38
+ it { expect(subject.found).not_to be nil }
39
+ it { expect(subject.found).to be_a Enc::Node }
40
+ it { expect(subject.found.classes.length).to eq 2 }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ require 'yaml'
3
+
4
+ describe Enc::Node do
5
+ let(:data) { YAML.load_file(fixture('data/nodes.yaml')) }
6
+ subject(:node) { Enc::Node.new(data[name]) }
7
+
8
+ context 'when searching for a node' do
9
+ subject { Enc::Node.lookup(query, data) }
10
+ let(:query) { 'dc1-server03' }
11
+ let(:data) { [fixture('data/nodes.yaml')] }
12
+
13
+ context 'with a node that exists in the data as a literal' do
14
+ it { is_expected.to be_a Enc::Node }
15
+ it { expect(subject.environment).to eq 'production' }
16
+ end
17
+
18
+ context 'with a node that exists in the data as a regex' do
19
+ let(:query) { 'dc1-server04' }
20
+
21
+ it { is_expected.to be_a Enc::Node }
22
+ it { expect(subject.environment).to be nil }
23
+ it { expect(subject.classes).to include 'roles::puppet::master' }
24
+ end
25
+
26
+ context 'with a node that does not exist in the data' do
27
+ let(:query) { 'dc1-server09' }
28
+ it { is_expected.to be nil }
29
+ end
30
+ end
31
+
32
+ context 'when looking up a node that exists in the data' do
33
+ let(:name) { 'dc1-server03' }
34
+
35
+ it { is_expected.to respond_to :to_yaml }
36
+ it { is_expected.to respond_to :classes }
37
+ it { is_expected.to respond_to :parameters }
38
+
39
+ context '#node' do
40
+ subject { node.node }
41
+ it { is_expected.to include('environment') }
42
+ end
43
+
44
+ context '#environment' do
45
+ subject { node.environment }
46
+ it { is_expected.to be_a String }
47
+ it { is_expected.to eq 'production' }
48
+ end
49
+
50
+ context '#classes' do
51
+ subject { node.classes }
52
+ it { is_expected.to be_a Array }
53
+ it { is_expected.to include 'base' }
54
+ it { is_expected.to include 'roles::puppet::master' }
55
+ end
56
+
57
+ context '#parameters' do
58
+ subject { node.parameters }
59
+ it { is_expected.to be_a Hash }
60
+ it { is_expected.to include('rack' => 'R5') }
61
+ it { is_expected.to include('role' => 'puppet/master') }
62
+ end
63
+
64
+ context '#to_yaml' do
65
+ subject { node.to_yaml }
66
+ it { is_expected.to be_a String }
67
+ it { expect(YAML.load node.to_yaml).to be_a Hash }
68
+ end
69
+ end
70
+
71
+ context 'when looking up a node that does not exist in the data' do
72
+ let(:name) { 'dc1-server09' }
73
+
74
+ context '#node' do
75
+ subject { node.node }
76
+ it { is_expected.to eq nil }
77
+ end
78
+
79
+ context '#environment' do
80
+ subject { node.environment }
81
+ it { is_expected.to eq nil }
82
+ end
83
+
84
+ context '#classes' do
85
+ subject { node.classes }
86
+ it { is_expected.to eq nil }
87
+ end
88
+
89
+ context '#parameters' do
90
+ subject { node.parameters }
91
+ it { is_expected.to eq nil }
92
+ end
93
+
94
+ context '#to_yaml' do
95
+ subject { node.to_yaml }
96
+ it { is_expected.to eq nil }
97
+ end
98
+ end
99
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: petef-yamlenc
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.1.1
5
+ platform: ruby
6
+ authors:
7
+ - David Danzilio
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: aruba
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: cucumber
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A Puppet ENC that pulls from YAML
70
+ email: ddanzilio@constantcontact.com
71
+ executables:
72
+ - enc
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - README.md
77
+ - bin/enc
78
+ - lib/enc.rb
79
+ - lib/enc/cli.rb
80
+ - lib/enc/node.rb
81
+ - lib/enc/version.rb
82
+ - spec/fixtures/data/nodes.yaml
83
+ - spec/fixtures/data/nodes2.yaml
84
+ - spec/spec_helper.rb
85
+ - spec/unit/cli_spec.rb
86
+ - spec/unit/node_spec.rb
87
+ homepage: https://github.com/danzilio/yamlenc
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.6.11
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: A Puppet ENC that pulls from YAML
111
+ test_files:
112
+ - spec/fixtures/data/nodes.yaml
113
+ - spec/fixtures/data/nodes2.yaml
114
+ - spec/spec_helper.rb
115
+ - spec/unit/cli_spec.rb
116
+ - spec/unit/node_spec.rb