dockerspec 0.2.0 → 0.3.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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/LICENSE +1 -1
  4. data/README.md +190 -24
  5. data/Rakefile +60 -6
  6. data/TODO.md +3 -2
  7. data/lib/dockerspec.rb +1 -2
  8. data/lib/dockerspec/builder.rb +13 -6
  9. data/lib/dockerspec/builder/config_helpers.rb +8 -3
  10. data/lib/dockerspec/builder/logger/ci.rb +2 -2
  11. data/lib/dockerspec/builder/matchers.rb +32 -51
  12. data/lib/dockerspec/configuration.rb +188 -0
  13. data/lib/dockerspec/docker_exception_parser.rb +2 -2
  14. data/lib/dockerspec/engine/base.rb +156 -0
  15. data/lib/dockerspec/engine/infrataster.rb +87 -0
  16. data/lib/dockerspec/engine/specinfra.rb +130 -0
  17. data/lib/dockerspec/engine/specinfra/backend.rb +163 -0
  18. data/lib/dockerspec/{serverspec/specinfra_hack.rb → engine/specinfra/backend_hack.rb} +17 -3
  19. data/lib/dockerspec/engine_list.rb +150 -0
  20. data/lib/dockerspec/exceptions.rb +13 -0
  21. data/lib/dockerspec/helper/multiple_sources_description.rb +3 -3
  22. data/lib/dockerspec/helper/rspec_example_helpers.rb +86 -6
  23. data/lib/dockerspec/infrataster.rb +26 -0
  24. data/lib/dockerspec/rspec.rb +21 -0
  25. data/lib/dockerspec/{rspec_configuration.rb → rspec/configuration.rb} +1 -1
  26. data/lib/dockerspec/rspec/resources.rb +602 -0
  27. data/lib/dockerspec/rspec/resources/its_container.rb +110 -0
  28. data/lib/dockerspec/{rspec_settings.rb → rspec/settings.rb} +5 -1
  29. data/lib/dockerspec/runner.rb +2 -357
  30. data/lib/dockerspec/runner/base.rb +367 -0
  31. data/lib/dockerspec/runner/compose.rb +322 -0
  32. data/lib/dockerspec/runner/docker.rb +302 -0
  33. data/lib/dockerspec/runner/serverspec.rb +21 -0
  34. data/lib/dockerspec/runner/serverspec/base.rb +185 -0
  35. data/lib/dockerspec/runner/serverspec/compose.rb +106 -0
  36. data/lib/dockerspec/runner/serverspec/docker.rb +116 -0
  37. data/lib/dockerspec/runner/serverspec/rspec.rb +20 -0
  38. data/lib/dockerspec/{serverspec/rspec_settings.rb → runner/serverspec/rspec/settings.rb} +1 -1
  39. data/lib/dockerspec/serverspec.rb +12 -2
  40. data/lib/dockerspec/version.rb +1 -1
  41. data/spec/spec_helper.rb +6 -2
  42. metadata +84 -15
  43. data/lib/dockerspec/rspec_assertions.rb +0 -54
  44. data/lib/dockerspec/rspec_resources.rb +0 -203
  45. data/lib/dockerspec/serverspec/rspec_resources.rb +0 -179
  46. data/lib/dockerspec/serverspec/runner.rb +0 -305
  47. data/lib/dockerspec/serverspec/specinfra_backend.rb +0 -128
@@ -0,0 +1,110 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2016 Xabier de Zuazo
5
+ # License:: Apache License, Version 2.0
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'dockerspec/runner/compose'
21
+ require 'dockerspec/helper/rspec_example_helpers'
22
+ require 'dockerspec/exceptions'
23
+
24
+ module Dockerspec
25
+ module RSpec
26
+ module Resources
27
+ #
28
+ # This generates the object to use within `its_container` calls.
29
+ #
30
+ class ItsContainer
31
+ #
32
+ # A message with description on how to avoid the error when you forget
33
+ # specifying the docker container you want to test with Docker Compose.
34
+ #
35
+ NO_DOCKER_COMPOSE_MESSAGE = <<-EOE
36
+
37
+ `its_container` can only be used within a `docker_compose` resource.
38
+
39
+ For example:
40
+
41
+ describe docker_compose('docker-compose.yml', wait: 30) do
42
+ its_container(:mysql) do
43
+ # [...]
44
+ end
45
+ end
46
+
47
+ EOE
48
+ .freeze
49
+
50
+ #
51
+ # Constructs a `its_container` object.
52
+ #
53
+ # @param container [String] The name of the container.
54
+ #
55
+ # @return [Dockerspec::RSpec::Resource::ItsContainer] The
56
+ # `its_container` object.
57
+ #
58
+ # @api public
59
+ #
60
+ def initialize(container)
61
+ @container = container
62
+ end
63
+
64
+ #
65
+ # Restores the testing context from the RSpec metadata.
66
+ #
67
+ # Searches for {Dockerspec::Runner::Compose} objects in the RSpec
68
+ # metadata and restores their context to run the tests.
69
+ #
70
+ # This is called from the `before` block in the
71
+ # *lib/dockerspec/runner/base.rb* file:
72
+ #
73
+ # ```ruby
74
+ # RSpec.configure do |c|
75
+ # c.before(:each) do
76
+ # metadata = RSpec.current_example.metadata
77
+ # Dockerspec::Helper::RSpecExampleHelpers
78
+ # .restore_rspec_context(metadata)
79
+ # end
80
+ # end
81
+ # ```
82
+ #
83
+ # @return void
84
+ #
85
+ # @api public
86
+ #
87
+ def restore_rspec_context
88
+ metadata = ::RSpec.current_example.metadata
89
+ compose =
90
+ Helper::RSpecExampleHelpers
91
+ .search_object(metadata, Dockerspec::Runner::Compose)
92
+ raise ItsContainerError, NO_DOCKER_COMPOSE_MESSAGE if compose.nil?
93
+ compose.restore_rspec_context
94
+ compose.select_container(@container)
95
+ end
96
+
97
+ #
98
+ # Gets the description for the `its_container` resource.
99
+ #
100
+ # @return [String] The description.
101
+ #
102
+ # @api public
103
+ #
104
+ def to_s
105
+ "\"#{@container}\" container"
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
  #
3
3
  # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
- # Copyright:: Copyright (c) 2015 Xabier de Zuazo
4
+ # Copyright:: Copyright (c) 2015-2016 Xabier de Zuazo
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -26,4 +26,8 @@ RSpec.configure do |c|
26
26
  c.add_setting :dockerfile_path
27
27
  c.add_setting :rm_build
28
28
  c.add_setting :log_level
29
+
30
+ # Docker Compose settings:
31
+ c.add_setting :docker_wait
32
+ c.add_setting :container_name
29
33
  end
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
  #
3
3
  # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
- # Copyright:: Copyright (c) 2015 Xabier de Zuazo
4
+ # Copyright:: Copyright (c) 2015-2016 Xabier de Zuazo
5
5
  # License:: Apache License, Version 2.0
6
6
  #
7
7
  # Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,359 +17,4 @@
17
17
  # limitations under the License.
18
18
  #
19
19
 
20
- require 'docker'
21
- require 'dockerspec/docker_gem'
22
- require 'dockerspec/exceptions'
23
- require 'dockerspec/helper/multiple_sources_description'
24
- require 'dockerspec/docker_exception_parser'
25
-
26
- module Dockerspec
27
- #
28
- # This class runs a docker image (without using Serverspec for that).
29
- #
30
- # This class is not used much, only inherited by
31
- # {Dockerspec::Serverspec::Runner}, which uses Serverspec to run the images.
32
- # Some of the methods here are used there, others not.
33
- #
34
- class Runner
35
- include Dockerspec::Helper::MultipleSourcesDescription
36
-
37
- #
38
- # Constructs a Docker runner class to run Docker images.
39
- #
40
- # @example From a Running Docker Image
41
- # Dockerspec::Runner.new('debian:8') #=> #<Dockerspec::Runner:0x0124>
42
- #
43
- # @example From a Running Docker Container ID
44
- # # This does not start any new container
45
- # Dockerspec::Runner.new(id: 'c51f86c28340')
46
- # #=> #<Dockerspec::Runner:0x0124>
47
- #
48
- # @example From a Running Docker Container Image Name
49
- # Dockerspec::Runner.new('my-debian') #=> #<Dockerspec::Runner:0x0125>
50
- #
51
- # @param opts [String, Hash] The `:tag` or a list of options.
52
- #
53
- # @option opts [String] :tag The Docker image tag name to run.
54
- # @option opts [String] :id The Docker container ID to use instead of
55
- # starting a new container.
56
- # @option opts [Boolean] :rm (calculated) Whether to remove the Docker
57
- # container afterwards.
58
- # @option opts [String] :path The environment `PATH` value of the
59
- # container.
60
- # @option opts [Hash, Array] :env Some `ENV` instructions to add to the
61
- # container.
62
- #
63
- # @return [Dockerspec::Runner] Runner object.
64
- #
65
- # @api public
66
- #
67
- def initialize(*opts)
68
- @options = parse_options(opts)
69
- send("setup_from_#{source}", @options[source])
70
- ObjectSpace.define_finalizer(self, proc { finalize })
71
- end
72
-
73
- #
74
- # Runs the Docker Container.
75
- #
76
- # @example
77
- # builder = Dockerspec::Builder.new('.')
78
- # builder.build
79
- # runner = Dockerspec::Runner.new(builder)
80
- # runner.run #=> #<Dockerspec::Runner:0x0123>
81
- #
82
- # @return [Dockerspec::Runner] Runner object.
83
- #
84
- # @api public
85
- #
86
- def run
87
- create_container
88
- run_container
89
- self
90
- end
91
-
92
- #
93
- # Gets the Docker container ID.
94
- #
95
- # @example
96
- # builder = Dockerspec::Builder.new('.').build
97
- # runner = Dockerspec::Runner.new(builder).run
98
- # runner.id #=> "b8ba0befc716[...]"
99
- #
100
- # @return [String] Container ID.
101
- #
102
- # @api public
103
- #
104
- def id
105
- return nil unless @container.respond_to?(:id)
106
- @container.id
107
- end
108
-
109
- #
110
- # Gets the Docker image ID.
111
- #
112
- # @example
113
- # builder = Dockerspec::Builder.new('.').build
114
- # runner = Dockerspec::Runner.new(builder)
115
- # runner.image_id #=> "c51f86c28340[...]"
116
- #
117
- # @return [String] Image ID.
118
- #
119
- # @api public
120
- #
121
- def image_id
122
- return @build.id unless @build.nil?
123
- @container.json['Image']
124
- end
125
-
126
- #
127
- # Stops and deletes the Docker Container.
128
- #
129
- # Automatically called when `:rm` option is enabled.
130
- #
131
- # @return void
132
- #
133
- # @api public
134
- #
135
- def finalize
136
- return unless @options[:rm] && !@container.nil?
137
- @container.stop
138
- @container.delete
139
- end
140
-
141
- #
142
- # Gets a descriptions of the object.
143
- #
144
- # @example Running from a Container Image ID
145
- # r = Dockerspec::Runner.new('debian')
146
- # r.to_s #=> "Docker Run from tag: \"debian\""
147
- #
148
- # @example Attaching to a Running Container ID
149
- # r = Dockerspec::Runner.new(id: '92cc98ab560a')
150
- # r.to_s #=> "Docker Run from id: \"92cc98ab560a\""
151
- #
152
- # @return [String] The object description.
153
- #
154
- # @api public
155
- #
156
- def to_s
157
- description('Docker Run from')
158
- end
159
-
160
- protected
161
-
162
- #
163
- # Gets the source to start the container from.
164
- #
165
- # Possible values: `:tag`, `:id`.
166
- #
167
- # @example Start the Container from an Image Tag
168
- # self.source #=> :tag
169
- #
170
- # @example Attach to a Running Container ID
171
- # self.source #=> :id
172
- #
173
- # @return [Symbol] The source.
174
- #
175
- # @api private
176
- #
177
- def source
178
- return @source unless @source.nil?
179
- @source = %i(tag id).find { |from| @options.key?(from) }
180
- end
181
-
182
- #
183
- # Generates a description from Docker tag name.
184
- #
185
- # @example
186
- # self.description_from_tag('debian') #=> "debian"
187
- # self.description_from_tag('92cc98ab560a92cc98ab560[...]')
188
- # #=> "92cc98ab560a"
189
- #
190
- # @return [String] The description, shortened if necessary.
191
- #
192
- # @see Dockerspec::Helper::MultipleSourceDescription#description_from_docker
193
- #
194
- # @api private
195
- #
196
- alias_method :description_from_tag, :description_from_docker
197
-
198
- #
199
- # Gets the default options configured using `RSpec.configuration`.
200
- #
201
- # @example
202
- # self.rspec_options #=> {}
203
- #
204
- # @return [Hash] The configuration options.
205
- #
206
- # @api private
207
- #
208
- def rspec_options
209
- {}
210
- end
211
-
212
- #
213
- # Gets the default configuration options after merging them with RSpec
214
- # configuration options.
215
- #
216
- # @example
217
- # self.default_options #=> {}
218
- #
219
- # @return [Hash] The configuration options.
220
- #
221
- # @api private
222
- #
223
- def default_options
224
- {}.merge(rspec_options)
225
- end
226
-
227
- #
228
- # Ensures that the passed options are correct.
229
- #
230
- # Currently this only checks that you passed the `:tag` or the `:id`
231
- # argument.
232
- #
233
- # @return void
234
- #
235
- # @raise [Dockerspec::DockerRunArgumentError] Raises this exception when
236
- # the required fields are missing.
237
- #
238
- # @api private
239
- #
240
- def assert_options!(opts)
241
- return if opts[:tag].is_a?(String) || opts[:id].is_a?(String)
242
- fail DockerRunArgumentError, 'You need to pass the `:tag` or the `:id` '\
243
- 'option to the #docker_run method.'
244
- end
245
-
246
- #
247
- # Parses the configuration options passed to the constructor.
248
- #
249
- # @example
250
- # self.parse_options #=> {:rm=>true}
251
- #
252
- # @param opts [Array<String, Hash>] The list of options. The strings will
253
- # be interpreted as `:tag`, others will be merged.
254
- #
255
- # @return [Hash] The configuration options.
256
- #
257
- # @raise [Dockerspec::DockerRunArgumentError] Raises this exception when
258
- # some required fields are missing.
259
- #
260
- # @see #initialize
261
- #
262
- # @api private
263
- #
264
- def parse_options(opts)
265
- opts_hs_ary = opts.map { |x| x.is_a?(Hash) ? x : { tag: x } }
266
- result = opts_hs_ary.reduce(default_options) { |a, e| a.merge(e) }
267
- assert_options!(result)
268
- result
269
- end
270
-
271
- #
272
- # Generates the build object from the Docker image tag.
273
- #
274
- # Saves the build internally.
275
- #
276
- # @param tag [String] The image name or ID.
277
- #
278
- # @return void
279
- #
280
- # @api private
281
- #
282
- def setup_from_tag(tag)
283
- @build = Builder.new(id: tag).build
284
- end
285
-
286
- #
287
- # Generates the container object from a running Docker container.
288
- #
289
- # Saves the container internally.
290
- #
291
- # @param id [String] The container ID or name.
292
- #
293
- # @return void
294
- #
295
- # @api private
296
- #
297
- def setup_from_id(id)
298
- @container = ::Docker::Container.get(id)
299
- rescue ::Docker::Error::DockerError => e
300
- DockerExceptionParser.new(e)
301
- end
302
-
303
- #
304
- # Ensures that the Docker container has a correct `CMD`.
305
- #
306
- # @param opts [Hash] {Docker::Container} options.
307
- #
308
- # @return [Hash] {Docker::Container} options.
309
- #
310
- # @api private
311
- #
312
- def add_container_cmd_option(opts)
313
- opts['Cmd'] = %w(/bin/sh) if @build.cmd.nil?
314
- opts
315
- end
316
-
317
- #
318
- # Adds some `ENV` options to the Docker container.
319
- #
320
- # @param opts [Hash] {Docker::Container} options.
321
- #
322
- # @return [Hash] {Docker::Container} options.
323
- #
324
- # @api private
325
- #
326
- def add_container_env_options(opts)
327
- opts['Env'] = opts['Env'].to_a << "PATH=#{path}" if @options.key?(:path)
328
- env = @options[:env].to_a.map { |v| v.join('=') }
329
- opts['Env'] = opts['Env'].to_a.concat(env)
330
- opts
331
- end
332
-
333
- #
334
- # Generates the Docker container options for {Docker::Container}.
335
- #
336
- # @return [Hash] The container options.
337
- #
338
- # @api private
339
- #
340
- def container_options
341
- opts = { 'Image' => image_id, 'OpenStdin' => true }
342
-
343
- add_container_cmd_option(opts)
344
- add_container_env_options(opts)
345
- opts
346
- end
347
-
348
- #
349
- # Creates the Docker container.
350
- #
351
- # *Note: Based on Specinfra `:docker` backend code.*
352
- #
353
- # @return void
354
- #
355
- # @api private
356
- #
357
- def create_container
358
- return @container unless @container.nil?
359
- @container = ::Docker::Container.create(container_options)
360
- rescue ::Docker::Error::DockerError => e
361
- DockerExceptionParser.new(e)
362
- end
363
-
364
- #
365
- # Runs the Docker container.
366
- #
367
- # @return void
368
- #
369
- # @api private
370
- #
371
- def run_container
372
- @container.start
373
- end
374
- end
375
- end
20
+ require 'dockerspec/runner/docker'