terraforming 0.0.5 → 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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/README.md +12 -2
  4. data/lib/terraforming.rb +4 -0
  5. data/lib/terraforming/cli.rb +33 -20
  6. data/lib/terraforming/resource/db_parameter_group.rb +5 -5
  7. data/lib/terraforming/resource/db_security_group.rb +5 -5
  8. data/lib/terraforming/resource/db_subnet_group.rb +5 -5
  9. data/lib/terraforming/resource/ec2.rb +5 -5
  10. data/lib/terraforming/resource/elb.rb +9 -5
  11. data/lib/terraforming/resource/iam_group.rb +5 -5
  12. data/lib/terraforming/resource/iam_group_policy.rb +6 -6
  13. data/lib/terraforming/resource/iam_instance_profile.rb +52 -0
  14. data/lib/terraforming/resource/iam_policy.rb +6 -6
  15. data/lib/terraforming/resource/iam_role.rb +53 -0
  16. data/lib/terraforming/resource/iam_role_policy.rb +69 -0
  17. data/lib/terraforming/resource/iam_user.rb +5 -5
  18. data/lib/terraforming/resource/iam_user_policy.rb +6 -6
  19. data/lib/terraforming/resource/network_acl.rb +10 -5
  20. data/lib/terraforming/resource/rds.rb +5 -5
  21. data/lib/terraforming/resource/route53_record.rb +5 -5
  22. data/lib/terraforming/resource/route53_zone.rb +5 -5
  23. data/lib/terraforming/resource/s3.rb +5 -5
  24. data/lib/terraforming/resource/security_group.rb +86 -7
  25. data/lib/terraforming/resource/subnet.rb +5 -5
  26. data/lib/terraforming/resource/vpc.rb +5 -5
  27. data/lib/terraforming/template/tf/elb.erb +4 -1
  28. data/lib/terraforming/template/tf/iam_group_policy.erb +1 -1
  29. data/lib/terraforming/template/tf/iam_instance_profile.erb +8 -0
  30. data/lib/terraforming/template/tf/iam_policy.erb +1 -1
  31. data/lib/terraforming/template/tf/iam_role.erb +10 -0
  32. data/lib/terraforming/template/tf/iam_role_policy.erb +10 -0
  33. data/lib/terraforming/template/tf/iam_user_policy.erb +1 -1
  34. data/lib/terraforming/template/tf/network_acl.erb +2 -1
  35. data/lib/terraforming/template/tf/security_group.erb +11 -2
  36. data/lib/terraforming/util.rb +21 -6
  37. data/lib/terraforming/version.rb +1 -1
  38. data/terraforming.gemspec +1 -1
  39. metadata +11 -12
@@ -3,12 +3,12 @@ module Terraforming
3
3
  class Route53Zone
4
4
  include Terraforming::Util
5
5
 
6
- def self.tf(client = Aws::Route53::Client.new)
6
+ def self.tf(client: Aws::Route53::Client.new)
7
7
  self.new(client).tf
8
8
  end
9
9
 
10
- def self.tfstate(client = Aws::Route53::Client.new)
11
- self.new(client).tfstate
10
+ def self.tfstate(client: Aws::Route53::Client.new, tfstate_base: nil)
11
+ self.new(client).tfstate(tfstate_base)
12
12
  end
13
13
 
14
14
  def initialize(client)
@@ -19,7 +19,7 @@ module Terraforming
19
19
  apply_template(@client, "tf/route53_zone")
20
20
  end
21
21
 
22
- def tfstate
22
+ def tfstate(tfstate_base)
23
23
  resources = hosted_zones.inject({}) do |result, hosted_zone|
24
24
  zone_id = zone_id_of(hosted_zone)
25
25
 
@@ -41,7 +41,7 @@ module Terraforming
41
41
  result
42
42
  end
43
43
 
44
- generate_tfstate(resources)
44
+ generate_tfstate(resources, tfstate_base)
45
45
  end
46
46
 
47
47
  private
@@ -3,12 +3,12 @@ module Terraforming
3
3
  class S3
4
4
  include Terraforming::Util
5
5
 
6
- def self.tf(client = Aws::S3::Client.new)
6
+ def self.tf(client: Aws::S3::Client.new)
7
7
  self.new(client).tf
8
8
  end
9
9
 
10
- def self.tfstate(client = Aws::S3::Client.new)
11
- self.new(client).tfstate
10
+ def self.tfstate(client: Aws::S3::Client.new, tfstate_base: nil)
11
+ self.new(client).tfstate(tfstate_base)
12
12
  end
13
13
 
14
14
  def initialize(client)
@@ -19,7 +19,7 @@ module Terraforming
19
19
  apply_template(@client, "tf/s3")
20
20
  end
21
21
 
22
- def tfstate
22
+ def tfstate(tfstate_base)
23
23
  resources = buckets.inject({}) do |result, bucket|
24
24
  result["aws_s3_bucket.#{module_name_of(bucket)}"] = {
25
25
  "type" => "aws_s3_bucket",
@@ -36,7 +36,7 @@ module Terraforming
36
36
  result
37
37
  end
38
38
 
39
- generate_tfstate(resources)
39
+ generate_tfstate(resources, tfstate_base)
40
40
  end
41
41
 
42
42
  private
@@ -3,12 +3,12 @@ module Terraforming
3
3
  class SecurityGroup
4
4
  include Terraforming::Util
5
5
 
6
- def self.tf(client = Aws::EC2::Client.new)
6
+ def self.tf(client: Aws::EC2::Client.new)
7
7
  self.new(client).tf
8
8
  end
9
9
 
10
- def self.tfstate(client = Aws::EC2::Client.new)
11
- self.new(client).tfstate
10
+ def self.tfstate(client: Aws::EC2::Client.new, tfstate_base: nil)
11
+ self.new(client).tfstate(tfstate_base)
12
12
  end
13
13
 
14
14
  def initialize(client)
@@ -19,17 +19,20 @@ module Terraforming
19
19
  apply_template(@client, "tf/security_group")
20
20
  end
21
21
 
22
- def tfstate
22
+ def tfstate(tfstate_base)
23
23
  resources = security_groups.inject({}) do |result, security_group|
24
24
  attributes = {
25
25
  "description" => security_group.description,
26
- "egress.#" => security_group.ip_permissions_egress.length.to_s,
27
26
  "id" => security_group.group_id,
28
- "ingress.#" => security_group.ip_permissions.length.to_s,
29
27
  "name" => security_group.group_name,
30
28
  "owner_id" => security_group.owner_id,
31
29
  "vpc_id" => security_group.vpc_id || "",
32
30
  }
31
+
32
+ attributes.merge!(tags_attributes_of(security_group))
33
+ attributes.merge!(egress_attributes_of(security_group))
34
+ attributes.merge!(ingress_attributes_of(security_group))
35
+
33
36
  result["aws_security_group.#{module_name_of(security_group)}"] = {
34
37
  "type" => "aws_security_group",
35
38
  "primary" => {
@@ -41,18 +44,94 @@ module Terraforming
41
44
  result
42
45
  end
43
46
 
44
- generate_tfstate(resources)
47
+ generate_tfstate(resources, tfstate_base)
45
48
  end
46
49
 
47
50
  private
48
51
 
52
+ def ingress_attributes_of(security_group)
53
+ attributes = { "ingress.#" => security_group.ip_permissions.length.to_s }
54
+
55
+ security_group.ip_permissions.each do |permission|
56
+ attributes.merge!(permission_attributes_of(security_group, permission, "ingress"))
57
+ end
58
+
59
+ attributes
60
+ end
61
+
62
+ def egress_attributes_of(security_group)
63
+ attributes = { "egress.#" => security_group.ip_permissions_egress.length.to_s }
64
+
65
+ security_group.ip_permissions_egress.each do |permission|
66
+ attributes.merge!(permission_attributes_of(security_group, permission, "egress"))
67
+ end
68
+
69
+ attributes
70
+ end
71
+
72
+ def group_hashcode_of(group)
73
+ Zlib.crc32(group)
74
+ end
75
+
49
76
  def module_name_of(security_group)
50
77
  normalize_module_name("#{security_group.group_id}-#{security_group.group_name}")
51
78
  end
52
79
 
80
+ def permission_attributes_of(security_group, permission, type)
81
+ hashcode = permission_hashcode_of(security_group, permission)
82
+ security_groups = security_groups_in(permission).reject { |group_id| group_id == security_group.group_id }
83
+
84
+ attributes = {
85
+ "#{type}.#{hashcode}.from_port" => (permission.from_port || 0).to_s,
86
+ "#{type}.#{hashcode}.to_port" => (permission.to_port || 0).to_s,
87
+ "#{type}.#{hashcode}.protocol" => permission.ip_protocol,
88
+ "#{type}.#{hashcode}.cidr_blocks.#" => permission.ip_ranges.length.to_s,
89
+ "#{type}.#{hashcode}.security_groups.#" => security_groups.length.to_s,
90
+ "#{type}.#{hashcode}.self" => self_referenced_permission?(security_group, permission).to_s,
91
+ }
92
+
93
+ permission.ip_ranges.each_with_index do |range, index|
94
+ attributes["#{type}.#{hashcode}.cidr_blocks.#{index}"] = range.cidr_ip
95
+ end
96
+
97
+ security_groups.each do |group|
98
+ attributes["#{type}.#{hashcode}.security_groups.#{group_hashcode_of(group)}"] = group
99
+ end
100
+
101
+ attributes
102
+ end
103
+
104
+ def permission_hashcode_of(security_group, permission)
105
+ string =
106
+ "#{permission.from_port || 0}-" <<
107
+ "#{permission.to_port || 0}-" <<
108
+ "#{permission.ip_protocol}-" <<
109
+ "#{self_referenced_permission?(security_group, permission).to_s}-"
110
+
111
+ permission.ip_ranges.each { |range| string << "#{range.cidr_ip}-" }
112
+ security_groups_in(permission).each { |group| string << "#{group}-" }
113
+
114
+ Zlib.crc32(string)
115
+ end
116
+
117
+ def self_referenced_permission?(security_group, permission)
118
+ security_groups_in(permission).include?(security_group.group_id)
119
+ end
120
+
53
121
  def security_groups
54
122
  @client.describe_security_groups.security_groups
55
123
  end
124
+
125
+ def security_groups_in(permission)
126
+ permission.user_id_group_pairs.map { |range| range.group_id }
127
+ end
128
+
129
+ def tags_attributes_of(security_group)
130
+ tags = security_group.tags
131
+ attributes = { "tags.#" => tags.length.to_s }
132
+ tags.each { |tag| attributes["tags.#{tag.key}"] = tag.value }
133
+ attributes
134
+ end
56
135
  end
57
136
  end
58
137
  end
@@ -3,12 +3,12 @@ module Terraforming
3
3
  class Subnet
4
4
  include Terraforming::Util
5
5
 
6
- def self.tf(client = Aws::EC2::Client.new)
6
+ def self.tf(client: Aws::EC2::Client.new)
7
7
  self.new(client).tf
8
8
  end
9
9
 
10
- def self.tfstate(client = Aws::EC2::Client.new)
11
- self.new(client).tfstate
10
+ def self.tfstate(client: Aws::EC2::Client.new, tfstate_base: nil)
11
+ self.new(client).tfstate(tfstate_base)
12
12
  end
13
13
 
14
14
  def initialize(client)
@@ -19,7 +19,7 @@ module Terraforming
19
19
  apply_template(@client, "tf/subnet")
20
20
  end
21
21
 
22
- def tfstate
22
+ def tfstate(tfstate_base)
23
23
  resources = subnets.inject({}) do |result, subnet|
24
24
  attributes = {
25
25
  "availability_zone" => subnet.availability_zone,
@@ -40,7 +40,7 @@ module Terraforming
40
40
  result
41
41
  end
42
42
 
43
- generate_tfstate(resources)
43
+ generate_tfstate(resources, tfstate_base)
44
44
  end
45
45
 
46
46
  private
@@ -3,12 +3,12 @@ module Terraforming
3
3
  class VPC
4
4
  include Terraforming::Util
5
5
 
6
- def self.tf(client = Aws::EC2::Client.new)
6
+ def self.tf(client: Aws::EC2::Client.new)
7
7
  self.new(client).tf
8
8
  end
9
9
 
10
- def self.tfstate(client = Aws::EC2::Client.new)
11
- self.new(client).tfstate
10
+ def self.tfstate(client: Aws::EC2::Client.new, tfstate_base: nil)
11
+ self.new(client).tfstate(tfstate_base)
12
12
  end
13
13
 
14
14
  def initialize(client)
@@ -19,7 +19,7 @@ module Terraforming
19
19
  apply_template(@client, "tf/vpc")
20
20
  end
21
21
 
22
- def tfstate
22
+ def tfstate(tfstate_base)
23
23
  resources = vpcs.inject({}) do |result, vpc|
24
24
  attributes = {
25
25
  "cidr_block" => vpc.cidr_block,
@@ -40,7 +40,7 @@ module Terraforming
40
40
  result
41
41
  end
42
42
 
43
- generate_tfstate(resources)
43
+ generate_tfstate(resources, tfstate_base)
44
44
  end
45
45
 
46
46
  private
@@ -2,8 +2,11 @@
2
2
  <%- load_balancer_attributes = load_balancer_attributes_of(load_balancer) -%>
3
3
  resource "aws_elb" "<%= module_name_of(load_balancer) %>" {
4
4
  name = "<%= load_balancer.load_balancer_name %>"
5
- availability_zones = <%= load_balancer.availability_zones.inspect %>
5
+ <%- if vpc_elb?(load_balancer) -%>
6
6
  subnets = <%= load_balancer.subnets.inspect %>
7
+ <%- else -%>
8
+ availability_zones = <%= load_balancer.availability_zones.inspect %>
9
+ <%- end -%>
7
10
  security_groups = <%= load_balancer.security_groups.inspect %>
8
11
  instances = <%= load_balancer.instances.map { |instance| instance.instance_id }.inspect %>
9
12
  cross_zone_load_balancing = <%= load_balancer_attributes.cross_zone_load_balancing.enabled %>
@@ -3,7 +3,7 @@ resource "aws_iam_group_policy" "<%= policy.policy_name %>" {
3
3
  name = "<%= policy.policy_name %>"
4
4
  group = "<%= policy.group_name %>"
5
5
  policy = <<POLICY
6
- <%= CGI.unescape(policy.policy_document).strip %>
6
+ <%= prettify_policy(policy.policy_document) %>
7
7
  POLICY
8
8
  }
9
9
 
@@ -0,0 +1,8 @@
1
+ <% iam_instance_profiles.each do |profile| -%>
2
+ resource "aws_iam_instance_profile" "<%= profile.instance_profile_name %>" {
3
+ name = "<%= profile.instance_profile_name %>"
4
+ path = "<%= profile.path %>"
5
+ roles = <%= profile.roles.map { |role| role.role_name }.inspect %>
6
+ }
7
+
8
+ <% end -%>
@@ -4,7 +4,7 @@ resource "aws_iam_policy" "<%= policy.policy_name %>" {
4
4
  name = "<%= policy.policy_name %>"
5
5
  path = "<%= policy.path %>"
6
6
  policy = <<POLICY
7
- <%= CGI.unescape(version.document).strip %>
7
+ <%= prettify_policy(version.document) %>
8
8
  POLICY
9
9
  }
10
10
 
@@ -0,0 +1,10 @@
1
+ <% iam_roles.each do |role| -%>
2
+ resource "aws_iam_role" "<%= role.role_name %>" {
3
+ name = "<%= role.role_name %>"
4
+ path = "<%= role.path %>"
5
+ assume_role_policy = <<POLICY
6
+ <%= prettify_policy(role.assume_role_policy_document) %>
7
+ POLICY
8
+ }
9
+
10
+ <% end -%>
@@ -0,0 +1,10 @@
1
+ <% iam_role_policies.each do |policy| -%>
2
+ resource "aws_iam_role_policy" "<%= policy.policy_name %>" {
3
+ name = "<%= policy.policy_name %>"
4
+ role = "<%= policy.role_name %>"
5
+ policy = <<POLICY
6
+ <%= prettify_policy(policy.policy_document) %>
7
+ POLICY
8
+ }
9
+
10
+ <% end -%>
@@ -3,7 +3,7 @@ resource "aws_iam_user_policy" "<%= policy.policy_name %>" {
3
3
  name = "<%= policy.policy_name %>"
4
4
  user = "<%= policy.user_name %>"
5
5
  policy = <<POLICY
6
- <%= CGI.unescape(policy.policy_document).strip %>
6
+ <%= prettify_policy(policy.policy_document) %>
7
7
  POLICY
8
8
  }
9
9
 
@@ -1,6 +1,7 @@
1
1
  <% network_acls.each do |network_acl| -%>
2
2
  resource "aws_network_acl" "<%= module_name_of(network_acl) %>" {
3
- vpc_id = "<%= network_acl.vpc_id %>"
3
+ vpc_id = "<%= network_acl.vpc_id %>"
4
+ subnet_ids = <%= subnet_ids_of(network_acl).inspect %>
4
5
 
5
6
  <% ingresses_of(network_acl).each do |ingress| -%>
6
7
  ingress {
@@ -5,6 +5,7 @@ resource "aws_security_group" "<%= module_name_of(security_group) %>" {
5
5
  vpc_id = "<%= security_group.vpc_id || '' %>"
6
6
 
7
7
  <% security_group.ip_permissions.each do |permission| -%>
8
+ <%- security_groups = security_groups_in(permission).reject { |group_id| group_id == security_group.group_id } -%>
8
9
  ingress {
9
10
  from_port = <%= permission.from_port || 0 %>
10
11
  to_port = <%= permission.to_port || 0 %>
@@ -13,7 +14,11 @@ resource "aws_security_group" "<%= module_name_of(security_group) %>" {
13
14
  cidr_blocks = <%= permission.ip_ranges.map { |range| range.cidr_ip }.inspect %>
14
15
  <%- end -%>
15
16
  <%- if permission.user_id_group_pairs.length > 0 -%>
16
- security_groups = <%= permission.user_id_group_pairs.map { |range| range.group_id }.inspect %>
17
+ <%- self_referenced = self_referenced_permission?(security_group, permission) -%>
18
+ <%- unless self_referenced -%>
19
+ security_groups = <%= security_groups.inspect %>
20
+ <%- end -%>
21
+ self = <%= self_referenced %>
17
22
  <%- end -%>
18
23
  }
19
24
 
@@ -28,7 +33,11 @@ resource "aws_security_group" "<%= module_name_of(security_group) %>" {
28
33
  cidr_blocks = <%= permission.ip_ranges.map { |range| range.cidr_ip }.inspect %>
29
34
  <%- end -%>
30
35
  <%- if permission.user_id_group_pairs.length > 0 -%>
31
- security_groups = <%= permission.user_id_group_pairs.map { |range| range.group_id }.inspect %>
36
+ <%- self_referenced = self_referenced_permission?(security_group, permission) -%>
37
+ <%- unless self_referenced -%>
38
+ security_groups = <%= security_groups_in(permission).inspect %>
39
+ <%- end -%>
40
+ self = <%= self_referenced %>
32
41
  <%- end -%>
33
42
  }
34
43
 
@@ -17,22 +17,37 @@ module Terraforming
17
17
  File.join(File.expand_path(File.dirname(__FILE__)), "template", template_name) << ".erb"
18
18
  end
19
19
 
20
- def generate_tfstate(resources)
21
- tfstate = {
20
+ def generate_tfstate(resources, tfstate_base = nil)
21
+ tfstate = tfstate_base || tfstate_skeleton
22
+ tfstate["serial"] = tfstate["serial"] + 1
23
+ tfstate["modules"][0]["resources"] = tfstate["modules"][0]["resources"].merge(resources)
24
+ JSON.pretty_generate(tfstate)
25
+ end
26
+
27
+ def prettify_policy(policy_document, breakline = false)
28
+ json = JSON.pretty_generate(JSON.parse(CGI.unescape(policy_document)))
29
+
30
+ if breakline
31
+ json[-1] != "\n" ? json << "\n" : json
32
+ else
33
+ json.strip
34
+ end
35
+ end
36
+
37
+ def tfstate_skeleton
38
+ {
22
39
  "version" => 1,
23
- "serial" => 1,
40
+ "serial" => 0,
24
41
  "modules" => [
25
42
  {
26
43
  "path" => [
27
44
  "root"
28
45
  ],
29
46
  "outputs" => {},
30
- "resources" => resources
47
+ "resources" => {},
31
48
  }
32
49
  ]
33
50
  }
34
-
35
- JSON.pretty_generate(tfstate)
36
51
  end
37
52
  end
38
53
  end
@@ -1,3 +1,3 @@
1
1
  module Terraforming
2
- VERSION = "0.0.5"
2
+ VERSION = "0.1.0"
3
3
  end
data/terraforming.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "aws-sdk", "~> 2.0", ">= 2.0.36"
22
+ spec.add_dependency "aws-sdk", "~> 2.1.0"
23
23
  spec.add_dependency "oj"
24
24
  spec.add_dependency "ox"
25
25
  spec.add_dependency "thor"