dockerspec 0.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +7 -0
  3. data/CHANGELOG.md +7 -0
  4. data/CONTRIBUTING.md +13 -0
  5. data/LICENSE +190 -0
  6. data/README.md +202 -0
  7. data/Rakefile +57 -0
  8. data/TESTING.md +30 -0
  9. data/TODO.md +6 -0
  10. data/lib/dockerspec.rb +21 -0
  11. data/lib/dockerspec/builder.rb +408 -0
  12. data/lib/dockerspec/builder/config_helpers.rb +425 -0
  13. data/lib/dockerspec/builder/image_gc.rb +71 -0
  14. data/lib/dockerspec/builder/logger.rb +56 -0
  15. data/lib/dockerspec/builder/logger/ci.rb +69 -0
  16. data/lib/dockerspec/builder/logger/debug.rb +47 -0
  17. data/lib/dockerspec/builder/logger/info.rb +111 -0
  18. data/lib/dockerspec/builder/logger/silent.rb +51 -0
  19. data/lib/dockerspec/builder/matchers.rb +134 -0
  20. data/lib/dockerspec/builder/matchers/helpers.rb +88 -0
  21. data/lib/dockerspec/docker_gem.rb +25 -0
  22. data/lib/dockerspec/exceptions.rb +26 -0
  23. data/lib/dockerspec/helper/ci.rb +61 -0
  24. data/lib/dockerspec/helper/docker.rb +44 -0
  25. data/lib/dockerspec/helper/multiple_sources_description.rb +142 -0
  26. data/lib/dockerspec/helper/rspec_example_helpers.rb +48 -0
  27. data/lib/dockerspec/rspec_assertions.rb +54 -0
  28. data/lib/dockerspec/rspec_resources.rb +198 -0
  29. data/lib/dockerspec/rspec_settings.rb +29 -0
  30. data/lib/dockerspec/runner.rb +374 -0
  31. data/lib/dockerspec/serverspec.rb +20 -0
  32. data/lib/dockerspec/serverspec/rspec_resources.rb +174 -0
  33. data/lib/dockerspec/serverspec/rspec_settings.rb +27 -0
  34. data/lib/dockerspec/serverspec/runner.rb +302 -0
  35. data/lib/dockerspec/serverspec/specinfra_backend.rb +128 -0
  36. data/lib/dockerspec/serverspec/specinfra_hack.rb +43 -0
  37. data/lib/dockerspec/version.rb +29 -0
  38. data/spec/spec_helper.rb +44 -0
  39. metadata +293 -0
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015 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/serverspec/rspec_resources'
@@ -0,0 +1,174 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015 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/serverspec/runner'
21
+ require 'dockerspec/serverspec/rspec_settings'
22
+
23
+ module Dockerspec
24
+ module Serverspec
25
+ #
26
+ # Some resources included inside {RSpec::Core::ExampleGroup} to build and
27
+ # run Docker containers with Serverspec.
28
+ #
29
+ # ## RSpec Settings
30
+ #
31
+ # * `family`: The OS family to use
32
+ #
33
+ # All the RSpec settings are optional.
34
+ #
35
+ # @example RSpec Settings
36
+ # RSpec.configure do |config|
37
+ # config.family = :debian
38
+ # end
39
+ #
40
+ module RSpecResources
41
+ #
42
+ # Runs a docker image and the Serverspec tests against it.
43
+ #
44
+ # See the [Serverspec Resource Types documentation]
45
+ # (http://serverspec.org/resource_types.html) to see the available
46
+ # resources.
47
+ #
48
+ # By default tries to detect the most appropriate Docker backend: native
49
+ # or LXC.
50
+ #
51
+ # @example A Basic Example to Test the HTTPd Service
52
+ # describe docker_build('.', tag: 'myapp') do
53
+ # describe docker_run('myapp') do
54
+ # describe service('httpd') do
55
+ # it { should be_enabled }
56
+ # it { should be_running }
57
+ # end
58
+ # # [...]
59
+ # end
60
+ # end
61
+ #
62
+ # @example Avoid Automatic OS Detection to Speed Up the Tests
63
+ # describe docker_build('.', tag: 'myapp') do
64
+ # describe docker_run('myapp', family: :debian) do
65
+ # # [...]
66
+ # end
67
+ # end
68
+ #
69
+ # @example Using Hash Format
70
+ # describe docker_build('.', tag: 'myapp') do
71
+ # describe docker_run(tag: 'myapp', family: :debian) do
72
+ # # [...]
73
+ # end
74
+ # end
75
+ #
76
+ # @example Force a Specific Docker Backend
77
+ # describe docker_build('.', tag: 'myapp') do
78
+ # describe docker_run('myapp', backend: :lxc) do
79
+ # # [...]
80
+ # end
81
+ # end
82
+ #
83
+ # @example Use a Backend Not Included by Default
84
+ # # specinfra-backend-docker_nsenter gem must be installed by hand
85
+ # require 'specinfra/backend/docker_nsenter'
86
+ # describe docker_build('.', tag: 'myapp') do
87
+ # describe docker_run('myapp', backend: :nsenter) do
88
+ # # [...]
89
+ # end
90
+ # end
91
+ #
92
+ # @example Running a Container Image Tag
93
+ # describe docker_run('debian:8') do
94
+ # # [...]
95
+ # end
96
+ #
97
+ # @example Testing `FROM` Dockerfile Instruction
98
+ # # FROM debian:8
99
+ # describe docker_run('myapp') do
100
+ # describe file('/etc/debian_version') do
101
+ # it { should be_file }
102
+ # its(:content) { should match /^8\./ }
103
+ # end
104
+ # # Another way to check it:
105
+ # describe command('lsb_release -ri') do
106
+ # its(:stdout) { should match /^Distributor ID:\s+Debian/ }
107
+ # its(:stdout) { should match /^Release:\s+8\./ }
108
+ # end
109
+ # end
110
+ #
111
+ # @example Testing `COPY` and `ADD` Dockerfile Instructions
112
+ # # COPY docker-entrypoint.sh /entrypoint.sh
113
+ # describe docker_run('myapp') do
114
+ # describe file('/entrypoint.sh') do
115
+ # it { should be_file }
116
+ # its(:content) { should match /^exec java -jar myapp\.jar/ }
117
+ # end
118
+ # end
119
+ #
120
+ # @example Testing `RUN` Dockerfile Instructions
121
+ # describe docker_run('myapp') do
122
+ # # RUN apt-get install -y wget
123
+ # describe package('wget') do
124
+ # it { should be_installed }
125
+ # end
126
+ # # RUN useradd -g myapp -d /opt/myapp myapp
127
+ # describe user('myapp') do
128
+ # it { should exist }
129
+ # it { should belong_to_group 'myapp' }
130
+ # it { should have_home_directory '/opt/myapp' }
131
+ # end
132
+ # end
133
+ #
134
+ # @param opts [String, Hash] The `:tag` or a list of options.
135
+ #
136
+ # @option opts [String] :tag The Docker image tag name to run.
137
+ # @option opts [String] :id The Docker container ID to use instead of
138
+ # starting a new container.
139
+ # @option opts [Boolean] :rm (calculated) Whether to remove the Docker
140
+ # container afterwards.
141
+ # @option opts [String] :path The environment `PATH` value of the
142
+ # container.
143
+ # @option opts [Hash, Array] :env Some `ENV` instructions to add to the
144
+ # container.
145
+ # @option opts [Symbol] :family (calculated) The OS family.
146
+ # It's automatically detected by default, but can be used to
147
+ # **speed up the tests**. Some possible values:
148
+ # `:alpine`, `:arch`, `:coreos`, `:debian`, `:gentoo`, `:nixos`,
149
+ # `:plamo`, `:poky`, `:redhat`, `:suse`.
150
+ # @option opts [Symbol] :backend (calculated) Docker backend to use:
151
+ # `:docker`, `:lxc`.
152
+ #
153
+ # @return [Dockerspec::ServerspecRunner] Runner object.
154
+ #
155
+ # @raise [Dockerspec::DockerRunArgumentError] Raises this exception when
156
+ # some required fields are missing.
157
+ #
158
+ # @api public
159
+ #
160
+ def docker_run(*opts)
161
+ runner = Dockerspec::Serverspec::Runner.new(*opts)
162
+ runner.run
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ #
169
+ # Add the Dockerspec::Serverspec resources to RSpec core.
170
+ #
171
+ RSpec::Core::ExampleGroup.class_eval do
172
+ extend Dockerspec::Serverspec::RSpecResources
173
+ include Dockerspec::Serverspec::RSpecResources
174
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015 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 'rspec'
21
+
22
+ #
23
+ # Add some RSpec custom settings for {Dockerspec::Serverspec}.
24
+ #
25
+ RSpec.configure do |c|
26
+ c.add_setting :family
27
+ end
@@ -0,0 +1,302 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author:: Xabier de Zuazo (<xabier@zuazo.org>)
4
+ # Copyright:: Copyright (c) 2015 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 'specinfra/backend/docker_lxc'
22
+ require 'dockerspec/runner'
23
+ require 'dockerspec/serverspec/specinfra_backend'
24
+ require 'dockerspec/helper/rspec_example_helpers'
25
+ require 'dockerspec/helper/docker'
26
+
27
+ #
28
+ # Silence error: No backend type is specified. Fall back to :exec type.
29
+ #
30
+ Specinfra.configuration.backend(:base)
31
+
32
+ module Dockerspec
33
+ #
34
+ # Contains the classes related to running Serverspec in docker containers.
35
+ #
36
+ module Serverspec
37
+ #
38
+ # Runs a Docker container using [Serverspec](http://serverspec.org/).
39
+ #
40
+ class Runner < Dockerspec::Runner
41
+ #
42
+ # Constructs a Docker Serverspec runner class to run Docker images.
43
+ #
44
+ # @example From a Docker Container Image Tag
45
+ # Dockerspec::Serverspec::Runner.new('myapp')
46
+ # #=> #<Dockerspec::Serverspec::Runner:0x0124>
47
+ #
48
+ # @example From a Docker Container Image Tag Using Hash Format
49
+ # Dockerspec::Serverspec::Runner.new(tag: 'myapp')
50
+ # #=> #<Dockerspec::Serverspec::Runner:0x0124>
51
+ #
52
+ # @example From a Running Docker Container ID
53
+ # Dockerspec::Serverspec::Runner.new(id: 'c51f86c28340')
54
+ # #=> #<Dockerspec::Serverspec::Runner:0x0125>
55
+ #
56
+ # @param opts [String, Hash] The `:tag` or a list of options.
57
+ #
58
+ # @option opts [String] :tag The Docker image tag name to run.
59
+ # @option opts [String] :id The Docker container ID to use instead of
60
+ # starting a new container.
61
+ # @option opts [Boolean] :rm (calculated) Whether to remove the Docker
62
+ # container afterwards.
63
+ # @option opts [String] :path The environment `PATH` value of the
64
+ # container.
65
+ # @option opts [Hash, Array] :env Some `ENV` instructions to add to the
66
+ # container.
67
+ # @option opts [Symbol] :family (calculated) The OS family.
68
+ # It's automatically detected by default, but can be used to
69
+ # **speed up the tests**. Some possible values:
70
+ # `:alpine`, `:arch`, `:coreos`, `:debian`, `:gentoo`, `:nixos`,
71
+ # `:plamo`, `:poky`, `:redhat`, `:suse`.
72
+ # @option opts [Symbol] :backend (calculated) Docker backend to use:
73
+ # `:docker`, `:lxc`.
74
+ #
75
+ # @return [Dockerspec::Serverspec::Runner] Runner object.
76
+ #
77
+ # @api public
78
+ #
79
+ def initialize(*opts)
80
+ super
81
+ @specinfra_backend = nil
82
+ @backend = calculate_docker_backend_name
83
+ end
84
+
85
+ #
86
+ # Runs the Docker Container and sets the Specinfra configuration.
87
+ #
88
+ # @example
89
+ # builder = Dockerspec::Builder.new('.', tag: 'myapp')
90
+ # builder.build
91
+ # runner = Dockerspec::Serverspec::Runner.new('myapp')
92
+ # runner.run #=> #<Dockerspec::Serverspec::Runner:0x0123>
93
+ #
94
+ # @return [Dockerspec::Serverspec::Runner] Runner object.
95
+ #
96
+ # @api public
97
+ #
98
+ def run
99
+ specinfra_setup
100
+ run_container
101
+ specinfra_save
102
+ self
103
+ end
104
+
105
+ #
106
+ # Stops and deletes the Docker Container.
107
+ #
108
+ # Actually does nothing. Do no delete anything, let Specinfra do that.
109
+ #
110
+ # @return void
111
+ #
112
+ # @api public
113
+ #
114
+ def finalize
115
+ # Do not stop the container
116
+ end
117
+
118
+ #
119
+ # Restores the Docker running container instance in the Specinfra
120
+ # internal reference.
121
+ #
122
+ # Gets the correct {Runner} reference from the RSpec metadata.
123
+ #
124
+ # @example Restore Specinfra Backend
125
+ # RSpec.configure do |c|
126
+ # c.before(:each) do
127
+ # metadata = RSpec.current_example.metadata
128
+ # Dockerspec::Serverspec::Runner.restore(metadata)
129
+ # end
130
+ # end
131
+ #
132
+ # @param metadata [Hash] RSpec metadata.
133
+ #
134
+ # @return void
135
+ #
136
+ # @api public
137
+ #
138
+ # @see restore
139
+ #
140
+ def self.restore(metadata)
141
+ runner = Helper::RSpecExampleHelpers.search_object(metadata, self)
142
+ return if runner.nil?
143
+ runner.restore
144
+ end
145
+
146
+ #
147
+ # Restores the Specinfra backend instance to point to this object's
148
+ # container.
149
+ #
150
+ # This is used to avoid Serverspec running against the last started
151
+ # container if you are testing multiple containers at the same time.
152
+ #
153
+ # @return void
154
+ #
155
+ def restore
156
+ @specinfra_backend.restore
157
+ end
158
+
159
+ #
160
+ # Generates a description of the object.
161
+ #
162
+ # @example Running Against a Container Image Tag
163
+ # self.description #=> "Serverspec on tag: \"debian\""
164
+ #
165
+ # @example Running Against a Running Container ID
166
+ # self.description #=> "Serverspec on id: \"92cc98ab560a\""
167
+ #
168
+ # @return [String] The object description.
169
+ #
170
+ # @api private
171
+ #
172
+ def to_s
173
+ description('Serverspec on')
174
+ end
175
+
176
+ protected
177
+
178
+ #
179
+ # Gets the default options configured using `RSpec.configuration`.
180
+ #
181
+ # @example
182
+ # self.rspec_options #=> { :family => :debian }
183
+ #
184
+ # @return [Hash] The configuration options.
185
+ #
186
+ # @api private
187
+ #
188
+ def rspec_options
189
+ config = RSpec.configuration
190
+ super.tap do |opts|
191
+ opts[:family] = config.family if config.family?
192
+ end
193
+ end
194
+
195
+ #
196
+ # Sets the Specinfra configuration.
197
+ #
198
+ # - Resets the internal Specinfra backend reference.
199
+ # - Sets the `:family`.
200
+ # - Sets the `:docker_image` or `:docker_container`.
201
+ #
202
+ # @return void
203
+ #
204
+ # @api private
205
+ #
206
+ def specinfra_setup
207
+ @specinfra_backend = SpecinfraBackend.new(@backend)
208
+ @specinfra_backend.reset
209
+ if @options.key?(:family)
210
+ Specinfra.configuration.os(family: @options[:family])
211
+ end
212
+ if id.nil?
213
+ Specinfra.configuration.docker_image(image_id)
214
+ else
215
+ Specinfra.configuration.docker_container(id)
216
+ end
217
+ end
218
+
219
+ #
220
+ # Saves the Specinfra backend internal reference internally to restore
221
+ # it later.
222
+ #
223
+ # @return void
224
+ #
225
+ # @api private
226
+ #
227
+ def specinfra_save
228
+ @specinfra_backend.save
229
+ end
230
+
231
+ #
232
+ # Generates the correct Specinfra backend name to use from a name.
233
+ #
234
+ # @example
235
+ # self.generate_docker_backend_name(:docker) #=> :docker
236
+ # self.generate_docker_backend_name(:lxc) #=> :docker_lxc
237
+ # self.generate_docker_backend_name(:docker_lxc) #=> :docker_lxc
238
+ # self.generate_docker_backend_name(:native) #=> :docker
239
+ #
240
+ # @param name [String, Symbol] The backend short (without the `docker`
241
+ # prefix) or long name.
242
+ #
243
+ # @return [Symbol] The backend name.
244
+ #
245
+ # @api private
246
+ #
247
+ def generate_docker_backend_name(name)
248
+ return name.to_s.to_sym unless name.to_s.match(/^docker/).nil?
249
+ return :docker if name.to_s.to_sym == :native
250
+ "docker_#{name}".to_sym
251
+ end
252
+
253
+ #
254
+ # Calculates the correct docker Specinfra backend to use on the system.
255
+ #
256
+ # Returns the LXC driver instead of the native driver when required.
257
+ #
258
+ # Reads the driver from the configuration options if set.
259
+ #
260
+ # @example Docker with Native Execution Driver
261
+ # self.calculate_docker_backend_name #=> :docker
262
+ #
263
+ # @example Docker with LXC Execution Driver
264
+ # self.calculate_docker_backend_name #=> :docker_lxc
265
+ #
266
+ # @return [Symbol] The backend name.
267
+ #
268
+ # @api private
269
+ #
270
+ def calculate_docker_backend_name
271
+ if @options.key?(:backend)
272
+ generate_docker_backend_name(@options[:backend])
273
+ elsif Helper::Docker.lxc_execution_driver?
274
+ :docker_lxc
275
+ else
276
+ :docker
277
+ end
278
+ end
279
+
280
+ #
281
+ # Starts the Docker container.
282
+ #
283
+ # @return void
284
+ #
285
+ # @api private
286
+ #
287
+ def run_container
288
+ Specinfra.configuration.backend(@backend)
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ #
295
+ # Restore Specinfra backend:
296
+ #
297
+ RSpec.configure do |c|
298
+ c.before(:each) do
299
+ metadata = RSpec.current_example.metadata
300
+ Dockerspec::Serverspec::Runner.restore(metadata)
301
+ end
302
+ end