terraform_inventory 0.1.0
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.md +22 -0
- data/README.md +11 -0
- data/Thorfile +40 -0
- data/bin/tinventory +3 -0
- data/lib/terraform_inventory.rb +5 -0
- data/lib/terraform_inventory/cli.rb +37 -0
- data/lib/terraform_inventory/exception.rb +15 -0
- data/lib/terraform_inventory/terraform_state.rb +80 -0
- data/lib/terraform_inventory/version.rb +3 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/terraform_inventory/terraform_state_spec.rb +81 -0
- data/templates/inventory.erb +6 -0
- data/terraform_inventory.gemspec +30 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 752923f38af589a6e9df6232e9892d9759bcff3a
|
4
|
+
data.tar.gz: d4748a2f30ff7599c733eb049e9657cc82b2eb82
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 941355c9930033fe7eb6218fc2df8c7aa11e2d2902fcf26feea113bcdc79948824b754ad7082abf99758313d19ac4826374bcf006066892e83d2b2f8b91883df
|
7
|
+
data.tar.gz: 725b48d538389aa18264e3ecc45a0488e83c1dedff1e08b6998972966e020274d86ad55965f16ffa86738bcb90814ce10d8936f47e31d8abd4bdbfdb5ec2a1f5
|
data/LICENSE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tyler Cross
|
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,11 @@
|
|
1
|
+
terraform_inventory
|
2
|
+
=========
|
3
|
+
|
4
|
+
Create an Ansible inventory file from a Terraform state file.
|
5
|
+
|
6
|
+
This tool was written so that Terraform can be used for only creating infrastructure and then Ansible for configuration management.
|
7
|
+
|
8
|
+
#### Example
|
9
|
+
```
|
10
|
+
tinventory --map=aws_instance.web:web aws_instance.web.1:db ./inventory
|
11
|
+
```
|
data/Thorfile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require "bundler"
|
4
|
+
require "thor/rake_compat"
|
5
|
+
|
6
|
+
class Default < Thor
|
7
|
+
include Thor::RakeCompat
|
8
|
+
Bundler::GemHelper.install_tasks
|
9
|
+
|
10
|
+
desc "build", "Build terraform_inventory-#{TerraformInventory::VERSION}.gem into the pkg directory"
|
11
|
+
def build
|
12
|
+
Rake::Task["build"].execute
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "install", "Build and install terraform_inventory-#{TerraformInventory::VERSION}.gem into system gems"
|
16
|
+
def install
|
17
|
+
Rake::Task["install"].execute
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "release", "Create tag v#{TerraformInventory::VERSION} and build and push terraform_inventory-#{TerraformInventory::VERSION}.gem to Rubygems"
|
21
|
+
def release
|
22
|
+
Rake::Task["release"].execute
|
23
|
+
end
|
24
|
+
|
25
|
+
desc "rubocop", "Run Rubocop on all Ruby files"
|
26
|
+
def rubocop
|
27
|
+
exec "rubocop"
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "spec", "Run tests."
|
31
|
+
def spec
|
32
|
+
exec "rspec spec"
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "check", "Lint, style, and test."
|
36
|
+
def check
|
37
|
+
invoke :rubocop
|
38
|
+
invoke :spec
|
39
|
+
end
|
40
|
+
end
|
data/bin/tinventory
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "terraform_inventory/terraform_state"
|
3
|
+
|
4
|
+
module TerraformInventory
|
5
|
+
class Cli < Thor
|
6
|
+
include Thor::Actions
|
7
|
+
|
8
|
+
source_root(File.join(File.dirname(File.dirname(__dir__)), "templates"))
|
9
|
+
|
10
|
+
desc "create", "Creates an Ansible inventory file from a Terraform state file"
|
11
|
+
option :map, {
|
12
|
+
required: true,
|
13
|
+
type: :hash,
|
14
|
+
banner: "resource_selector:host_group",
|
15
|
+
desc: "Maps between Terraform resource selector and Ansible host group."
|
16
|
+
}
|
17
|
+
option :state, {
|
18
|
+
banner: "<path to state file>",
|
19
|
+
desc: "Path to a Terraform state file.",
|
20
|
+
default: File.join(Dir.pwd, "terraform.tfstate")
|
21
|
+
}
|
22
|
+
def create(inventory_path)
|
23
|
+
state = TerraformState.new `terraform show -no-color #{options[:state]}`
|
24
|
+
|
25
|
+
@groups = state.group_by_host(options[:map])
|
26
|
+
@ungrouped_resources = @groups[:none] || []
|
27
|
+
@groups.delete(:none)
|
28
|
+
|
29
|
+
template(
|
30
|
+
"inventory.erb",
|
31
|
+
inventory_path
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
default_task :create
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module TerraformInventory
|
2
|
+
module Exception
|
3
|
+
class ResourceNotFoundException < StandardError
|
4
|
+
def initialize(resource_selector)
|
5
|
+
super("Resource not found using selector: '#{resource_selector}'")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class InvalidResourceSelectorException < StandardError
|
10
|
+
def initialize(resource_selector)
|
11
|
+
super("Invalid resource selector: '#{resource_selector}'")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "terraform_inventory/exception"
|
3
|
+
|
4
|
+
module TerraformInventory
|
5
|
+
class TerraformState
|
6
|
+
# Find a resource given a resource selector.
|
7
|
+
#
|
8
|
+
# A resource selector is composed of the following:
|
9
|
+
# resource_type (ex. aws_instance)
|
10
|
+
# resource_name (ex. web)
|
11
|
+
# resource_number (ex. 1)
|
12
|
+
#
|
13
|
+
# Here are a few examples of resource selectors:
|
14
|
+
# aws_instance.web.1 - selects a specific aws_instance resource named web
|
15
|
+
# aws_instance.web - selects all aws_instance resources named web
|
16
|
+
#
|
17
|
+
def find_resource(resource_selector)
|
18
|
+
resource_type, resource_name, resource_number = parse_resource_selector(resource_selector)
|
19
|
+
|
20
|
+
if resource_number
|
21
|
+
[@state[resource_type][resource_name][resource_number]]
|
22
|
+
else
|
23
|
+
@state[resource_type][resource_name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def group_by_host(resource_host_group_mapping)
|
28
|
+
resource_host_group_mapping.reduce({}) do |data, (resource_selector, host_group)| # rubocop:disable Style/EachWithObject
|
29
|
+
data[host_group.to_sym] ||= []
|
30
|
+
data[host_group.to_sym].concat find_resource(resource_selector)
|
31
|
+
|
32
|
+
data
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(stateText)
|
37
|
+
@state = split_state(
|
38
|
+
YAML.load(
|
39
|
+
stateText
|
40
|
+
.gsub(/\e\[(\d+)m/, "") # Hack to get rid of ASCII color codes in Terraform output
|
41
|
+
.gsub(/\s=\s/, ": ") # Use colons so we can parse as YAML
|
42
|
+
.gsub(/: \n/, ": ''\n") # Need to have quotes in order to parse as YAML
|
43
|
+
.split(/\n\n/) # Grab only text that is related to resource state (exclude outputs)
|
44
|
+
.first # We only want the first match
|
45
|
+
)
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def parse_resource_selector(resource_selector)
|
52
|
+
resource_selector_regex = /^(\w+)\.(\w+)(?:\.(\d+))?$/
|
53
|
+
matches = resource_selector_regex.match(resource_selector)
|
54
|
+
|
55
|
+
raise Exception::InvalidResourceSelectorException, resource_selector if matches.nil?
|
56
|
+
|
57
|
+
resource_type = matches[1]
|
58
|
+
resource_name = matches[2]
|
59
|
+
resource_number = if matches[3].nil?
|
60
|
+
nil
|
61
|
+
else
|
62
|
+
matches[3].to_i
|
63
|
+
end
|
64
|
+
|
65
|
+
[resource_type, resource_name, resource_number]
|
66
|
+
end
|
67
|
+
|
68
|
+
def split_state(state)
|
69
|
+
state.reduce({}) do |data, (resource_selector, resource_attributes)| # rubocop:disable Style/EachWithObject
|
70
|
+
resource_type, resource_name, _resource_number = parse_resource_selector(resource_selector)
|
71
|
+
|
72
|
+
data[resource_type] ||= {}
|
73
|
+
data[resource_type][resource_name] ||= []
|
74
|
+
data[resource_type][resource_name] << resource_attributes
|
75
|
+
|
76
|
+
data
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
# rubocop:disable Style/TrailingWhitespace
|
4
|
+
terraform_show_output = %(
|
5
|
+
aws_instance.web.0:
|
6
|
+
id = i-1299293e
|
7
|
+
ami = ami-e84d8480
|
8
|
+
availability_zone = us-east-1a
|
9
|
+
instance_type = m3.medium
|
10
|
+
key_name = some-key
|
11
|
+
private_dns = ip-10-63-149-246.ec2.internal
|
12
|
+
private_ip = 10.63.149.246
|
13
|
+
public_dns = ec2-54-167-40-22.compute-1.amazonaws.com
|
14
|
+
public_ip = 54.167.40.22
|
15
|
+
security_groups.# = 1
|
16
|
+
security_groups.0 = ansible-managed
|
17
|
+
subnet_id =
|
18
|
+
aws_instance.web.1:
|
19
|
+
id = i-b69b2b9a
|
20
|
+
ami = ami-e84d8480
|
21
|
+
availability_zone = us-east-1a
|
22
|
+
instance_type = m3.medium
|
23
|
+
key_name = some-key
|
24
|
+
private_dns = ip-10-179-191-171.ec2.internal
|
25
|
+
private_ip = 10.179.191.171
|
26
|
+
public_dns = ec2-54-83-95-180.compute-1.amazonaws.com
|
27
|
+
public_ip = 54.83.95.180
|
28
|
+
security_groups.# = 1
|
29
|
+
security_groups.0 = ansible-managed
|
30
|
+
subnet_id =
|
31
|
+
heroku_app.default:
|
32
|
+
id = some-heroku-app
|
33
|
+
config_vars.# = 1
|
34
|
+
config_vars.0.PORT = 80
|
35
|
+
git_url = git@heroku.com:some-heroku-app.git
|
36
|
+
heroku_hostname = some-heroku-app.herokuapp.com
|
37
|
+
name = some-heroku-app
|
38
|
+
region = us
|
39
|
+
stack = cedar
|
40
|
+
web_url = http://some-heroku-app.herokuapp.com/
|
41
|
+
|
42
|
+
Outputs:
|
43
|
+
|
44
|
+
some-output = ADDRESS<54.167.40.22,54.83.95.180> USER<ubuntu>
|
45
|
+
)
|
46
|
+
|
47
|
+
describe TerraformInventory::TerraformState do
|
48
|
+
let(:state) { TerraformInventory::TerraformState.new terraform_show_output }
|
49
|
+
|
50
|
+
describe "#find_resource" do
|
51
|
+
context "using a resource_selector without resource_number" do
|
52
|
+
let(:resources) { state.find_resource("aws_instance.web") }
|
53
|
+
|
54
|
+
it "returns the correct resources" do
|
55
|
+
expect(resources.size).to eq(2)
|
56
|
+
expect(resources.first["id"]).to eq("i-1299293e")
|
57
|
+
expect(resources[1]["id"]).to eq("i-b69b2b9a")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "using a resource_selector with a resource_number" do
|
62
|
+
let(:resources) { state.find_resource("aws_instance.web.1") }
|
63
|
+
|
64
|
+
it "returns the correct resources" do
|
65
|
+
expect(resources.size).to eq(1)
|
66
|
+
expect(resources.first["id"]).to eq("i-b69b2b9a")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#group_by_host" do
|
72
|
+
let(:groups) { state.group_by_host Hash["aws_instance.web", "web"] }
|
73
|
+
|
74
|
+
it "properly groups resources" do
|
75
|
+
expect(groups.keys.size).to eq(1)
|
76
|
+
expect(groups.keys.first).to eq(:web)
|
77
|
+
expect(groups[:web].first["id"]).to eq("i-1299293e")
|
78
|
+
expect(groups[:web][1]["id"]).to eq("i-b69b2b9a")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "terraform_inventory/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "terraform_inventory"
|
8
|
+
spec.version = TerraformInventory::VERSION
|
9
|
+
spec.authors = ["Tyler Cross"]
|
10
|
+
spec.email = ["tcross@bandwidth.com"]
|
11
|
+
spec.description = %q(Map Terraform state to an Ansible inventory file)
|
12
|
+
spec.summary = spec.description
|
13
|
+
spec.homepage = "https://github.com/bandwidthcom/terraform-inventory"
|
14
|
+
spec.licenses = %w[MIT]
|
15
|
+
|
16
|
+
spec.executables = %w[tinventory]
|
17
|
+
|
18
|
+
spec.files = %w[LICENSE.md README.md Thorfile terraform_inventory.gemspec]
|
19
|
+
spec.files += Dir.glob("bin/**/*")
|
20
|
+
spec.files += Dir.glob("lib/**/*.rb")
|
21
|
+
spec.files += Dir.glob("spec/**/*")
|
22
|
+
spec.files += Dir.glob("templates/**/*")
|
23
|
+
|
24
|
+
spec.test_files = Dir.glob("spec/**/*")
|
25
|
+
|
26
|
+
spec.require_paths = %w[lib]
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
29
|
+
spec.add_development_dependency "thor"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: terraform_inventory
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tyler Cross
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-13 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Map Terraform state to an Ansible inventory file
|
42
|
+
email:
|
43
|
+
- tcross@bandwidth.com
|
44
|
+
executables:
|
45
|
+
- tinventory
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- LICENSE.md
|
50
|
+
- README.md
|
51
|
+
- Thorfile
|
52
|
+
- bin/tinventory
|
53
|
+
- lib/terraform_inventory.rb
|
54
|
+
- lib/terraform_inventory/cli.rb
|
55
|
+
- lib/terraform_inventory/exception.rb
|
56
|
+
- lib/terraform_inventory/terraform_state.rb
|
57
|
+
- lib/terraform_inventory/version.rb
|
58
|
+
- spec/spec_helper.rb
|
59
|
+
- spec/terraform_inventory/terraform_state_spec.rb
|
60
|
+
- templates/inventory.erb
|
61
|
+
- terraform_inventory.gemspec
|
62
|
+
homepage: https://github.com/bandwidthcom/terraform-inventory
|
63
|
+
licenses:
|
64
|
+
- MIT
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.2.2
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: Map Terraform state to an Ansible inventory file
|
86
|
+
test_files:
|
87
|
+
- spec/terraform_inventory/terraform_state_spec.rb
|
88
|
+
- spec/spec_helper.rb
|