carinadigital-hiera-cloudformation 0.0.1.3
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/LICENSE.txt +22 -0
- data/README.md +112 -0
- data/Rakefile +43 -0
- data/lib/hiera/backend/cloudformation_backend.rb +145 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 921c7ea7efc3b03457499dd609ddfb3f57ec8d9d
|
4
|
+
data.tar.gz: e2c167dbf96e4aa198f46e61cb5c805ce3319d02
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4d078b8a89eca0ab8357bde1f1333dcc2f10bc008decd132e881633b3106815f26daf49d141effab3e738400924b619851b60359de2e99fb8db7db187bff1567
|
7
|
+
data.tar.gz: 51e97025b3e3bc337cb6cd5d83d5a52912004dee7e22801302db6224e1d4d02b8b09e1ca21fc151868025e4a762389be9878684f0fdd69ee5cd55e21ed6fa5a6
|
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,112 @@
|
|
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
|
+
You can also add a region setting to the `:cloudformation` section in
|
33
|
+
hiera.yaml:
|
34
|
+
|
35
|
+
:cloudformation:
|
36
|
+
:region: us-west-2
|
37
|
+
|
38
|
+
If not specified, the region setting will be looked up from the `AWS_REGION`
|
39
|
+
environment variable, or `us-west-2` if not set.
|
40
|
+
|
41
|
+
To use this backend you also need to add entries to your "hierarchy" in your hiera.yaml file.
|
42
|
+
If you put an entry of this form in your hierarchy:
|
43
|
+
|
44
|
+
cfstack/<stack name>/outputs
|
45
|
+
|
46
|
+
the backend will request the outputs of the stack named `<stack name>` and search for an output
|
47
|
+
named the same as the requested key. If an output named the same as the key is found, the backend
|
48
|
+
will return the value of that output (as a string).
|
49
|
+
If you put an entry of this form in your hierarchy:
|
50
|
+
|
51
|
+
cfstack/<stack name>/resources/<logical resource ID>
|
52
|
+
|
53
|
+
the backend will request the JSON metadata of the logical resource identified by the given ID,
|
54
|
+
in the named stack, and look for a JSON object under the top-level key "hiera" in the metadata.
|
55
|
+
If a JSON object is present under the top-level key "hiera", the backend will search for the
|
56
|
+
requested key in that JSON object, and return the value of the key if present.
|
57
|
+
|
58
|
+
The recommended way of constructing your hierarchy so that Puppet-managed nodes can retrieve
|
59
|
+
data relating to the CloudFormation stack they're part of, is to create facts on the Puppet nodes
|
60
|
+
describing what CloudFormation stack they're part of and what logical resource ID corresponds to
|
61
|
+
their instance. Then, assuming you have two facts "cloudformation_stack" and "cloudformation_resource"
|
62
|
+
reported on your node, you can add these entries to the "hierarchy" in hiera.yaml:
|
63
|
+
|
64
|
+
- cfstack/%{cloudformation_stack}/outputs
|
65
|
+
- cfstack/%{cloudformation_stack}/resources/%{cloudformation_resource}
|
66
|
+
|
67
|
+
and Hiera will replace the `%{...}` placeholders with the value of the facts on the node, enabling
|
68
|
+
the node to look up information about the stack it "belongs" to.
|
69
|
+
|
70
|
+
For example, if this snippet is included in your CloudFormation template, and you include an entry
|
71
|
+
|
72
|
+
cfstack/<stack name>/outputs
|
73
|
+
|
74
|
+
in your hierarchy, you can look up the key "example_key" in Hiera and get the value of the logical
|
75
|
+
resource identified by "AWSEC2Instance" (for example, this might be an EC2 instance, so Hiera
|
76
|
+
would return the instance ID)
|
77
|
+
|
78
|
+
"Outputs" : {
|
79
|
+
"example_key": {
|
80
|
+
"Description" : "AWSEC2Instance is the logical resource ID of the instance that was created",
|
81
|
+
"Value" : { "Ref": "AWSEC2Instance" }
|
82
|
+
}
|
83
|
+
}
|
84
|
+
|
85
|
+
As another example, if you define an EC2 instance with the following snippet in your CloudFormation
|
86
|
+
template, including some metadata:
|
87
|
+
|
88
|
+
"Resources" : {
|
89
|
+
"MyIAMKey" : {
|
90
|
+
"Type" : "AWS::IAM::AccessKey",
|
91
|
+
...
|
92
|
+
},
|
93
|
+
"AWSEC2Instance" : {
|
94
|
+
"Type" : "AWS::EC2::Instance",
|
95
|
+
"Properties" : {
|
96
|
+
...
|
97
|
+
},
|
98
|
+
"Metadata" : {
|
99
|
+
"hiera" : {
|
100
|
+
"class::access_key_id": { "Ref": "MyIAMKey" },
|
101
|
+
"class::secret_access_key": { "Fn::GetAtt" : [ "MyIAMKey" , "SecretAccessKey" ] }
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
and you include an entry:
|
108
|
+
|
109
|
+
cfstack/<stack name>/resources/AWSEC2Instance
|
110
|
+
|
111
|
+
in your hierarchy, you can query hiera for the key "class::access_key_id" or "class::secret_access_key"
|
112
|
+
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 = "carinadigital-hiera-cloudformation"
|
22
|
+
gem.version = '0.0.1.3'
|
23
|
+
gem.authors = ["carinadigital"]
|
24
|
+
gem.email = ["carinadigital@gmail.com"]
|
25
|
+
gem.summary = %q{CloudFormation backend for Hiera}
|
26
|
+
gem.description = %q{Forked from hiera-cloudformation. Queries CloudFormation stack outputs or resource metadata for Hiera data.}
|
27
|
+
gem.homepage = "https://github.com/carinadigital/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.33.0"
|
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,145 @@
|
|
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
|
+
aws_config = {}
|
36
|
+
if Config[:cloudformation].include?(:access_key_id) && Config[:cloudformation].include?(:secret_access_key)
|
37
|
+
Hiera.debug("Found AWS access key from configuration")
|
38
|
+
aws_config[:access_key_id] = Config[:cloudformation][:access_key_id]
|
39
|
+
aws_config[:secret_access_key] = Config[:cloudformation][:secret_access_key]
|
40
|
+
end
|
41
|
+
if Config[:cloudformation].include?(:region)
|
42
|
+
Hiera.debug("Found AWS region from configuration")
|
43
|
+
aws_config[:region] = Config[:cloudformation][:region]
|
44
|
+
end
|
45
|
+
if aws_config.length == 0
|
46
|
+
Hiera.debug("CloudFormation configuration is found, but none of the settings are recognized.")
|
47
|
+
else
|
48
|
+
AWS.config(aws_config)
|
49
|
+
end
|
50
|
+
else
|
51
|
+
Hiera.debug("No configuration found, will fall back to env variables or IAM role")
|
52
|
+
end
|
53
|
+
|
54
|
+
@cf = AWS::CloudFormation.new
|
55
|
+
@output_cache = TimedCache.new
|
56
|
+
@resource_cache = TimedCache.new
|
57
|
+
|
58
|
+
Hiera.debug("Hiera cloudformation backend loaded")
|
59
|
+
end
|
60
|
+
|
61
|
+
def lookup(key, scope, order_override, resolution_type)
|
62
|
+
answer = nil
|
63
|
+
|
64
|
+
Backend.datasources(scope, order_override) do |elem|
|
65
|
+
case elem
|
66
|
+
when /cfstack\/([^\/]+)\/outputs/
|
67
|
+
Hiera.debug("Looking up #{key} as an output of stack #{$1}")
|
68
|
+
raw_answer = stack_output_query($1, key)
|
69
|
+
when /cfstack\/([^\/]+)\/resources\/([^\/]+)/
|
70
|
+
Hiera.debug("Looking up #{key} in metadata of stack #{$1} resource #{$2}")
|
71
|
+
raw_answer = stack_resource_query($1, $2, key)
|
72
|
+
else
|
73
|
+
Hiera.debug("#{elem} doesn't seem to be a CloudFormation hierarchy element")
|
74
|
+
next
|
75
|
+
end
|
76
|
+
|
77
|
+
next if raw_answer.nil?
|
78
|
+
|
79
|
+
new_answer = Backend.parse_answer(raw_answer, scope)
|
80
|
+
|
81
|
+
case resolution_type
|
82
|
+
when :array
|
83
|
+
raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
|
84
|
+
answer ||= []
|
85
|
+
answer << new_answer
|
86
|
+
when :hash
|
87
|
+
raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
|
88
|
+
answer ||= {}
|
89
|
+
answer = Backend.merge_answer(new_answer, answer)
|
90
|
+
else
|
91
|
+
answer = new_answer
|
92
|
+
break
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
return answer
|
97
|
+
end
|
98
|
+
|
99
|
+
def stack_output_query(stack_name, key)
|
100
|
+
outputs = @output_cache.get(stack_name)
|
101
|
+
|
102
|
+
if outputs.nil? then
|
103
|
+
Hiera.debug("#{stack_name} outputs not cached, fetching...")
|
104
|
+
begin
|
105
|
+
outputs = @cf.stacks[stack_name].outputs
|
106
|
+
rescue AWS::CloudFormation::Errors::ValidationError
|
107
|
+
Hiera.debug("Stack #{stack_name} outputs can't be retrieved")
|
108
|
+
outputs = [] # this is just a non-nil value to serve as marker in cache
|
109
|
+
end
|
110
|
+
@output_cache.put(stack_name, outputs, TIMEOUT)
|
111
|
+
end
|
112
|
+
|
113
|
+
output = outputs.select { |item| item.key == key }
|
114
|
+
|
115
|
+
return output.empty? ? nil : output.shift.value
|
116
|
+
end
|
117
|
+
|
118
|
+
def stack_resource_query(stack_name, resource_id, key)
|
119
|
+
metadata = @resource_cache.get({:stack => stack_name, :resource => resource_id})
|
120
|
+
|
121
|
+
if metadata.nil? then
|
122
|
+
Hiera.debug("#{stack_name} #{resource_id} metadata not cached, fetching")
|
123
|
+
begin
|
124
|
+
metadata = @cf.stacks[stack_name].resources[resource_id].metadata
|
125
|
+
rescue AWS::CloudFormation::Errors::ValidationError
|
126
|
+
# Stack or resource doesn't exist
|
127
|
+
Hiera.debug("Stack #{stack_name} resource #{resource_id} can't be retrieved")
|
128
|
+
metadata = "{}" # This is just a non-nil value to serve as marker in cache
|
129
|
+
end
|
130
|
+
@resource_cache.put({:stack => stack_name, :resource => resource_id}, metadata, TIMEOUT)
|
131
|
+
end
|
132
|
+
|
133
|
+
if metadata.respond_to?(:to_str) then
|
134
|
+
data = JSON.parse(metadata)
|
135
|
+
|
136
|
+
if data.include?('hiera') then
|
137
|
+
return data['hiera'][key] if data['hiera'].include?(key)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
return nil
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: carinadigital-hiera-cloudformation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- carinadigital
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.33.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.33.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: timedcache
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.4.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.4.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.8.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.8.0
|
69
|
+
description: Forked from hiera-cloudformation. Queries CloudFormation stack outputs
|
70
|
+
or resource metadata for Hiera data.
|
71
|
+
email:
|
72
|
+
- carinadigital@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- lib/hiera/backend/cloudformation_backend.rb
|
78
|
+
- Rakefile
|
79
|
+
- README.md
|
80
|
+
- LICENSE.txt
|
81
|
+
homepage: https://github.com/carinadigital/hiera-cloudformation
|
82
|
+
licenses:
|
83
|
+
- Apache License (2.0)
|
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.0.7
|
102
|
+
signing_key:
|
103
|
+
specification_version: 4
|
104
|
+
summary: CloudFormation backend for Hiera
|
105
|
+
test_files: []
|