terraforming 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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"