geoengineer 0.1.0 → 0.1.1
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 +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
|
+
[](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
|