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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/README.md +18 -8
- data/lib/geoengineer/cli/geo_cli.rb +4 -1
- data/lib/geoengineer/environment.rb +1 -1
- data/lib/geoengineer/resources/aws_cloudtrail.rb +20 -0
- data/lib/geoengineer/resources/aws_cloudwatch_event_rule.rb +26 -0
- data/lib/geoengineer/resources/aws_cloudwatch_event_target.rb +50 -0
- data/lib/geoengineer/resources/aws_db_instance.rb +5 -1
- data/lib/geoengineer/resources/aws_dynamodb_table.rb +24 -0
- data/lib/geoengineer/resources/aws_elasticache_cluster.rb +22 -9
- data/lib/geoengineer/resources/aws_iam_policy.rb +1 -0
- data/lib/geoengineer/resources/aws_iam_role_policy.rb +63 -0
- data/lib/geoengineer/resources/aws_kinesis_stream.rb +40 -0
- data/lib/geoengineer/resources/aws_lambda_alias.rb +18 -3
- data/lib/geoengineer/resources/aws_lambda_function.rb +11 -0
- data/lib/geoengineer/resources/aws_lambda_permission.rb +9 -0
- data/lib/geoengineer/resources/aws_load_balancer_backend_server_policy.rb +48 -0
- data/lib/geoengineer/resources/aws_load_balancer_policy.rb +43 -0
- data/lib/geoengineer/resources/aws_network_acl_rule.rb +27 -2
- data/lib/geoengineer/resources/aws_redshift_cluster.rb +10 -4
- data/lib/geoengineer/resources/aws_route.rb +2 -0
- data/lib/geoengineer/resources/aws_vpn_gateway_attachment.rb +8 -0
- data/lib/geoengineer/sub_resource.rb +13 -3
- data/lib/geoengineer/utils/aws_clients.rb +18 -2
- data/lib/geoengineer/utils/has_projects.rb +3 -1
- data/lib/geoengineer/utils/has_validations.rb +7 -0
- data/lib/geoengineer/version.rb +1 -1
- data/spec/resource_spec.rb +14 -0
- data/spec/resources/aws_cloudtrail_spec.rb +35 -0
- data/spec/resources/aws_cloudwatch_event_rule_spec.rb +33 -0
- data/spec/resources/aws_cloudwatch_event_target_spec.rb +54 -0
- data/spec/resources/aws_dynamodb_table_spec.rb +24 -0
- data/spec/resources/aws_iam_role_spec.rb +21 -18
- data/spec/resources/aws_iam_rule_policy_spec.rb +49 -0
- data/spec/resources/{aws_iam_user.rb → aws_iam_user_spec.rb} +9 -6
- data/spec/resources/aws_kinesis_stream_spec.rb +52 -0
- data/spec/resources/aws_lambda_alias_spec.rb +22 -3
- data/spec/resources/aws_load_balancer_backend_server_policy_spec.rb +42 -0
- data/spec/resources/aws_load_balancer_policy_spec.rb +44 -0
- data/spec/resources/aws_network_acl_rule_spec.rb +29 -1
- data/spec/resources/{aws_ses_receipt_rule_set.rb → aws_ses_receipt_rule_set_spec.rb} +13 -6
- data/spec/resources/{aws_ses_receipt_rule.rb → aws_ses_receipt_rule_spec.rb} +6 -5
- data/spec/resources/{aws_sns_topic_subscription.rb → aws_sns_topic_subscription_spec.rb} +0 -0
- data/spec/resources/aws_subnet_spec.rb +2 -2
- data/spec/resources/aws_vpc_dhcp_options_association_spec.rb +2 -2
- data/spec/resources/aws_vpc_spec.rb +2 -2
- data/spec/sub_resource_spec.rb +12 -0
- data/spec/utils/has_subresources_spec.rb +9 -0
- data/spec/utils/has_validations_spec.rb +23 -3
- metadata +34 -10
- 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
|
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]
|
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
|
-
|
10
|
-
[:
|
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
|
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
|
-
"
|
29
|
+
" #{k.to_s.inspect} = #{v.inspect}"
|
27
30
|
}
|
28
|
-
|
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
|
-
|
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.
|
65
|
-
@
|
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 = []
|
data/lib/geoengineer/version.rb
CHANGED
data/spec/resource_spec.rb
CHANGED
@@ -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
|