geoengineer 0.1.4 → 0.1.5
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +1 -0
- data/lib/geoengineer/cli/geo_cli.rb +1 -0
- data/lib/geoengineer/cli/terraform_commands.rb +35 -0
- data/lib/geoengineer/environment.rb +1 -0
- data/lib/geoengineer/resource.rb +31 -0
- data/lib/geoengineer/resources/aws_alb.rb +2 -0
- data/lib/geoengineer/resources/aws_alb_listener.rb +16 -6
- data/lib/geoengineer/resources/aws_alb_target_group.rb +19 -7
- data/lib/geoengineer/resources/aws_customer_gateway.rb +4 -0
- data/lib/geoengineer/resources/aws_emr_cluster.rb +20 -0
- data/lib/geoengineer/resources/aws_iam_role.rb +1 -0
- data/lib/geoengineer/resources/aws_kinesis_stream.rb +10 -7
- data/lib/geoengineer/resources/aws_lambda_function.rb +4 -5
- data/lib/geoengineer/resources/aws_nat_gateway.rb +6 -7
- data/lib/geoengineer/resources/aws_network_interface.rb +22 -0
- data/lib/geoengineer/resources/aws_placement_group.rb +25 -0
- data/lib/geoengineer/resources/aws_route53_record.rb +45 -4
- data/lib/geoengineer/resources/aws_route53_zone.rb +34 -7
- data/lib/geoengineer/resources/aws_route_table.rb +1 -1
- data/lib/geoengineer/resources/aws_s3_bucket_object.rb +24 -0
- data/lib/geoengineer/resources/aws_vpn_connection.rb +17 -9
- data/lib/geoengineer/resources/aws_vpn_connection_route.rb +50 -14
- data/lib/geoengineer/resources/aws_waf_ipset.rb +35 -0
- data/lib/geoengineer/resources/aws_waf_rule.rb +26 -0
- data/lib/geoengineer/resources/aws_waf_web_acl.rb +26 -0
- data/lib/geoengineer/utils/aws_clients.rb +15 -0
- data/lib/geoengineer/utils/has_validations.rb +1 -0
- data/lib/geoengineer/version.rb +1 -1
- data/spec/resource_spec.rb +56 -0
- data/spec/resources/aws_alb_listener_spec.rb +6 -1
- data/spec/resources/aws_alb_spec.rb +7 -0
- data/spec/resources/aws_alb_target_group_spec.rb +22 -0
- data/spec/resources/aws_emr_cluster_spec.rb +23 -0
- data/spec/resources/aws_kinesis_stream_spec.rb +26 -11
- data/spec/resources/aws_network_interface_spec.rb +32 -0
- data/spec/resources/aws_placement_group_spec.rb +29 -0
- data/spec/resources/aws_route53_zone_spec.rb +19 -3
- data/spec/resources/aws_s3_bucket_object_spec.rb +4 -0
- data/spec/resources/aws_waf_ipset_spec.rb +65 -0
- data/spec/resources/aws_waf_rule_spec.rb +36 -0
- data/spec/resources/aws_waf_web_acl_spec.rb +36 -0
- data/spec/spec_helper.rb +1 -1
- metadata +23 -2
- metadata.gz.sig +0 -0
@@ -7,15 +7,42 @@ class GeoEngineer::Resources::AwsRoute53Zone < GeoEngineer::Resource
|
|
7
7
|
validate -> { validate_required_attributes([:name]) }
|
8
8
|
|
9
9
|
after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
|
10
|
-
after :initialize, -> { _geo_id -> { self.name } }
|
10
|
+
after :initialize, -> { _geo_id -> { "#{self._public_or_private}-#{self.name}." } }
|
11
|
+
|
12
|
+
def _public_or_private
|
13
|
+
self.vpc_id.nil? ? 'public' : self.vpc_id
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_terraform_state
|
17
|
+
tfstate = super
|
18
|
+
tfstate[:primary][:attributes] = {
|
19
|
+
'name' => name,
|
20
|
+
'vpc_id' => vpc_id,
|
21
|
+
'force_destroy' => (force_destroy || 'false')
|
22
|
+
}
|
23
|
+
tfstate
|
24
|
+
end
|
11
25
|
|
12
26
|
def self._fetch_remote_resources(provider)
|
13
|
-
|
27
|
+
_fetch_zones(provider).map { |zone| _generate_remote_zone(provider, zone) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def self._fetch_zones(provider)
|
31
|
+
AwsClients.route53(provider).list_hosted_zones.hosted_zones.map(&:to_h)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self._generate_remote_zone(provider, zone)
|
35
|
+
is_private_zone = zone.dig(:config, :private_zone) || false
|
36
|
+
|
37
|
+
zone[:id] = zone[:id].gsub(%r{^/hostedzone/}, '')
|
38
|
+
zone[:zone_id] = zone[:id]
|
39
|
+
zone[:_terraform_id] = zone[:id]
|
40
|
+
zone[:vpc_id] = _get_zone_vpc_id(provider, zone[:id]) if is_private_zone
|
41
|
+
zone[:_geo_id] = "#{is_private_zone ? zone[:vpc_id] : 'public'}-#{zone[:name]}"
|
42
|
+
zone
|
43
|
+
end
|
14
44
|
|
15
|
-
|
16
|
-
|
17
|
-
zone[:_geo_id] = zone[:name]
|
18
|
-
zone
|
19
|
-
end
|
45
|
+
def self._get_zone_vpc_id(provider, zone_id)
|
46
|
+
AwsClients.route53(provider).get_hosted_zone({ id: zone_id }).to_h[:vp_cs].first[:vpc_id]
|
20
47
|
end
|
21
48
|
end
|
@@ -7,7 +7,7 @@ class GeoEngineer::Resources::AwsRouteTable < GeoEngineer::Resource
|
|
7
7
|
validate -> { validate_required_attributes([:vpc_id]) }
|
8
8
|
validate -> { validate_has_tag(:Name) }
|
9
9
|
validate -> {
|
10
|
-
validate_subresource_required_attributes(:route, [:
|
10
|
+
validate_subresource_required_attributes(:route, [:cidr_block]) unless self.all_route.empty?
|
11
11
|
}
|
12
12
|
|
13
13
|
after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
|
@@ -0,0 +1,24 @@
|
|
1
|
+
########################################################################
|
2
|
+
# AwsS3Bucket is the +aws_s3_bucket_object+ terrform resource,
|
3
|
+
#
|
4
|
+
# {https://www.terraform.io/docs/providers/aws/r/s3_bucket_object.html Terraform Docs}
|
5
|
+
########################################################################
|
6
|
+
class GeoEngineer::Resources::AwsS3BucketObject < GeoEngineer::Resource
|
7
|
+
validate -> { validate_required_attributes([:bucket]) }
|
8
|
+
|
9
|
+
after :initialize, -> { _terraform_id -> { bucket } }
|
10
|
+
|
11
|
+
def to_terraform_state
|
12
|
+
tfstate = super
|
13
|
+
tfstate[:primary][:attributes] = {
|
14
|
+
'bucket' => bucket,
|
15
|
+
'key' => key,
|
16
|
+
'source' => source
|
17
|
+
}
|
18
|
+
tfstate
|
19
|
+
end
|
20
|
+
|
21
|
+
def short_type
|
22
|
+
's3_object'
|
23
|
+
end
|
24
|
+
end
|
@@ -10,15 +10,23 @@ class GeoEngineer::Resources::AwsVpnConnection < GeoEngineer::Resource
|
|
10
10
|
after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
|
11
11
|
after :initialize, -> { _geo_id -> { NullObject.maybe(tags)[:Name] } }
|
12
12
|
|
13
|
+
def vpn_type(val = nil)
|
14
|
+
val ? self["type"] = val : self["type"]
|
15
|
+
end
|
16
|
+
|
13
17
|
def self._fetch_remote_resources(provider)
|
14
|
-
AwsClients
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
AwsClients
|
19
|
+
.ec2(provider)
|
20
|
+
.describe_vpn_connections['vpn_connections']
|
21
|
+
.reject { |connection| connection['state'] == 'deleted' } # Necessary for development
|
22
|
+
.map(&:to_h)
|
23
|
+
.map do |connection|
|
24
|
+
connection.merge(
|
25
|
+
{
|
26
|
+
_terraform_id: connection[:vpn_connection_id],
|
27
|
+
_geo_id: connection[:tags].find { |tag| tag[:key] == "Name" }&.dig(:value)
|
28
|
+
}
|
29
|
+
)
|
30
|
+
end
|
23
31
|
end
|
24
32
|
end
|
@@ -6,30 +6,66 @@
|
|
6
6
|
class GeoEngineer::Resources::AwsVpnConnectionRoute < GeoEngineer::Resource
|
7
7
|
validate -> { validate_required_attributes([:destination_cidr_block, :vpn_connection_id]) }
|
8
8
|
|
9
|
-
after :initialize, -> {
|
9
|
+
after :initialize, -> {
|
10
|
+
_terraform_id -> {
|
11
|
+
connection_route_id unless terraform_ref?
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
15
|
+
after :initialize, -> {
|
16
|
+
_geo_id -> {
|
17
|
+
connection_route_id
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
# Is the VPN connection id a terraform ref or an id
|
22
|
+
def terraform_ref?
|
23
|
+
/^\${[a-zA-Z0-9\._-]+}$/.match(vpn_connection_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_terraform_state
|
27
|
+
tfstate = super
|
28
|
+
|
29
|
+
tfstate[:primary][:attributes] = {
|
30
|
+
'destination_cidr_block' => destination_cidr_block,
|
31
|
+
'vpn_connection_id' => vpn_connection_id
|
32
|
+
}
|
33
|
+
|
34
|
+
tfstate
|
35
|
+
end
|
36
|
+
|
37
|
+
def connection_route_id
|
38
|
+
self.class.build_connection_route_id(
|
39
|
+
destination_cidr_block,
|
40
|
+
vpn_connection_id
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.build_connection_route_id(cidr, connection_id)
|
45
|
+
"#{cidr}:#{connection_id}"
|
46
|
+
end
|
10
47
|
|
11
48
|
def support_tags?
|
12
49
|
false
|
13
50
|
end
|
14
51
|
|
15
52
|
def self._fetch_remote_resources(provider)
|
16
|
-
AwsClients
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
.flatten
|
53
|
+
AwsClients.ec2(provider)
|
54
|
+
.describe_vpn_connections['vpn_connections']
|
55
|
+
.map(&:to_h)
|
56
|
+
.select { |connection| !connection[:routes].empty? }
|
57
|
+
.map { |connection| _generate_routes(connection) }
|
58
|
+
.flatten
|
23
59
|
end
|
24
60
|
|
25
61
|
def self._generate_routes(connection)
|
26
62
|
connection[:routes].map do |route|
|
27
|
-
route
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
)
|
63
|
+
cidr = route[:destination_cidr_block]
|
64
|
+
connection_id = connection[:vpn_connection_id]
|
65
|
+
|
66
|
+
id = build_connection_route_id(cidr, connection_id)
|
67
|
+
|
68
|
+
route.merge({ _terraform_id: id, _geo_id: id })
|
33
69
|
end
|
34
70
|
end
|
35
71
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
########################################################################
|
2
|
+
# AwsWafIpset is the +aws_waf_ipset+ terrform resource,
|
3
|
+
#
|
4
|
+
# {https://www.terraform.io/docs/providers/aws/r/waf_ipset.html Terraform Docs}
|
5
|
+
########################################################################
|
6
|
+
class GeoEngineer::Resources::AwsWafIpset < GeoEngineer::Resource
|
7
|
+
validate -> { validate_required_attributes([:name]) }
|
8
|
+
validate :validate_correct_cidr_blocks
|
9
|
+
|
10
|
+
after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
|
11
|
+
after :initialize, -> { _geo_id -> { name } }
|
12
|
+
|
13
|
+
def self._fetch_remote_resources(provider)
|
14
|
+
AwsClients.waf(provider).list_ip_sets['ip_sets'].map(&:to_h).map do |s|
|
15
|
+
s.merge(
|
16
|
+
{
|
17
|
+
_terraform_id: s[:ip_set_id],
|
18
|
+
_geo_id: s[:name]
|
19
|
+
}
|
20
|
+
)
|
21
|
+
s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate_correct_cidr_blocks
|
26
|
+
errors = []
|
27
|
+
error = validate_cidr_block(self.ip_set_descriptors&.value)
|
28
|
+
errors << error unless error.nil?
|
29
|
+
errors
|
30
|
+
end
|
31
|
+
|
32
|
+
def support_tags?
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
########################################################################
|
2
|
+
# AwsWafRule is the +aws_waf_rule+ terrform resource,
|
3
|
+
#
|
4
|
+
# {https://www.terraform.io/docs/providers/aws/r/waf_rule.html Terraform Docs}
|
5
|
+
########################################################################
|
6
|
+
class GeoEngineer::Resources::AwsWafRule < GeoEngineer::Resource
|
7
|
+
validate -> { validate_required_attributes([:metric_name, :name]) }
|
8
|
+
|
9
|
+
after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
|
10
|
+
after :initialize, -> { _geo_id -> { name } }
|
11
|
+
|
12
|
+
def self._fetch_remote_resources(provider)
|
13
|
+
AwsClients.waf(provider).list_rules['rules'].map(&:to_h).map do |s|
|
14
|
+
s.merge(
|
15
|
+
{
|
16
|
+
_terraform_id: s[:rule_id],
|
17
|
+
_geo_id: s[:name]
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def support_tags?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
########################################################################
|
2
|
+
# AwsWafWebAcl is the +aws_waf_web_acl+ terrform resource,
|
3
|
+
#
|
4
|
+
# {https://www.terraform.io/docs/providers/aws/r/waf_web_acl.html Terraform Docs}
|
5
|
+
########################################################################
|
6
|
+
class GeoEngineer::Resources::AwsWafWebAcl < GeoEngineer::Resource
|
7
|
+
validate -> { validate_required_attributes([:metric_name, :default_action, :name, :rules]) }
|
8
|
+
|
9
|
+
after :initialize, -> { _terraform_id -> { NullObject.maybe(remote_resource)._terraform_id } }
|
10
|
+
after :initialize, -> { _geo_id -> { name } }
|
11
|
+
|
12
|
+
def self._fetch_remote_resources(provider)
|
13
|
+
AwsClients.waf(provider).list_web_acls['web_acls'].map(&:to_h).map do |acl|
|
14
|
+
acl.merge(
|
15
|
+
{
|
16
|
+
_terraform_id: acl[:web_acl_id],
|
17
|
+
_geo_id: acl[:name]
|
18
|
+
}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def support_tags?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
@@ -14,6 +14,7 @@ class AwsClients
|
|
14
14
|
def self.client_params(provider = nil)
|
15
15
|
client_params = { stub_responses: stubbed? }
|
16
16
|
client_params[:region] = provider.region if provider
|
17
|
+
client_params[:retry_limit] = Integer(ENV['AWS_RETRY_LIMIT']) if ENV['AWS_RETRY_LIMIT']
|
17
18
|
client_params
|
18
19
|
end
|
19
20
|
|
@@ -178,4 +179,18 @@ class AwsClients
|
|
178
179
|
Aws::KMS::Client
|
179
180
|
)
|
180
181
|
end
|
182
|
+
|
183
|
+
def self.waf(provider = nil)
|
184
|
+
self.client_cache(
|
185
|
+
provider,
|
186
|
+
Aws::WAF::Client
|
187
|
+
)
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.emr(provider = nil)
|
191
|
+
self.client_cache(
|
192
|
+
provider,
|
193
|
+
Aws::EMR::Client
|
194
|
+
)
|
195
|
+
end
|
181
196
|
end
|
@@ -51,6 +51,7 @@ module HasValidations
|
|
51
51
|
# Validates CIDR block format
|
52
52
|
# Returns error when argument fails validation
|
53
53
|
def validate_cidr_block(cidr_block)
|
54
|
+
return "Empty cidr block" if cidr_block.nil? || cidr_block.empty?
|
54
55
|
return if NetAddr::CIDR.create(cidr_block)
|
55
56
|
rescue NetAddr::ValidationError
|
56
57
|
return "Bad cidr block \"#{cidr_block}\" #{for_resource}"
|
data/lib/geoengineer/version.rb
CHANGED
data/spec/resource_spec.rb
CHANGED
@@ -325,6 +325,62 @@ describe GeoEngineer::Resource do
|
|
325
325
|
end
|
326
326
|
end
|
327
327
|
|
328
|
+
describe '#duplicate' do
|
329
|
+
let!(:project) do
|
330
|
+
GeoEngineer::Project.new('org', 'project_name', nil) {
|
331
|
+
tags {
|
332
|
+
a '1'
|
333
|
+
}
|
334
|
+
}
|
335
|
+
end
|
336
|
+
let!(:resource_class) do
|
337
|
+
class GeoEngineer::Resources::Derp < GeoEngineer::Resource
|
338
|
+
validate -> { validate_has_tag(:Name) }
|
339
|
+
after :initialize, -> {
|
340
|
+
_terraform_id -> { NullObject.maybe(remote_resource)._terraform_id }
|
341
|
+
}
|
342
|
+
after :initialize, -> { _geo_id -> { NullObject.maybe(tags)[:Name] } }
|
343
|
+
after :initialize, -> { _number -> { NullObject.maybe(_geo_id)[-1] } }
|
344
|
+
|
345
|
+
def self._fetch_remote_resources(provider)
|
346
|
+
[
|
347
|
+
{ _geo_id: "geo_id1", _terraform_id: "t1 baby!" },
|
348
|
+
{ _geo_id: "geo_id2", _terraform_id: "t who?" }
|
349
|
+
]
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
let(:subject) do
|
355
|
+
project.resource('derp', 'id') {
|
356
|
+
tags {
|
357
|
+
Name "geo_id1"
|
358
|
+
}
|
359
|
+
}
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'copies over attributes and subresources' do
|
363
|
+
copy = subject.duplicate('duplicate')
|
364
|
+
# We haven't changed anything, so it should all match
|
365
|
+
expect(copy.type).to eq(subject.type)
|
366
|
+
expect(copy._geo_id).to eq(subject._geo_id)
|
367
|
+
expect(copy._terraform_id).to eq(subject._terraform_id)
|
368
|
+
expect(copy._number).to eq(subject._number)
|
369
|
+
expect(copy.tags["Name"]).to eq(subject.tags["Name"])
|
370
|
+
end
|
371
|
+
|
372
|
+
it 'handles procs appropriately' do
|
373
|
+
copy = subject.duplicate('duplicate')
|
374
|
+
copy.tags["Name"] = "geo_id2"
|
375
|
+
|
376
|
+
expect(copy.type).to eq(subject.type)
|
377
|
+
expect(copy._geo_id).to_not eq(subject._geo_id)
|
378
|
+
expect(copy._terraform_id).to_not eq(subject._terraform_id)
|
379
|
+
expect(copy._number).to_not eq(subject._number)
|
380
|
+
expect(copy._number).to eq("2")
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
328
384
|
describe 'class method' do
|
329
385
|
describe('#type_from_class_name') do
|
330
386
|
it 'should return resource' do
|
@@ -12,7 +12,12 @@ describe GeoEngineer::Resources::AwsAlbListener do
|
|
12
12
|
alb_client.stub_responses(
|
13
13
|
:describe_load_balancers,
|
14
14
|
{
|
15
|
-
load_balancers: [
|
15
|
+
load_balancers: [
|
16
|
+
{
|
17
|
+
load_balancer_arn: "foo/bar-baz",
|
18
|
+
load_balancer_name: "foo-bar-baz"
|
19
|
+
}
|
20
|
+
]
|
16
21
|
}
|
17
22
|
)
|
18
23
|
alb_client.stub_responses(
|
@@ -29,5 +29,12 @@ describe GeoEngineer::Resources::AwsAlb do
|
|
29
29
|
remote_resources = GeoEngineer::Resources::AwsAlb._fetch_remote_resources(nil)
|
30
30
|
expect(remote_resources.length).to eq 1
|
31
31
|
end
|
32
|
+
|
33
|
+
it "should work if no ALB's exist" do
|
34
|
+
alb_client.stub_responses(:describe_load_balancers, { load_balancers: [] })
|
35
|
+
|
36
|
+
remote_resources = GeoEngineer::Resources::AwsAlb._fetch_remote_resources(nil)
|
37
|
+
expect(remote_resources.length).to eq 0
|
38
|
+
end
|
32
39
|
end
|
33
40
|
end
|
@@ -28,8 +28,30 @@ describe GeoEngineer::Resources::AwsAlbTargetGroup do
|
|
28
28
|
]
|
29
29
|
}
|
30
30
|
)
|
31
|
+
alb_client.stub_responses(
|
32
|
+
:describe_tags,
|
33
|
+
{
|
34
|
+
tag_descriptions: [
|
35
|
+
{
|
36
|
+
resource_arn: "targetgroup/foo/bar-baz",
|
37
|
+
tags: [{ key: "Name", value: "foo/bar-baz" }]
|
38
|
+
},
|
39
|
+
{
|
40
|
+
resource_arn: "targetgroup/foo/test-test",
|
41
|
+
tags: [{ key: "Name", value: "foo/test-test" }]
|
42
|
+
}
|
43
|
+
]
|
44
|
+
}
|
45
|
+
)
|
31
46
|
remote_resources = GeoEngineer::Resources::AwsAlbTargetGroup._fetch_remote_resources(nil)
|
32
47
|
expect(remote_resources.length).to eq 2
|
33
48
|
end
|
49
|
+
|
50
|
+
it "should work if no ALB's exist" do
|
51
|
+
alb_client.stub_responses(:describe_target_groups, { target_groups: [] })
|
52
|
+
|
53
|
+
remote_resources = GeoEngineer::Resources::AwsAlbTargetGroup._fetch_remote_resources(nil)
|
54
|
+
expect(remote_resources.length).to eq 0
|
55
|
+
end
|
34
56
|
end
|
35
57
|
end
|