geoengineer 0.1.1 → 0.1.2

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 (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