dockerspec 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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