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