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