dockerspec 0.1.0

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