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,302 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015-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/docker_gem'
21
+ require 'dockerspec/exceptions'
22
+ require 'dockerspec/helper/multiple_sources_description'
23
+ require 'dockerspec/docker_exception_parser'
24
+ require 'dockerspec/runner/base'
25
+
26
+ module Dockerspec
27
+ #
28
+ # Contains the classes related to creating and starting docker containers.
29
+ #
30
+ module Runner
31
+ #
32
+ # This class runs a docker image (without using Serverspec for that).
33
+ #
34
+ # This class is used mainly when you are not using Serverspec to run the
35
+ # tests.
36
+ #
37
+ class Docker < Base
38
+ include Dockerspec::Helper::MultipleSourcesDescription
39
+
40
+ #
41
+ # @return [Symbol] The option key to set when you pass a string instead
42
+ # of a hash of options.
43
+ #
44
+ OPTIONS_DEFAULT_KEY = :tag
45
+
46
+ #
47
+ # The internal {Docker::Container} object.
48
+ #
49
+ # @return [Docker::Container] The container.
50
+ #
51
+ attr_reader :container
52
+
53
+ #
54
+ # Constructs a Docker runner class to run Docker images.
55
+ #
56
+ # @example From a Running Docker Image
57
+ # Dockerspec::Runner::Docker.new('debian:8')
58
+ # #=> #<Dockerspec::Runner::Docker:0x0124>
59
+ #
60
+ # @example From a Running Docker Container ID
61
+ # # This does not start any new container
62
+ # Dockerspec::Runner::Docker.new(id: 'c51f86c28340')
63
+ # #=> #<Dockerspec::Runner::Docker:0x0124>
64
+ #
65
+ # @example From a Running Docker Container Image Name
66
+ # Dockerspec::Runner::Docker.new('my-debian')
67
+ # #=> #<Dockerspec::Runner:0x0125>
68
+ #
69
+ # @param opts [String, Hash] The `:tag` or a list of options.
70
+ #
71
+ # @option opts [String] :tag The Docker image tag name to run.
72
+ # @option opts [String] :id The Docker container ID to use instead of
73
+ # starting a new container.
74
+ # @option opts [Boolean] :rm (calculated) Whether to remove the Docker
75
+ # container afterwards.
76
+ # @option opts [String] :path The environment `PATH` value of the
77
+ # container.
78
+ # @option opts [Hash, Array] :env Some `ENV` instructions to add to the
79
+ # container.
80
+ # @option opts [Integer] :wait Time to wait before running the tests.
81
+ #
82
+ # @return [Dockerspec::Runner::Docker] Runner object.
83
+ #
84
+ # @raise [Dockerspec::DockerRunArgumentError] Raises this exception when
85
+ # some required options are missing.
86
+ #
87
+ # @raise [Dockerspec::DockerError] For underlaying docker errors.
88
+ #
89
+ # @api public
90
+ #
91
+ def initialize(*opts)
92
+ super
93
+ send("setup_from_#{source}", options[source])
94
+ end
95
+
96
+ #
97
+ # Gets the Docker image ID.
98
+ #
99
+ # @example
100
+ # builder = Dockerspec::Builder.new('.').build
101
+ # runner = Dockerspec::Runner::Docker.new(builder)
102
+ # runner.image_id #=> "c51f86c28340[...]"
103
+ #
104
+ # @return [String] Image ID.
105
+ #
106
+ # @api public
107
+ #
108
+ def image_id
109
+ return @build.id unless @build.nil?
110
+ super
111
+ end
112
+
113
+ #
114
+ # Gets a descriptions of the object.
115
+ #
116
+ # @example Running from a Container Image ID
117
+ # r = Dockerspec::Runner::Docker.new('debian')
118
+ # r.to_s #=> "Docker Run from tag: \"debian\""
119
+ #
120
+ # @example Attaching to a Running Container ID
121
+ # r = Dockerspec::Runner::Docker.new(id: '92cc98ab560a')
122
+ # r.to_s #=> "Docker Run from id: \"92cc98ab560a\""
123
+ #
124
+ # @return [String] The object description.
125
+ #
126
+ # @api public
127
+ #
128
+ def to_s
129
+ description('Docker Run from')
130
+ end
131
+
132
+ protected
133
+
134
+ #
135
+ # Gets the source to start the container from.
136
+ #
137
+ # Possible values: `:tag`, `:id`.
138
+ #
139
+ # @example Start the Container from an Image Tag
140
+ # self.source #=> :tag
141
+ #
142
+ # @example Attach to a Running Container ID
143
+ # self.source #=> :id
144
+ #
145
+ # @return [Symbol] The source.
146
+ #
147
+ # @api private
148
+ #
149
+ def source
150
+ return @source unless @source.nil?
151
+ @source = %i(tag id).find { |from| options.key?(from) }
152
+ end
153
+
154
+ #
155
+ # Generates a description from Docker tag name.
156
+ #
157
+ # @example
158
+ # self.description_from_tag('debian') #=> "debian"
159
+ # self.description_from_tag('92cc98ab560a92cc98ab560[...]')
160
+ # #=> "92cc98ab560a"
161
+ #
162
+ # @return [String] The description, shortened if necessary.
163
+ #
164
+ # @see Dockerspec::Helper::MultipleSourceDescription
165
+ # #description_from_docker
166
+ #
167
+ # @api private
168
+ #
169
+ alias description_from_tag description_from_docker
170
+
171
+ #
172
+ # Ensures that the passed options are correct.
173
+ #
174
+ # Currently this only checks that you passed the `:tag` or the `:id`
175
+ # argument.
176
+ #
177
+ # @return void
178
+ #
179
+ # @raise [Dockerspec::DockerRunArgumentError] Raises this exception when
180
+ # the required fields are missing.
181
+ #
182
+ # @api private
183
+ #
184
+ def assert_options!(opts)
185
+ return if opts[:tag].is_a?(String) || opts[:id].is_a?(String)
186
+ raise DockerRunArgumentError, 'You need to pass the `:tag` or the '\
187
+ '`:id` option to the #docker_run method.'
188
+ end
189
+
190
+ #
191
+ # Generates the build object from the Docker image tag.
192
+ #
193
+ # Saves the build internally.
194
+ #
195
+ # @param tag [String] The image name or ID.
196
+ #
197
+ # @return void
198
+ #
199
+ # @api private
200
+ #
201
+ def setup_from_tag(tag)
202
+ @build = Builder.new(id: tag).build
203
+ end
204
+
205
+ #
206
+ # Generates the container object from a running Docker container.
207
+ #
208
+ # Saves the container internally.
209
+ #
210
+ # @param id [String] The container ID or name.
211
+ #
212
+ # @return void
213
+ #
214
+ # @raise [Dockerspec::DockerError] For underlaying docker errors.
215
+ #
216
+ # @api private
217
+ #
218
+ def setup_from_id(id)
219
+ @container = ::Docker::Container.get(id)
220
+ rescue ::Docker::Error::DockerError => e
221
+ DockerExceptionParser.new(e)
222
+ end
223
+
224
+ #
225
+ # Ensures that the Docker container has a correct `CMD`.
226
+ #
227
+ # @param opts [Hash] {Docker::Container} options.
228
+ #
229
+ # @return [Hash] {Docker::Container} options.
230
+ #
231
+ # @api private
232
+ #
233
+ def add_container_cmd_option(opts)
234
+ opts['Cmd'] = %w(/bin/sh) if @build.cmd.nil?
235
+ opts
236
+ end
237
+
238
+ #
239
+ # Adds some `ENV` options to the Docker container.
240
+ #
241
+ # @param opts [Hash] {Docker::Container} options.
242
+ #
243
+ # @return [Hash] {Docker::Container} options.
244
+ #
245
+ # @api private
246
+ #
247
+ def add_container_env_options(opts)
248
+ opts['Env'] = opts['Env'].to_a << "PATH=#{path}" if options.key?(:path)
249
+ env = options[:env].to_a.map { |v| v.join('=') }
250
+ opts['Env'] = opts['Env'].to_a.concat(env)
251
+ opts
252
+ end
253
+
254
+ #
255
+ # Generates the Docker container options for {Docker::Container}.
256
+ #
257
+ # @return [Hash] The container options.
258
+ #
259
+ # @api private
260
+ #
261
+ def container_options
262
+ opts = { 'Image' => image_id, 'OpenStdin' => true }
263
+
264
+ add_container_cmd_option(opts)
265
+ add_container_env_options(opts)
266
+ opts
267
+ end
268
+
269
+ #
270
+ # Creates the Docker container.
271
+ #
272
+ # *Note: Based on Specinfra `:docker` backend code.*
273
+ #
274
+ # @return void
275
+ #
276
+ # @raise [Dockerspec::DockerError] For underlaying docker errors.
277
+ #
278
+ # @api private
279
+ #
280
+ def create_container
281
+ return @container unless @container.nil?
282
+ @container = ::Docker::Container.create(container_options)
283
+ rescue ::Docker::Error::DockerError => e
284
+ DockerExceptionParser.new(e)
285
+ end
286
+
287
+ #
288
+ # Creates and runs the Docker container.
289
+ #
290
+ # @return void
291
+ #
292
+ # @raise [Dockerspec::DockerError] For underlaying docker errors.
293
+ #
294
+ # @api private
295
+ #
296
+ def run_container
297
+ create_container
298
+ super
299
+ end
300
+ end
301
+ end
302
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015-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/serverspec/docker'
21
+ require 'dockerspec/runner/serverspec/compose'
@@ -0,0 +1,185 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015-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 'serverspec'
21
+ require 'dockerspec/runner/docker'
22
+ require 'dockerspec/helper/docker'
23
+ require 'dockerspec/docker_exception_parser'
24
+ require 'dockerspec/runner/serverspec/rspec'
25
+
26
+ #
27
+ # Silence error: No backend type is specified. Fall back to :exec type.
28
+ #
29
+ Specinfra.configuration.backend(:base)
30
+
31
+ module Dockerspec
32
+ module Runner
33
+ #
34
+ # Contains the classes used to start docker containers using Serverspec.
35
+ #
36
+ module Serverspec
37
+ #
38
+ # Base class to be included by Serverspec runners.
39
+ #
40
+ # @example
41
+ # module Dockerspec
42
+ # module Runner
43
+ # module Serverspec
44
+ # class MyRunner
45
+ # include Base
46
+ # end
47
+ # end
48
+ # end
49
+ # end
50
+ #
51
+ module Base
52
+ #
53
+ # The Specinfra backend name to use.
54
+ #
55
+ # @return [Symbol] The backend name.
56
+ #
57
+ attr_reader :backend_name
58
+
59
+ #
60
+ # Stops and deletes the Docker Container.
61
+ #
62
+ # Actually does nothing. Do no delete anything, lets Specinfra do that.
63
+ #
64
+ # @return void
65
+ #
66
+ # @api public
67
+ #
68
+ def finalize
69
+ # Do not stop the container
70
+ end
71
+
72
+ #
73
+ # Generates a description of the object.
74
+ #
75
+ # @example Running Against a Container Image Tag
76
+ # self.description #=> "Serverspec on tag: \"debian\""
77
+ #
78
+ # @example Running Against a Running Container ID
79
+ # self.description #=> "Serverspec on id: \"92cc98ab560a\""
80
+ #
81
+ # @return [String] The object description.
82
+ #
83
+ # @api private
84
+ #
85
+ def to_s
86
+ description('Serverspec on')
87
+ end
88
+
89
+ protected
90
+
91
+ #
92
+ # Gets the default options configured using `RSpec.configuration`.
93
+ #
94
+ # @example
95
+ # self.rspec_options #=> { :family => :debian }
96
+ #
97
+ # @return [Hash] The configuration options.
98
+ #
99
+ # @api private
100
+ #
101
+ def rspec_options
102
+ config = ::RSpec.configuration
103
+ super.tap do |opts|
104
+ opts[:family] = config.family if config.family?
105
+ end
106
+ end
107
+
108
+ #
109
+ # Generates the correct Specinfra backend name to use from a name.
110
+ #
111
+ # @example
112
+ # self.generate_docker_backend_name(:docker, :docker) #=> :docker
113
+ # self.generate_docker_backend_name(:lxc, :docker) #=> :docker_lxc
114
+ # self.generate_docker_backend_name(:docker_lxc, :docker)
115
+ # #=> :docker_lxc
116
+ # self.generate_docker_backend_name(:native, :docker) #=> :docker
117
+ #
118
+ # @param name [String, Symbol] The backend short (without the `docker`
119
+ # prefix) or long name.
120
+ # @param prefix [Symbol, String] The prefix to use: `:docker` or
121
+ # `:docker_compose`.
122
+ #
123
+ # @return [Symbol] The backend name.
124
+ #
125
+ # @api private
126
+ #
127
+ def generate_docker_backend_name(name, prefix)
128
+ return name.to_s.to_sym if name.to_s.start_with?(prefix)
129
+ return prefix.to_sym if name.to_s.to_sym == :native
130
+ "#{prefix}_#{name}".to_sym
131
+ end
132
+
133
+ #
134
+ # Calculates and saves the correct docker Specinfra backend to use on
135
+ # the system.
136
+ #
137
+ # Returns the LXC driver instead of the native driver when required.
138
+ #
139
+ # Reads the driver from the configuration options if set.
140
+ #
141
+ # @example Docker with Native Execution Driver
142
+ # self.calculate_docker_backend_name(:docker) #=> :docker
143
+ #
144
+ # @example Docker with LXC Execution Driver
145
+ # self.calculate_docker_backend_name(:docker) #=> :docker_lxc
146
+ #
147
+ # @example Compose with LXC Execution Driver
148
+ # self.calculate_docker_backend_name(:compose) #=> :docker_compose_lxc
149
+ #
150
+ # @param prefix [Symbol, String] The prefix to use: `:docker` or
151
+ # `:docker_compose`.
152
+ #
153
+ # @return [Symbol] The backend name.
154
+ #
155
+ # @api private
156
+ #
157
+ def calculate_docker_backend_name(prefix)
158
+ @backend_name =
159
+ if options.key?(:backend)
160
+ generate_docker_backend_name(options[:backend], prefix)
161
+ elsif Helper::Docker.lxc_execution_driver?
162
+ "#{prefix}_lxc".to_sym
163
+ else
164
+ prefix.to_sym
165
+ end
166
+ end
167
+
168
+ #
169
+ # Starts the Docker container.
170
+ #
171
+ # @return void
172
+ #
173
+ # @raise [Dockerspec::DockerError] For underlaying docker errors.
174
+ #
175
+ # @api private
176
+ #
177
+ def run_container
178
+ Specinfra.configuration.backend(@backend_name)
179
+ rescue ::Docker::Error::DockerError => e
180
+ DockerExceptionParser.new(e)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end