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 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,3 @@
1
+ #!/usr/bin/env ruby
2
+ require "terraform_inventory/cli"
3
+ TerraformInventory::Cli.start
@@ -0,0 +1,5 @@
1
+ require "terraform_inventory/version"
2
+
3
+ module TerraformInventory
4
+ autoload :TerraformState, "terraform_inventory/terraform_state"
5
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module TerraformInventory
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "rubygems"
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
5
+
6
+ require "terraform_inventory"
7
+ require "rspec"
@@ -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,6 @@
1
+ <% for resource in @ungrouped_resources %><%= resource["public_ip"] %><% end %>
2
+ <% for group_name, resources in @groups %>
3
+ [<%= group_name %>]
4
+ <% for resource in resources %><%= resource["public_ip"] %>
5
+ <% end %>
6
+ <% 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