geoengineer 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/README.md +18 -8
  5. data/lib/geoengineer/cli/geo_cli.rb +4 -1
  6. data/lib/geoengineer/environment.rb +1 -1
  7. data/lib/geoengineer/resources/aws_cloudtrail.rb +20 -0
  8. data/lib/geoengineer/resources/aws_cloudwatch_event_rule.rb +26 -0
  9. data/lib/geoengineer/resources/aws_cloudwatch_event_target.rb +50 -0
  10. data/lib/geoengineer/resources/aws_db_instance.rb +5 -1
  11. data/lib/geoengineer/resources/aws_dynamodb_table.rb +24 -0
  12. data/lib/geoengineer/resources/aws_elasticache_cluster.rb +22 -9
  13. data/lib/geoengineer/resources/aws_iam_policy.rb +1 -0
  14. data/lib/geoengineer/resources/aws_iam_role_policy.rb +63 -0
  15. data/lib/geoengineer/resources/aws_kinesis_stream.rb +40 -0
  16. data/lib/geoengineer/resources/aws_lambda_alias.rb +18 -3
  17. data/lib/geoengineer/resources/aws_lambda_function.rb +11 -0
  18. data/lib/geoengineer/resources/aws_lambda_permission.rb +9 -0
  19. data/lib/geoengineer/resources/aws_load_balancer_backend_server_policy.rb +48 -0
  20. data/lib/geoengineer/resources/aws_load_balancer_policy.rb +43 -0
  21. data/lib/geoengineer/resources/aws_network_acl_rule.rb +27 -2
  22. data/lib/geoengineer/resources/aws_redshift_cluster.rb +10 -4
  23. data/lib/geoengineer/resources/aws_route.rb +2 -0
  24. data/lib/geoengineer/resources/aws_vpn_gateway_attachment.rb +8 -0
  25. data/lib/geoengineer/sub_resource.rb +13 -3
  26. data/lib/geoengineer/utils/aws_clients.rb +18 -2
  27. data/lib/geoengineer/utils/has_projects.rb +3 -1
  28. data/lib/geoengineer/utils/has_validations.rb +7 -0
  29. data/lib/geoengineer/version.rb +1 -1
  30. data/spec/resource_spec.rb +14 -0
  31. data/spec/resources/aws_cloudtrail_spec.rb +35 -0
  32. data/spec/resources/aws_cloudwatch_event_rule_spec.rb +33 -0
  33. data/spec/resources/aws_cloudwatch_event_target_spec.rb +54 -0
  34. data/spec/resources/aws_dynamodb_table_spec.rb +24 -0
  35. data/spec/resources/aws_iam_role_spec.rb +21 -18
  36. data/spec/resources/aws_iam_rule_policy_spec.rb +49 -0
  37. data/spec/resources/{aws_iam_user.rb → aws_iam_user_spec.rb} +9 -6
  38. data/spec/resources/aws_kinesis_stream_spec.rb +52 -0
  39. data/spec/resources/aws_lambda_alias_spec.rb +22 -3
  40. data/spec/resources/aws_load_balancer_backend_server_policy_spec.rb +42 -0
  41. data/spec/resources/aws_load_balancer_policy_spec.rb +44 -0
  42. data/spec/resources/aws_network_acl_rule_spec.rb +29 -1
  43. data/spec/resources/{aws_ses_receipt_rule_set.rb → aws_ses_receipt_rule_set_spec.rb} +13 -6
  44. data/spec/resources/{aws_ses_receipt_rule.rb → aws_ses_receipt_rule_spec.rb} +6 -5
  45. data/spec/resources/{aws_sns_topic_subscription.rb → aws_sns_topic_subscription_spec.rb} +0 -0
  46. data/spec/resources/aws_subnet_spec.rb +2 -2
  47. data/spec/resources/aws_vpc_dhcp_options_association_spec.rb +2 -2
  48. data/spec/resources/aws_vpc_spec.rb +2 -2
  49. data/spec/sub_resource_spec.rb +12 -0
  50. data/spec/utils/has_subresources_spec.rb +9 -0
  51. data/spec/utils/has_validations_spec.rb +23 -3
  52. metadata +34 -10
  53. metadata.gz.sig +0 -0
@@ -18,6 +18,17 @@ class GeoEngineer::Resources::AwsLambdaFunction < GeoEngineer::Resource
18
18
 
19
19
  after :initialize, -> { _terraform_id -> { function_name } }
20
20
 
21
+ def to_terraform_state
22
+ tfstate = super
23
+ tfstate[:primary][:attributes] = {
24
+ 'function_name' => function_name,
25
+ 'publish' => (publish || "false"),
26
+ 's3_bucket' => (s3_bucket || ""),
27
+ 's3_key' => (s3_key || "")
28
+ }
29
+ tfstate
30
+ end
31
+
21
32
  def support_tags?
22
33
  false
23
34
  end
@@ -12,6 +12,15 @@ class GeoEngineer::Resources::AwsLambdaPermission < GeoEngineer::Resource
12
12
  false
13
13
  end
14
14
 
15
+ def to_terraform_state
16
+ tfstate = super
17
+ tfstate[:primary][:attributes] = {
18
+ 'function_name' => self.function_name,
19
+ 'statement_id' => self.statement_id
20
+ }
21
+ tfstate
22
+ end
23
+
15
24
  def self._fetch_functions
16
25
  AwsClients
17
26
  .lambda
@@ -0,0 +1,48 @@
1
+ ########################################################################
2
+ # AwsLoadBalancerBackendServerPolicy is the
3
+ # +aws_load_balancer_backend_server_policy+ terrform resource,
4
+ #
5
+ # {https://www.terraform.io/docs/providers/aws/r/aws_load_balancer_backend_server_policy.html
6
+ # Terraform Docs}
7
+ ########################################################################
8
+ class GeoEngineer::Resources::AwsLoadBalancerBackendServerPolicy < GeoEngineer::Resource
9
+ validate -> { validate_required_attributes([:instance_port, :load_balancer_name, :policy_names]) }
10
+
11
+ after :initialize, -> {
12
+ _terraform_id -> { "#{load_balancer_name}:#{instance_port}" }
13
+ }
14
+
15
+ def to_terraform_state
16
+ tfstate = super
17
+ tfstate[:primary][:attributes] = {
18
+ 'load_balancer_name' => load_balancer_name,
19
+ 'instance_port' => instance_port.to_s
20
+ }
21
+ tfstate
22
+ end
23
+
24
+ def support_tags?
25
+ false
26
+ end
27
+
28
+ def self._fetch_remote_resources
29
+ AwsClients
30
+ .elb
31
+ .describe_load_balancers
32
+ .load_balancer_descriptions
33
+ .map { |load_balancer| _extract_backend_servers(load_balancer.to_h) }
34
+ .flatten
35
+ .compact
36
+ end
37
+
38
+ def self._extract_backend_servers(load_balancer)
39
+ load_balancer[:backend_server_descriptions].map do |server|
40
+ server.merge(
41
+ {
42
+ load_balancer_name: load_balancer[:load_balancer_name],
43
+ _terraform_id: "#{load_balancer[:load_balancer_name]}:#{server[:instance_port]}"
44
+ }
45
+ )
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,43 @@
1
+ ########################################################################
2
+ # AwsLoadBalancerPolicy is the +aws_load_balancer_policy+ terrform resource,
3
+ #
4
+ # {https://www.terraform.io/docs/providers/aws/r/aws_load_balancer_policy.html Terraform Docs}
5
+ ########################################################################
6
+ class GeoEngineer::Resources::AwsLoadBalancerPolicy < GeoEngineer::Resource
7
+ validate -> {
8
+ validate_required_attributes([:policy_name, :policy_type_name, :load_balancer_name])
9
+ }
10
+
11
+ after :initialize, -> { _terraform_id -> { "#{load_balancer_name}:#{policy_name}" } }
12
+
13
+ def support_tags?
14
+ false
15
+ end
16
+
17
+ def self._fetch_remote_resources
18
+ AwsClients
19
+ .elb
20
+ .describe_load_balancers
21
+ .load_balancer_descriptions
22
+ .map(&:to_h)
23
+ .map { |load_balancer| _policies_for_load_balancer(load_balancer) }
24
+ .flatten
25
+ .compact
26
+ end
27
+
28
+ def self._policies_for_load_balancer(load_balancer)
29
+ AwsClients
30
+ .elb
31
+ .describe_load_balancer_policies({ load_balancer_name: load_balancer[:load_balancer_name] })
32
+ .policy_descriptions
33
+ .map(&:to_h)
34
+ .map do |policy|
35
+ policy.merge(
36
+ {
37
+ _terraform_id: "#{load_balancer[:load_balancer_name]}:#{policy[:policy_name]}",
38
+ load_balancer_name: load_balancer[:load_balancer_name]
39
+ }
40
+ )
41
+ end
42
+ end
43
+ end
@@ -16,12 +16,22 @@ class GeoEngineer::Resources::AwsNetworkAclRule < GeoEngineer::Resource
16
16
  "#{network_acl_id}-",
17
17
  "#{rule_number}-",
18
18
  "#{egress}-",
19
- "#{protocol == 'all' ? '-1' : protocol}-"
19
+ "#{self.class._number_for_protocol(protocol)}-"
20
20
  ]
21
21
  "nacl-#{Crc32.hashcode(terraform_id_components.join)}"
22
22
  }
23
23
  }
24
24
 
25
+ def to_terraform_state
26
+ tfstate = super
27
+ tfstate[:primary][:attributes] = {
28
+ 'network_acl_id' => network_acl_id,
29
+ 'rule_number' => rule_number,
30
+ 'egress' => egress
31
+ }
32
+ tfstate
33
+ end
34
+
25
35
  def support_tags?
26
36
  false
27
37
  end
@@ -34,6 +44,7 @@ class GeoEngineer::Resources::AwsNetworkAclRule < GeoEngineer::Resource
34
44
  .select { |network_acl| !network_acl[:entries].empty? }
35
45
  .map { |network_acl| _generate_rules(network_acl) }
36
46
  .flatten
47
+ .reject { |rule| rule[:rule_number] == 32_767 }
37
48
  end
38
49
 
39
50
  def self._generate_rules(network_acl)
@@ -42,9 +53,23 @@ class GeoEngineer::Resources::AwsNetworkAclRule < GeoEngineer::Resource
42
53
  "#{network_acl[:network_acl_id]}-",
43
54
  "#{rule[:rule_number]}-",
44
55
  "#{rule[:egress]}-",
45
- "#{rule[:protocol] == 'all' ? '-1' : rule[:protocol]}-"
56
+ "#{_number_for_protocol(rule[:protocol])}-"
46
57
  ]
47
58
  rule.merge({ _terraform_id: "nacl-#{Crc32.hashcode(terraform_id_components.join)}" })
48
59
  end
49
60
  end
61
+
62
+ def self._number_for_protocol(protocol)
63
+ protocols = {
64
+ ah: 51,
65
+ esp: 50,
66
+ udp: 17,
67
+ tcp: 6,
68
+ icmp: 1,
69
+ all: -1
70
+ }
71
+ return unless protocol
72
+ return protocol if protocols.values.map(&:to_s).include?(protocol.to_s)
73
+ protocols[protocol.to_s.downcase.to_sym]
74
+ end
50
75
  end
@@ -5,10 +5,11 @@
5
5
  # {https://www.terraform.io/docs/providers/aws/r/redshift_cluster.html#cluster_version}
6
6
  ########################################################################
7
7
  class GeoEngineer::Resources::AwsRedshiftCluster < GeoEngineer::Resource
8
+ validate -> { validate_required_attributes([:cluster_identifier, :node_type]) }
8
9
  validate -> {
9
- validate_required_attributes(
10
- [:cluster_identifier, :node_type, :master_password, :master_username]
11
- )
10
+ if new? && !snapshot_identifier
11
+ validate_required_attributes([:master_password, :master_username])
12
+ end
12
13
  }
13
14
  validate -> {
14
15
  validate_required_attributes([:number_of_nodes]) if self.cluster_type == 'multi-node'
@@ -18,6 +19,11 @@ class GeoEngineer::Resources::AwsRedshiftCluster < GeoEngineer::Resource
18
19
  after :initialize, -> { _terraform_id -> { cluster_identifier } }
19
20
 
20
21
  def self._fetch_remote_resources
21
- AwsClients.redshift.describe_clusters.clusters.map(&:to_h)
22
+ AwsClients
23
+ .redshift
24
+ .describe_clusters
25
+ .clusters
26
+ .map(&:to_h)
27
+ .map { |cluster| cluster.merge({ _terraform_id: cluster[:cluster_identifier] }) }
22
28
  end
23
29
  end
@@ -30,6 +30,8 @@ class GeoEngineer::Resources::AwsRoute < GeoEngineer::Resource
30
30
  .map { |route_table| _extract_routes(route_table) }
31
31
  .flatten
32
32
  .compact
33
+ .reject { |route| route[:gateway_id] == "local" }
34
+ .reject { |route| route.key?(:destination_prefix_list_id) }
33
35
  end
34
36
 
35
37
  def self._extract_routes(route_table)
@@ -11,6 +11,14 @@ class GeoEngineer::Resources::AwsVpnGatewayAttachment < GeoEngineer::Resource
11
11
  }
12
12
  after :initialize, -> { _geo_id -> { "#{vpc_id}::#{vpn_gateway_id}" } }
13
13
 
14
+ def to_terraform_state
15
+ tfstate = super
16
+ tfstate[:primary][:attributes] = {
17
+ 'vpn_gateway_id' => vpn_gateway_id
18
+ }
19
+ tfstate
20
+ end
21
+
14
22
  def support_tags?
15
23
  false
16
24
  end
@@ -7,6 +7,7 @@
7
7
  ########################################################################
8
8
  class GeoEngineer::SubResource
9
9
  include HasAttributes
10
+ include HasSubResources
10
11
 
11
12
  attr_reader :type
12
13
 
@@ -20,16 +21,25 @@ class GeoEngineer::SubResource
20
21
  @resource._terraform_id
21
22
  end
22
23
 
24
+ ## Terraform methods
23
25
  def to_terraform
24
26
  sb = [" #{@type} { "]
27
+
25
28
  sb.concat terraform_attributes.map { |k, v|
26
- " #{k.to_s.inspect} = #{v.inspect}"
29
+ " #{k.to_s.inspect} = #{v.inspect}"
27
30
  }
28
- sb << " }"
31
+
32
+ sb.concat subresources.map(&:to_terraform)
33
+ sb << " }"
29
34
  sb.join("\n")
30
35
  end
31
36
 
32
37
  def to_terraform_json
33
- [@type, terraform_attributes]
38
+ json = terraform_attributes
39
+ subresources.map(&:to_terraform_json).each do |k, v|
40
+ json[k] ||= []
41
+ json[k] << v
42
+ end
43
+ [@type, json]
34
44
  end
35
45
  end
@@ -13,6 +13,14 @@ class AwsClients
13
13
 
14
14
  # Clients
15
15
 
16
+ def self.cloudwatchevents
17
+ @aws_cloudwatchevents ||= Aws::CloudWatchEvents::Client.new({ stub_responses: stubbed? })
18
+ end
19
+
20
+ def self.dynamo
21
+ @aws_dynamo ||= Aws::DynamoDB::Client.new({ stub_responses: stubbed? })
22
+ end
23
+
16
24
  def self.ec2
17
25
  @aws_ec2 ||= Aws::EC2::Client.new({ stub_responses: stubbed? })
18
26
  end
@@ -33,6 +41,14 @@ class AwsClients
33
41
  @aws_iam ||= Aws::IAM::Client.new({ stub_responses: stubbed? })
34
42
  end
35
43
 
44
+ def self.kinesis
45
+ @aws_kinesis ||= Aws::Kinesis::Client.new({ stub_responses: stubbed? })
46
+ end
47
+
48
+ def self.lambda
49
+ @aws_lambda ||= Aws::Lambda::Client.new({ stub_responses: stubbed? })
50
+ end
51
+
36
52
  def self.rds
37
53
  @aws_rds ||= Aws::RDS::Client.new({ stub_responses: stubbed? })
38
54
  end
@@ -61,7 +77,7 @@ class AwsClients
61
77
  @aws_sqs ||= Aws::SQS::Client.new({ stub_responses: stubbed? })
62
78
  end
63
79
 
64
- def self.lambda
65
- @aws_lambda ||= Aws::Lambda::Client.new({ stub_responses: stubbed? })
80
+ def self.cloudtrail
81
+ @aws_cloudtrail ||= Aws::CloudTrail::Client.new({ stub_responses: stubbed? })
66
82
  end
67
83
  end
@@ -12,7 +12,9 @@ module HasProjects
12
12
  repository = "#{org}/#{name}"
13
13
  return projects[repository] if projects.key?(repository)
14
14
 
15
- GeoEngineer::Project.new(org, name, self, &block)
15
+ proj = GeoEngineer::Project.new(org, name, self, &block)
16
+ projects[repository] = proj
17
+ proj
16
18
  end
17
19
 
18
20
  def all_project_resources
@@ -4,6 +4,8 @@ require 'netaddr'
4
4
  # HasValidations provides methods to enable validations
5
5
  ########################################################################
6
6
  module HasValidations
7
+ MAX_POLICY_LENGTH = 5120
8
+
7
9
  def self.included(base)
8
10
  base.extend(ClassMethods)
9
11
  end
@@ -54,6 +56,11 @@ module HasValidations
54
56
  return "Bad cidr block \"#{cidr_block}\" #{for_resource}"
55
57
  end
56
58
 
59
+ def validate_policy_length(policy)
60
+ return unless policy.to_s.length >= MAX_POLICY_LENGTH
61
+ "Policy is too large - must be less than #{MAX_POLICY_LENGTH} characters"
62
+ end
63
+
57
64
  # Validates that at least one of the specified attributes is present
58
65
  def validate_at_least_one_present(attributes)
59
66
  errs = []
@@ -1,3 +1,3 @@
1
1
  module GeoEngineer
2
- VERSION = '0.1.1'.freeze
2
+ VERSION = '0.1.2'.freeze
3
3
  end
@@ -40,12 +40,26 @@ describe("GeoEngineer::Resource") do
40
40
  tags {
41
41
  not_blue "FALSE"
42
42
  }
43
+ # i.e. s3 bucket multilevel subresources
44
+ lifecycle_rule {
45
+ expiration {
46
+ days 90
47
+ }
48
+ }
49
+
50
+ lifecycle_rule {
51
+ transition {
52
+ days 60
53
+ }
54
+ }
43
55
  }
44
56
 
45
57
  tfjson = res.to_terraform_json
46
58
 
47
59
  expect(tfjson['blue']).to eq 'TRUE'
48
60
  expect(tfjson['tags'][0]['not_blue']).to eq 'FALSE'
61
+ expect(tfjson['lifecycle_rule'][0]['expiration'][0]['days']).to eq 90
62
+ expect(tfjson['lifecycle_rule'][1]['transition'][0]['days']).to eq 60
49
63
  end
50
64
  end
51
65
 
@@ -0,0 +1,35 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe "GeoEngineer::Resources::AwsCloudtrail" do
4
+ let(:aws_client) { AwsClients.cloudtrail }
5
+
6
+ before { aws_client.setup_stubbing }
7
+ common_resource_tests(GeoEngineer::Resources::AwsCloudtrail, 'aws_cloudtrail')
8
+
9
+ let(:trail_name) { 'some-fake-cloudtrail' }
10
+
11
+ describe '#_fetch_remote_resources' do
12
+ before do
13
+ aws_client.stub_responses(
14
+ :describe_trails,
15
+ {
16
+ trail_list: [
17
+ { name: trail_name },
18
+ { name: 'another-trail-name' }
19
+ ]
20
+ }
21
+ )
22
+ end
23
+
24
+ it 'should create an array of hashes from the AWS response' do
25
+ resources = GeoEngineer::Resources::AwsCloudtrail._fetch_remote_resources
26
+ expect(resources.count).to eql 2
27
+
28
+ test_cloudtrail = resources.first
29
+
30
+ expect(test_cloudtrail[:name]).to eql(trail_name)
31
+ expect(test_cloudtrail[:_geo_id]).to eql(trail_name)
32
+ expect(test_cloudtrail[:_terraform_id]).to eql(trail_name)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe("GeoEngineer::Resources::AwsCloudwatchEventRule") do
4
+ let(:aws_client) { AwsClients.cloudwatchevents }
5
+
6
+ before { aws_client.setup_stubbing }
7
+
8
+ common_resource_tests(GeoEngineer::Resources::AwsCloudwatchEventRule, 'aws_cloudwatch_event_rule')
9
+
10
+ describe "#_fetch_remote_resources" do
11
+ before do
12
+ aws_client.stub_responses(
13
+ :list_rules,
14
+ {
15
+ rules:
16
+ [
17
+ {
18
+ name: "test",
19
+ arn: "arn:aws:cloudwatchevents:us-east-1:1234567890:test",
20
+ state: "ENABLED",
21
+ schedule_expression: "rate(5 minutes)"
22
+ }
23
+ ]
24
+ }
25
+ )
26
+ end
27
+
28
+ it 'should create list of hashes from returned AWS SDK' do
29
+ remote_resources = GeoEngineer::Resources::AwsCloudwatchEventRule._fetch_remote_resources
30
+ expect(remote_resources.length).to eq 1
31
+ end
32
+ end
33
+ end