enviera 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 +7 -0
- data/.gitignore +34 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +12 -0
- data/LICENSE +21 -0
- data/README.md +115 -0
- data/Rakefile +1 -0
- data/bin/enviera +20 -0
- data/enviera.gemspec +24 -0
- data/lib/enviera/CLI.rb +53 -0
- data/lib/enviera/commands.rb +17 -0
- data/lib/enviera/options.rb +36 -0
- data/lib/enviera/subcommand.rb +136 -0
- data/lib/enviera/subcommands/help.rb +39 -0
- data/lib/enviera/subcommands/hierarchy.rb +93 -0
- data/lib/enviera/subcommands/lookup.rb +106 -0
- data/lib/enviera/subcommands/unknown_command.rb +42 -0
- data/lib/enviera/subcommands/version.rb +27 -0
- data/lib/enviera/utils.rb +124 -0
- data/lib/enviera.rb +32 -0
- metadata +106 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: edc085b819383c89b4f57823326b9cee64d07f9b
|
4
|
+
data.tar.gz: 489faf52b1a6e619e617a4ba17c00cb1313381cb
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 43f0aab66b2c654872cbdc639ec67b1578880d0a3d0532d5e052265cf670aa1e1f97c875316165725ac7f2b2ad344e81034a2c6690245854c4a55ce996261cb3
|
7
|
+
data.tar.gz: da82472d40050b880ed9f031439009d9d154fb7208406b9fc69a98d11f005a91a149d44e3d5cd504816fc7b854dcbb503e03bfda334badbada89ba6638c83947
|
data/.gitignore
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Specific to RubyMotion:
|
13
|
+
.dat*
|
14
|
+
.repl_history
|
15
|
+
build/
|
16
|
+
|
17
|
+
## Documentation cache and generated files:
|
18
|
+
/.yardoc/
|
19
|
+
/_yardoc/
|
20
|
+
/doc/
|
21
|
+
/rdoc/
|
22
|
+
|
23
|
+
## Environment normalisation:
|
24
|
+
/.bundle/
|
25
|
+
/lib/bundler/man/
|
26
|
+
|
27
|
+
# for a library or gem, you might want to ignore these files since the code is
|
28
|
+
# intended to run in multiple environments; otherwise, check them in:
|
29
|
+
# Gemfile.lock
|
30
|
+
# .ruby-version
|
31
|
+
# .ruby-gemset
|
32
|
+
|
33
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
34
|
+
.rvmrc
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 Tom Poulton
|
4
|
+
Modified work Copyright (c) 2015 Josh Souza
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
7
|
+
this software and associated documentation files (the "Software"), to deal in
|
8
|
+
the Software without restriction, including without limitation the rights to
|
9
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
10
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
11
|
+
subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
18
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
19
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
20
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
21
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
Enviera
|
2
|
+
===========
|
3
|
+
|
4
|
+
Enviera is a tool to go along with Puppet and Hiera that will allow you to see
|
5
|
+
all of the variables defined within Hiera for a particular environment
|
6
|
+
(or for Puppet, a set of facts)
|
7
|
+
|
8
|
+
:new: *v0.0.1 - Created*
|
9
|
+
|
10
|
+
Why it's useful
|
11
|
+
-------------------------
|
12
|
+
|
13
|
+
Hiera is a fantastic tool for separating your data from your code, particularly with Puppet. However, it serves a
|
14
|
+
very specific purpose: Find a value in a hierarchy of files, and return the first one found. Unfortunately this
|
15
|
+
means that it does not (nor does it entirely make sense for it to) allow you to query *all* the values that are
|
16
|
+
available. There are, however, times when this can be very useful for troubleshooting, migrating to hiera, or
|
17
|
+
understanding state. For example, if you wanted to know what defaults are presently would be provided to a particular
|
18
|
+
server, were you to change a fact about it (I.E. its role), how would you determine this? Presently, by reviewing
|
19
|
+
your hiera.yaml, and searching through your hierarchy manually.
|
20
|
+
Enter Enviera. Compile all the facts about the machine in question, and perform a lookup. This will provide you all
|
21
|
+
of the keys available, and their values (It shows the raw hiera values, so no worries if you're using eyaml etc...).
|
22
|
+
You can also do a hierarchy lookup, and it will tell you which hierarchy files are being polled for your data as well.
|
23
|
+
Hopefully this can be helpful in isolating what variables are being (or will be) assigned to your nodes.
|
24
|
+
|
25
|
+
Setup
|
26
|
+
-----
|
27
|
+
|
28
|
+
### Installing Enviera
|
29
|
+
|
30
|
+
$ gem install enviera
|
31
|
+
|
32
|
+
Hiera Config
|
33
|
+
-----
|
34
|
+
|
35
|
+
Unless told otherwise, Enviera will use /etc/puppet/hiera.yaml for its hiera config. You can customize this, and
|
36
|
+
in certain events may even want to consider using a separate hiera file for your lookups (I.E. If you want to test,
|
37
|
+
or exclude certain files)
|
38
|
+
|
39
|
+
|
40
|
+
Configuration file for Enviera
|
41
|
+
----------------------------
|
42
|
+
|
43
|
+
Default parameters for the Enviera command line tool can be provided by creating a configuration YAML file.
|
44
|
+
|
45
|
+
Config files will be read first from `/etc/enviera/config.yaml`, then from `~/.enviera/config.yaml` and finally by anything referenced in the `ENVIERA_CONFIG` environment variable
|
46
|
+
|
47
|
+
The file takes any long form argument that you can provide on the command line. For example, to override the hiera config
|
48
|
+
path:
|
49
|
+
```yaml
|
50
|
+
---
|
51
|
+
hiera_config: /etc/hiera.yaml
|
52
|
+
```
|
53
|
+
|
54
|
+
Or to override to output JSON by default:
|
55
|
+
```yaml
|
56
|
+
---
|
57
|
+
output: json
|
58
|
+
```
|
59
|
+
|
60
|
+
Notes
|
61
|
+
-----
|
62
|
+
|
63
|
+
Enviera will use whatever backends you have configured, but unfortunately at this time only supports
|
64
|
+
files with a .yaml extension, and may not work properly with other backends (for determining the hierarchy).
|
65
|
+
This means JSON or hiera-file backends for sure don't work yet.
|
66
|
+
|
67
|
+
Troubleshooting
|
68
|
+
---------------
|
69
|
+
|
70
|
+
### Installing from behind a corporate/application proxy
|
71
|
+
|
72
|
+
$ export HTTP_PROXY=http://yourcorporateproxy:3128/
|
73
|
+
$ export HTTPS_PROXY=http://yourcorporateproxy:3128/
|
74
|
+
|
75
|
+
then run your install
|
76
|
+
|
77
|
+
$ gem install enviera
|
78
|
+
|
79
|
+
Examples
|
80
|
+
-------
|
81
|
+
|
82
|
+
```bash
|
83
|
+
---
|
84
|
+
enviera hierarchy -s '{"::fqdn": "some.host.com"}' -c "/etc/puppet/hiera.yaml" -o "yaml"
|
85
|
+
```
|
86
|
+
Would show all files being used in the hierarchy for the given facts, using the specified hiera config, outputting in yaml format
|
87
|
+
|
88
|
+
```bash
|
89
|
+
---
|
90
|
+
enviera lookup -s '{"::fqdn": "some.host.com"}' -c "/etc/puppet/hiera.yaml" -m "classes" -o "json"
|
91
|
+
```
|
92
|
+
Would show all variables available in the hierarchy, and their values, for the given facts, using the specified hiera config, emulating the behavior of a 'hiera_include("classes")' and concatenating all results for the 'classes' parameter,
|
93
|
+
and outputting in json
|
94
|
+
|
95
|
+
Issues
|
96
|
+
------
|
97
|
+
|
98
|
+
If you have found a bug then please raise an issue on the github page.
|
99
|
+
|
100
|
+
|
101
|
+
Tests
|
102
|
+
-----
|
103
|
+
|
104
|
+
I don't have any yet. I'm very new to the Ruby gem concept, but should get something eventually
|
105
|
+
|
106
|
+
Kudos
|
107
|
+
-----
|
108
|
+
|
109
|
+
My hat is off to [Tom Poulton](http://github.com/TomPoulton) for the [hiera-eyaml](https://github.com/TomPoulton/hiera-eyaml) project, which I have mimicked the file structure of this project on. Without his incredibly clean, consise, and
|
110
|
+
well architected code it would have taken me much longer to figure out how to make a command-line gem.
|
111
|
+
|
112
|
+
Authors
|
113
|
+
-------
|
114
|
+
|
115
|
+
- [Josh Souza](http://github.com/joshsouza) - Author.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/enviera
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'enviera/CLI'
|
5
|
+
|
6
|
+
begin
|
7
|
+
Enviera::CLI.parse
|
8
|
+
rescue StandardError => e
|
9
|
+
Enviera::Utils.warn e.message
|
10
|
+
Enviera::Utils.debug e.backtrace.join("\n")
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
begin
|
15
|
+
Enviera::CLI.execute
|
16
|
+
rescue StandardError => e
|
17
|
+
Enviera::Utils.warn e.message
|
18
|
+
Enviera::Utils.debug e.backtrace.join("\n")
|
19
|
+
exit 1
|
20
|
+
end
|
data/enviera.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'enviera'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "enviera"
|
8
|
+
gem.version = Enviera::VERSION
|
9
|
+
gem.description = "Puppet tool for looking up all Hiera values for a given set of facts"
|
10
|
+
gem.summary = "Given an environment, looks up Hiera data about it. Env(ironment H)iera(chy)"
|
11
|
+
gem.author = "Josh Souza"
|
12
|
+
gem.email = "development@pureinsomnia.com"
|
13
|
+
gem.license = "MIT"
|
14
|
+
|
15
|
+
gem.homepage = "http://github.com/josh.souza/enviera"
|
16
|
+
gem.files = `git ls-files`.split($/).reject { |file| file =~ /^features.*$/ }
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_dependency('trollop', '~> 2.0')
|
22
|
+
gem.add_dependency('json', '~> 1.5')
|
23
|
+
gem.add_dependency('hiera', '~> 1.3')
|
24
|
+
end
|
data/lib/enviera/CLI.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'enviera'
|
2
|
+
require 'enviera/utils'
|
3
|
+
require 'enviera/options'
|
4
|
+
require 'enviera/subcommand'
|
5
|
+
require 'trollop'
|
6
|
+
|
7
|
+
module Enviera
|
8
|
+
class CLI
|
9
|
+
|
10
|
+
def self.parse
|
11
|
+
|
12
|
+
Utils.require_dir 'enviera/subcommands'
|
13
|
+
Enviera.subcommands = Utils.find_all_subclasses_of({ :parent_class => Enviera::Subcommands }).collect {|classname| Utils.snakecase classname}
|
14
|
+
|
15
|
+
Enviera.subcommand = ARGV.shift
|
16
|
+
subcommand = case Enviera.subcommand
|
17
|
+
when nil
|
18
|
+
ARGV.delete_if {true}
|
19
|
+
"unknown_command"
|
20
|
+
when /^\-/
|
21
|
+
ARGV.delete_if {true}
|
22
|
+
"help"
|
23
|
+
else
|
24
|
+
Enviera.subcommand
|
25
|
+
end
|
26
|
+
|
27
|
+
command_class = Subcommand.find subcommand
|
28
|
+
|
29
|
+
options = command_class.parse
|
30
|
+
options[:executor] = command_class
|
31
|
+
|
32
|
+
options = command_class.validate options
|
33
|
+
Enviera::Options.set options
|
34
|
+
Enviera::Options.trace
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.execute
|
39
|
+
|
40
|
+
executor = Enviera::Options[:executor]
|
41
|
+
begin
|
42
|
+
result = executor.execute
|
43
|
+
puts result unless result.nil?
|
44
|
+
rescue Exception => e
|
45
|
+
Utils.warn e.message
|
46
|
+
Utils.debug e.backtrace.join("\n")
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
module Enviera
|
3
|
+
class Options
|
4
|
+
|
5
|
+
def self.[]= key, value
|
6
|
+
@@options ||= {}
|
7
|
+
@@options[ key.to_sym ] = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.[] key
|
11
|
+
@@options ||= {}
|
12
|
+
@@options[ key.to_sym ]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.set hash
|
16
|
+
@@options = {}
|
17
|
+
hash.each do |k, v|
|
18
|
+
@@options[ k.to_sym ] = v
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.trace
|
23
|
+
Utils::trace "Dump of enviera tool options dict:"
|
24
|
+
Utils::trace "--------------------------------"
|
25
|
+
@@options.each do |k, v|
|
26
|
+
begin
|
27
|
+
Utils::trace sprintf "%18s %-18s = %18s %-18s", "(#{k.class.name})", k.to_s, "(#{v.class.name})", v.to_s
|
28
|
+
rescue
|
29
|
+
Utils::trace sprintf "%18s %-18s = %18s %-18s", "(#{k.class.name})", k.to_s, "(#{v.class.name})", "<unprintable>" # case where v is binary
|
30
|
+
end
|
31
|
+
end
|
32
|
+
Utils::trace "--------------------------------"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'yaml'
|
3
|
+
# require 'enviera/subcommands/unknown_command'
|
4
|
+
|
5
|
+
module Enviera
|
6
|
+
|
7
|
+
class Subcommand
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_accessor :global_options, :options, :helptext
|
11
|
+
end
|
12
|
+
|
13
|
+
@@global_options = [
|
14
|
+
{:name => :version,
|
15
|
+
:description => "Show version information"},
|
16
|
+
{:name => :verbose,
|
17
|
+
:description => "Be more verbose",
|
18
|
+
:short => 'v'},
|
19
|
+
{:name => :help,
|
20
|
+
:description => "Information on how to use this command",
|
21
|
+
:short => 'h'}
|
22
|
+
]
|
23
|
+
|
24
|
+
def self.load_config_file
|
25
|
+
config = {}
|
26
|
+
[ "/etc/enviera/config.yaml", "#{ENV['HOME']}/.enviera/config.yaml", "#{ENV['ENVIERA_CONFIG']}" ].each do |config_file|
|
27
|
+
begin
|
28
|
+
yaml_contents = YAML.load_file(config_file)
|
29
|
+
Utils::info "Loaded config from #{config_file}"
|
30
|
+
config.merge! yaml_contents
|
31
|
+
rescue
|
32
|
+
raise StandardError, "Could not open config file \"#{config_file}\" for reading"
|
33
|
+
end if config_file and File.file? config_file
|
34
|
+
end
|
35
|
+
config
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.all_options
|
39
|
+
options = @@global_options.dup
|
40
|
+
options += self.options if self.options
|
41
|
+
# merge in defaults from configuration files
|
42
|
+
config_file = self.load_config_file
|
43
|
+
options.map!{ | opt|
|
44
|
+
key_name = "#{opt[:name]}"
|
45
|
+
if config_file.has_key? key_name
|
46
|
+
opt[:default] = config_file[key_name]
|
47
|
+
opt
|
48
|
+
else
|
49
|
+
opt
|
50
|
+
end
|
51
|
+
}
|
52
|
+
options
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.attach_option opt
|
56
|
+
self.suboptions += opt
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.find commandname = "unknown_command"
|
60
|
+
begin
|
61
|
+
require "enviera/subcommands/#{commandname.downcase}"
|
62
|
+
rescue Exception => e
|
63
|
+
require "enviera/subcommands/unknown_command"
|
64
|
+
return Enviera::Subcommands::UnknownCommand
|
65
|
+
end
|
66
|
+
command_module = Module.const_get('Enviera').const_get('Subcommands')
|
67
|
+
command_class = Utils.find_closest_class :parent_class => command_module, :class_name => commandname
|
68
|
+
command_class || Enviera::Subcommands::UnknownCommand
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.parse
|
72
|
+
|
73
|
+
me = self
|
74
|
+
|
75
|
+
options = Trollop::options do
|
76
|
+
|
77
|
+
version "Enviera version " + Enviera::VERSION.to_s
|
78
|
+
banner ["enviera #{me.prettyname}: #{me.description}", me.helptext, "Options:"].compact.join("\n\n")
|
79
|
+
|
80
|
+
me.all_options.each do |available_option|
|
81
|
+
|
82
|
+
skeleton = {:description => "",
|
83
|
+
:short => :none}
|
84
|
+
|
85
|
+
skeleton.merge! available_option
|
86
|
+
opt skeleton[:name],
|
87
|
+
skeleton[:desc] || skeleton[:description], #legacy plugins
|
88
|
+
:short => skeleton[:short],
|
89
|
+
:default => skeleton[:default],
|
90
|
+
:type => skeleton[:type]
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
stop_on Enviera.subcommands
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
if options[:verbose]
|
99
|
+
Enviera.verbosity_level += 1
|
100
|
+
end
|
101
|
+
|
102
|
+
if options[:quiet]
|
103
|
+
Enviera.verbosity_level = 0
|
104
|
+
end
|
105
|
+
|
106
|
+
options
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.validate args
|
111
|
+
args
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.description
|
115
|
+
"no description"
|
116
|
+
end
|
117
|
+
|
118
|
+
def self.helptext
|
119
|
+
"Usage: enviera #{self.prettyname} [options]"
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.execute
|
123
|
+
raise StandardError, "This command is not implemented yet (#{self.to_s.split('::').last})"
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.prettyname
|
127
|
+
Utils.snakecase self.to_s.split('::').last
|
128
|
+
end
|
129
|
+
|
130
|
+
def self.hidden?
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'enviera/subcommand'
|
2
|
+
require 'enviera'
|
3
|
+
|
4
|
+
module Enviera
|
5
|
+
module Subcommands
|
6
|
+
|
7
|
+
class Help < Subcommand
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.description
|
14
|
+
"this page"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.execute
|
18
|
+
|
19
|
+
puts <<-EOS
|
20
|
+
Welcome to enviera #{Enviera::VERSION}
|
21
|
+
Usage:
|
22
|
+
enviera subcommand [global-opts] [subcommand-opts]
|
23
|
+
Available subcommands:
|
24
|
+
#{Enviera.subcommands.collect {|command|
|
25
|
+
command_class = Subcommands.const_get(Utils.camelcase command)
|
26
|
+
sprintf "%15s: %-65s", command.downcase, command_class.description unless command_class.hidden?
|
27
|
+
}.compact.join("\n")}
|
28
|
+
For more help on an individual command, use --help on that command
|
29
|
+
EOS
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.hidden?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'enviera'
|
2
|
+
require 'enviera/utils'
|
3
|
+
require 'enviera/options'
|
4
|
+
require 'enviera/subcommand'
|
5
|
+
require 'hiera'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Enviera
|
9
|
+
module Subcommands
|
10
|
+
|
11
|
+
class Hierarchy < Subcommand
|
12
|
+
|
13
|
+
def self.options
|
14
|
+
[{:name => :string,
|
15
|
+
:description => "Source input is a json string provided as an argument",
|
16
|
+
:short => 's',
|
17
|
+
:type => :string},
|
18
|
+
{:name => :file,
|
19
|
+
:description => "Source input is a regular json file",
|
20
|
+
:short => 'f',
|
21
|
+
:type => :string},
|
22
|
+
{:name => :stdin,
|
23
|
+
:description => "Source input is taken from stdin (json string)",
|
24
|
+
:short => :none},
|
25
|
+
{:name => :hiera_config,
|
26
|
+
:description => "Path to your hiera config yaml file",
|
27
|
+
:short => 'c',
|
28
|
+
:type => :string,
|
29
|
+
:default => '/etc/puppet/hiera.yaml'},
|
30
|
+
{:name => :output,
|
31
|
+
:description => "What format to output in. [json|yaml|block]",
|
32
|
+
:short => 'o',
|
33
|
+
:type => :string,
|
34
|
+
:default => 'yaml'}
|
35
|
+
]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.description
|
39
|
+
"display the hierarchy in use with the given environment"
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.validate options
|
43
|
+
sources = [:string, :file, :stdin].collect {|x| x if options[x]}.compact
|
44
|
+
Trollop::die "You must specify a source (I.E. -s '{}')" if sources.count.zero?
|
45
|
+
Trollop::die "You can only specify one of (#{sources.join(', ')})" if sources.count > 1
|
46
|
+
options[:source] = sources.first
|
47
|
+
|
48
|
+
options[:input_data] = case options[:source]
|
49
|
+
when :stdin
|
50
|
+
STDIN.read
|
51
|
+
when :string
|
52
|
+
options[:string]
|
53
|
+
when :file
|
54
|
+
File.read options[:file]
|
55
|
+
end
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.execute
|
60
|
+
begin
|
61
|
+
environment = JSON.parse(Enviera::Options[:input_data])
|
62
|
+
rescue
|
63
|
+
raise "Unable to parse input as valid JSON. Please ensure structure is proper."
|
64
|
+
end
|
65
|
+
hiera_config = Hiera::Config.load(Enviera::Options[:hiera_config])
|
66
|
+
hiera= Hiera.new(:config=>hiera_config)
|
67
|
+
sources = []
|
68
|
+
hiera.config[:backends].each do |backend|
|
69
|
+
Hiera::Backend.datasources(environment) { |source|
|
70
|
+
# Presently I'm only handling 'yaml' extensions. I hope someday this works for other stuff
|
71
|
+
# But I haven't figured out a smart way to do this with the tools available
|
72
|
+
# And it's not urgent for me
|
73
|
+
sourcefile = Hiera::Backend.datafile(backend,environment,source,"yaml") or next
|
74
|
+
sources.push(sourcefile)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
sources = {'hierarchy' => sources}
|
78
|
+
case Enviera::Options[:output]
|
79
|
+
when "block"
|
80
|
+
sources
|
81
|
+
when "json"
|
82
|
+
sources.to_json
|
83
|
+
when "yaml"
|
84
|
+
sources.to_yaml
|
85
|
+
else
|
86
|
+
sources.to_yaml
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'enviera'
|
2
|
+
require 'enviera/utils'
|
3
|
+
require 'enviera/options'
|
4
|
+
require 'enviera/subcommand'
|
5
|
+
require 'hiera'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
module Enviera
|
9
|
+
module Subcommands
|
10
|
+
|
11
|
+
class Lookup < Subcommand
|
12
|
+
|
13
|
+
def self.options
|
14
|
+
[{:name => :string,
|
15
|
+
:description => "Source input is a json string provided as an argument",
|
16
|
+
:short => 's',
|
17
|
+
:type => :string},
|
18
|
+
{:name => :file,
|
19
|
+
:description => "Source input is a regular json file",
|
20
|
+
:short => 'f',
|
21
|
+
:type => :string},
|
22
|
+
{:name => :stdin,
|
23
|
+
:description => "Source input is taken from stdin (json string)",
|
24
|
+
:short => :none},
|
25
|
+
{:name => :hiera_config,
|
26
|
+
:description => "Path to your hiera config yaml file",
|
27
|
+
:short => 'c',
|
28
|
+
:type => :string,
|
29
|
+
:default => '/etc/puppet/hiera.yaml'},
|
30
|
+
{:name => :merged_keys,
|
31
|
+
:description => "Keys that should be merged (i.e. hiera_include style) separated by commas",
|
32
|
+
:short => 'm',
|
33
|
+
:type => :string,
|
34
|
+
:default => ''},
|
35
|
+
{:name => :output,
|
36
|
+
:description => "What format to output in. [json|yaml|block]",
|
37
|
+
:short => 'o',
|
38
|
+
:type => :string,
|
39
|
+
:default => 'yaml'}
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.description
|
44
|
+
"lookup hiera data for environment"
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.validate options
|
48
|
+
sources = [:string, :file, :stdin].collect {|x| x if options[x]}.compact
|
49
|
+
Trollop::die "You must specify a source (I.E. -s '{}')" if sources.count.zero?
|
50
|
+
Trollop::die "You can only specify one of (#{sources.join(', ')})" if sources.count > 1
|
51
|
+
options[:source] = sources.first
|
52
|
+
|
53
|
+
options[:input_data] = case options[:source]
|
54
|
+
when :stdin
|
55
|
+
STDIN.read
|
56
|
+
when :string
|
57
|
+
options[:string]
|
58
|
+
when :file
|
59
|
+
File.read options[:file]
|
60
|
+
end
|
61
|
+
options
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.execute
|
65
|
+
begin
|
66
|
+
environment = JSON.parse(Enviera::Options[:input_data])
|
67
|
+
rescue
|
68
|
+
raise "Unable to parse input as valid JSON. Please ensure structure is proper."
|
69
|
+
end
|
70
|
+
keys_to_merge = Enviera::Options[:merged_keys].split(',')
|
71
|
+
hiera_config = Hiera::Config.load(Enviera::Options[:hiera_config])
|
72
|
+
hiera= Hiera.new(:config=>hiera_config)
|
73
|
+
keys = {}
|
74
|
+
hiera.config[:backends].each do |backend|
|
75
|
+
Hiera::Backend.datasources(environment) { |source|
|
76
|
+
# Presently I'm only handling 'yaml' extensions. I hope someday this works for other stuff
|
77
|
+
# But I haven't figured out a smart way to do this with the tools available
|
78
|
+
# And it's not urgent for me
|
79
|
+
sourcefile = Hiera::Backend.datafile(backend,environment,source,"yaml") or next
|
80
|
+
source = YAML.load_file(sourcefile)
|
81
|
+
source.each_key do |key|
|
82
|
+
if !keys.has_key?(key) then
|
83
|
+
keys[key] = source[key]
|
84
|
+
elsif keys_to_merge.include?(key) then
|
85
|
+
keys[key] = (keys[key] + source[key]).uniq
|
86
|
+
end
|
87
|
+
end
|
88
|
+
}
|
89
|
+
end
|
90
|
+
keys = keys.sort.to_h
|
91
|
+
case Enviera::Options[:output]
|
92
|
+
when "block"
|
93
|
+
keys
|
94
|
+
when "json"
|
95
|
+
keys.to_json
|
96
|
+
when "yaml"
|
97
|
+
keys.to_yaml
|
98
|
+
else
|
99
|
+
keys.to_yaml
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'enviera/subcommand'
|
2
|
+
|
3
|
+
module Enviera
|
4
|
+
module Subcommands
|
5
|
+
|
6
|
+
class UnknownCommand < Enviera::Subcommand
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :original_command
|
10
|
+
end
|
11
|
+
|
12
|
+
@@original_command = "unknown"
|
13
|
+
|
14
|
+
def self.options
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.description
|
19
|
+
"Unknown command (#{@@original_command})"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.execute
|
23
|
+
subcommands = Enviera.subcommands
|
24
|
+
puts <<-EOS
|
25
|
+
Unknown subcommand#{ ": " + Enviera.subcommand if Enviera.subcommand }
|
26
|
+
Usage: enviera <subcommand>
|
27
|
+
Please use one of the following subcommands or help for more help:
|
28
|
+
#{Enviera.subcommands.sort.collect {|command|
|
29
|
+
command_class = Subcommands.const_get(Utils.camelcase command)
|
30
|
+
command unless command_class.hidden?
|
31
|
+
}.compact.join(", ")}
|
32
|
+
EOS
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.hidden?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'enviera/subcommand'
|
2
|
+
require 'enviera'
|
3
|
+
|
4
|
+
module Enviera
|
5
|
+
module Subcommands
|
6
|
+
|
7
|
+
class Version < Subcommand
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
[]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.description
|
14
|
+
"show version information"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.execute
|
18
|
+
Enviera::Utils.info "enviera (core): #{Enviera::VERSION}"
|
19
|
+
|
20
|
+
nil
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
module Enviera
|
3
|
+
class Utils
|
4
|
+
|
5
|
+
def self.read_password
|
6
|
+
ask("Enter password: ") {|q| q.echo = "*" }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.confirm? message
|
10
|
+
result = ask("#{message} (y/N): ")
|
11
|
+
if result.downcase == "y" or result.downcase == "yes"
|
12
|
+
true
|
13
|
+
else
|
14
|
+
false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.camelcase string
|
19
|
+
return string if string !~ /_/ && string =~ /[A-Z]+.*/
|
20
|
+
string.split('_').map{|e| e.capitalize}.join
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.snakecase string
|
24
|
+
return string if string !~ /[A-Z]/
|
25
|
+
string.split(/(?=[A-Z])/).collect {|x| x.downcase}.join("_")
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.find_closest_class args
|
29
|
+
parent_class = args[ :parent_class ]
|
30
|
+
class_name = args[ :class_name ]
|
31
|
+
constants = parent_class.constants
|
32
|
+
candidates = []
|
33
|
+
constants.each do | candidate |
|
34
|
+
candidates << candidate.to_s if candidate.to_s.downcase == class_name.downcase
|
35
|
+
end
|
36
|
+
if candidates.count > 0
|
37
|
+
parent_class.const_get candidates.first
|
38
|
+
else
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.require_dir classdir
|
44
|
+
num_class_hierarchy_levels = self.to_s.split("::").count - 1
|
45
|
+
root_folder = File.dirname(__FILE__) + "/" + Array.new(num_class_hierarchy_levels).fill("..").join("/")
|
46
|
+
class_folder = root_folder + "/" + classdir
|
47
|
+
Dir[File.expand_path("#{class_folder}/*.rb")].uniq.each do |file|
|
48
|
+
self.trace "Requiring file: #{file}"
|
49
|
+
require file
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.find_all_subclasses_of args
|
54
|
+
parent_class = args[ :parent_class ]
|
55
|
+
constants = parent_class.constants
|
56
|
+
candidates = []
|
57
|
+
constants.each do | candidate |
|
58
|
+
candidates << candidate.to_s.split('::').last if parent_class.const_get(candidate).class.to_s == "Class"
|
59
|
+
end
|
60
|
+
candidates
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.structure_message messageinfo
|
64
|
+
message = {:from => "enviera-core"}
|
65
|
+
case messageinfo.class.to_s
|
66
|
+
when 'Hash'
|
67
|
+
message.merge!(messageinfo)
|
68
|
+
else
|
69
|
+
message.merge!({:msg => messageinfo.to_s})
|
70
|
+
end
|
71
|
+
message[:prefix] = "[#{message[:from]}]"
|
72
|
+
message[:spacer] = " #{' ' * message[:from].length} "
|
73
|
+
formatted_output = message[:msg].split("\n").each_with_index.map do |line, index|
|
74
|
+
if index == 0
|
75
|
+
"#{message[:prefix]} #{line}"
|
76
|
+
else
|
77
|
+
"#{message[:spacer]} #{line}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
formatted_output.join "\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.warn messageinfo
|
84
|
+
self.print_message({ :message => self.structure_message( messageinfo ), :enviera_loglevel => :warn, :cli_color => :red })
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.info messageinfo
|
88
|
+
self.print_message({ :message => self.structure_message( messageinfo ), :enviera_loglevel => :debug, :cli_color => :white, :threshold => 0 })
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.debug messageinfo
|
92
|
+
self.print_message({ :message => self.structure_message( messageinfo ), :enviera_loglevel => :debug, :cli_color => :green, :threshold => 1 })
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.trace messageinfo
|
96
|
+
self.print_message({ :message => self.structure_message( messageinfo ), :enviera_loglevel => :debug, :cli_color => :blue, :threshold => 2 })
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.print_message( args )
|
100
|
+
message = args[:message] ||= ""
|
101
|
+
enviera_loglevel = args[:enviera_loglevel] ||= :debug
|
102
|
+
cli_color = args[:cli_color] ||= :blue
|
103
|
+
threshold = args[:threshold]
|
104
|
+
|
105
|
+
STDERR.puts self.colorize( message, cli_color ) if threshold.nil? or Enviera.verbosity_level > threshold
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.colorize message, color
|
109
|
+
suffix = "\e[0m"
|
110
|
+
prefix = case color
|
111
|
+
when :red
|
112
|
+
"\e[31m"
|
113
|
+
when :green
|
114
|
+
"\e[32m"
|
115
|
+
when :blue
|
116
|
+
"\e[34m"
|
117
|
+
else #:white
|
118
|
+
"\e[0m"
|
119
|
+
end
|
120
|
+
"#{prefix}#{message}#{suffix}"
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
data/lib/enviera.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Enviera
|
2
|
+
VERSION = "0.0.1"
|
3
|
+
DESCRIPTION = "Enviera is a tool for looking up all Hiera values available based on a given set of puppet facts"
|
4
|
+
|
5
|
+
class RecoverableError < StandardError
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.subcommand= command
|
9
|
+
@@subcommand = command
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.subcommand
|
13
|
+
@@subcommand
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.verbosity_level= new_verbosity_level
|
17
|
+
@@debug_level = new_verbosity_level
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.verbosity_level
|
21
|
+
@@debug_level ||= 1
|
22
|
+
@@debug_level
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.subcommands= commands
|
26
|
+
@@subcommands = commands
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.subcommands
|
30
|
+
@@subcommands
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: enviera
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Josh Souza
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: trollop
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: hiera
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
description: Puppet tool for looking up all Hiera values for a given set of facts
|
56
|
+
email: development@pureinsomnia.com
|
57
|
+
executables:
|
58
|
+
- enviera
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- Gemfile.lock
|
65
|
+
- LICENSE
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/enviera
|
69
|
+
- enviera.gemspec
|
70
|
+
- lib/enviera.rb
|
71
|
+
- lib/enviera/CLI.rb
|
72
|
+
- lib/enviera/commands.rb
|
73
|
+
- lib/enviera/options.rb
|
74
|
+
- lib/enviera/subcommand.rb
|
75
|
+
- lib/enviera/subcommands/help.rb
|
76
|
+
- lib/enviera/subcommands/hierarchy.rb
|
77
|
+
- lib/enviera/subcommands/lookup.rb
|
78
|
+
- lib/enviera/subcommands/unknown_command.rb
|
79
|
+
- lib/enviera/subcommands/version.rb
|
80
|
+
- lib/enviera/utils.rb
|
81
|
+
homepage: http://github.com/josh.souza/enviera
|
82
|
+
licenses:
|
83
|
+
- MIT
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
require_paths:
|
88
|
+
- lib
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
requirements: []
|
100
|
+
rubyforge_project:
|
101
|
+
rubygems_version: 2.2.2
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: Given an environment, looks up Hiera data about it. Env(ironment H)iera(chy)
|
105
|
+
test_files: []
|
106
|
+
has_rdoc:
|