geoengineer 0.1.0 → 0.1.1
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 +5 -0
- data.tar.gz.sig +0 -0
- data/README.md +5 -5
- data/lib/geoengineer/cli/geo_cli.rb +4 -5
- data/lib/geoengineer/cli/status_command.rb +7 -1
- data/lib/geoengineer/environment.rb +53 -51
- data/lib/geoengineer/project.rb +5 -24
- data/lib/geoengineer/resource.rb +89 -20
- data/lib/geoengineer/resources/aws_customer_gateway.rb +23 -0
- data/lib/geoengineer/resources/aws_eip.rb +43 -0
- data/lib/geoengineer/resources/aws_iam_group.rb +26 -0
- data/lib/geoengineer/resources/aws_iam_group_membership.rb +50 -0
- data/lib/geoengineer/resources/aws_iam_policy.rb +12 -4
- data/lib/geoengineer/resources/aws_iam_policy_attachment.rb +95 -0
- data/lib/geoengineer/resources/aws_iam_role.rb +45 -0
- data/lib/geoengineer/resources/aws_instance.rb +7 -4
- data/lib/geoengineer/resources/aws_internet_gateway.rb +23 -0
- data/lib/geoengineer/resources/aws_lambda_alias.rb +50 -0
- data/lib/geoengineer/resources/aws_lambda_event_source_mapping.rb +47 -0
- data/lib/geoengineer/resources/aws_lambda_function.rb +30 -0
- data/lib/geoengineer/resources/aws_lambda_permission.rb +74 -0
- data/lib/geoengineer/resources/aws_main_route_table_association.rb +51 -0
- data/lib/geoengineer/resources/aws_nat_gateway.rb +29 -0
- data/lib/geoengineer/resources/aws_network_acl.rb +38 -0
- data/lib/geoengineer/resources/aws_network_acl_rule.rb +50 -0
- data/lib/geoengineer/resources/aws_route.rb +47 -0
- data/lib/geoengineer/resources/aws_route53_record.rb +4 -0
- data/lib/geoengineer/resources/aws_route_table.rb +26 -0
- data/lib/geoengineer/resources/aws_route_table_association.rb +45 -0
- data/lib/geoengineer/resources/aws_security_group.rb +8 -5
- data/lib/geoengineer/resources/aws_subnet.rb +24 -0
- data/lib/geoengineer/resources/aws_vpc.rb +24 -0
- data/lib/geoengineer/resources/aws_vpc_dhcp_options.rb +29 -0
- data/lib/geoengineer/resources/aws_vpc_dhcp_options_association.rb +40 -0
- data/lib/geoengineer/resources/aws_vpc_endpoint.rb +26 -0
- data/lib/geoengineer/resources/aws_vpc_peering_connection.rb +29 -0
- data/lib/geoengineer/resources/aws_vpn_connection.rb +23 -0
- data/lib/geoengineer/resources/aws_vpn_connection_route.rb +35 -0
- data/lib/geoengineer/resources/aws_vpn_gateway.rb +22 -0
- data/lib/geoengineer/resources/aws_vpn_gateway_attachment.rb +41 -0
- data/lib/geoengineer/template.rb +20 -4
- data/lib/geoengineer/utils/aws_clients.rb +4 -0
- data/lib/geoengineer/utils/crc32.rb +61 -0
- data/lib/geoengineer/utils/has_attributes.rb +25 -11
- data/lib/geoengineer/utils/has_projects.rb +21 -0
- data/lib/geoengineer/utils/has_resources.rb +17 -4
- data/lib/geoengineer/utils/has_templates.rb +31 -0
- data/lib/geoengineer/utils/has_validations.rb +18 -3
- data/lib/geoengineer/version.rb +1 -1
- data/spec/environment_spec.rb +40 -19
- data/spec/project_spec.rb +2 -2
- data/spec/resource_spec.rb +87 -6
- data/spec/resources/aws_customer_gateway_spec.rb +24 -0
- data/spec/resources/aws_eip_spec.rb +29 -0
- data/spec/resources/aws_iam_group_membership_spec.rb +83 -0
- data/spec/resources/aws_iam_group_spec.rb +43 -0
- data/spec/resources/aws_iam_policy_attachment_spec.rb +80 -0
- data/spec/resources/{aws_iam_policy.rb → aws_iam_policy_spec.rb} +6 -5
- data/spec/resources/aws_iam_role_spec.rb +45 -0
- data/spec/resources/aws_internet_gateway_spec.rb +24 -0
- data/spec/resources/aws_lambda_alias_spec.rb +39 -0
- data/spec/resources/aws_lambda_event_source_mapping_spec.rb +53 -0
- data/spec/resources/aws_lambda_function_spec.rb +29 -0
- data/spec/resources/aws_lambda_permission_spec.rb +90 -0
- data/spec/resources/aws_main_route_table_association_spec.rb +57 -0
- data/spec/resources/aws_nat_gateway_spec.rb +31 -0
- data/spec/resources/aws_network_acl_rule_spec.rb +73 -0
- data/spec/resources/aws_network_acl_spec.rb +31 -0
- data/spec/resources/aws_route53_record_spec.rb +5 -0
- data/spec/resources/aws_route_spec.rb +47 -0
- data/spec/resources/aws_route_table_association_spec.rb +47 -0
- data/spec/resources/aws_route_table_spec.rb +24 -0
- data/spec/resources/aws_security_group_spec.rb +36 -2
- data/spec/resources/aws_subnet_spec.rb +24 -0
- data/spec/resources/aws_vpc_dhcp_options_association_spec.rb +43 -0
- data/spec/resources/aws_vpc_dhcp_options_spec.rb +24 -0
- data/spec/resources/aws_vpc_endpoint_spec.rb +41 -0
- data/spec/resources/aws_vpc_peering_connection_spec.rb +33 -0
- data/spec/resources/aws_vpc_spec.rb +24 -0
- data/spec/resources/aws_vpn_connection_route_spec.rb +43 -0
- data/spec/resources/aws_vpn_connection_spec.rb +41 -0
- data/spec/resources/aws_vpn_gateway_attachment_spec.rb +41 -0
- data/spec/resources/aws_vpn_gateway_spec.rb +39 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/utils/crc32_spec.rb +14 -0
- data/spec/utils/has_attributes_spec.rb +22 -0
- data/spec/utils/has_resources_spec.rb +4 -0
- data/spec/utils/has_validations_spec.rb +45 -0
- metadata +117 -6
- metadata.gz.sig +1 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca09f8d5b8d93ce13661980ff56dd0e28763ecbf
|
4
|
+
data.tar.gz: 4d9e40dfd4402a197a4bab1fb3a184966045fd24
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d99a2fe4223c269d192a8c662626fba2aacddc813ee12fc03fe1a58179a4e6ab5f2a38c21d58cb8aa12ca1e53097906cee497b4853e8ce407003c9890eaf1f7e
|
7
|
+
data.tar.gz: cb63c4c1a257d390ee22f992a725adb180e8a64d9374875b8cc185efb35b9114522248412cf60db060d7128fde5f7e3c6a563bdedee5b0b7be88c0381ccd695d
|
checksums.yaml.gz.sig
ADDED
data.tar.gz.sig
ADDED
Binary file
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# GeoEngineer
|
2
2
|
|
3
|
-
|
3
|
+
[![CircleCI](https://circleci.com/gh/coinbase/geoengineer.svg?style=shield)](https://circleci.com/gh/coinbase/geoengineer)
|
4
4
|
|
5
|
-
|
5
|
+
<a href="https://commons.wikimedia.org/wiki/File:Mantle_of_Responsibility.png"><img src="./assets/mantle.png" align="right" alt="Mantle_of_Responsibility" /></a>
|
6
6
|
|
7
7
|
GeoEngineer provides a Ruby DSL and command line tool (`geo`) to *codeify* then plan and execute changes to cloud resources.
|
8
8
|
|
@@ -25,13 +25,13 @@ Instructions to install Terraform can be found [here](https://www.terraform.io/d
|
|
25
25
|
|
26
26
|
### Install Ruby
|
27
27
|
|
28
|
-
|
28
|
+
Instructions to install Ruby can be found [here](https://www.ruby-lang.org/en/documentation/installation/).
|
29
29
|
|
30
30
|
### Install GeoEngineer
|
31
31
|
|
32
32
|
```
|
33
|
-
gem
|
34
|
-
gem install geoengineer
|
33
|
+
gem cert --add <(https://raw.githubusercontent.com/coinbase/geoengineer/master/certs/geoengineer-gem.pem)
|
34
|
+
gem install geoengineer --trust-policy HighSecurity
|
35
35
|
```
|
36
36
|
|
37
37
|
Test it is installed correctly with:
|
@@ -139,7 +139,6 @@ class GeoCLI
|
|
139
139
|
throw "Environment not set" unless @environment
|
140
140
|
|
141
141
|
@environment.execute_lifecycle(:before, action_name.to_sym)
|
142
|
-
|
143
142
|
errs = @environment.errors.flatten.sort
|
144
143
|
return print_validation_errors(errs) unless errs.empty?
|
145
144
|
|
@@ -164,7 +163,7 @@ class GeoCLI
|
|
164
163
|
end
|
165
164
|
end
|
166
165
|
|
167
|
-
def
|
166
|
+
def global_options
|
168
167
|
global_option('-e', '--environment <name>', "Environment to use")
|
169
168
|
|
170
169
|
@verbose = true
|
@@ -186,15 +185,15 @@ class GeoCLI
|
|
186
185
|
|
187
186
|
def run
|
188
187
|
program :name, 'GeoEngineer'
|
189
|
-
program :version,
|
188
|
+
program :version, GeoEngineer::VERSION
|
190
189
|
program :description, 'GeoEngineer will help you Terraform your resources'
|
191
190
|
always_trace!
|
192
191
|
|
193
192
|
# check terraform installed
|
194
193
|
return puts "Please install terraform" unless terraform_installed?
|
195
194
|
|
196
|
-
#
|
197
|
-
|
195
|
+
# global_options
|
196
|
+
global_options
|
198
197
|
|
199
198
|
# Add commands
|
200
199
|
plan_cmd
|
@@ -39,6 +39,11 @@ module GeoCLI::StatusCommand
|
|
39
39
|
puts Terminal::Table.new({ rows: rows })
|
40
40
|
end
|
41
41
|
|
42
|
+
def status_types(options)
|
43
|
+
return options.resources.split(',') if options.resources
|
44
|
+
environment.status_types ? environment.status_types : default_status_types
|
45
|
+
end
|
46
|
+
|
42
47
|
def default_status_types
|
43
48
|
[
|
44
49
|
"aws_security_group",
|
@@ -77,7 +82,7 @@ module GeoCLI::StatusCommand
|
|
77
82
|
def status_action
|
78
83
|
lambda do |args, options|
|
79
84
|
type_stats = {}
|
80
|
-
|
85
|
+
status_types(options).each do |type|
|
81
86
|
codified = @environment.codified_resources(type)
|
82
87
|
uncodified = @environment.uncodified_resources(type)
|
83
88
|
type_stats[type] = calculate_type_status(codified, uncodified)
|
@@ -94,6 +99,7 @@ module GeoCLI::StatusCommand
|
|
94
99
|
command :status do |c|
|
95
100
|
c.syntax = 'geo status [<geo_files>]'
|
96
101
|
c.description = 'Displays the the new, managed and unmanaged resources'
|
102
|
+
c.option '--resources COMMA SEPERATED STRING', String, 'select resources for statuses'
|
97
103
|
action = status_action
|
98
104
|
c.action init_action(:status, &action)
|
99
105
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
|
1
2
|
########################################################################
|
2
3
|
# An Environment is a group of projects, resources and attributes,
|
3
4
|
# build to create a terraform file.
|
@@ -8,48 +9,64 @@
|
|
8
9
|
class GeoEngineer::Environment
|
9
10
|
include HasAttributes
|
10
11
|
include HasResources
|
12
|
+
include HasProjects
|
13
|
+
include HasTemplates
|
11
14
|
include HasValidations
|
12
15
|
include HasLifecycle
|
13
16
|
|
14
|
-
attr_reader :name
|
17
|
+
attr_reader :name
|
15
18
|
|
16
19
|
validate -> { validate_required_attributes([:region, :account_id]) }
|
17
20
|
|
18
21
|
# Validate resources have unique attributes
|
19
22
|
validate -> {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
+
resources_of_type_grouped_by(&:terraform_name).map do |klass, grouped_resources|
|
24
|
+
grouped_resources
|
25
|
+
.select { |k, v| v.length > 1 }
|
26
|
+
.map { |k, v| "Non-unique type.id #{v.first.for_resource}" }
|
27
|
+
end.flatten
|
23
28
|
}
|
24
29
|
|
25
30
|
validate -> {
|
26
|
-
|
27
|
-
|
28
|
-
|
31
|
+
resources_of_type_grouped_by(&:_terraform_id).map do |klass, grouped_resources|
|
32
|
+
grouped_resources
|
33
|
+
.select { |k, v| v.length > 1 && !v.first._terraform_id.nil? }
|
34
|
+
.map { |k, v| "Non-unique _terraform_id #{v.first._terraform_id} #{v.first.for_resource}" }
|
35
|
+
end.flatten
|
29
36
|
}
|
30
37
|
|
31
38
|
validate -> {
|
32
|
-
|
33
|
-
|
34
|
-
|
39
|
+
resources_of_type_grouped_by(&:_geo_id).map do |klass, grouped_resources|
|
40
|
+
grouped_resources
|
41
|
+
.select { |k, v| v.length > 1 }
|
42
|
+
.map { |k, v| "Non-unique _geo_id #{v.first._geo_id} #{v.first.for_resource}" }
|
43
|
+
end.flatten
|
35
44
|
}
|
36
45
|
|
37
|
-
# Validate all resources
|
38
|
-
validate -> { resources.map(&:errors).flatten }
|
39
|
-
|
40
46
|
# Validate all projects (which validate resources)
|
41
|
-
validate -> { projects.map(&:errors).flatten }
|
47
|
+
validate -> { projects.values.map(&:errors).flatten }
|
42
48
|
|
43
|
-
|
49
|
+
# Validate all resources
|
50
|
+
validate -> { all_resources.map(&:errors).flatten }
|
51
|
+
|
52
|
+
before :validation, -> { self.region ||= ENV['AWS_REGION'] }
|
44
53
|
|
45
54
|
def initialize(name, &block)
|
46
55
|
@name = name
|
47
|
-
@projects = []
|
48
56
|
@outputs = []
|
49
57
|
self.send("#{name}?=", true) # e.g. staging?
|
50
58
|
instance_exec(self, &block) if block_given?
|
51
59
|
end
|
52
60
|
|
61
|
+
def project(org, name, &block)
|
62
|
+
project = create_project(org, name, &block)
|
63
|
+
supported_environments = [project.environments].flatten
|
64
|
+
# do not add the project if the project is not supported by this environment
|
65
|
+
return NullObject.new unless supported_environments.include?(@name)
|
66
|
+
|
67
|
+
projects[name] = project
|
68
|
+
end
|
69
|
+
|
53
70
|
def resource(type, id, &block)
|
54
71
|
return find_resource(type, id) unless block_given?
|
55
72
|
resource = create_resource(type, id, &block)
|
@@ -64,53 +81,35 @@ class GeoEngineer::Environment
|
|
64
81
|
end
|
65
82
|
|
66
83
|
def all_resources
|
67
|
-
|
68
|
-
@projects.each { |project| reses += project.all_resources }
|
69
|
-
reses
|
70
|
-
end
|
71
|
-
|
72
|
-
# Factory for creating projects inside an environment
|
73
|
-
def project(org, name, &block)
|
74
|
-
# do not add the project a second time
|
75
|
-
exists = @projects.select { |p| p.org == org && p.name == name }.first
|
76
|
-
return exists if exists
|
77
|
-
|
78
|
-
project = GeoEngineer::Project.new(org, name, self, &block)
|
79
|
-
|
80
|
-
supported_environments = [project.environments].flatten
|
81
|
-
# do not add the project if the project is not supported by this environment
|
82
|
-
return NullObject.new unless supported_environments.include? @name
|
83
|
-
|
84
|
-
@projects << project
|
85
|
-
project
|
84
|
+
[resources, all_template_resources, all_project_resources].flatten
|
86
85
|
end
|
87
86
|
|
88
87
|
# DOT Methods
|
89
88
|
# Given an attribute it tries to identify a dependency and return it
|
90
89
|
def extract_dependencies(x)
|
91
|
-
if x.is_a? Array
|
92
|
-
|
93
|
-
|
94
|
-
|
90
|
+
return x.map { |y| extract_dependencies(y) }.flatten if x.is_a? Array
|
91
|
+
return [x] if x.is_a?(GeoEngineer::Resource)
|
92
|
+
|
93
|
+
if x.is_a?(String)
|
94
|
+
res = find_resource_by_ref(x)
|
95
95
|
return [res] if res
|
96
|
-
elsif x.is_a?(GeoEngineer::Resource)
|
97
|
-
return [x]
|
98
96
|
end
|
97
|
+
|
99
98
|
[]
|
100
99
|
end
|
101
100
|
|
102
101
|
def depends_on(res)
|
103
|
-
all_attributes = []
|
104
|
-
all_attributes
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
102
|
+
all_attributes = [res.attributes.values]
|
103
|
+
all_attributes
|
104
|
+
.concat(res.subresources.map { |sr| sr.attributes.values })
|
105
|
+
.map { |attr| extract_dependencies(attr) }
|
106
|
+
.flatten
|
107
|
+
.compact
|
108
|
+
.uniq
|
109
109
|
end
|
110
110
|
|
111
111
|
def to_dot
|
112
|
-
str = ["digraph {"]
|
113
|
-
str.concat(projects.map(&:to_dot))
|
112
|
+
str = ["digraph {", projects.values.map(&:to_dot)]
|
114
113
|
all_resources.each do |res|
|
115
114
|
str << depends_on(res).map { |r| " #{res.to_ref.inspect} -> #{r.to_ref.inspect}" }
|
116
115
|
end
|
@@ -124,7 +123,8 @@ class GeoEngineer::Environment
|
|
124
123
|
# Hopefully this will stop accidentally the environment
|
125
124
|
unless self.allow_destroy
|
126
125
|
all_resources.each { |r|
|
127
|
-
r.lifecycle {
|
126
|
+
r.lifecycle {} unless r.lifecycle
|
127
|
+
r.lifecycle.prevent_destroy = true
|
128
128
|
}
|
129
129
|
end
|
130
130
|
|
@@ -136,7 +136,8 @@ class GeoEngineer::Environment
|
|
136
136
|
def to_terraform_json
|
137
137
|
unless self.allow_destroy
|
138
138
|
all_resources.each { |r|
|
139
|
-
r.lifecycle {
|
139
|
+
r.lifecycle {} unless r.lifecycle
|
140
|
+
r.lifecycle.prevent_destroy = true
|
140
141
|
}
|
141
142
|
end
|
142
143
|
|
@@ -155,6 +156,7 @@ class GeoEngineer::Environment
|
|
155
156
|
|
156
157
|
def to_terraform_state
|
157
158
|
reses = all_resources.select(&:_terraform_id) # _terraform_id must not be nil
|
159
|
+
|
158
160
|
reses = reses.map { |r| { "#{r.type}.#{r.id}" => r.to_terraform_state() } }.reduce({}, :merge)
|
159
161
|
|
160
162
|
{
|
data/lib/geoengineer/project.rb
CHANGED
@@ -5,11 +5,13 @@
|
|
5
5
|
########################################################################
|
6
6
|
class GeoEngineer::Project
|
7
7
|
include HasAttributes
|
8
|
+
include HasLifecycle
|
8
9
|
include HasResources
|
10
|
+
include HasTemplates
|
11
|
+
include HasSubResources
|
9
12
|
include HasValidations
|
10
13
|
|
11
14
|
attr_accessor :org, :name
|
12
|
-
attr_reader :templates
|
13
15
|
attr_reader :environment
|
14
16
|
|
15
17
|
validate -> { environments.nil? ? "Project #{full_name} must have an environment" : nil }
|
@@ -19,8 +21,8 @@ class GeoEngineer::Project
|
|
19
21
|
@org = org
|
20
22
|
@name = name
|
21
23
|
@environment = environment
|
22
|
-
@templates = {}
|
23
24
|
instance_exec(self, &block) if block_given?
|
25
|
+
execute_lifecycle(:after, :initialize)
|
24
26
|
end
|
25
27
|
|
26
28
|
def full_id_name
|
@@ -40,28 +42,7 @@ class GeoEngineer::Project
|
|
40
42
|
end
|
41
43
|
|
42
44
|
def all_resources
|
43
|
-
|
44
|
-
@templates.each { |name, template| reses += template.all_resources }
|
45
|
-
reses
|
46
|
-
end
|
47
|
-
|
48
|
-
def find_template(type)
|
49
|
-
clazz_name = type.split('_').collect(&:capitalize).join
|
50
|
-
return Object.const_get(clazz_name) if Object.const_defined? clazz_name
|
51
|
-
|
52
|
-
module_clazz = "GeoEngineer::Templates::#{clazz_name}"
|
53
|
-
return Object.const_get(module_clazz) if Object.const_defined? module_clazz
|
54
|
-
|
55
|
-
throw "undefined template '#{type}' for '#{clazz_name}' or 'GeoEngineer::#{clazz_name}'"
|
56
|
-
end
|
57
|
-
|
58
|
-
def from_template(type, name, parameters = {}, &block)
|
59
|
-
throw "Template '#{name}' already defined for project #{full_name}" if @templates[name]
|
60
|
-
clazz = find_template(type)
|
61
|
-
template = clazz.new(name, self, parameters)
|
62
|
-
@templates[name] = template
|
63
|
-
template.instance_exec(*template.template_resources, &block) if block_given?
|
64
|
-
template
|
45
|
+
[resources, all_template_resources].flatten
|
65
46
|
end
|
66
47
|
|
67
48
|
# dot method
|
data/lib/geoengineer/resource.rb
CHANGED
@@ -17,6 +17,8 @@ class GeoEngineer::Resource
|
|
17
17
|
|
18
18
|
attr_reader :type, :id
|
19
19
|
|
20
|
+
before :validation, :merge_project_tags
|
21
|
+
|
20
22
|
validate -> { validate_required_attributes([:_geo_id]) }
|
21
23
|
|
22
24
|
def initialize(type, id, &block)
|
@@ -93,16 +95,48 @@ class GeoEngineer::Resource
|
|
93
95
|
"${#{terraform_name}.#{attribute}}"
|
94
96
|
end
|
95
97
|
|
98
|
+
# This tries to return the terraform ID, if that is nil, then it will return the ref
|
99
|
+
def to_id_or_ref
|
100
|
+
_terraform_id || to_ref
|
101
|
+
end
|
102
|
+
|
103
|
+
def reset
|
104
|
+
reset_attributes
|
105
|
+
@_remote_searched = false
|
106
|
+
@_remote = nil
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
def _json_file(attribute, path, binding_obj = nil)
|
111
|
+
raise "file #{path} not found" unless File.file?(path)
|
112
|
+
|
113
|
+
raw = File.open(path, "rb").read
|
114
|
+
interpolated = ERB.new(raw).result(binding_obj)
|
115
|
+
escaped = interpolated.gsub("$", "$$")
|
116
|
+
|
117
|
+
# normalize JSON to prevent terraform from e.g. newlines as legitimate changes
|
118
|
+
normalized = _normalize_json(escaped)
|
119
|
+
|
120
|
+
send(attribute, normalized)
|
121
|
+
end
|
122
|
+
|
123
|
+
def _normalize_json(json)
|
124
|
+
h = JSON.parse(json)
|
125
|
+
h.to_json
|
126
|
+
end
|
127
|
+
|
96
128
|
# REMOTE METHODS
|
97
129
|
|
98
130
|
# This method will fetch the remote resource that has the same _geo_id as the codified resource.
|
99
131
|
# This method will:
|
100
|
-
# 1. return
|
101
|
-
# 2. return
|
102
|
-
# 3.
|
132
|
+
# 1. return resource individually if class has defined how to do so
|
133
|
+
# 2. return nil if no resource is found
|
134
|
+
# 3. return an instance of Resource with the remote attributes
|
135
|
+
# 4. throw an error if more than one resource has the same _geo_id
|
103
136
|
def _find_remote_resource
|
104
|
-
|
105
|
-
|
137
|
+
return GeoEngineer::Resource.build(remote_resource_params) if find_remote_as_individual?
|
138
|
+
|
139
|
+
matches = matched_remote_resource
|
106
140
|
|
107
141
|
return matches.first if matches.length == 1
|
108
142
|
return nil if matches.empty?
|
@@ -110,25 +144,29 @@ class GeoEngineer::Resource
|
|
110
144
|
throw "ERROR:\"#{self.type}.#{self.id}\" has #{matches.length} remote resources"
|
111
145
|
end
|
112
146
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
147
|
+
# By default, remote resources are bulk-retrieved. In order to fetch a remote resource as an
|
148
|
+
# individual, the child-class over-write 'find_remote_as_individual?' and 'remote_resource_params'
|
149
|
+
def find_remote_as_individual?
|
150
|
+
false
|
151
|
+
end
|
117
152
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
res_hash.each do |k, v|
|
122
|
-
self[k] = v
|
123
|
-
end
|
124
|
-
}
|
125
|
-
end
|
153
|
+
def remote_resource_params
|
154
|
+
{}
|
155
|
+
end
|
126
156
|
|
127
|
-
|
157
|
+
def build_individual_remote_resource
|
158
|
+
self.class.build(remote_resource_params)
|
128
159
|
end
|
129
160
|
|
130
|
-
def
|
131
|
-
|
161
|
+
def matched_remote_resource
|
162
|
+
aws_resources = self.class.fetch_remote_resources()
|
163
|
+
aws_resources.select { |r| r._geo_id == self._geo_id }
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.fetch_remote_resources
|
167
|
+
return @_rr_cache if @_rr_cache
|
168
|
+
resource_hashes = _fetch_remote_resources()
|
169
|
+
@_rr_cache = resource_hashes.map { |res_hash| GeoEngineer::Resource.build(res_hash) }
|
132
170
|
end
|
133
171
|
|
134
172
|
# This method must be implemented for each resource type
|
@@ -137,6 +175,18 @@ class GeoEngineer::Resource
|
|
137
175
|
throw "NOT IMPLEMENTED ERROR for #{self.name}"
|
138
176
|
end
|
139
177
|
|
178
|
+
def self.build(resource_hash)
|
179
|
+
GeoEngineer::Resource.new(self.type_from_class_name, resource_hash['_geo_id']) {
|
180
|
+
resource_hash.each do |k, v|
|
181
|
+
self[k] = v
|
182
|
+
end
|
183
|
+
}
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.clear_remote_resource_cache
|
187
|
+
@_rr_cache = nil
|
188
|
+
end
|
189
|
+
|
140
190
|
# VIEW METHODS
|
141
191
|
def short_type
|
142
192
|
type
|
@@ -162,6 +212,25 @@ class GeoEngineer::Resource
|
|
162
212
|
"for resource \"#{type}.#{id}\" #{in_project}"
|
163
213
|
end
|
164
214
|
|
215
|
+
def setup_tags_if_needed
|
216
|
+
tags {} unless tags
|
217
|
+
end
|
218
|
+
|
219
|
+
def merge_project_tags
|
220
|
+
return unless self.project && self.project.tags && self.support_tags?
|
221
|
+
|
222
|
+
setup_tags_if_needed
|
223
|
+
|
224
|
+
self
|
225
|
+
.project
|
226
|
+
.all_tags
|
227
|
+
.map(&:attributes)
|
228
|
+
.reduce({}, :merge)
|
229
|
+
.each { |key, value| tags.attributes[key] ||= value }
|
230
|
+
|
231
|
+
tags
|
232
|
+
end
|
233
|
+
|
165
234
|
# VALIDATION METHODS
|
166
235
|
def support_tags?
|
167
236
|
true
|