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.
- checksums.yaml +7 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +7 -0
- data/CONTRIBUTING.md +13 -0
- data/LICENSE +190 -0
- data/README.md +202 -0
- data/Rakefile +57 -0
- data/TESTING.md +30 -0
- data/TODO.md +6 -0
- data/lib/dockerspec.rb +21 -0
- data/lib/dockerspec/builder.rb +408 -0
- data/lib/dockerspec/builder/config_helpers.rb +425 -0
- data/lib/dockerspec/builder/image_gc.rb +71 -0
- data/lib/dockerspec/builder/logger.rb +56 -0
- data/lib/dockerspec/builder/logger/ci.rb +69 -0
- data/lib/dockerspec/builder/logger/debug.rb +47 -0
- data/lib/dockerspec/builder/logger/info.rb +111 -0
- data/lib/dockerspec/builder/logger/silent.rb +51 -0
- data/lib/dockerspec/builder/matchers.rb +134 -0
- data/lib/dockerspec/builder/matchers/helpers.rb +88 -0
- data/lib/dockerspec/docker_gem.rb +25 -0
- data/lib/dockerspec/exceptions.rb +26 -0
- data/lib/dockerspec/helper/ci.rb +61 -0
- data/lib/dockerspec/helper/docker.rb +44 -0
- data/lib/dockerspec/helper/multiple_sources_description.rb +142 -0
- data/lib/dockerspec/helper/rspec_example_helpers.rb +48 -0
- data/lib/dockerspec/rspec_assertions.rb +54 -0
- data/lib/dockerspec/rspec_resources.rb +198 -0
- data/lib/dockerspec/rspec_settings.rb +29 -0
- data/lib/dockerspec/runner.rb +374 -0
- data/lib/dockerspec/serverspec.rb +20 -0
- data/lib/dockerspec/serverspec/rspec_resources.rb +174 -0
- data/lib/dockerspec/serverspec/rspec_settings.rb +27 -0
- data/lib/dockerspec/serverspec/runner.rb +302 -0
- data/lib/dockerspec/serverspec/specinfra_backend.rb +128 -0
- data/lib/dockerspec/serverspec/specinfra_hack.rb +43 -0
- data/lib/dockerspec/version.rb +29 -0
- data/spec/spec_helper.rb +44 -0
- metadata +293 -0
data/TESTING.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Testing
|
2
|
+
|
3
|
+
## Installing the Requirements
|
4
|
+
|
5
|
+
You can install gem dependencies with bundler:
|
6
|
+
|
7
|
+
$ gem install bundler
|
8
|
+
$ bundler install
|
9
|
+
|
10
|
+
## Generate Documentation
|
11
|
+
|
12
|
+
$ bundle exec rake doc
|
13
|
+
|
14
|
+
This will generate the HTML documentation in the `doc/` directory.
|
15
|
+
|
16
|
+
## All the Tests
|
17
|
+
|
18
|
+
$ bundle exec rake test
|
19
|
+
|
20
|
+
## Running the Syntax Style Tests
|
21
|
+
|
22
|
+
$ bundle exec rake style
|
23
|
+
|
24
|
+
## Running the Unit Tests
|
25
|
+
|
26
|
+
$ bundle exec rake unit
|
27
|
+
|
28
|
+
## Running the Integration Tests
|
29
|
+
|
30
|
+
$ bundle exec rake integration
|
data/TODO.md
ADDED
data/lib/dockerspec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
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/version'
|
21
|
+
require 'dockerspec/rspec_resources'
|
@@ -0,0 +1,408 @@
|
|
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
|
+
require 'rspec/its'
|
22
|
+
require 'tmpdir'
|
23
|
+
require 'erubis'
|
24
|
+
require 'dockerspec/docker_gem'
|
25
|
+
require 'dockerspec/builder/config_helpers'
|
26
|
+
require 'dockerspec/builder/matchers'
|
27
|
+
require 'dockerspec/builder/logger'
|
28
|
+
require 'dockerspec/builder/image_gc'
|
29
|
+
require 'dockerspec/helper/ci'
|
30
|
+
require 'dockerspec/helper/multiple_sources_description'
|
31
|
+
|
32
|
+
module Dockerspec
|
33
|
+
#
|
34
|
+
# A class to build a container image.
|
35
|
+
#
|
36
|
+
class Builder
|
37
|
+
include Dockerspec::Builder::ConfigHelpers
|
38
|
+
include Dockerspec::Helper::CI
|
39
|
+
include Dockerspec::Helper::MultipleSourcesDescription
|
40
|
+
|
41
|
+
#
|
42
|
+
# Constructs a Docker image builder class.
|
43
|
+
#
|
44
|
+
# @example Build an Image From CWD or `DOCKERFILE_PATH`
|
45
|
+
# Dockerspec::Builder.new #=> #<Dockerspec::Builder:0x0123>
|
46
|
+
#
|
47
|
+
# @example Build an Image from a Directory
|
48
|
+
# Dockerspec::Builder.new('imagedir') #=> #<Dockerspec::Builder:0x0124>
|
49
|
+
#
|
50
|
+
# @example Do Not Remove the Image
|
51
|
+
# Dockerspec::Builder.new('../', rm: false)
|
52
|
+
# #=> #<Dockerspec::Builder:0x0125>
|
53
|
+
#
|
54
|
+
# @example Passing Multiple Params
|
55
|
+
# Dockerspec::Builder.new(path: '../', tag: 'myapp', rm: false)
|
56
|
+
# #=> #<Dockerspec::Builder:0x0125>
|
57
|
+
#
|
58
|
+
# @param opts [String, Hash] The `:path` or a list of options.
|
59
|
+
#
|
60
|
+
# @option opts [String] :path ('.') The directory or file that contains the
|
61
|
+
# *Dockerfile*. By default tries to read it from the `DOCKERFILE_PATH`
|
62
|
+
# environment variable and uses `'.'` if it is not set.
|
63
|
+
# @option opts [String] :string Use this string as *Dockerfile* instead of
|
64
|
+
# `:path`. Not set by default.
|
65
|
+
# @option opts [String] :template Use this [Erubis]
|
66
|
+
# (http://www.kuwata-lab.com/erubis/users-guide.html) template file as
|
67
|
+
# *Dockerfile*.
|
68
|
+
# @option opts [String] :id Use this Docker image ID instead of a
|
69
|
+
# *Dockerfile*.
|
70
|
+
# @option opts [Boolean] :rm Whether to remove the generated docker images
|
71
|
+
# after running the tests. By default only removes them if it is running
|
72
|
+
# on a CI machine.
|
73
|
+
# @option opts [Hash, Erubis::Context] :context ({}) Template *context*
|
74
|
+
# used when the `:template` source is used.
|
75
|
+
# @option opts [String] :tag Repository tag to be applied to the resulting
|
76
|
+
# image.
|
77
|
+
# @option opts [Fixnum, Symbol] :log_level Sets the docker library
|
78
|
+
# verbosity level. Possible values:
|
79
|
+
# `:silent` or `0` (no output),
|
80
|
+
# `:ci` or `1` (enables some outputs recommended for CI environments),
|
81
|
+
# `:info` or `2` (gives information about main build steps),
|
82
|
+
# `:debug` or `3` (outputs all the provided information in its raw
|
83
|
+
# original form).
|
84
|
+
#
|
85
|
+
# @see Dockerspec::RSpecResources#docker_build
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
#
|
89
|
+
def initialize(*opts)
|
90
|
+
@image = nil
|
91
|
+
@options = parse_options(opts)
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Returns Docker image ID.
|
96
|
+
#
|
97
|
+
# @example Get the Image ID After Building the Image
|
98
|
+
# d = Dockerspec::Builder.new
|
99
|
+
# d.build
|
100
|
+
# d.id #=> "9f8866b49bfb[...]"
|
101
|
+
#
|
102
|
+
# @return [String] Docker image ID.
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
#
|
106
|
+
def id
|
107
|
+
@image.id
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Builds the docker image.
|
112
|
+
#
|
113
|
+
# @example Build an Image From a Path
|
114
|
+
# d = Dockerspec::Builder.new(path: 'dockerfile_dir')
|
115
|
+
# d.build #=> #<Dockerspec::Builder:0x0125>
|
116
|
+
#
|
117
|
+
# @return [String] Docker image ID.
|
118
|
+
#
|
119
|
+
# @api public
|
120
|
+
#
|
121
|
+
def build
|
122
|
+
send("build_from_#{source}", @options[source])
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
#
|
127
|
+
# Gets a descriptions of the object.
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# d = Dockerspec::Builder.new('.')
|
131
|
+
# d.to_s #=> "Docker Build from path: ."
|
132
|
+
#
|
133
|
+
# @return [String] The object description.
|
134
|
+
#
|
135
|
+
# @api public
|
136
|
+
#
|
137
|
+
def to_s
|
138
|
+
description('Docker Build from')
|
139
|
+
end
|
140
|
+
|
141
|
+
protected
|
142
|
+
|
143
|
+
#
|
144
|
+
# Gets the source to generate the image from.
|
145
|
+
#
|
146
|
+
# Possible values: `:string`, `:template`, `:id`, `:path`.
|
147
|
+
#
|
148
|
+
# @example Building an Image from a Path
|
149
|
+
# self.source #=> :path
|
150
|
+
#
|
151
|
+
# @example Building an Image from a Template
|
152
|
+
# self.source #=> :template
|
153
|
+
#
|
154
|
+
# @return [Symbol] The source.
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
#
|
158
|
+
def source
|
159
|
+
return @source unless @source.nil?
|
160
|
+
%i(string template id path).any? do |from|
|
161
|
+
next false unless @options.key?(from)
|
162
|
+
@source = from # Used for description
|
163
|
+
end
|
164
|
+
@source
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Generates a description when build from a template.
|
169
|
+
#
|
170
|
+
# @example
|
171
|
+
# self.description_from_template("file.erb") #=> "file.erb"
|
172
|
+
#
|
173
|
+
# @return [String] A description.
|
174
|
+
#
|
175
|
+
# @see Dockerspec::Helper::MultipleSourcesDescription#description_from_file
|
176
|
+
#
|
177
|
+
# @api private
|
178
|
+
#
|
179
|
+
alias_method :description_from_template, :description_from_file
|
180
|
+
|
181
|
+
#
|
182
|
+
# Sets or gets the Docker image.
|
183
|
+
#
|
184
|
+
# @param img [Docker::Image] The Docker image to set.
|
185
|
+
#
|
186
|
+
# @return [Docker::Image] The Docker image object.
|
187
|
+
#
|
188
|
+
# @api private
|
189
|
+
#
|
190
|
+
def image(img = nil)
|
191
|
+
return @image if img.nil?
|
192
|
+
ImageGC.instance.add(img.id) if @options[:rm]
|
193
|
+
@image = img
|
194
|
+
end
|
195
|
+
|
196
|
+
#
|
197
|
+
# Gets the default options configured using `RSpec.configuration`.
|
198
|
+
#
|
199
|
+
# @example
|
200
|
+
# self.rspec_options #=> {:path=>".", :rm=>true, :log_level=>:silent}
|
201
|
+
#
|
202
|
+
# @return [Hash] The configuration options.
|
203
|
+
#
|
204
|
+
# @api private
|
205
|
+
#
|
206
|
+
def rspec_options
|
207
|
+
config = RSpec.configuration
|
208
|
+
{}.tap do |opts|
|
209
|
+
opts[:path] = config.dockerfile_path if config.dockerfile_path?
|
210
|
+
opts[:rm] = config.rm_build if config.rm_build?
|
211
|
+
opts[:log_level] = config.log_level if config.log_level?
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# Gets the default configuration options after merging them with RSpec
|
217
|
+
# configuration options.
|
218
|
+
#
|
219
|
+
# @example
|
220
|
+
# self.default_options #=> {:path=>".", :rm=>true, :log_level=>:silent}
|
221
|
+
#
|
222
|
+
# @return [Hash] The configuration options.
|
223
|
+
#
|
224
|
+
# @api private
|
225
|
+
#
|
226
|
+
def default_options
|
227
|
+
{
|
228
|
+
path: ENV['DOCKERFILE_PATH'] || '.',
|
229
|
+
# Autoremove images in all CIs except Travis (not supported):
|
230
|
+
rm: ci? && !travis_ci?,
|
231
|
+
# Avoid CI timeout errors:
|
232
|
+
log_level: ci? ? :ci : :silent
|
233
|
+
}.merge(rspec_options)
|
234
|
+
end
|
235
|
+
|
236
|
+
#
|
237
|
+
# Parses the configuration options passed to the constructor.
|
238
|
+
#
|
239
|
+
# @example
|
240
|
+
# self.parse_options #=> {:path=>".", :rm=>true, :log_level=>:silent}
|
241
|
+
#
|
242
|
+
# @param opts [Array<String, Hash>] The list of optitag. The strings will
|
243
|
+
# be interpreted as `:path`, others will be merged.
|
244
|
+
#
|
245
|
+
# @return [Hash] The configuration options.
|
246
|
+
#
|
247
|
+
# @see #initialize
|
248
|
+
#
|
249
|
+
# @api private
|
250
|
+
#
|
251
|
+
def parse_options(opts)
|
252
|
+
opts_hs_ary = opts.map { |x| x.is_a?(Hash) ? x : { path: x } }
|
253
|
+
opts_hs_ary.reduce(default_options) { |a, e| a.merge(e) }
|
254
|
+
end
|
255
|
+
|
256
|
+
#
|
257
|
+
# Generates the Ruby block used to parse the logs during image construction.
|
258
|
+
#
|
259
|
+
# @return [Proc] The Ruby block.
|
260
|
+
#
|
261
|
+
# @api private
|
262
|
+
#
|
263
|
+
def build_block
|
264
|
+
proc { |chunk| logger.print_chunk(chunk) }
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# Builds the image from a string. Generates the Docker tag if required.
|
269
|
+
#
|
270
|
+
# It also saves the generated image in the object internally.
|
271
|
+
#
|
272
|
+
# This creates a temporary directory where it copies all the files and
|
273
|
+
# generates the temporary Dockerfile.
|
274
|
+
#
|
275
|
+
# @param string [String] The Dockerfile content.
|
276
|
+
#
|
277
|
+
# @return void
|
278
|
+
#
|
279
|
+
# @api private
|
280
|
+
#
|
281
|
+
def build_from_string(string, dir = '.')
|
282
|
+
Dir.mktmpdir do |tmpdir|
|
283
|
+
FileUtils.cp_r("#{dir}/.", tmpdir)
|
284
|
+
dockerfile = File.join(tmpdir, 'Dockerfile')
|
285
|
+
File.open(dockerfile, 'w') { |f| f.write(string) }
|
286
|
+
build_from_dir(tmpdir)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
#
|
291
|
+
# Builds the image from a file that is not called *Dockerfile*.
|
292
|
+
#
|
293
|
+
# It also saves the generated image in the object internally.
|
294
|
+
#
|
295
|
+
# This creates a temporary directory where it copies all the files and
|
296
|
+
# generates the temporary Dockerfile.
|
297
|
+
#
|
298
|
+
# @param file [String] The Dockerfile file path.
|
299
|
+
#
|
300
|
+
# @return void
|
301
|
+
#
|
302
|
+
# @api private
|
303
|
+
#
|
304
|
+
def build_from_file(file)
|
305
|
+
dir = File.dirname(file)
|
306
|
+
string = IO.read(file)
|
307
|
+
build_from_string(string, dir)
|
308
|
+
end
|
309
|
+
|
310
|
+
#
|
311
|
+
# Builds the image from a directory with a Dockerfile.
|
312
|
+
#
|
313
|
+
# It also saves the generated image in the object internally.
|
314
|
+
#
|
315
|
+
# @param dir [String] The directory path.
|
316
|
+
#
|
317
|
+
# @return void
|
318
|
+
#
|
319
|
+
# @api private
|
320
|
+
#
|
321
|
+
def build_from_dir(dir)
|
322
|
+
image(::Docker::Image.build_from_dir(dir, &build_block))
|
323
|
+
add_respository_tag
|
324
|
+
end
|
325
|
+
|
326
|
+
#
|
327
|
+
# Builds the image from a directory or a file.
|
328
|
+
#
|
329
|
+
# It also saves the generated image in the object internally.
|
330
|
+
#
|
331
|
+
# @param path [String] The path.
|
332
|
+
#
|
333
|
+
# @return void
|
334
|
+
#
|
335
|
+
# @api private
|
336
|
+
#
|
337
|
+
def build_from_path(path)
|
338
|
+
if !File.directory?(path) && File.basename(path) == 'Dockerfile'
|
339
|
+
path = File.dirname(path)
|
340
|
+
end
|
341
|
+
File.directory?(path) ? build_from_dir(path) : build_from_file(path)
|
342
|
+
end
|
343
|
+
|
344
|
+
#
|
345
|
+
# Builds the image from a template.
|
346
|
+
#
|
347
|
+
# It also saves the generated image in the object internally.
|
348
|
+
#
|
349
|
+
# @param file [String] The Dockerfile [Erubis]
|
350
|
+
# (http://www.kuwata-lab.com/erubis/users-guide.html) template path.
|
351
|
+
#
|
352
|
+
# @return void
|
353
|
+
#
|
354
|
+
# @api private
|
355
|
+
#
|
356
|
+
def build_from_template(file)
|
357
|
+
context = @options[:context] || {}
|
358
|
+
|
359
|
+
dir = File.dirname(file)
|
360
|
+
template = IO.read(file)
|
361
|
+
eruby = Erubis::Eruby.new(template)
|
362
|
+
string = eruby.evaluate(context)
|
363
|
+
build_from_string(string, dir)
|
364
|
+
end
|
365
|
+
|
366
|
+
#
|
367
|
+
# Gets the image from a Image ID.
|
368
|
+
#
|
369
|
+
# It also saves the image in the object internally.
|
370
|
+
#
|
371
|
+
# @param id [String] The Docker image ID.
|
372
|
+
#
|
373
|
+
# @return void
|
374
|
+
#
|
375
|
+
# @api private
|
376
|
+
#
|
377
|
+
def build_from_id(id)
|
378
|
+
@image = ::Docker::Image.get(id)
|
379
|
+
add_respository_tag
|
380
|
+
rescue ::Docker::Error::NotFoundError
|
381
|
+
@image = ::Docker::Image.create('fromImage' => id)
|
382
|
+
end
|
383
|
+
|
384
|
+
#
|
385
|
+
# Adds a repository name and a tag to the Docker image.
|
386
|
+
#
|
387
|
+
# @return void
|
388
|
+
#
|
389
|
+
# @api private
|
390
|
+
#
|
391
|
+
def add_respository_tag
|
392
|
+
return unless @options.key?(:tag)
|
393
|
+
repo, repo_tag = @options[:tag].split(':', 2)
|
394
|
+
@image.tag(repo: repo, tag: repo_tag, force: true)
|
395
|
+
end
|
396
|
+
|
397
|
+
#
|
398
|
+
# Gets the Docker Logger to use during the build process.
|
399
|
+
#
|
400
|
+
# @return void
|
401
|
+
#
|
402
|
+
# @api private
|
403
|
+
#
|
404
|
+
def logger
|
405
|
+
@logger ||= Logger.instance(@options[:log_level])
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|