tfrb 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.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rspec +3 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +7 -0
  7. data/Gemfile +3 -0
  8. data/Gemfile.lock +57 -0
  9. data/LICENSE +202 -0
  10. data/README.md +35 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +8 -0
  14. data/exe/tfrb +5 -0
  15. data/lib/tfrb/base.rb +198 -0
  16. data/lib/tfrb/block.rb +44 -0
  17. data/lib/tfrb/cli.rb +68 -0
  18. data/lib/tfrb/config.rb +15 -0
  19. data/lib/tfrb/provider/aws.rb +10 -0
  20. data/lib/tfrb/provider.rb +22 -0
  21. data/lib/tfrb/resource/aws_db_instance.rb +32 -0
  22. data/lib/tfrb/resource/aws_db_subnet_group.rb +28 -0
  23. data/lib/tfrb/resource/aws_dynamodb_table.rb +26 -0
  24. data/lib/tfrb/resource/aws_ebs_volume.rb +32 -0
  25. data/lib/tfrb/resource/aws_elasticache_replication_group.rb +30 -0
  26. data/lib/tfrb/resource/aws_elasticache_subnet_group.rb +24 -0
  27. data/lib/tfrb/resource/aws_iam_policy.rb +142 -0
  28. data/lib/tfrb/resource/aws_iam_role.rb +39 -0
  29. data/lib/tfrb/resource/aws_iam_role_policy_attachment.rb +27 -0
  30. data/lib/tfrb/resource/aws_instance.rb +46 -0
  31. data/lib/tfrb/resource/aws_kms_key.rb +26 -0
  32. data/lib/tfrb/resource/aws_s3_bucket.rb +20 -0
  33. data/lib/tfrb/resource/aws_security_group.rb +39 -0
  34. data/lib/tfrb/resource/aws_storagegateway_cache.rb +21 -0
  35. data/lib/tfrb/resource/aws_storagegateway_gateway.rb +24 -0
  36. data/lib/tfrb/resource/aws_storagegateway_nfs_file_share.rb +27 -0
  37. data/lib/tfrb/resource/aws_subnet.rb +28 -0
  38. data/lib/tfrb/resource/aws_volume_attachment.rb +30 -0
  39. data/lib/tfrb/resource/aws_vpc.rb +24 -0
  40. data/lib/tfrb/resource.rb +124 -0
  41. data/lib/tfrb/version.rb +3 -0
  42. data/lib/tfrb.rb +1 -0
  43. data/tfrb.gemspec +32 -0
  44. metadata +187 -0
@@ -0,0 +1,24 @@
1
+ require 'aws-sdk'
2
+
3
+ module Tfrb::Resource::AwsStoragegatewayGateway
4
+ extend Tfrb::Resource
5
+
6
+ def self.preload(base, environment_name, resource_type, new_resources)
7
+ new_resources.each do |resource_name, resource|
8
+ set_default(resource, 'gateway_name', resource_name.gsub('_', ' '))
9
+ end
10
+ end
11
+
12
+ def self.load(base, environment_name, resource_type, new_resources)
13
+ new_resources.each do |resource_name, resource|
14
+ client = ::Aws::StorageGateway::Client.new(aws_options(base, resource))
15
+ response = client.list_gateways({
16
+ limit: 100
17
+ })
18
+ if response.gateways && gateway = response.gateways.find { |g| g.gateway_name == resource_name }
19
+ id = gateway.gateway_arn
20
+ import!(base, resource_type, resource_name, id)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,27 @@
1
+ require 'aws-sdk'
2
+
3
+ module Tfrb::Resource::AwsStoragegatewayNfsFileShare
4
+ extend Tfrb::Resource
5
+
6
+ def self.load(base, environment_name, resource_type, new_resources)
7
+ new_resources.each do |resource_name, resource|
8
+ client = ::Aws::StorageGateway::Client.new(aws_options(base, resource))
9
+ gateway_arn = resolve_tfvar(base, resource_type, resource_name, 'gateway_arn')
10
+ location_arn = resolve_tfvar(base, resource_type, resource_name, 'location_arn')
11
+ next if gateway_arn.empty? || location_arn.empty?
12
+ response = client.list_file_shares({
13
+ gateway_arn: gateway_arn,
14
+ limit: 100
15
+ })
16
+ if response.file_share_info_list && nfs_file_shares = response.file_share_info_list.select { |s| s.file_share_type == 'NFS' }.map { |s| s.file_share_arn }
17
+ response = client.describe_nfs_file_shares({
18
+ file_share_arn_list: nfs_file_shares
19
+ })
20
+ if response.nfs_file_share_info_list && file_share = response.nfs_file_share_info_list.find { |s| s.location_arn == location_arn }
21
+ id = file_share.file_share_arn
22
+ import!(base, resource_type, resource_name, id)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'aws-sdk'
2
+
3
+ module Tfrb::Resource::AwsSubnet
4
+ extend Tfrb::Resource
5
+
6
+ def self.load(base, environment_name, resource_type, new_resources)
7
+ new_resources.each do |resource_name, resource|
8
+ client = ::Aws::EC2::Client.new(aws_options(base, resource))
9
+ response = client.describe_subnets({
10
+ filters: [
11
+ {
12
+ name: "tag:Name",
13
+ values: [resource_name],
14
+ },
15
+ {
16
+ name: "cidr-block",
17
+ values: [resource['cidr_block']],
18
+ },
19
+ ],
20
+ dry_run: false,
21
+ })
22
+ if response.subnets.size >= 1
23
+ id = response.subnets.first.subnet_id
24
+ import!(base, resource_type, resource_name, id)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,30 @@
1
+ require 'aws-sdk'
2
+
3
+ module Tfrb::Resource::AwsVolumeAttachment
4
+ extend Tfrb::Resource
5
+
6
+ def self.preload(base, environment_name, resource_type, new_resources)
7
+ new_resources.each do |resource_name, resource|
8
+ set_default(resource, 'volume_id', "${aws_ebs_volume.#{resource_name}.id}")
9
+ end
10
+ end
11
+
12
+ def self.load(base, environment_name, resource_type, new_resources)
13
+ new_resources.each do |resource_name, resource|
14
+ # client = ::Aws::EC2::Client.new(aws_options(base, resource))
15
+ # response = client.describe_volumes({
16
+ # volume_ids: [
17
+ # resolve_tfvar(base, resource_type, resource_name, 'volume_id')
18
+ # ],
19
+ # })
20
+ # if response.volumes && response.volumes.size >= 1
21
+ # if response.volumes.first.attachments && response.volumes.first.attachments.size >= 1
22
+ # if response.volumes.first.attachments.find { |a| a.instance_id == resolve_tfvar(base, resource_type, resource_name, 'instance_id') }
23
+ # id = response.volumes.first.volume_id
24
+ # import!(base, resource_type, resource_name, id)
25
+ # end
26
+ # end
27
+ # end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ require 'aws-sdk'
2
+
3
+ module Tfrb::Resource::AwsVpc
4
+ extend Tfrb::Resource
5
+
6
+ def self.load(base, environment_name, resource_type, new_resources)
7
+ new_resources.each do |resource_name, resource|
8
+ client = ::Aws::EC2::Client.new(aws_options(base, resource))
9
+ response = client.describe_vpcs({
10
+ filters: [
11
+ {
12
+ name: "tag:Name",
13
+ values: [resource_name],
14
+ },
15
+ ],
16
+ dry_run: false,
17
+ })
18
+ if response.vpcs.size >= 1
19
+ id = response.vpcs.first.vpc_id
20
+ import!(base, resource_type, resource_name, id)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,124 @@
1
+ require 'mixlib/shellout'
2
+
3
+ module Tfrb::Resource
4
+ class << self
5
+ def extended(mod)
6
+ Tfrb::Config[:extra_modules].each do |extra_module|
7
+ mod.send(:include, extra_module)
8
+ mod.send(:extend, extra_module)
9
+ end
10
+ end
11
+
12
+ def load_helpers!
13
+ # Include helper methods from resources for use in ERB/YAML
14
+ Tfrb::Resource::constants.each do |constant|
15
+ mod = Kernel.const_get("Tfrb::Resource::#{constant}")
16
+ Tfrb::Base.send(:include, mod) if mod.is_a?(Module)
17
+ end
18
+ end
19
+
20
+ def preload(tfrb)
21
+ tfrb.environments.each do |environment_name, environment|
22
+ if environment['resource']
23
+ environment['resource'].each do |resource_type, resources|
24
+ # Call resource preload methods
25
+ if custom_resource = get_custom_resource(resource_type)
26
+ if custom_resource.singleton_methods.include?(:preload)
27
+ custom_resource.preload(tfrb, environment_name, resource_type, resources)
28
+ end
29
+ end
30
+
31
+ # Inject overrides from Config
32
+ if Tfrb::Config[:overrides].has_key?('resource') && Tfrb::Config[:overrides]['resource'].has_key?(resource_type)
33
+ custom_resource.instance_exec(tfrb, environment_name, resource_type, resources, &Tfrb::Config[:overrides]['resource'][resource_type])
34
+ end
35
+
36
+ # Preload resources from local state wherever possible
37
+ unless tfrb.s3_state?
38
+ tfrb.state[resource_type] = {} unless tfrb.state.has_key?(resource_type)
39
+ resources.each do |resource_name, resource|
40
+ if ::File.exist?(::File.join(tfrb.temp_path, 'terraform.tfstate')) && !tfrb.state[resource_type].has_key?(resource_name)
41
+ printf "\033[1m%s.%s: Loading state...\033[0m\n", resource_type, resource_name
42
+ state = get_state(tfrb, resource_type, resource_name)
43
+ tfrb.state[resource_type][resource_name] = state if state && state.keys.size > 0
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def load(tfrb)
53
+ tfrb.environments.each do |environment_name, environment|
54
+ if environment['resource']
55
+ environment['resource'].each do |resource_type, resources|
56
+ if custom_resource = get_custom_resource(resource_type)
57
+ new_resources = resources.select { |resource_name, _| !tfrb.state.has_key?(resource_type) || !tfrb.state[resource_type].has_key?(resource_name) }
58
+ if custom_resource.singleton_methods.include?(:load) && new_resources.size > 0
59
+ custom_resource.load(tfrb, environment_name, resource_type, new_resources)
60
+ end
61
+ else
62
+ printf "\033[31mWarning: no custom resource definition found for %s\n consider creating one or you may not receive the desired result!\033[0m\n", resource_type, resource_type
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def get_state(base, resource, name)
70
+ tf_state = Mixlib::ShellOut.new('terraform', 'state', 'show', "#{resource}.#{name}", cwd: base.temp_path)
71
+ tf_state.run_command
72
+ tf_state.error!
73
+ tf_state.stdout.split("\n").each_with_object({}) { |line, hash| key, value = line.split(' = '); hash[key.strip] = value }
74
+ end
75
+
76
+ def get_custom_resource(resource_type)
77
+ custom_resource_name = self.constants.find { |c| resource_type == c.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase }
78
+ if custom_resource_name
79
+ Kernel.const_get("Tfrb::Resource::#{custom_resource_name}")
80
+ end
81
+ end
82
+ end
83
+
84
+ def resolve_tfvar(base, resource_type, resource_name, var)
85
+ base.environments.each do |environment_name, environment|
86
+ if environment.has_key?('resource') &&
87
+ environment['resource'].has_key?(resource_type) &&
88
+ environment['resource'][resource_type].has_key?(resource_name) &&
89
+ environment['resource'][resource_type][resource_name].has_key?(var)
90
+ return environment['resource'][resource_type][resource_name][var].gsub(/\$\{([^}]+)\}/) { |match| match.sub(/\$\{([^}]+)\}/, '\1').split('.').inject(base.state) { |state, key| state[key] if state } }
91
+ end
92
+ end
93
+ nil
94
+ end
95
+
96
+ def set_default(entity, key, value)
97
+ entity[key] = value unless entity.has_key?(key)
98
+ end
99
+
100
+ def aws_options(base, resource)
101
+ if resource.has_key?('provider')
102
+ aws_providers = base.environments.find { |_, e|
103
+ e['provider'] &&
104
+ e['provider']['aws'] &&
105
+ e['provider']['aws']['alias'] &&
106
+ e['provider']['aws']['alias'] == resource['provider'].sub(/^aws\./, '')
107
+ }
108
+ end
109
+ aws_providers = base.environments.find { |_, e| e['provider'] && e['provider']['aws'] } unless aws_providers
110
+ if aws_provider = aws_providers[1]['provider']['aws']
111
+ {
112
+ region: aws_provider['region'],
113
+ access_key_id: aws_provider['access_key'],
114
+ secret_access_key: aws_provider['secret_key']
115
+ }
116
+ end
117
+ end
118
+
119
+ def import!(base, resource, name, id)
120
+ base.import!(resource, name, id)
121
+ end
122
+ end
123
+
124
+ Dir[File.join(File.dirname(__FILE__), 'resource', '*.rb')].each { |file| require_relative file }
@@ -0,0 +1,3 @@
1
+ module Tfrb
2
+ VERSION = '0.1.0'
3
+ end
data/lib/tfrb.rb ADDED
@@ -0,0 +1 @@
1
+ module Tfrb; end
data/tfrb.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'tfrb/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'tfrb'
7
+ spec.version = Tfrb::VERSION
8
+ spec.authors = ["Matt Kasa\n"]
9
+ spec.email = ['matt.kasa@granicus.com']
10
+ spec.licenses = ['Apache-2.0']
11
+
12
+ spec.summary = %q{Ruby DSL for terraform}
13
+ spec.homepage = 'https://github.com/Granicus/tfrb'
14
+
15
+ # Specify which files should be added to the gem when it is released.
16
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = 'exe'
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 2.0'
25
+ spec.add_development_dependency 'rake', '~> 10.0'
26
+ spec.add_development_dependency 'rspec', '~> 3.0'
27
+ spec.add_development_dependency 'pry', '~> 0'
28
+
29
+ spec.add_runtime_dependency 'aws-sdk', '~> 2'
30
+ spec.add_runtime_dependency 'mixlib-shellout', '~> 0'
31
+ spec.add_runtime_dependency 'thor', '~> 0'
32
+ end
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tfrb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - 'Matt Kasa
8
+
9
+ '
10
+ autorequire:
11
+ bindir: exe
12
+ cert_chain: []
13
+ date: 2019-05-01 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '2.0'
29
+ - !ruby/object:Gem::Dependency
30
+ name: rake
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '10.0'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '10.0'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rspec
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.0'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '3.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: pry
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: aws-sdk
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - "~>"
76
+ - !ruby/object:Gem::Version
77
+ version: '2'
78
+ type: :runtime
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - "~>"
83
+ - !ruby/object:Gem::Version
84
+ version: '2'
85
+ - !ruby/object:Gem::Dependency
86
+ name: mixlib-shellout
87
+ requirement: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - "~>"
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :runtime
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - "~>"
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ - !ruby/object:Gem::Dependency
100
+ name: thor
101
+ requirement: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - "~>"
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ type: :runtime
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - "~>"
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ description:
114
+ email:
115
+ - matt.kasa@granicus.com
116
+ executables:
117
+ - tfrb
118
+ extensions: []
119
+ extra_rdoc_files: []
120
+ files:
121
+ - ".gitignore"
122
+ - ".rspec"
123
+ - ".ruby-gemset"
124
+ - ".ruby-version"
125
+ - ".travis.yml"
126
+ - Gemfile
127
+ - Gemfile.lock
128
+ - LICENSE
129
+ - README.md
130
+ - Rakefile
131
+ - bin/console
132
+ - bin/setup
133
+ - exe/tfrb
134
+ - lib/tfrb.rb
135
+ - lib/tfrb/base.rb
136
+ - lib/tfrb/block.rb
137
+ - lib/tfrb/cli.rb
138
+ - lib/tfrb/config.rb
139
+ - lib/tfrb/provider.rb
140
+ - lib/tfrb/provider/aws.rb
141
+ - lib/tfrb/resource.rb
142
+ - lib/tfrb/resource/aws_db_instance.rb
143
+ - lib/tfrb/resource/aws_db_subnet_group.rb
144
+ - lib/tfrb/resource/aws_dynamodb_table.rb
145
+ - lib/tfrb/resource/aws_ebs_volume.rb
146
+ - lib/tfrb/resource/aws_elasticache_replication_group.rb
147
+ - lib/tfrb/resource/aws_elasticache_subnet_group.rb
148
+ - lib/tfrb/resource/aws_iam_policy.rb
149
+ - lib/tfrb/resource/aws_iam_role.rb
150
+ - lib/tfrb/resource/aws_iam_role_policy_attachment.rb
151
+ - lib/tfrb/resource/aws_instance.rb
152
+ - lib/tfrb/resource/aws_kms_key.rb
153
+ - lib/tfrb/resource/aws_s3_bucket.rb
154
+ - lib/tfrb/resource/aws_security_group.rb
155
+ - lib/tfrb/resource/aws_storagegateway_cache.rb
156
+ - lib/tfrb/resource/aws_storagegateway_gateway.rb
157
+ - lib/tfrb/resource/aws_storagegateway_nfs_file_share.rb
158
+ - lib/tfrb/resource/aws_subnet.rb
159
+ - lib/tfrb/resource/aws_volume_attachment.rb
160
+ - lib/tfrb/resource/aws_vpc.rb
161
+ - lib/tfrb/version.rb
162
+ - tfrb.gemspec
163
+ homepage: https://github.com/Granicus/tfrb
164
+ licenses:
165
+ - Apache-2.0
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project:
183
+ rubygems_version: 2.7.6
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: Ruby DSL for terraform
187
+ test_files: []