j-enc 0.0.2

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.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +31 -0
  6. data/Rakefile +2 -0
  7. data/bin/j-enc +12 -0
  8. data/j-enc.gemspec +28 -0
  9. data/j-enc.iml +24 -0
  10. data/lib/enc/builder.rb +28 -0
  11. data/lib/enc/cache/exceptions.rb +6 -0
  12. data/lib/enc/cache/node_cache.rb +57 -0
  13. data/lib/enc/cache.rb +2 -0
  14. data/lib/enc/collins_helper/api.rb +43 -0
  15. data/lib/enc/collins_helper/connection.rb +26 -0
  16. data/lib/enc/collins_helper/node/exceptions.rb +9 -0
  17. data/lib/enc/collins_helper/node/node_asset.rb +135 -0
  18. data/lib/enc/collins_helper/node.rb +2 -0
  19. data/lib/enc/collins_helper.rb +3 -0
  20. data/lib/enc/config.rb +63 -0
  21. data/lib/enc/runner.rb +58 -0
  22. data/lib/enc/utils/logging.rb +68 -0
  23. data/lib/enc/utils.rb +1 -0
  24. data/lib/enc/version.rb +3 -0
  25. data/lib/enc.rb +38 -0
  26. data/spec/api_spec.rb +42 -0
  27. data/spec/builder_spec.rb +43 -0
  28. data/spec/cli_helper.rb +22 -0
  29. data/spec/config_spec.rb +15 -0
  30. data/spec/connection_spec.rb +9 -0
  31. data/spec/enc_spec.rb +25 -0
  32. data/spec/factories/api.rb +60 -0
  33. data/spec/factories/builder.rb +27 -0
  34. data/spec/factories/cache.rb +9 -0
  35. data/spec/factories/config.rb +21 -0
  36. data/spec/factories/connection.rb +11 -0
  37. data/spec/factories/node_asset.rb +20 -0
  38. data/spec/factories/response.rb +49 -0
  39. data/spec/factories/runner.rb +19 -0
  40. data/spec/factories.rb +21 -0
  41. data/spec/features/external_request_spec.rb +9 -0
  42. data/spec/node_asset_spec.rb +54 -0
  43. data/spec/node_cache_spec.rb +25 -0
  44. data/spec/runner_spec.rb +41 -0
  45. data/spec/spec_helper.rb +52 -0
  46. data/spec/support/factory_girl.rb +8 -0
  47. data/spec/support/utils.rb +37 -0
  48. metadata +197 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c8fb70746bbb43bcc52ad7eff52488f41dd45222
4
+ data.tar.gz: 0582985c08fe3ab505572beac8b80bc5f3c94b06
5
+ SHA512:
6
+ metadata.gz: 0a67465a92406e9a4aefa32180d1f4f8cdcc975baa01e1d42f03b13aedff2b46e0f76a58a4e6ff1b86b8896d2c83022436d6e4eb6dde564c9f644546d536a02d
7
+ data.tar.gz: f7c3f72d7ca574f5f5a932b4a448d06d7a4e7d26040a0f710fcdef43a565fbe480369c15ed2e2f97b87979783803954d1460a80622e4ad3ce4bfe9686ab4e705
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ binstubs
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in j-enc.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Jestin Woods
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # Collins::Enc
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'j-enc'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install j-enc
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/j-enc/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/j-enc ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'enc'
5
+ rescue LoadError
6
+ unless $:.include?(File.dirname(__FILE__) + '/../lib/')
7
+ $: << File.dirname(__FILE__) + '/../lib'
8
+ end
9
+ require 'enc'
10
+ end
11
+
12
+ Enc.run(ARGV)
data/j-enc.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'enc/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "j-enc"
8
+ spec.version = Enc::VERSION
9
+ spec.authors = ["Jestin Woods"]
10
+ spec.email = ["jestin.woods@jivesoftware.com"]
11
+ spec.summary = %q{Puppet ENC using Collins.}
12
+ spec.description = %q{Puppet ENC using Collins.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.1"
24
+ spec.add_development_dependency "webmock", "~> 1.20"
25
+ spec.add_development_dependency "factory_girl", "~> 4.5"
26
+
27
+ spec.add_runtime_dependency "collins_client", "~> 0.2"
28
+ end
data/j-enc.iml ADDED
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="RUBY_MODULE" version="4">
3
+ <component name="CompassSettings">
4
+ <option name="compassSupportEnabled" value="true" />
5
+ </component>
6
+ <component name="FacetManager">
7
+ <facet type="gem" name="Ruby Gem">
8
+ <configuration>
9
+ <option name="GEM_APP_ROOT_PATH" value="$MODULE_DIR$" />
10
+ <option name="GEM_APP_TEST_PATH" value="" />
11
+ <option name="GEM_APP_LIB_PATH" value="" />
12
+ </configuration>
13
+ </facet>
14
+ </component>
15
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
16
+ <exclude-output />
17
+ <content url="file://$MODULE_DIR$" />
18
+ <orderEntry type="jdk" jdkName="RVM: ruby-2.0.0-p247" jdkType="RUBY_SDK" />
19
+ <orderEntry type="sourceFolder" forTests="false" />
20
+ <orderEntry type="library" scope="PROVIDED" name="bundler (v1.7.4, RVM: ruby-2.0.0-p247) [gem]" level="application" />
21
+ <orderEntry type="library" scope="PROVIDED" name="rake (v10.3.2, RVM: ruby-2.0.0-p247) [gem]" level="application" />
22
+ </component>
23
+ </module>
24
+
@@ -0,0 +1,28 @@
1
+ module Enc
2
+ class Builder
3
+ def initialize(asset)
4
+ @asset = asset
5
+ end
6
+
7
+ def to_yaml
8
+ return String.new.to_yaml unless @asset.uses_enc?
9
+ output = {'parameters' => {'deployment_environment' => @asset.get_deployment_environment,
10
+ 'datacenter' => @asset.get_datacenter,
11
+ 'roles' => @asset.get_roles}}
12
+
13
+ # Do not require the puppet environment until we upgrade to puppet 3.x.
14
+ # output[:environment] => @asset.get_puppet_environment
15
+
16
+ # We do this to satisfy the current requirement to have a roleN for each role, where N is the sorted index of the
17
+ # role list.
18
+ @asset.get_roles_by_index.each do |index, role|
19
+ output['parameters']['role' + (index + 1).to_s] = role
20
+ end
21
+
22
+ # It's not strictly necessary, but nice to have the $collins parameter at the end of the output.
23
+ output['parameters']['collins'] = @asset.get_flattened_attributes
24
+ output.to_yaml
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ module Enc
2
+ module Cache
3
+ class CacheDoesNotExist < Exception; end
4
+ class UnableToCreateCacheDirectory < Exception; end
5
+ end
6
+ end
@@ -0,0 +1,57 @@
1
+ module Enc
2
+ module Cache
3
+ class NodeCache
4
+ include Enc::Utils::Logging
5
+
6
+ def initialize(config)
7
+ ensure_cache_dir(config.get('jive_enc_cache_dir'))
8
+ end
9
+
10
+ def exists?(hostname)
11
+ logger.debug("Checking to see if cache file #{cache_path(hostname)} exists")
12
+ File.exist?(cache_path(hostname))
13
+ end
14
+
15
+ def read(hostname)
16
+ logger.debug("Attempting to read cache #{cache_path(hostname)}")
17
+ begin
18
+ File.open(cache_path(hostname), 'r') { |file| Marshal.load(file.read)}
19
+ rescue Errno::ENOENT
20
+ logger.error("The node #{hostname} does not exist in the cache.")
21
+ raise CacheDoesNotExist, "The node #{hostname} does not exist in the cache."
22
+ end
23
+ end
24
+
25
+ def write(hostname, node)
26
+ logger.debug("Writing to cache #{cache_path(hostname)}")
27
+ File.open(cache_path(hostname), 'w') { |file| file.write(Marshal.dump(node)) }
28
+ end
29
+
30
+ def delete!(hostname)
31
+ logger.debug("Deleting cache #{cache_path(hostname)}")
32
+ File.delete(cache_path(hostname)) if File.exist?(cache_path(hostname))
33
+ end
34
+
35
+ protected
36
+
37
+ def cache_path(node)
38
+ if node.is_a?String
39
+ return "#{@cache_dir}/#{node}"
40
+ end
41
+ "#{@cache_dir}/#{node.get_hostname}"
42
+ end
43
+
44
+ def ensure_cache_dir(cache_dir)
45
+ @cache_dir = cache_dir
46
+ unless File.directory?(@cache_dir)
47
+ logger.debug("Creating cache directory #{@cache_dir}")
48
+ begin
49
+ Dir.mkdir(@cache_dir)
50
+ rescue => e
51
+ raise UnableToCreateCacheDirectory, e
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
data/lib/enc/cache.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'enc/cache/exceptions'
2
+ require 'enc/cache/node_cache'
@@ -0,0 +1,43 @@
1
+ require 'collins_client'
2
+
3
+ module Enc
4
+ module CollinsHelper
5
+ class Api < Collins::Client
6
+ # The logger method is used by Collins::Client, so an override is needed rather than a simple include.
7
+ def logger
8
+ Enc::Utils::Logging.logger_for(self.class.name)
9
+ end
10
+
11
+ class TooManyAssets < Exception; end
12
+ class NoAssets < Exception; end
13
+ class AssetNotValid < Exception; end
14
+ class CannotConnect < Exception; end
15
+
16
+ def safe_find(options = {})
17
+ query = asset_hash_to_find_query(options)
18
+ params = query.to_a.map do |param|
19
+ (key, val) = param
20
+ if val.is_a?(Array)
21
+ val.map{|v| "#{key}=#{asset_escape_attribute(v)}"}.join('&')
22
+ else
23
+ "#{key}=#{asset_escape_attribute(val)}"
24
+ end
25
+ end.reject{|s| s.empty?}
26
+ logger.debug("Searching for asset using query: #{params.join('&')}")
27
+ begin
28
+ response = http_get('/api/assets', params) do |r|
29
+ parse_response(r, :expects => 200, :as => :paginated)
30
+ end
31
+ rescue SocketError, TimeoutError => e
32
+ raise CannotConnect, e
33
+ end
34
+ logger.debug("Got response: #{response.inspect}")
35
+ raise TooManyAssets, 'Too many assets' if response.count > 1
36
+ raise NoAssets, 'No assets found' if response.first.nil? or response.first.empty?
37
+ asset = CollinsHelper::Node::NodeAsset.from_json(response.first)
38
+ raise AssetNotValid, 'Asset is not valid' unless asset.is_valid?
39
+ asset
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,26 @@
1
+ module Enc
2
+ module CollinsHelper
3
+ class Connection
4
+ include Enc::Utils::Logging
5
+
6
+ def initialize(config)
7
+ @config = config
8
+ @session = nil
9
+
10
+ end
11
+
12
+ def api
13
+ unless @session
14
+ logger.debug("Attempting to connect to #{@config.get('host')} as user #{@config.get('username')}")
15
+ @session = Enc::CollinsHelper::Api.new(:host => @config.get('host'),
16
+ :username => @config.get('username'),
17
+ :password => @config.get('password'),
18
+ :timeout => @config.get('timeout'),
19
+ :strict => true)
20
+ end
21
+ @session
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module Enc
2
+ module CollinsHelper
3
+ module Node
4
+ class NoDatacenter < Exception; end
5
+ class NoDeploymentEnvironment < Exception; end
6
+ class NoHostname < Exception; end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,135 @@
1
+ require 'collins_client'
2
+
3
+ # Suppress warnings from Collins::Asset class.
4
+ $VERBOSE = nil
5
+
6
+ module Enc
7
+ module CollinsHelper
8
+ module Node
9
+ class NodeAsset < Collins::Asset
10
+ # Note: Do not include the logging module here. If the logging module loads a file handle, the Marshal.dump will
11
+ # fail horribly. This class is meant to be used as a resource.
12
+
13
+ VALID_ENC_FLAGS = %w(1 true yes y on)
14
+
15
+ class << self
16
+ def from_json(json_hash, bare_asset=false)
17
+ raise Collins::CollinsError, 'Invalid JSON specified for Asset.from_json' if
18
+ (json_hash.nil? || !json_hash.is_a?(Hash))
19
+ json = deep_copy_hash(json_hash)
20
+ json = json['data'] && json['data'] || json
21
+ if bare_asset or !json.include?('ASSET')
22
+ asset = NodeAsset.new json
23
+ else
24
+ asset = NodeAsset.new json.delete('ASSET')
25
+ end
26
+ asset.send('ipmi='.to_sym, Collins::Ipmi.from_json(json.delete('IPMI')))
27
+ asset.send('addresses='.to_sym, Collins::Address.from_json(json.delete('ADDRESSES')))
28
+ asset.send('power='.to_sym, Collins::Power.from_json(json.delete('POWER')))
29
+ asset.send('location=', json.delete('LOCATION'))
30
+ asset.send('extras=', json)
31
+ asset
32
+ end
33
+ end
34
+
35
+ def is_valid?
36
+ begin
37
+ get_hostname
38
+ get_deployment_environment
39
+ get_datacenter
40
+ rescue NoHostname, NoDeploymentEnvironment, NoDatacenter
41
+ return false
42
+ end
43
+ true
44
+ end
45
+
46
+ def uses_enc?
47
+ has_attribute?('PUPPET_ENC') && VALID_ENC_FLAGS.include?(get_attribute('PUPPET_ENC').to_s.downcase)
48
+ end
49
+
50
+ def has_attribute?(attribute)
51
+ begin
52
+ get_safe_attribute(attribute)
53
+ rescue KeyError
54
+ return false
55
+ end
56
+ true
57
+ end
58
+
59
+ def get_hostname
60
+ begin
61
+ value = get_safe_attribute('HOSTNAME')
62
+ rescue KeyError
63
+ raise NoHostname, 'The node does not have a hostname tag set'
64
+ end
65
+ value
66
+ end
67
+
68
+ def get_deployment_environment
69
+ begin
70
+ value = get_safe_attribute('ENVIRONMENT')
71
+ rescue KeyError
72
+ raise NoDeploymentEnvironment, 'The node does not have an environment tag set'
73
+ end
74
+ value
75
+ end
76
+
77
+ def get_datacenter
78
+ begin
79
+ value = get_safe_attribute('DATA_CENTER')
80
+ rescue KeyError
81
+ raise NoDatacenter, 'The node does not have a data_center tag set'
82
+ end
83
+ value
84
+ end
85
+
86
+ def get_puppet_environment
87
+ begin
88
+ value = get_safe_attribute('PUPPET_ENVIRONMENT')
89
+ rescue KeyError
90
+ value = 'production'
91
+ end
92
+ value
93
+ end
94
+
95
+ def get_roles
96
+ get_multi_attribute('CONFIGURATION_ROLES')
97
+ end
98
+
99
+ def get_roles_by_index
100
+ get_multi_attribute_with_index('CONFIGURATION_ROLES')
101
+ end
102
+
103
+ # We need to take all the attributes and make them more human-readable.
104
+ # TODO: Make it not so ugly.
105
+ def get_flattened_attributes
106
+ flattened_attributes = Hash.new
107
+ Hash[extract(extras, 'ATTRIBS').inject({}){ |hash, (k, v)| hash.merge(k.to_i => v) }.
108
+ sort_by {|k,_| k} ].each { |_,hash| hash.each {
109
+ |k,v| flattened_attributes.has_key?(k) && (flattened_attributes[k].is_a?(Array) &&
110
+ (flattened_attributes[k] << (v)) ||
111
+ (flattened_attributes[k] = [flattened_attributes[k], v])) ||
112
+ flattened_attributes[k] = v
113
+ }
114
+ }
115
+ flattened_attributes
116
+ end
117
+
118
+ def get_safe_attribute(attribute)
119
+ value = get_attribute(attribute)
120
+ raise KeyError unless value
121
+ value
122
+ end
123
+
124
+ def get_multi_attribute_with_index(attribute)
125
+ Hash[extract(extras, 'ATTRIBS').inject({}){ |hash, (k, v)| hash.merge(k.to_i => v[attribute]) if
126
+ v.has_key?(attribute) }.sort_by {|k,_| k} ]
127
+ end
128
+
129
+ def get_multi_attribute(attribute)
130
+ get_multi_attribute_with_index(attribute).values
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,2 @@
1
+ require 'enc/collins_helper/node/exceptions'
2
+ require 'enc/collins_helper/node/node_asset'
@@ -0,0 +1,3 @@
1
+ require 'enc/collins_helper/connection'
2
+ require 'enc/collins_helper/api'
3
+ require 'enc/collins_helper/node'
data/lib/enc/config.rb ADDED
@@ -0,0 +1,63 @@
1
+ module Enc
2
+ class Config
3
+ attr_reader :config_file
4
+
5
+ class InvalidConfiguration < Exception; end
6
+ class ConfigurationDoesNotExist < Exception; end
7
+
8
+ def initialize(config_file=nil, options={}, use_file=true)
9
+ @config_file = get_default_config(config_file)
10
+ @options = options.each { |k,v| {k.to_sym => v} }
11
+ @use_file = use_file
12
+ end
13
+
14
+ def get(key)
15
+ load[key.to_sym]
16
+ end
17
+
18
+ def load
19
+ if @options.empty?
20
+ @options = YAML.load_file(@config_file).inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
21
+ end
22
+ set_defaults
23
+ @options
24
+ end
25
+
26
+ def config_file_exists
27
+ File.exist?(@config_file)
28
+ end
29
+
30
+ def create!
31
+ raise InvalidConfiguration, 'Configuration requires host, username and password to be present.' unless
32
+ @options[:host] and @options[:username] and @options[:password]
33
+ File.open(@config_file, 'w') {|f| f.write @options.to_yaml }
34
+ end
35
+
36
+ def delete!
37
+ File.delete(@config_file) if File.exist?(@config_file)
38
+ end
39
+
40
+ protected
41
+
42
+ def set_defaults
43
+ @options[:timeout] = 30 unless @options.has_key?(:timeout)
44
+ @options[:jive_enc_cache_dir] = '/var/tmp/enc_cache' unless @options.has_key?(:jive_enc_cache_dir)
45
+ @options[:jive_enc_log_file] = '/var/log/enc.log' unless @options.has_key?(:jive_enc_log_file)
46
+ end
47
+
48
+ # Control the default config location order here. We need to assume at least this much so we know where to load the
49
+ # main collins config file.
50
+ def get_default_config(config_file=nil)
51
+ if config_file.nil? or config_file == ''
52
+ return "#{ENV['HOME']}/.collins.yaml" if File.exists?("#{ENV['HOME']}/.collins.yaml")
53
+ return'/var/lib/collins/.collins.yaml' if File.exists?('/var/lib/collins/.collins.yaml')
54
+ raise ConfigurationDoesNotExist,
55
+ 'Could not find configuration in ~/.collins.yaml or /var/lib/collins/.collins.yaml.'
56
+ else
57
+ raise ConfigurationDoesNotExist, "Config file #{config_file} does not exist" if
58
+ (not File.exists?(config_file) and @use_file)
59
+ config_file
60
+ end
61
+ end
62
+ end
63
+ end
data/lib/enc/runner.rb ADDED
@@ -0,0 +1,58 @@
1
+ module Enc
2
+ class Runner
3
+ include Enc::Utils::Logging
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ @cache = get_cache
8
+ end
9
+
10
+ def build(hostname)
11
+ asset, from_collins = get_asset(hostname)
12
+ # Do not rewrite the cache unless the asset comes from Collins.
13
+ @cache.write(hostname, asset) if from_collins
14
+ Enc::Builder.new(asset)
15
+ end
16
+
17
+ protected
18
+
19
+ def get_cache
20
+ begin
21
+ return Enc::Cache::NodeCache.new(@config)
22
+ rescue Enc::Cache::UnableToCreateCacheDirectory => e
23
+ bail('Could access cache directory', e)
24
+ end
25
+ end
26
+
27
+ def get_asset(hostname)
28
+ # It's important to handle any errors here so we know when to fall back to the cache.
29
+ begin
30
+ connection = Enc::CollinsHelper::Connection.new(@config)
31
+ return connection.api.safe_find(:hostname => hostname, :details => true), true
32
+ rescue Enc::CollinsHelper::Api::TooManyAssets,
33
+ Enc::CollinsHelper::Api::AssetNotValid,
34
+ Enc::CollinsHelper::Api::CannotConnect,
35
+ Collins::AuthenticationError,
36
+ Collins::RequestError
37
+ logger.error('Failed to connect to Collins. Attempting to read from cache')
38
+ # If the asset does not exist in Collins, it's important to save an empty asset in the cache that way in the case
39
+ # Collins is down, we will still return a valid, empty, response to puppet.
40
+ rescue Enc::CollinsHelper::Api::NoAssets
41
+ logger.info("Did not find any assets with hostname #{hostname}, saving an empty asset to the cache")
42
+ return Enc::CollinsHelper::Node::NodeAsset.new, true
43
+ end
44
+ begin
45
+ return @cache.read(hostname), false
46
+ rescue Enc::Cache::CacheDoesNotExist => e
47
+ bail('Could not find asset anywhere', e)
48
+ end
49
+ end
50
+
51
+ def bail(msg, exception)
52
+ logger.fatal("#{msg}: #{exception.message}")
53
+ logger.debug("Backtrace: #{exception.backtrace}")
54
+ exit(2)
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,68 @@
1
+ require 'logger'
2
+
3
+ module Enc
4
+ module Utils
5
+ module Logging
6
+ def logger
7
+ unless @logger
8
+ @logger = Logging.logger_for(self.class.name)
9
+ end
10
+ @logger
11
+ end
12
+
13
+ @loggers = {}
14
+
15
+ class << self
16
+ def logger_for(classname)
17
+ unless @loggers.has_key?(classname)
18
+ set_config
19
+ set_log_level
20
+ set_log_file
21
+ @loggers[classname] = configure_logger(classname)
22
+ end
23
+ @loggers[classname]
24
+ end
25
+
26
+ def configure_logger(classname)
27
+ logger = Logger.new(@file)
28
+ logger.level = @level
29
+ logger.progname = classname
30
+ logger
31
+ end
32
+
33
+ def set_config
34
+ unless @config
35
+ @config = Enc::Config.new
36
+ end
37
+ @config
38
+ end
39
+
40
+ def set_log_level
41
+ unless @level
42
+ @level = convert_log_level(@config.get('jive_enc_log_level'))
43
+ end
44
+ @level
45
+ end
46
+
47
+ def set_log_file
48
+ unless @file
49
+ @file = @config.get('jive_enc_log_file')
50
+ end
51
+ end
52
+
53
+ def convert_log_level(level)
54
+ case level
55
+ when 'warn'
56
+ Logger::WARN
57
+ when 'error'
58
+ Logger::ERROR
59
+ when 'debug'
60
+ Logger::DEBUG
61
+ else
62
+ Logger::INFO
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
data/lib/enc/utils.rb ADDED
@@ -0,0 +1 @@
1
+ require 'enc/utils/logging'
@@ -0,0 +1,3 @@
1
+ module Enc
2
+ VERSION = "0.0.2"
3
+ end