chef-provisioning-aws 1.0.4 → 1.1.0
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
- data/README.md +18 -0
- data/Rakefile +5 -0
- data/lib/chef/provider/aws_ebs_volume.rb +14 -4
- data/lib/chef/provider/aws_image.rb +31 -0
- data/lib/chef/provider/aws_instance.rb +14 -0
- data/lib/chef/provider/aws_load_balancer.rb +9 -0
- data/lib/chef/provider/aws_network_interface.rb +209 -0
- data/lib/chef/provider/aws_security_group.rb +9 -4
- data/lib/chef/provider/aws_subnet.rb +16 -1
- data/lib/chef/provider/aws_vpc.rb +16 -0
- data/lib/chef/provisioning/aws_driver/aws_provider.rb +44 -0
- data/lib/chef/provisioning/aws_driver/aws_resource.rb +1 -1
- data/lib/chef/provisioning/aws_driver/driver.rb +6 -5
- data/lib/chef/provisioning/aws_driver/version.rb +1 -1
- data/lib/chef/resource/aws_image.rb +1 -2
- data/lib/chef/resource/aws_instance.rb +1 -2
- data/lib/chef/resource/aws_load_balancer.rb +1 -1
- data/lib/chef/resource/aws_network_interface.rb +23 -5
- data/lib/chef/resource/aws_vpc.rb +0 -8
- data/spec/aws_support.rb +235 -0
- data/spec/aws_support/aws_resource_run_wrapper.rb +45 -0
- data/spec/aws_support/deep_matcher.rb +40 -0
- data/spec/aws_support/deep_matcher/fuzzy_match_objects.rb +57 -0
- data/spec/aws_support/deep_matcher/match_values_failure_messages.rb +145 -0
- data/spec/aws_support/deep_matcher/matchable_array.rb +24 -0
- data/spec/aws_support/deep_matcher/matchable_object.rb +25 -0
- data/spec/aws_support/deep_matcher/rspec_monkeypatches.rb +25 -0
- data/spec/aws_support/delayed_stream.rb +41 -0
- data/spec/aws_support/matchers/create_an_aws_object.rb +60 -0
- data/spec/aws_support/matchers/update_an_aws_object.rb +66 -0
- data/spec/integration/aws_ebs_volume_spec.rb +31 -0
- data/spec/integration/aws_key_pair_spec.rb +21 -0
- data/spec/integration/aws_route_table_spec.rb +40 -0
- data/spec/integration/aws_security_group_spec.rb +7 -5
- data/spec/integration/aws_subnet_spec.rb +56 -0
- data/spec/integration/aws_vpc_spec.rb +109 -0
- data/spec/integration/machine_batch_spec.rb +36 -0
- data/spec/integration/machine_image_spec.rb +49 -0
- data/spec/integration/machine_spec.rb +64 -0
- data/spec/spec_helper.rb +8 -2
- data/spec/unit/aws_driver/credentials_spec.rb +54 -0
- metadata +27 -5
- data/spec/support/aws_support.rb +0 -211
@@ -5,7 +5,7 @@ require 'chef/provisioning/chef_managed_entry_store'
|
|
5
5
|
# Common AWS resource - contains metadata that all AWS resources will need
|
6
6
|
module Chef::Provisioning::AWSDriver
|
7
7
|
class AWSResource < Chef::Provisioning::AWSDriver::SuperLWRP
|
8
|
-
actions :create, :destroy, :nothing
|
8
|
+
actions :create, :destroy, :purge, :nothing
|
9
9
|
default_action :create
|
10
10
|
|
11
11
|
def initialize(name, run_context=nil)
|
@@ -54,6 +54,8 @@ module AWSDriver
|
|
54
54
|
access_key_id: credentials[:aws_access_key_id],
|
55
55
|
secret_access_key: credentials[:aws_secret_access_key],
|
56
56
|
region: region || credentials[:region],
|
57
|
+
proxy_uri: credentials[:proxy_uri] || nil,
|
58
|
+
session_token: credentials[:aws_session_token] || nil,
|
57
59
|
logger: Chef::Log.logger
|
58
60
|
)
|
59
61
|
end
|
@@ -331,6 +333,7 @@ module AWSDriver
|
|
331
333
|
image_options[:description] ||= "Image #{image_spec.name} created from machine #{machine_spec.name}"
|
332
334
|
Chef::Log.debug "AWS Image options: #{image_options.inspect}"
|
333
335
|
image = ec2.images.create(image_options.to_hash)
|
336
|
+
image.add_tag('From-Instance', :value => image_options[:instance_id]) if image_options[:instance_id]
|
334
337
|
image_spec.reference = {
|
335
338
|
'driver_url' => driver_url,
|
336
339
|
'driver_version' => Chef::Provisioning::AWSDriver::VERSION,
|
@@ -428,9 +431,7 @@ EOD
|
|
428
431
|
end
|
429
432
|
|
430
433
|
def allocate_machines(action_handler, specs_and_options, parallelizer)
|
431
|
-
#Chef::Log.warn("#{specs_and_options}")
|
432
434
|
create_servers(action_handler, specs_and_options, parallelizer) do |machine_spec, server|
|
433
|
-
#Chef::Log.warn("#{machine_spec}")
|
434
435
|
yield machine_spec
|
435
436
|
end
|
436
437
|
specs_and_options.keys
|
@@ -812,7 +813,7 @@ EOD
|
|
812
813
|
image ||= image_for(image_spec)
|
813
814
|
time_elapsed = 0
|
814
815
|
sleep_time = 10
|
815
|
-
max_wait_time =
|
816
|
+
max_wait_time = 300
|
816
817
|
if !yield(image)
|
817
818
|
action_handler.report_progress "waiting for #{image_spec.name} (#{image.id} on #{driver_url}) to be ready ..."
|
818
819
|
while time_elapsed < max_wait_time && !yield(image)
|
@@ -821,7 +822,7 @@ EOD
|
|
821
822
|
time_elapsed += sleep_time
|
822
823
|
end
|
823
824
|
unless yield(image)
|
824
|
-
raise "Image #{image.id} did not become ready within
|
825
|
+
raise "Image #{image.id} did not become ready within #{max_wait_time} seconds"
|
825
826
|
end
|
826
827
|
action_handler.report_progress "Image #{image_spec.name} is now ready"
|
827
828
|
end
|
@@ -845,7 +846,7 @@ EOD
|
|
845
846
|
time_elapsed += sleep_time
|
846
847
|
end
|
847
848
|
unless yield(instance)
|
848
|
-
raise "Image #{instance.id} did not become ready within
|
849
|
+
raise "Image #{instance.id} did not become ready within #{max_wait_time} seconds"
|
849
850
|
end
|
850
851
|
action_handler.report_progress "#{machine_spec.name} is now ready"
|
851
852
|
end
|
@@ -3,8 +3,7 @@ require 'chef/provisioning/aws_driver/aws_resource_with_entry'
|
|
3
3
|
class Chef::Resource::AwsImage < Chef::Provisioning::AWSDriver::AWSResourceWithEntry
|
4
4
|
aws_sdk_type AWS::EC2::Image,
|
5
5
|
managed_entry_type: :machine_image,
|
6
|
-
managed_entry_id_name: 'image_id'
|
7
|
-
load_provider: false
|
6
|
+
managed_entry_id_name: 'image_id'
|
8
7
|
|
9
8
|
attribute :name, kind_of: String, name_attribute: true
|
10
9
|
|
@@ -3,8 +3,7 @@ require 'chef/provisioning/aws_driver/aws_resource_with_entry'
|
|
3
3
|
class Chef::Resource::AwsInstance < Chef::Provisioning::AWSDriver::AWSResourceWithEntry
|
4
4
|
aws_sdk_type AWS::EC2::Instance,
|
5
5
|
managed_entry_type: :machine,
|
6
|
-
managed_entry_id_name: 'instance_id'
|
7
|
-
load_provider: false
|
6
|
+
managed_entry_id_name: 'instance_id'
|
8
7
|
|
9
8
|
attribute :name, kind_of: String, name_attribute: true
|
10
9
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'chef/provisioning/aws_driver/aws_resource'
|
2
2
|
|
3
3
|
class Chef::Resource::AwsLoadBalancer < Chef::Provisioning::AWSDriver::AWSResource
|
4
|
-
aws_sdk_type AWS::ELB::LoadBalancer
|
4
|
+
aws_sdk_type AWS::ELB::LoadBalancer
|
5
5
|
|
6
6
|
attribute :name, kind_of: String, name_attribute: true
|
7
7
|
|
@@ -1,16 +1,34 @@
|
|
1
1
|
require 'chef/provisioning/aws_driver/aws_resource'
|
2
|
+
require 'chef/resource/aws_subnet'
|
3
|
+
require 'chef/resource/aws_eip_address'
|
2
4
|
|
3
|
-
class Chef::Resource::AwsNetworkInterface < Chef::Provisioning::AWSDriver::
|
4
|
-
aws_sdk_type AWS::EC2::NetworkInterface
|
5
|
+
class Chef::Resource::AwsNetworkInterface < Chef::Provisioning::AWSDriver::AWSResourceWithEntry
|
6
|
+
aws_sdk_type AWS::EC2::NetworkInterface
|
5
7
|
|
6
|
-
attribute :name,
|
8
|
+
attribute :name, kind_of: String, name_attribute: true
|
7
9
|
|
8
|
-
attribute :network_interface_id,
|
10
|
+
attribute :network_interface_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
|
9
11
|
name =~ /^eni-[a-f0-9]{8}$/ ? name : nil
|
10
12
|
}
|
11
13
|
|
14
|
+
attribute :subnet, kind_of: [ String, AWS::EC2::Subnet, AwsSubnet ]
|
15
|
+
|
16
|
+
attribute :private_ip_address, kind_of: String
|
17
|
+
|
18
|
+
attribute :description, kind_of: String
|
19
|
+
|
20
|
+
attribute :security_groups, kind_of: Array #(Array<SecurityGroup>, Array<String>)
|
21
|
+
|
22
|
+
attribute :machine, kind_of: [ String, FalseClass, AwsInstance, AWS::EC2::Instance ]
|
23
|
+
|
24
|
+
attribute :device_index, kind_of: Integer
|
25
|
+
|
26
|
+
# TODO implement eip address association
|
27
|
+
#attribute :elastic_ip_address, kind_of: [ String, AWS::EC2::ElasticIp, AwsEipAddress, FalseClass ]
|
28
|
+
|
12
29
|
def aws_object
|
13
|
-
|
30
|
+
driver, id = get_driver_and_id
|
31
|
+
result = driver.ec2.network_interfaces[id] if id
|
14
32
|
result && result.exists? ? result : nil
|
15
33
|
end
|
16
34
|
end
|
@@ -125,14 +125,6 @@ class Chef::Resource::AwsVpc < Chef::Provisioning::AWSDriver::AWSResourceWithEnt
|
|
125
125
|
#
|
126
126
|
attribute :enable_dns_hostnames, equal_to: [ true, false ]
|
127
127
|
|
128
|
-
#
|
129
|
-
# A list of tags to put on the VPC.
|
130
|
-
#
|
131
|
-
# The "Name" tag will always be set to the Chef name of the instance if you do
|
132
|
-
# not specify it.
|
133
|
-
#
|
134
|
-
attribute :tags, kind_of: Array
|
135
|
-
|
136
128
|
attribute :vpc_id, kind_of: String, aws_id_attribute: true, lazy_default: proc {
|
137
129
|
name =~ /^vpc-[a-f0-9]{8}$/ ? name : nil
|
138
130
|
}
|
data/spec/aws_support.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
#
|
2
|
+
# Provides a `with_aws` method that when used in your tests will create a new
|
3
|
+
# context pointed at the user's chosen driver, and helper methods to create
|
4
|
+
# AWS objects and clean them up.
|
5
|
+
#
|
6
|
+
module AWSSupport
|
7
|
+
require 'cheffish/rspec/chef_run_support'
|
8
|
+
def self.extended(other)
|
9
|
+
other.extend Cheffish::RSpec::ChefRunSupport
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'chef/provisioning/aws_driver'
|
13
|
+
require 'aws_support/matchers/create_an_aws_object'
|
14
|
+
require 'aws_support/matchers/update_an_aws_object'
|
15
|
+
require 'aws_support/delayed_stream'
|
16
|
+
require 'chef/provisioning/aws_driver/resources'
|
17
|
+
require 'aws_support/aws_resource_run_wrapper'
|
18
|
+
|
19
|
+
# Add AWS to the list of objects which can be matched against a Hash or Array
|
20
|
+
require 'aws'
|
21
|
+
require 'aws_support/deep_matcher/matchable_object'
|
22
|
+
require 'aws_support/deep_matcher/matchable_array'
|
23
|
+
DeepMatcher::MatchableObject.matchable_classes << proc { |o| o.class.name =~ /^AWS::EC2($|::)/ }
|
24
|
+
DeepMatcher::MatchableArray.matchable_classes << AWS::Core::Data::List
|
25
|
+
|
26
|
+
def purge_all
|
27
|
+
before :all do
|
28
|
+
driver = self.driver
|
29
|
+
recipe do
|
30
|
+
driver.ec2.vpcs.with_tag('Name', 'test_vpc').each do |vpc|
|
31
|
+
aws_vpc vpc do
|
32
|
+
action :purge
|
33
|
+
end
|
34
|
+
end
|
35
|
+
aws_key_pair 'test_key_pair' do
|
36
|
+
action :purge
|
37
|
+
end
|
38
|
+
end.converge
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def setup_public_vpc
|
43
|
+
aws_vpc 'test_vpc' do
|
44
|
+
cidr_block '10.0.0.0/24'
|
45
|
+
internet_gateway true
|
46
|
+
enable_dns_hostnames true
|
47
|
+
main_routes '0.0.0.0/0' => :internet_gateway
|
48
|
+
end
|
49
|
+
|
50
|
+
aws_key_pair 'test_key_pair' do
|
51
|
+
allow_overwrite true
|
52
|
+
end
|
53
|
+
|
54
|
+
before :context do
|
55
|
+
image = driver.ec2.images.filter('name', 'test_machine_image').first
|
56
|
+
image.delete if image
|
57
|
+
|
58
|
+
default_sg = test_vpc.aws_object.security_groups.filter('group-name', 'default').first
|
59
|
+
recipe do
|
60
|
+
aws_security_group default_sg do
|
61
|
+
inbound_rules '0.0.0.0/0' => 22
|
62
|
+
end
|
63
|
+
end.converge
|
64
|
+
end
|
65
|
+
|
66
|
+
aws_subnet 'test_public_subnet' do
|
67
|
+
vpc 'test_vpc'
|
68
|
+
map_public_ip_on_launch true
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def with_aws(description, *tags, &block)
|
73
|
+
aws_driver = nil
|
74
|
+
context_block = proc do
|
75
|
+
extend WithAWSClassMethods
|
76
|
+
include WithAWSInstanceMethods
|
77
|
+
|
78
|
+
@@driver = aws_driver
|
79
|
+
def self.driver
|
80
|
+
@@driver
|
81
|
+
end
|
82
|
+
|
83
|
+
module_eval(&block)
|
84
|
+
end
|
85
|
+
|
86
|
+
if ENV['AWS_TEST_DRIVER']
|
87
|
+
aws_driver = Chef::Provisioning.driver_for_url(ENV['AWS_TEST_DRIVER'])
|
88
|
+
when_the_repository "exists #{description ? "and #{description}" : ""}", *tags, &context_block
|
89
|
+
else
|
90
|
+
# warn <<EOM
|
91
|
+
# --------------------------------------------------------------------------------------------------------------------------
|
92
|
+
# AWS_TEST_DRIVER not set ... cannot run AWS test. Set AWS_TEST_DRIVER=aws or aws:profile:region to run tests that hit AWS.
|
93
|
+
# --------------------------------------------------------------------------------------------------------------------------
|
94
|
+
# EOM
|
95
|
+
skip "AWS_TEST_DRIVER not set ... cannot run AWS tests. Set AWS_TEST_DRIVER=aws or aws:profile:region to run tests that hit AWS." do
|
96
|
+
context description, *tags, &context_block
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
module WithAWSClassMethods
|
102
|
+
instance_eval do
|
103
|
+
#
|
104
|
+
# Create a context-level method for each AWS resource:
|
105
|
+
#
|
106
|
+
# with_aws do
|
107
|
+
# context 'mycontext' do
|
108
|
+
# aws_vpc 'myvpc' do
|
109
|
+
# ...
|
110
|
+
# end
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# Creates the AWS thing when the first example in the context runs.
|
115
|
+
# Destroys it after the last example in the context runs. Objects created
|
116
|
+
# in the order declared, and destroyed in reverse order.
|
117
|
+
#
|
118
|
+
Chef::Provisioning::AWSDriver::Resources.constants.each do |resource_class|
|
119
|
+
resource_class = Chef::Provisioning::AWSDriver::Resources.const_get(resource_class)
|
120
|
+
resource_name = resource_class.resource_name
|
121
|
+
# def aws_vpc(name, &block)
|
122
|
+
define_method(resource_name) do |name, &block|
|
123
|
+
# def myvpc
|
124
|
+
# @@myvpc
|
125
|
+
# end
|
126
|
+
instance_eval do
|
127
|
+
define_method(name) { class_variable_get(:"@@#{name}") }
|
128
|
+
end
|
129
|
+
module_eval do
|
130
|
+
define_method(name) { self.class.class_variable_get(:"@@#{name}") }
|
131
|
+
end
|
132
|
+
|
133
|
+
resource = nil
|
134
|
+
|
135
|
+
before :context do
|
136
|
+
resource = AWSResourceRunWrapper.new(self, resource_name, name, &block)
|
137
|
+
# @myvpc = resource
|
138
|
+
begin
|
139
|
+
self.class.class_variable_set(:"@@#{name}", resource.resource)
|
140
|
+
rescue NameError
|
141
|
+
end
|
142
|
+
begin
|
143
|
+
resource.converge
|
144
|
+
rescue
|
145
|
+
puts "ERROR #{$!}"
|
146
|
+
puts $!.backtrace.join("\n")
|
147
|
+
raise
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
after :context do
|
152
|
+
resource.destroy if resource
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
module WithAWSInstanceMethods
|
160
|
+
def self.included(context)
|
161
|
+
context.module_eval do
|
162
|
+
after :example do
|
163
|
+
# Close up delayed streams so they don't print out their garbage later in the run
|
164
|
+
delayed_streams.each { |s| s.close }
|
165
|
+
|
166
|
+
# Destroy any objects we know got created during the test
|
167
|
+
created_during_test.reverse_each do |resource_name, name|
|
168
|
+
(recipe do
|
169
|
+
public_send(resource_name, name) do
|
170
|
+
action :purge
|
171
|
+
end
|
172
|
+
end).converge
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
#
|
179
|
+
# expect_recipe { }.to create_an_aws_vpc
|
180
|
+
# expect_recipe { }.to update_an_aws_security_object
|
181
|
+
#
|
182
|
+
Chef::Provisioning::AWSDriver::Resources.constants.each do |resource_class|
|
183
|
+
resource_class = Chef::Provisioning::AWSDriver::Resources.const_get(resource_class)
|
184
|
+
resource_name = resource_class.resource_name
|
185
|
+
define_method("update_an_#{resource_name}") do |name, expected_updates={}|
|
186
|
+
AWSSupport::Matchers::UpdateAnAWSObject.new(self, resource_class, name, expected_updates)
|
187
|
+
end
|
188
|
+
define_method("create_an_#{resource_name}") do |name, expected_values={}|
|
189
|
+
AWSSupport::Matchers::CreateAnAWSObject.new(self, resource_class, name, expected_values)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def chef_config
|
194
|
+
@chef_config ||= {
|
195
|
+
driver: driver,
|
196
|
+
stdout: delayed_stream(delay_before_streaming, STDOUT),
|
197
|
+
stderr: delayed_stream(delay_before_streaming, STDERR),
|
198
|
+
log_location: delayed_stream(delay_before_streaming_logs, STDOUT),
|
199
|
+
log_level: :info
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def delayed_streams
|
204
|
+
@delayed_streams ||= []
|
205
|
+
end
|
206
|
+
|
207
|
+
def delayed_stream(delay, stream)
|
208
|
+
stream = DelayedStream.new(delay, stream)
|
209
|
+
delayed_streams << stream
|
210
|
+
stream
|
211
|
+
end
|
212
|
+
|
213
|
+
# Override in tests if you want different numbers
|
214
|
+
def delay_before_streaming_logs
|
215
|
+
30
|
216
|
+
end
|
217
|
+
|
218
|
+
def delay_before_streaming
|
219
|
+
10
|
220
|
+
end
|
221
|
+
|
222
|
+
def created_during_test
|
223
|
+
@created_during_test ||= []
|
224
|
+
end
|
225
|
+
|
226
|
+
def default_vpc
|
227
|
+
@default_vpc ||= driver.ec2.vpcs.filter('isDefault', 'true').first
|
228
|
+
end
|
229
|
+
|
230
|
+
def driver
|
231
|
+
self.class.driver
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'cheffish/rspec/recipe_run_wrapper'
|
2
|
+
|
3
|
+
module AWSSupport
|
4
|
+
class AWSResourceRunWrapper < Cheffish::RSpec::RecipeRunWrapper
|
5
|
+
def initialize(example, resource_type, name, &properties)
|
6
|
+
super(example.chef_config) do
|
7
|
+
if properties && properties.parameters.size > 0
|
8
|
+
public_send(resource_type, name) { instance_exec(example, &properties) }
|
9
|
+
else
|
10
|
+
public_send(resource_type, name, &properties)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@example = example
|
14
|
+
@resource_type = resource_type
|
15
|
+
@name = name
|
16
|
+
@properties = properties
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :example
|
20
|
+
attr_reader :resource_type
|
21
|
+
attr_reader :name
|
22
|
+
|
23
|
+
def resource
|
24
|
+
resources.first
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"#{resource_type}[#{name}]"
|
29
|
+
end
|
30
|
+
|
31
|
+
def destroy
|
32
|
+
resource_type = self.resource_type
|
33
|
+
name = self.name
|
34
|
+
example.recipe do
|
35
|
+
public_send(resource_type, name) do
|
36
|
+
action :purge
|
37
|
+
end
|
38
|
+
end.converge
|
39
|
+
end
|
40
|
+
|
41
|
+
def aws_object
|
42
|
+
resource.aws_object
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module AWSSupport
|
2
|
+
#
|
3
|
+
# Include this and override `match_failure_messages`, and your class becomes
|
4
|
+
# a matcher which will have `matches?` call `match_failure_messages` and
|
5
|
+
# cache the result, which is then returned verbatim from failure_message.
|
6
|
+
#
|
7
|
+
module DeepMatcher
|
8
|
+
|
9
|
+
require 'aws_support/deep_matcher/match_values_failure_messages'
|
10
|
+
|
11
|
+
include MatchValuesFailureMessages
|
12
|
+
|
13
|
+
def matches?(actual)
|
14
|
+
@failure_messages = match_failure_messages(actual)
|
15
|
+
@failure_messages.empty?
|
16
|
+
end
|
17
|
+
|
18
|
+
def failure_message
|
19
|
+
@failure_messages.flat_map { |message| message.split("\n") }.join("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
def failure_message_when_negated
|
23
|
+
"expected #{@actual.inspect} not to #{description}"
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Return the failure message resulting from matching `actual`. Meant to be
|
28
|
+
# overridden in implementors.
|
29
|
+
#
|
30
|
+
# @param actual The actual value to compare against
|
31
|
+
# @param identifier The name of the thing being compared (may not be passed,
|
32
|
+
# in which case the class will choose an appropriate default.)
|
33
|
+
#
|
34
|
+
# @return A failure message, or empty string if it does not fail.
|
35
|
+
#
|
36
|
+
def match_failure_messages(actual, identifier='value')
|
37
|
+
raise NotImplementedError, :match_failure_messages
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|