petef-yamlenc 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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