hiera-cloudformation 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA512:
3
+ data.tar.gz: 528e5773c1de04e1eb38d19c73ae8629fac0b0ebe235694c8bdb724b666d4aee0badafa093bd34e1f7b840b1ed92b8788ce3f3b045d390cd11f0ccfc66e6766c
4
+ metadata.gz: 891a69aff5ed2455a88d4e3c008084bf7e964a4cd36227258977232881331e1f0a230ddf2d4504a5d43870779b4baf105ca5f0348d894ca55d2d8862e102d1b7
5
+ SHA1:
6
+ data.tar.gz: 048f0606a4b2313a1de2e20732a136b2fb071b33
7
+ metadata.gz: 1fb1aba71ac1d4d39bf325d21a0af7ab1ae484b3
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Hugh Cole-Baker
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,103 @@
1
+ # Hiera::Cloudformation
2
+
3
+ This backend for Hiera can retrieve information from:
4
+
5
+ * the outputs of a CloudFormation stack
6
+ * the metadata of a resource in a stack
7
+
8
+ ## Installation
9
+
10
+ gem install hiera-cloudformation
11
+
12
+ ## Usage
13
+
14
+ Add the backend to the list of backends in hiera.yaml:
15
+
16
+ ---
17
+ :backends:
18
+ - yaml
19
+ - cloudformation
20
+
21
+ To provide the backend with an AWS access key, you can add the following configuration to the
22
+ `:cloudformation` section in hiera.yaml:
23
+
24
+ :cloudformation:
25
+ :access_key_id: Your_AWS_Access_Key_ID_Here
26
+ :secret_access_key: Your_AWS_Secret_Access_Key_Here
27
+
28
+ If you do not add these keys to your configuration file, the access keys will be looked up from
29
+ the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables, or from an IAM
30
+ instance role (if you are running Hiera on an EC2 instance with an IAM role assigned).
31
+
32
+ To use this backend you also need to add entries to your "hierarchy" in your hiera.yaml file.
33
+ If you put an entry of this form in your hierarchy:
34
+
35
+ cfstack/<stack name>/outputs
36
+
37
+ the backend will request the outputs of the stack named `<stack name>` and search for an output
38
+ named the same as the requested key. If an output named the same as the key is found, the backend
39
+ will return the value of that output (as a string).
40
+ If you put an entry of this form in your hierarchy:
41
+
42
+ cfstack/<stack name>/resources/<logical resource ID>
43
+
44
+ the backend will request the JSON metadata of the logical resource identified by the given ID,
45
+ in the named stack, and look for a JSON object under the top-level key "hiera" in the metadata.
46
+ If a JSON object is present under the top-level key "hiera", the backend will search for the
47
+ requested key in that JSON object, and return the value of the key if present.
48
+
49
+ The recommended way of constructing your hierarchy so that Puppet-managed nodes can retrieve
50
+ data relating to the CloudFormation stack they're part of, is to create facts on the Puppet nodes
51
+ describing what CloudFormation stack they're part of and what logical resource ID corresponds to
52
+ their instance. Then, assuming you have two facts "cloudformation_stack" and "cloudformation_resource"
53
+ reported on your node, you can add these entries to the "hierarchy" in hiera.yaml:
54
+
55
+ - cfstack/%{cloudformation_stack}/outputs
56
+ - cfstack/%{cloudformation_stack}/resources/%{cloudformation_resource}
57
+
58
+ and Hiera will replace the `%{...}` placeholders with the value of the facts on the node, enabling
59
+ the node to look up information about the stack it "belongs" to.
60
+
61
+ For example, if this snippet is included in your CloudFormation template, and you include an entry
62
+
63
+ cfstack/<stack name>/outputs
64
+
65
+ in your hierarchy, you can look up the key "example_key" in Hiera and get the value of the logical
66
+ resource identified by "AWSEC2Instance" (for example, this might be an EC2 instance, so Hiera
67
+ would return the instance ID)
68
+
69
+ "Outputs" : {
70
+ "example_key": {
71
+ "Description" : "AWSEC2Instance is the logical resource ID of the instance that was created",
72
+ "Value" : { "Ref": "AWSEC2Instance" }
73
+ }
74
+ }
75
+
76
+ As another example, if you define an EC2 instance with the following snippet in your CloudFormation
77
+ template, including some metadata:
78
+
79
+ "Resources" : {
80
+ "MyIAMKey" : {
81
+ "Type" : "AWS::IAM::AccessKey",
82
+ ...
83
+ },
84
+ "AWSEC2Instance" : {
85
+ "Type" : "AWS::EC2::Instance",
86
+ "Properties" : {
87
+ ...
88
+ },
89
+ "Metadata" : {
90
+ "hiera" : {
91
+ "class::access_key_id": { "Ref": "MyIAMKey" },
92
+ "class::secret_access_key": { "Fn::GetAtt" : [ "MyIAMKey" , "SecretAccessKey" ] }
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ and you include an entry:
99
+
100
+ cfstack/<stack name>/resources/AWSEC2Instance
101
+
102
+ in your hierarchy, you can query hiera for the key "class::access_key_id" or "class::secret_access_key"
103
+ and retrieve the attributes of the "MyIAMKey" resource created by CloudFormation.
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ =begin
2
+ Copyright 2013 FanDuel Ltd.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ require 'rubygems'
18
+ require 'rubygems/package_task'
19
+
20
+ spec = Gem::Specification.new do |gem|
21
+ gem.name = "hiera-cloudformation"
22
+ gem.version = '0.0.1'
23
+ gem.authors = ["Hugh Cole-Baker"]
24
+ gem.email = ["hugh@fanduel.com"]
25
+ gem.summary = %q{CloudFormation backend for Hiera}
26
+ gem.description = %q{Queries CloudFormation stack outputs or resource metadata for Hiera data}
27
+ gem.homepage = "https://github.com/fanduel/hiera-cloudformation"
28
+ gem.license = "Apache License (2.0)"
29
+
30
+ gem.files = Dir['{bin,lib,man,test,spec}/**/*', 'Rakefile', 'README*', 'LICENSE*']
31
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
32
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
33
+ gem.require_paths = ["lib"]
34
+
35
+ gem.add_development_dependency "rake"
36
+ gem.add_runtime_dependency "aws-sdk", "~> 1.11.2"
37
+ gem.add_runtime_dependency "timedcache", "~> 0.4.0"
38
+ gem.add_runtime_dependency "json", "~> 1.8.0"
39
+ end
40
+
41
+ Gem::PackageTask.new(spec) do |pkg|
42
+ pkg.need_tar = true
43
+ end
@@ -0,0 +1,136 @@
1
+ =begin
2
+ Copyright 2013 FanDuel Ltd.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ =end
16
+
17
+ class Hiera
18
+ module Backend
19
+ class Cloudformation_backend
20
+ TIMEOUT = 60 # 1 minute timeout for AWS API response caching
21
+
22
+ def initialize
23
+ begin
24
+ require 'aws'
25
+ require 'timedcache'
26
+ require 'json'
27
+ rescue LoadError
28
+ require 'rubygems'
29
+ require 'aws'
30
+ require 'timedcache'
31
+ require 'json'
32
+ end
33
+
34
+ if Config.include?(:cloudformation) && !Config[:cloudformation].nil? &&
35
+ Config[:cloudformation].include?(:access_key_id) && Config[:cloudformation].include?(:secret_access_key)
36
+ Hiera.debug("Found AWS access key from configuration")
37
+ AWS.config({
38
+ :access_key_id => Config[:cloudformation][:access_key_id],
39
+ :secret_access_key => Config[:cloudformation][:secret_access_key]
40
+ })
41
+ else
42
+ Hiera.debug("No configuration found, will fall back to env variables or IAM role")
43
+ end
44
+
45
+ @cf = AWS::CloudFormation.new
46
+ @output_cache = TimedCache.new
47
+ @resource_cache = TimedCache.new
48
+
49
+ Hiera.debug("Hiera cloudformation backend loaded")
50
+ end
51
+
52
+ def lookup(key, scope, order_override, resolution_type)
53
+ answer = nil
54
+
55
+ Backend.datasources(scope, order_override) do |elem|
56
+ case elem
57
+ when /cfstack\/([^\/]+)\/outputs/
58
+ Hiera.debug("Looking up #{key} as an output of stack #{$1}")
59
+ raw_answer = stack_output_query($1, key)
60
+ when /cfstack\/([^\/]+)\/resources\/([^\/]+)/
61
+ Hiera.debug("Looking up #{key} in metadata of stack #{$1} resource #{$2}")
62
+ raw_answer = stack_resource_query($1, $2, key)
63
+ else
64
+ Hiera.debug("#{elem} doesn't seem to be a CloudFormation hierarchy element")
65
+ next
66
+ end
67
+
68
+ next if raw_answer.nil?
69
+
70
+ new_answer = Backend.parse_answer(raw_answer, scope)
71
+
72
+ case resolution_type
73
+ when :array
74
+ raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
75
+ answer ||= []
76
+ answer << new_answer
77
+ when :hash
78
+ raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
79
+ answer ||= {}
80
+ answer = Backend.merge_answer(new_answer, answer)
81
+ else
82
+ answer = new_answer
83
+ break
84
+ end
85
+ end
86
+
87
+ return answer
88
+ end
89
+
90
+ def stack_output_query(stack_name, key)
91
+ outputs = @output_cache.get(stack_name)
92
+
93
+ if outputs.nil? then
94
+ Hiera.debug("#{stack_name} outputs not cached, fetching...")
95
+ begin
96
+ outputs = @cf.stacks[stack_name].outputs
97
+ rescue AWS::CloudFormation::Errors::ValidationError
98
+ Hiera.debug("Stack #{stack_name} outputs can't be retrieved")
99
+ outputs = [] # this is just a non-nil value to serve as marker in cache
100
+ end
101
+ @output_cache.put(stack_name, outputs, TIMEOUT)
102
+ end
103
+
104
+ output = outputs.select { |item| item.key == key }
105
+
106
+ return output.empty? ? nil : output.shift.value
107
+ end
108
+
109
+ def stack_resource_query(stack_name, resource_id, key)
110
+ metadata = @resource_cache.get({:stack => stack_name, :resource => resource_id})
111
+
112
+ if metadata.nil? then
113
+ Hiera.debug("#{stack_name} #{resource_id} metadata not cached, fetching")
114
+ begin
115
+ metadata = @cf.stacks[stack_name].resources[resource_id].metadata
116
+ rescue AWS::CloudFormation::Errors::ValidationError
117
+ # Stack or resource doesn't exist
118
+ Hiera.debug("Stack #{stack_name} resource #{resource_id} can't be retrieved")
119
+ metadata = "{}" # This is just a non-nil value to serve as marker in cache
120
+ end
121
+ @resource_cache.put({:stack => stack_name, :resource => resource_id}, metadata, TIMEOUT)
122
+ end
123
+
124
+ if metadata.respond_to?(:to_str) then
125
+ data = JSON.parse(metadata)
126
+
127
+ if data.include?('hiera') then
128
+ return data['hiera'][key] if data['hiera'].include?(key)
129
+ end
130
+ end
131
+
132
+ return nil
133
+ end
134
+ end
135
+ end
136
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hiera-cloudformation
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Hugh Cole-Baker
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2013-07-19 00:00:00 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ prerelease: false
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - &id005
20
+ - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ type: :development
24
+ version_requirements: *id001
25
+ - !ruby/object:Gem::Dependency
26
+ name: aws-sdk
27
+ prerelease: false
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.11.2
33
+ type: :runtime
34
+ version_requirements: *id002
35
+ - !ruby/object:Gem::Dependency
36
+ name: timedcache
37
+ prerelease: false
38
+ requirement: &id003 !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ version: 0.4.0
43
+ type: :runtime
44
+ version_requirements: *id003
45
+ - !ruby/object:Gem::Dependency
46
+ name: json
47
+ prerelease: false
48
+ requirement: &id004 !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: 1.8.0
53
+ type: :runtime
54
+ version_requirements: *id004
55
+ description: Queries CloudFormation stack outputs or resource metadata for Hiera data
56
+ email:
57
+ - hugh@fanduel.com
58
+ executables: []
59
+
60
+ extensions: []
61
+
62
+ extra_rdoc_files: []
63
+
64
+ files:
65
+ - lib/hiera/backend/cloudformation_backend.rb
66
+ - Rakefile
67
+ - README.md
68
+ - LICENSE.txt
69
+ homepage: https://github.com/fanduel/hiera-cloudformation
70
+ licenses:
71
+ - Apache License (2.0)
72
+ metadata: {}
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - *id005
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - *id005
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 2.0.5
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: CloudFormation backend for Hiera
92
+ test_files: []
93
+