conjur-debify 1.8.2 → 1.10.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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +49 -0
- data/VERSION +1 -1
- data/ci/test.sh +5 -0
- data/debify.gemspec +1 -0
- data/example/Gemfile +8 -0
- data/example/Gemfile.lock +24 -0
- data/example/docker-compose.yml +11 -0
- data/example/net-test.sh +7 -0
- data/example/test.sh +1 -0
- data/features/package.feature +2 -5
- data/features/sandbox.feature +19 -0
- data/features/step_definitions/debify_steps.rb +25 -0
- data/features/support/env.rb +1 -10
- data/features/support/hooks.rb +29 -0
- data/features/support/world.rb +10 -0
- data/features/test.feature +24 -0
- data/lib/conjur/debify.rb +107 -36
- data/lib/conjur/debify/Dockerfile.fpm +1 -1
- data/lib/conjur/debify/action/publish.rb +1 -1
- data/lib/conjur/debify/utils.rb +16 -0
- data/lib/conjur/fpm/Dockerfile +2 -1
- data/lib/conjur/fpm/debify_utils.sh +6 -6
- data/lib/conjur/fpm/package.sh +32 -1
- data/publish-rubygem.sh +1 -1
- data/push-image.sh +6 -3
- data/spec/data/Makefile +5 -0
- data/spec/data/test.tar +0 -0
- data/spec/utils_spec.rb +22 -0
- data/tag-image.sh +6 -3
- metadata +34 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88040281ebb6c9d449d4d1d76ad5a8336227ddf8c4fe5ec3753d413ff16b47d2
|
|
4
|
+
data.tar.gz: 98cc9d92be782db60c56ee85c6cdea1eff52733eded68543964f510f37a69e17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5642578c564449462ec292c828a30fa0eeabee0e764bd47b7437564c4be3181808fda0f02fcd516d7aed617c98d50deb062a96b3fb9a5066fae5ba1e1eb130e1
|
|
7
|
+
data.tar.gz: 05e3bb4cac1d4188b19e84adf42b93e875efd300d725efa04177f0366c1da4de25960fbe417757420406b09a78b895c51a599d8dcd02a602d77218bd72f1e800
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# 1.10.0
|
|
2
|
+
|
|
3
|
+
* add `--net` support to `test` and `sandbox` subcommands
|
|
4
|
+
* Use Docker::Container.start! to start containers, to avoid
|
|
5
|
+
swallowing important errors.
|
|
6
|
+
|
|
7
|
+
# 1.9.1
|
|
8
|
+
|
|
9
|
+
* Make sure .bundle/config in the 'main' package excludes test and development groups.
|
|
10
|
+
|
|
11
|
+
# 1.9.0
|
|
12
|
+
|
|
13
|
+
* Build -dev package with development/test dependencies and use it on `debify test`.
|
|
14
|
+
|
|
1
15
|
# 1.8.2
|
|
2
16
|
|
|
3
17
|
* Install fpm dependency libffi-dev
|
data/README.md
CHANGED
|
@@ -242,6 +242,55 @@ root@7d4217655332:/src/authz# export RAILS_ENV=test
|
|
|
242
242
|
root@7d4217655332:/src/authz# bundle exec rake db:migrate
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
+
## Usage with docker-compose
|
|
246
|
+
|
|
247
|
+
As of v1.10.0, both the `test` and `sandbox` subcommands support the `--net` switch. This allows you to specify a network to which the Conjur appliance container should be attached.
|
|
248
|
+
|
|
249
|
+
There are a variety of ways to make use of this feature. One
|
|
250
|
+
possiblity is creating a network using `docker network create`, then
|
|
251
|
+
attaching both the docker-compose services, as well as the Conjur
|
|
252
|
+
appliance container created by debify, to it.
|
|
253
|
+
|
|
254
|
+
As a (somewhat contrived) example, create a new docker network:
|
|
255
|
+
|
|
256
|
+
```sh-session
|
|
257
|
+
$ docker network create testnet
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
Use a docker-compose file like [example/docker-compose.yml](example/docker-compose.yml)
|
|
261
|
+
|
|
262
|
+
```yaml
|
|
263
|
+
version: "2"
|
|
264
|
+
networks:
|
|
265
|
+
svcnet:
|
|
266
|
+
external:
|
|
267
|
+
name: testnet
|
|
268
|
+
services:
|
|
269
|
+
db:
|
|
270
|
+
image: postgres
|
|
271
|
+
container_name: mydb
|
|
272
|
+
networks:
|
|
273
|
+
- svcnet
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Bring up the db service:
|
|
277
|
+
|
|
278
|
+
```sh-session
|
|
279
|
+
debify $ cd example
|
|
280
|
+
example $ docker-compose up -d
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Start a sandbox, see that it can resolve the hostname `mydb`:
|
|
284
|
+
|
|
285
|
+
```sh-session
|
|
286
|
+
|
|
287
|
+
example $ debify sandbox -t 4.9-stable --net testnet
|
|
288
|
+
example $ docker exec -it example-sandbox /bin/bash
|
|
289
|
+
root@7d4217655332:/src/example# getent hosts mydb
|
|
290
|
+
172.19.0.2 mydb
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
|
|
245
294
|
## Contributing
|
|
246
295
|
|
|
247
296
|
1. Fork it ( https://github.com/[my-github-username]/debify/fork )
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.10.0
|
data/ci/test.sh
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
bundle
|
|
4
4
|
|
|
5
|
+
# Some tests need to be logged in to the registry, to pull a base
|
|
6
|
+
# image if it's not already available. Have entrypoint.sh do something
|
|
7
|
+
# simple, and log in as a side effect.
|
|
8
|
+
/debify/distrib/entrypoint.sh detect-version
|
|
9
|
+
|
|
5
10
|
for target in spec cucumber; do
|
|
6
11
|
bundle exec rake $target
|
|
7
12
|
done
|
data/debify.gemspec
CHANGED
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |spec|
|
|
|
23
23
|
spec.add_dependency "conjur-api", "~> 4"
|
|
24
24
|
|
|
25
25
|
spec.add_development_dependency "bundler", "~> 1.7"
|
|
26
|
+
spec.add_development_dependency "fakefs", "~> 0"
|
|
26
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
27
28
|
|
|
28
29
|
# Pin to cucumbe v2. cucumber v3 changes (breaks) the behavior of
|
data/example/Gemfile
CHANGED
data/example/Gemfile.lock
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
GEM
|
|
2
2
|
remote: https://rubygems.org/
|
|
3
3
|
specs:
|
|
4
|
+
coderay (1.1.2)
|
|
5
|
+
diff-lcs (1.3)
|
|
6
|
+
method_source (0.9.0)
|
|
7
|
+
pry (0.11.3)
|
|
8
|
+
coderay (~> 1.1.0)
|
|
9
|
+
method_source (~> 0.9.0)
|
|
10
|
+
rspec (3.7.0)
|
|
11
|
+
rspec-core (~> 3.7.0)
|
|
12
|
+
rspec-expectations (~> 3.7.0)
|
|
13
|
+
rspec-mocks (~> 3.7.0)
|
|
14
|
+
rspec-core (3.7.1)
|
|
15
|
+
rspec-support (~> 3.7.0)
|
|
16
|
+
rspec-expectations (3.7.0)
|
|
17
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
18
|
+
rspec-support (~> 3.7.0)
|
|
19
|
+
rspec-mocks (3.7.0)
|
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
21
|
+
rspec-support (~> 3.7.0)
|
|
22
|
+
rspec-support (3.7.1)
|
|
4
23
|
|
|
5
24
|
PLATFORMS
|
|
6
25
|
ruby
|
|
7
26
|
|
|
8
27
|
DEPENDENCIES
|
|
28
|
+
pry
|
|
29
|
+
rspec
|
|
30
|
+
|
|
31
|
+
BUNDLED WITH
|
|
32
|
+
1.16.1
|
data/example/net-test.sh
ADDED
data/example/test.sh
CHANGED
data/features/package.feature
CHANGED
|
@@ -5,16 +5,13 @@ Feature: Packaging
|
|
|
5
5
|
Given I successfully run `env DEBUG=true GLI_DEBUG=true debify package -d ../../example -v 0.0.1 example -- --post-install /distrib/postinstall.sh`
|
|
6
6
|
|
|
7
7
|
Scenario: 'example' project can be packaged successfully
|
|
8
|
-
Then the stdout should contain
|
|
8
|
+
Then the stdout should contain "conjur-example_0.0.1_amd64.deb"
|
|
9
|
+
And the stdout should contain "conjur-example-dev_0.0.1_amd64.deb"
|
|
9
10
|
|
|
10
11
|
Scenario: 'clean' command will delete non-Git-managed files
|
|
11
12
|
When I successfully run `env DEBUG=true GLI_DEBUG=true debify clean -d ../../example --force`
|
|
12
13
|
And I successfully run `find ../../example`
|
|
13
14
|
Then the stdout from "find ../../example" should not contain "conjur-example_0.0.1_amd64.deb"
|
|
14
15
|
|
|
15
|
-
Scenario: 'example' project can be tested successfully
|
|
16
|
-
When I successfully run `env DEBUG=true GLI_DEBUG=true debify test -t 4.9-stable -v 0.0.1 -d ../../example --no-pull example test.sh`
|
|
17
|
-
Then the stderr should contain "Test succeeded"
|
|
18
|
-
|
|
19
16
|
Scenario: 'example' project can be published
|
|
20
17
|
When I successfully run `env DEBUG=true GLI_DEBUG=true debify publish -v 0.0.1 -d ../../example 4.9 example`
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
@announce-output
|
|
2
|
+
Feature: Running a sandbox
|
|
3
|
+
Background:
|
|
4
|
+
Given I successfully run `docker pull registry.tld/conjur-appliance-cuke-master:4.9-stable`
|
|
5
|
+
|
|
6
|
+
Scenario: sandbox for 'example' project be started
|
|
7
|
+
Given I successfully start a sandbox for "example" with arguments "-t 4.9-stable --no-pull"
|
|
8
|
+
|
|
9
|
+
Scenario: sandbox for 'example' project be started linked to another container
|
|
10
|
+
Given I start a container named "other_host"
|
|
11
|
+
Then I successfully start a sandbox for "example" with arguments "-t 4.9-stable --no-pull --link other_host -c 'ping -c1 other_host'"
|
|
12
|
+
|
|
13
|
+
Scenario: sandbox for 'example' project be started on a network other than the default
|
|
14
|
+
Given I start a container named "other_host" on network "test-net"
|
|
15
|
+
Then I successfully start a sandbox for "example" with arguments "-t 4.9-stable --no-pull --net test-net -c 'ping -c1 other_host'"
|
|
16
|
+
|
|
17
|
+
Scenario: sandbox for 'example' project be started on a network other than the default with a host aliased
|
|
18
|
+
Given I start a container named "another_host" on network "test-net"
|
|
19
|
+
Then I successfully start a sandbox for "example" with arguments "-t 4.9-stable --no-pull --net test-net --link another_host:other_host -c 'ping -c1 other_host'"
|
|
@@ -1,6 +1,31 @@
|
|
|
1
|
+
|
|
1
2
|
When /^I get help for "([^"]*)"$/ do |app_name|
|
|
2
3
|
@app_name = app_name
|
|
3
4
|
step %(I run `#{app_name} help`)
|
|
4
5
|
end
|
|
5
6
|
|
|
6
7
|
# Add more step definitions here
|
|
8
|
+
|
|
9
|
+
When /^I start a container named "(.*?)"(?: on network "(.*?)")*$/ do |name, net_name|
|
|
10
|
+
if net_name
|
|
11
|
+
network = Docker::Network.create(net_name)
|
|
12
|
+
networks << network
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
alpine = Docker::Image.create('fromImage' => 'alpine')
|
|
16
|
+
options = {
|
|
17
|
+
'name' => name,
|
|
18
|
+
'Cmd' => [ "sh", "-c", "while true; do sleep 1; done" ],
|
|
19
|
+
'Image' => alpine.id
|
|
20
|
+
}
|
|
21
|
+
options['HostConfig'] = { 'NetworkMode' => net_name } if net_name
|
|
22
|
+
|
|
23
|
+
container = Docker::Container.create(options)
|
|
24
|
+
container.start!
|
|
25
|
+
containers << container
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
When /^I successfully start a sandbox for "(.*?)" with arguments "(.*?)"$/ do |project, args|
|
|
29
|
+
step %Q{I successfully run `env DEBUG=true GLI_DEBUG=true debify sandbox -d ../../#{project} #{args}`}
|
|
30
|
+
containers << Docker::Container.get("#{project}-sandbox")
|
|
31
|
+
end
|
data/features/support/env.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'aruba/cucumber'
|
|
2
|
+
require 'docker-api'
|
|
2
3
|
|
|
3
4
|
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
|
4
5
|
LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
|
|
@@ -7,13 +8,3 @@ Aruba.configure do |config|
|
|
|
7
8
|
config.exit_timeout = 1200
|
|
8
9
|
end
|
|
9
10
|
|
|
10
|
-
Before do
|
|
11
|
-
# Using "announce" causes massive warnings on 1.9.2
|
|
12
|
-
@puts = true
|
|
13
|
-
@original_rubylib = ENV['RUBYLIB']
|
|
14
|
-
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
After do
|
|
18
|
-
ENV['RUBYLIB'] = @original_rubylib
|
|
19
|
-
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Before do
|
|
2
|
+
# Using "announce" causes massive warnings on 1.9.2
|
|
3
|
+
@puts = true
|
|
4
|
+
@original_rubylib = ENV['RUBYLIB']
|
|
5
|
+
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
After do
|
|
9
|
+
ENV['RUBYLIB'] = @original_rubylib
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Around do |scenario, block|
|
|
13
|
+
# Note that self in an Around hook is the instance of the world
|
|
14
|
+
# (here, a DebifyWorld) for the current scenario.
|
|
15
|
+
initialize
|
|
16
|
+
begin
|
|
17
|
+
block.call
|
|
18
|
+
ensure
|
|
19
|
+
unless ENV['KEEP_CONTAINERS']
|
|
20
|
+
containers.each do |c|
|
|
21
|
+
c.remove(force: true)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
networks.each do |n|
|
|
25
|
+
n.remove
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
@announce-output
|
|
2
|
+
Feature: Testing
|
|
3
|
+
|
|
4
|
+
Background:
|
|
5
|
+
Given I successfully run `env DEBUG=true GLI_DEBUG=true debify package -d ../../example -v 0.0.1 example -- --post-install /distrib/postinstall.sh`
|
|
6
|
+
|
|
7
|
+
Scenario: 'example' project can be tested successfully
|
|
8
|
+
When I successfully run `env DEBUG=true GLI_DEBUG=true debify test -t 4.9-stable -v 0.0.1 -d ../../example --no-pull example test.sh`
|
|
9
|
+
Then the stderr should contain "Test succeeded"
|
|
10
|
+
|
|
11
|
+
Scenario: 'example' project can be tested when linked to another container
|
|
12
|
+
Given I start a container named "other_host"
|
|
13
|
+
When I successfully run `env DEBUG=true GLI_DEBUG=true debify test -t 4.9-stable -v 0.0.1 -d ../../example --no-pull --link other_host example net-test.sh`
|
|
14
|
+
Then the stderr should contain "Test succeeded"
|
|
15
|
+
|
|
16
|
+
Scenario: 'example' project can be tested on a network other than the default
|
|
17
|
+
Given I start a container named "other_host" on network "test-net"
|
|
18
|
+
When I successfully run `env DEBUG=true GLI_DEBUG=true debify test -t 4.9-stable -v 0.0.1 -d ../../example --no-pull --net test-net example net-test.sh`
|
|
19
|
+
Then the stderr should contain "Test succeeded"
|
|
20
|
+
|
|
21
|
+
Scenario: 'example' project can be tested on a network other than the default with a host aliased
|
|
22
|
+
Given I start a container named "another_host" on network "test-net"
|
|
23
|
+
When I successfully run `env DEBUG=true GLI_DEBUG=true debify test -t 4.9-stable -v 0.0.1 -d ../../example --no-pull --link another_host:other_host --net test-net example net-test.sh`
|
|
24
|
+
Then the stderr should contain "Test succeeded"
|
data/lib/conjur/debify.rb
CHANGED
|
@@ -5,6 +5,11 @@ require 'gli'
|
|
|
5
5
|
require 'json'
|
|
6
6
|
require 'base64'
|
|
7
7
|
|
|
8
|
+
require 'conjur/debify/utils'
|
|
9
|
+
|
|
10
|
+
require 'active_support'
|
|
11
|
+
require 'active_support/core_ext'
|
|
12
|
+
|
|
8
13
|
include GLI::App
|
|
9
14
|
|
|
10
15
|
config_file '.debifyrc'
|
|
@@ -169,7 +174,7 @@ command "clean" do |c|
|
|
|
169
174
|
options['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
|
170
175
|
container = Docker::Container.create options
|
|
171
176
|
begin
|
|
172
|
-
container.start
|
|
177
|
+
container.start!
|
|
173
178
|
delete_files.each do |file|
|
|
174
179
|
puts file
|
|
175
180
|
|
|
@@ -244,8 +249,6 @@ command "package" do |c|
|
|
|
244
249
|
dockerfile_path = cmd_options[:dockerfile] || File.expand_path("debify/Dockerfile.fpm", pwd)
|
|
245
250
|
dockerfile = File.read(dockerfile_path)
|
|
246
251
|
|
|
247
|
-
package_name = "conjur-#{project_name}_#{version}_amd64.deb"
|
|
248
|
-
|
|
249
252
|
output = StringIO.new
|
|
250
253
|
Gem::Package::TarWriter.new(output) do |tar|
|
|
251
254
|
git_files.each do |fname|
|
|
@@ -269,18 +272,19 @@ command "package" do |c|
|
|
|
269
272
|
container = Docker::Container.create options
|
|
270
273
|
begin
|
|
271
274
|
DebugMixin.debug_write "Packaging #{project_name} in container #{container.id}\n"
|
|
272
|
-
container.tap(&:start).streaming_logs(follow: true, stdout: true, stderr: true) { |stream, chunk| $stderr.puts "#{chunk}" }
|
|
275
|
+
container.tap(&:start!).streaming_logs(follow: true, stdout: true, stderr: true) { |stream, chunk| $stderr.puts "#{chunk}" }
|
|
273
276
|
status = container.wait
|
|
274
277
|
raise "Failed to package #{project_name}" unless status['StatusCode'] == 0
|
|
275
278
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
container
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
279
|
+
pkg = "conjur-#{project_name}_#{version}_amd64.deb"
|
|
280
|
+
dev_pkg = "conjur-#{project_name}-dev_#{version}_amd64.deb"
|
|
281
|
+
Conjur::Debify::Utils.copy_from_container container, "/src/#{pkg}"
|
|
282
|
+
puts "#{pkg}"
|
|
283
|
+
begin
|
|
284
|
+
Conjur::Debify::Utils.copy_from_container container, "/dev-pkg/#{dev_pkg}"
|
|
285
|
+
puts "#{dev_pkg}"
|
|
286
|
+
rescue Docker::Error::NotFoundError
|
|
287
|
+
warn "#{dev_pkg} not found. The package might not have any development dependencies."
|
|
284
288
|
end
|
|
285
289
|
ensure
|
|
286
290
|
container.delete(force: true)
|
|
@@ -297,8 +301,57 @@ end
|
|
|
297
301
|
|
|
298
302
|
def wait_for_conjur appliance_image, container
|
|
299
303
|
container_command container, '/opt/conjur/evoke/bin/wait_for_conjur'
|
|
304
|
+
rescue
|
|
305
|
+
$stderr.puts container.logs
|
|
306
|
+
raise
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
def network_options(cmd)
|
|
310
|
+
cmd.desc "Specify link for test container"
|
|
311
|
+
cmd.flag [ :l, :link ], :multiple => true
|
|
312
|
+
|
|
313
|
+
cmd.desc 'Attach to the specified network'
|
|
314
|
+
cmd.flag [ :n, :net ]
|
|
300
315
|
end
|
|
301
316
|
|
|
317
|
+
def short_id(id)
|
|
318
|
+
if id =~ /\A[0-9a-f]{64}\z/ # 64 hex digits, docker only allows lower case letters in ids
|
|
319
|
+
$stderr.puts "Warning: found full container id, using short id instead (#{id[0..11]} for #{id})"
|
|
320
|
+
id[0..11]
|
|
321
|
+
else
|
|
322
|
+
id
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# If the source of the link is a full container id, use the short id
|
|
327
|
+
# instead. (Docker doesn't add full container ids as network aliases,
|
|
328
|
+
# only short ids).
|
|
329
|
+
def shorten_source_id(link)
|
|
330
|
+
src,dest = link.split(':')
|
|
331
|
+
src && dest ? "#{short_id(src)}:#{dest}" : link
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def add_network_config(container_config, cmd_options)
|
|
335
|
+
host_config = container_config['HostConfig']
|
|
336
|
+
has_links = cmd_options[:link] && !cmd_options[:link].empty?
|
|
337
|
+
net_name = cmd_options[:net]
|
|
338
|
+
if net_name
|
|
339
|
+
host_config['NetworkMode'] = net_name
|
|
340
|
+
if has_links
|
|
341
|
+
container_config['NetworkingConfig'] ||= {}
|
|
342
|
+
container_config['NetworkingConfig'].deep_merge!(
|
|
343
|
+
'EndpointsConfig' => {
|
|
344
|
+
net_name => {
|
|
345
|
+
'Links' => cmd_options[:link].collect(&method(:shorten_source_id))
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
)
|
|
349
|
+
end
|
|
350
|
+
elsif has_links
|
|
351
|
+
# Don't shorten source ids here
|
|
352
|
+
host_config['Links'] = cmd_options[:link]
|
|
353
|
+
end
|
|
354
|
+
end
|
|
302
355
|
|
|
303
356
|
desc "Test a Conjur debian package in a Conjur appliance container"
|
|
304
357
|
long_desc <<DESC
|
|
@@ -344,12 +397,11 @@ command "test" do |c|
|
|
|
344
397
|
c.desc "Specify the deb version; by default, it's read from the VERSION file"
|
|
345
398
|
c.flag [ :v, :version ]
|
|
346
399
|
|
|
347
|
-
c.desc "Specify link for test container"
|
|
348
|
-
c.flag [ :l, :link ], :multiple => true
|
|
349
|
-
|
|
350
400
|
c.desc "Specify volume for test container"
|
|
351
401
|
c.flag [ :'volumes-from' ], :multiple => true
|
|
352
402
|
|
|
403
|
+
network_options(c)
|
|
404
|
+
|
|
353
405
|
c.action do |global_options,cmd_options,args|
|
|
354
406
|
raise "project-name is required" unless project_name = args.shift
|
|
355
407
|
raise "test-script is required" unless test_script = args.shift
|
|
@@ -366,6 +418,7 @@ command "test" do |c|
|
|
|
366
418
|
appliance_image_id = [ cmd_options[:image], image_tag ].join(":")
|
|
367
419
|
version = cmd_options[:version] || detect_version
|
|
368
420
|
package_name = "conjur-#{project_name}_#{version}_amd64.deb"
|
|
421
|
+
dev_package_name = "conjur-#{project_name}-dev_#{version}_amd64.deb"
|
|
369
422
|
|
|
370
423
|
raise "#{test_script} does not exist or is not a file" unless File.file?(test_script)
|
|
371
424
|
|
|
@@ -378,15 +431,16 @@ command "test" do |c|
|
|
|
378
431
|
end
|
|
379
432
|
|
|
380
433
|
|
|
381
|
-
def build_test_image(appliance_image_id, project_name,
|
|
434
|
+
def build_test_image(appliance_image_id, project_name, packages)
|
|
435
|
+
packages = packages.join " "
|
|
382
436
|
dockerfile = <<-DOCKERFILE
|
|
383
437
|
FROM #{appliance_image_id}
|
|
384
438
|
|
|
385
|
-
COPY #{
|
|
439
|
+
COPY #{packages} /tmp/
|
|
386
440
|
|
|
387
441
|
RUN if dpkg --list | grep conjur-#{project_name}; then dpkg --force all --purge conjur-#{project_name}; fi
|
|
388
442
|
RUN if [ -f /opt/conjur/etc/#{project_name}.conf ]; then rm /opt/conjur/etc/#{project_name}.conf; fi
|
|
389
|
-
RUN dpkg --install
|
|
443
|
+
RUN cd /tmp; dpkg --install #{packages}
|
|
390
444
|
|
|
391
445
|
RUN touch /etc/service/conjur/down
|
|
392
446
|
DOCKERFILE
|
|
@@ -394,7 +448,7 @@ RUN touch /etc/service/conjur/down
|
|
|
394
448
|
tmpfile = Tempfile.new('Dockerfile', tmpdir)
|
|
395
449
|
File.write(tmpfile, dockerfile)
|
|
396
450
|
dockerfile_name = File.basename(tmpfile.path)
|
|
397
|
-
tar_cmd = "tar -cvzh -C #{tmpdir} #{dockerfile_name} -C #{Dir.pwd} #{
|
|
451
|
+
tar_cmd = "tar -cvzh -C #{tmpdir} #{dockerfile_name} -C #{Dir.pwd} #{packages}"
|
|
398
452
|
tar = open("| #{tar_cmd}")
|
|
399
453
|
begin
|
|
400
454
|
Docker::Image.build_from_tar(tar, :dockerfile => dockerfile_name, &DebugMixin::DOCKER)
|
|
@@ -404,9 +458,12 @@ RUN touch /etc/service/conjur/down
|
|
|
404
458
|
end
|
|
405
459
|
end
|
|
406
460
|
|
|
461
|
+
packages = [package_name]
|
|
462
|
+
packages << dev_package_name if File.exist? dev_package_name
|
|
463
|
+
|
|
407
464
|
begin
|
|
408
465
|
tries ||=2
|
|
409
|
-
appliance_image = build_test_image(appliance_image_id, project_name,
|
|
466
|
+
appliance_image = build_test_image(appliance_image_id, project_name, packages)
|
|
410
467
|
rescue
|
|
411
468
|
login_to_registry appliance_image_id
|
|
412
469
|
retry unless (tries -= 1).zero?
|
|
@@ -424,20 +481,26 @@ RUN touch /etc/service/conjur/down
|
|
|
424
481
|
"CONJUR_AUTHN_API_KEY=secret",
|
|
425
482
|
"CONJUR_ADMIN_PASSWORD=secret",
|
|
426
483
|
],
|
|
427
|
-
'
|
|
428
|
-
|
|
429
|
-
|
|
484
|
+
'HostConfig' => {
|
|
485
|
+
'Binds' => [
|
|
486
|
+
[ dir, "/src/#{project_name}" ].join(':')
|
|
487
|
+
]
|
|
488
|
+
}
|
|
430
489
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
490
|
+
host_config = options['HostConfig']
|
|
491
|
+
|
|
492
|
+
host_config['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
|
493
|
+
host_config['VolumesFrom'] = cmd_options[:'volumes-from'] if cmd_options[:'volumes-from'] && !cmd_options[:'volumes-from'].empty?
|
|
494
|
+
|
|
495
|
+
add_network_config(options, cmd_options)
|
|
496
|
+
|
|
434
497
|
if global_options[:'local-bundle']
|
|
435
|
-
|
|
498
|
+
host_config['Binds']
|
|
436
499
|
.push([ vendor_dir, "/src/#{project_name}/vendor" ].join(':'))
|
|
437
500
|
.push([ dot_bundle_dir, "/src/#{project_name}/.bundle" ].join(':'))
|
|
438
501
|
end
|
|
439
502
|
|
|
440
|
-
container = Docker::Container.create(options)
|
|
503
|
+
container = Docker::Container.create(options.tap {|o| DebugMixin.debug_write "creating container with options #{o.inspect}"})
|
|
441
504
|
|
|
442
505
|
begin
|
|
443
506
|
DebugMixin.debug_write "Testing #{project_name} in container #{container.id}\n"
|
|
@@ -445,7 +508,7 @@ RUN touch /etc/service/conjur/down
|
|
|
445
508
|
spawn("docker logs -f #{container.id}", [ :out, :err ] => $stderr).tap do |pid|
|
|
446
509
|
Process.detach pid
|
|
447
510
|
end
|
|
448
|
-
container.start
|
|
511
|
+
container.start!
|
|
449
512
|
|
|
450
513
|
# Wait for pg/main so that migrations can run
|
|
451
514
|
30.times do
|
|
@@ -471,8 +534,10 @@ RUN touch /etc/service/conjur/down
|
|
|
471
534
|
system "./#{test_script} #{container.id}"
|
|
472
535
|
exit_now! "#{test_script} failed with exit code #{$?.exitstatus}", $?.exitstatus unless $?.exitstatus == 0
|
|
473
536
|
ensure
|
|
474
|
-
|
|
475
|
-
|
|
537
|
+
unless cmd_options[:keep] || ENV['KEEP_CONTAINERS']
|
|
538
|
+
DebugMixin.debug_write "deleting container"
|
|
539
|
+
container.delete(force: true)
|
|
540
|
+
end
|
|
476
541
|
end
|
|
477
542
|
end
|
|
478
543
|
end
|
|
@@ -507,8 +572,7 @@ command "sandbox" do |c|
|
|
|
507
572
|
c.default_value false
|
|
508
573
|
c.switch [ :pull ]
|
|
509
574
|
|
|
510
|
-
c
|
|
511
|
-
c.flag [ :l, :link ], :multiple => true
|
|
575
|
+
network_options(c)
|
|
512
576
|
|
|
513
577
|
c.desc "Specify volume for container"
|
|
514
578
|
c.flag [ :'volumes-from' ], :multiple => true
|
|
@@ -524,6 +588,9 @@ command "sandbox" do |c|
|
|
|
524
588
|
c.default_value false
|
|
525
589
|
c.switch [:kill]
|
|
526
590
|
|
|
591
|
+
c.desc 'A command to run in the sandbox'
|
|
592
|
+
c.flag [ :c, :command ]
|
|
593
|
+
|
|
527
594
|
c.action do |global_options,cmd_options,args|
|
|
528
595
|
raise "Received extra command-line arguments" if args.shift
|
|
529
596
|
|
|
@@ -579,8 +646,9 @@ command "sandbox" do |c|
|
|
|
579
646
|
end
|
|
580
647
|
|
|
581
648
|
host_config['Privileged'] = true if Docker.version['Version'] >= '1.10.0'
|
|
582
|
-
host_config['Links'] = cmd_options[:link] unless cmd_options[:link].empty?
|
|
583
649
|
host_config['VolumesFrom'] = cmd_options[:'volumes-from'] unless cmd_options[:'volumes-from'].empty?
|
|
650
|
+
|
|
651
|
+
add_network_config(options, cmd_options)
|
|
584
652
|
|
|
585
653
|
unless cmd_options[:port].empty?
|
|
586
654
|
port_bindings = Hash.new({})
|
|
@@ -596,9 +664,9 @@ command "sandbox" do |c|
|
|
|
596
664
|
previous.delete(:force => true) if previous
|
|
597
665
|
end
|
|
598
666
|
|
|
599
|
-
container = Docker::Container.create(options)
|
|
667
|
+
container = Docker::Container.create(options.tap {|o| DebugMixin.debug_write "creating container with options #{o.inspect}"})
|
|
600
668
|
$stdout.puts container.id
|
|
601
|
-
container.start
|
|
669
|
+
container.start!
|
|
602
670
|
|
|
603
671
|
wait_for_conjur appliance_image, container
|
|
604
672
|
|
|
@@ -607,6 +675,9 @@ command "sandbox" do |c|
|
|
|
607
675
|
container_command(container, 'sv', 'restart', "conjur/#{project_name}")
|
|
608
676
|
end
|
|
609
677
|
|
|
678
|
+
if cmd_options[:command]
|
|
679
|
+
container_command(container, '/bin/bash', '-c', cmd_options[:command])
|
|
680
|
+
end
|
|
610
681
|
end
|
|
611
682
|
end
|
|
612
683
|
end
|
|
@@ -80,7 +80,7 @@ module Conjur::Debify
|
|
|
80
80
|
def publish(options)
|
|
81
81
|
container = Docker::Container.create(options)
|
|
82
82
|
begin
|
|
83
|
-
container.tap(&:start).streaming_logs(follow: true, stdout: true, stderr: true) { |stream, chunk| puts "#{chunk}" }
|
|
83
|
+
container.tap(&:start!).streaming_logs(follow: true, stdout: true, stderr: true) { |stream, chunk| puts "#{chunk}" }
|
|
84
84
|
status = container.wait
|
|
85
85
|
raise "Failed to publish package" unless status['StatusCode'] == 0
|
|
86
86
|
ensure
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require 'rubygems/package'
|
|
2
|
+
|
|
3
|
+
module Conjur::Debify::Utils
|
|
4
|
+
module_function
|
|
5
|
+
|
|
6
|
+
# copy a file from container to the current working directory
|
|
7
|
+
def copy_from_container container, path
|
|
8
|
+
tar = StringIO.new
|
|
9
|
+
container.copy(path) { |chunk| tar.write chunk }
|
|
10
|
+
tar.rewind
|
|
11
|
+
Gem::Package::TarReader.new(tar).each do |entry|
|
|
12
|
+
File.write entry.full_name, entry.read
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
data/lib/conjur/fpm/Dockerfile
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
function bundle_clean() {
|
|
2
2
|
ruby_version="$(ruby -v | grep -o '[0-9]\.[0-9]\.[0-9]')"
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
if [ -d vendor/bundle ]; then
|
|
5
|
+
chmod og+r -R vendor/bundle # some gems have broken perms
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
rm -rf vendor/bundle/ruby/${ruby_version}/gems/*/{test,spec,examples,example,contrib,doc,ext,sample}
|
|
7
|
+
# some cleanup
|
|
8
|
+
rm -rf vendor/bundle/ruby/${ruby_version}/cache
|
|
9
|
+
rm -rf vendor/bundle/ruby/${ruby_version}/gems/*/{test,spec,examples,example,contrib,doc,ext,sample}
|
|
10
|
+
fi
|
|
11
11
|
}
|
data/lib/conjur/fpm/package.sh
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#!/bin/bash -ex
|
|
2
2
|
|
|
3
|
+
source /debify_utils.sh
|
|
4
|
+
|
|
3
5
|
project_name=$1
|
|
4
6
|
shift
|
|
5
7
|
version=$1
|
|
@@ -15,6 +17,36 @@ if [ -z "$version" ]; then
|
|
|
15
17
|
fi
|
|
16
18
|
|
|
17
19
|
package_name=conjur-"$project_name"_"$version"_amd64.deb
|
|
20
|
+
dev_package_name=conjur-"$project_name"-dev_"$version"_amd64.deb
|
|
21
|
+
|
|
22
|
+
# Build dev package first
|
|
23
|
+
echo Building $dev_package_name
|
|
24
|
+
prefix=/src/opt/conjur/project
|
|
25
|
+
cp -al $prefix /dev-pkg
|
|
26
|
+
cd $prefix
|
|
27
|
+
bundle --without development test
|
|
28
|
+
bundle clean
|
|
29
|
+
cp /usr/local/bundle/config .bundle/config # bundler for some reason stores config there...
|
|
30
|
+
cd /dev-pkg
|
|
31
|
+
find $prefix -type f | sed -e "s@^$prefix@.@" | xargs rm -f
|
|
32
|
+
find . -type d -empty -delete
|
|
33
|
+
bundle_clean
|
|
34
|
+
|
|
35
|
+
if [ `ls | wc -l` -eq 0 ]; then
|
|
36
|
+
echo No dev dependencies, skipping dev package
|
|
37
|
+
else
|
|
38
|
+
fpm -s dir -t deb -n conjur-$project_name-dev -v $version -C . \
|
|
39
|
+
--maintainer "Conjur Inc." \
|
|
40
|
+
--vendor "Conjur Inc." \
|
|
41
|
+
--license "Proprietary" \
|
|
42
|
+
--url "https://www.conjur.net" \
|
|
43
|
+
--deb-no-default-config-files \
|
|
44
|
+
--deb-user conjur \
|
|
45
|
+
--deb-group conjur \
|
|
46
|
+
--depends "conjur-$project_name = $version" \
|
|
47
|
+
--prefix /opt/conjur/$project_name \
|
|
48
|
+
--description "Conjur $project_name service - development files"
|
|
49
|
+
fi
|
|
18
50
|
|
|
19
51
|
echo Building $package_name
|
|
20
52
|
|
|
@@ -22,7 +54,6 @@ mv /src/opt/conjur/project /src/opt/conjur/$project_name
|
|
|
22
54
|
|
|
23
55
|
cd /src/opt/conjur/$project_name
|
|
24
56
|
|
|
25
|
-
source /debify_utils.sh
|
|
26
57
|
bundle_clean
|
|
27
58
|
|
|
28
59
|
cd /src
|
data/publish-rubygem.sh
CHANGED
|
@@ -4,7 +4,7 @@ docker pull registry.tld/conjurinc/publish-rubygem
|
|
|
4
4
|
|
|
5
5
|
docker run -i --rm -v $PWD:/src -w /src alpine/git clean -fxd
|
|
6
6
|
|
|
7
|
-
summon --yaml
|
|
7
|
+
summon --yaml "RUBYGEMS_API_KEY: !var rubygems/api-key" \
|
|
8
8
|
docker run --rm --env-file @SUMMONENVFILE -v "$(pwd)":/opt/src \
|
|
9
9
|
registry.tld/conjurinc/publish-rubygem debify
|
|
10
10
|
|
data/push-image.sh
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/bin/bash -ex
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
IFS=. read MAJOR MINOR PATCH <VERSION
|
|
4
|
+
|
|
5
|
+
TAGS="latest $(docker images --filter reference="registry.tld/conjurinc/debify:$MAJOR.$MINOR*" --format '{{.Tag}}')"
|
|
6
|
+
for t in $TAGS; do
|
|
7
|
+
docker push registry.tld/conjurinc/debify:$t
|
|
8
|
+
done
|
|
4
9
|
|
|
5
|
-
docker push registry.tld/conjurinc/debify:$TAG
|
|
6
|
-
docker push registry.tld/conjurinc/debify:latest
|
data/spec/data/Makefile
ADDED
data/spec/data/test.tar
ADDED
|
Binary file
|
data/spec/utils_spec.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'fakefs/safe'
|
|
2
|
+
|
|
3
|
+
require 'conjur/debify/utils'
|
|
4
|
+
|
|
5
|
+
describe 'Conjur::Debify::Utils.copy_from_container' do
|
|
6
|
+
it "copies a file from the container to the current directory" do
|
|
7
|
+
tar = File.read "#{__dir__}/data/test.tar"
|
|
8
|
+
container = instance_double Docker::Container
|
|
9
|
+
allow(container).to receive(:copy).with "/tmp/test.tar" do |&b|
|
|
10
|
+
StringIO.new(tar).each(nil, 512) do |c|
|
|
11
|
+
# docker api sends three arguments, so emulate that
|
|
12
|
+
b[c, nil, nil]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
FakeFS do
|
|
17
|
+
Conjur::Debify::Utils.copy_from_container container, "/tmp/test.tar"
|
|
18
|
+
expect(File.read 'test.txt').to eq "this is a test\n"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
data/tag-image.sh
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
#!/bin/bash -ex
|
|
2
|
-
TAG=$(< VERSION)
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
IFS=. read MAJOR MINOR PATCH <VERSION
|
|
4
|
+
TAG=$MAJOR.$MINOR.$PATCH
|
|
5
|
+
|
|
6
|
+
for t in latest $TAG $MAJOR.$MINOR; do
|
|
7
|
+
docker tag debify:$TAG registry.tld/conjurinc/debify:$t
|
|
8
|
+
done
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: conjur-debify
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kevin Gilpin
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-
|
|
11
|
+
date: 2018-03-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: gli
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '1.7'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: fakefs
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
83
97
|
- !ruby/object:Gem::Dependency
|
|
84
98
|
name: rake
|
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -184,14 +198,21 @@ files:
|
|
|
184
198
|
- example/Gemfile.lock
|
|
185
199
|
- example/debify.sh
|
|
186
200
|
- example/distrib/postinstall.sh
|
|
201
|
+
- example/docker-compose.yml
|
|
202
|
+
- example/net-test.sh
|
|
187
203
|
- example/test.sh
|
|
188
204
|
- features/detect_version.feature
|
|
189
205
|
- features/package.feature
|
|
206
|
+
- features/sandbox.feature
|
|
190
207
|
- features/step_definitions/debify_steps.rb
|
|
191
208
|
- features/support/env.rb
|
|
209
|
+
- features/support/hooks.rb
|
|
210
|
+
- features/support/world.rb
|
|
211
|
+
- features/test.feature
|
|
192
212
|
- lib/conjur/debify.rb
|
|
193
213
|
- lib/conjur/debify/Dockerfile.fpm
|
|
194
214
|
- lib/conjur/debify/action/publish.rb
|
|
215
|
+
- lib/conjur/debify/utils.rb
|
|
195
216
|
- lib/conjur/debify/version.rb
|
|
196
217
|
- lib/conjur/fpm/Dockerfile
|
|
197
218
|
- lib/conjur/fpm/debify_utils.sh
|
|
@@ -201,7 +222,10 @@ files:
|
|
|
201
222
|
- push-image.sh
|
|
202
223
|
- secrets.yml
|
|
203
224
|
- spec/action/publish_spec.rb
|
|
225
|
+
- spec/data/Makefile
|
|
226
|
+
- spec/data/test.tar
|
|
204
227
|
- spec/spec_helper.rb
|
|
228
|
+
- spec/utils_spec.rb
|
|
205
229
|
- tag-image.sh
|
|
206
230
|
- test.sh
|
|
207
231
|
homepage: https://github.com/conjurinc/debify
|
|
@@ -224,14 +248,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
224
248
|
version: '0'
|
|
225
249
|
requirements: []
|
|
226
250
|
rubyforge_project:
|
|
227
|
-
rubygems_version: 2.7.
|
|
251
|
+
rubygems_version: 2.7.6
|
|
228
252
|
signing_key:
|
|
229
253
|
specification_version: 4
|
|
230
254
|
summary: Utility commands to build and package Conjur services as Debian packages
|
|
231
255
|
test_files:
|
|
232
256
|
- features/detect_version.feature
|
|
233
257
|
- features/package.feature
|
|
258
|
+
- features/sandbox.feature
|
|
234
259
|
- features/step_definitions/debify_steps.rb
|
|
235
260
|
- features/support/env.rb
|
|
261
|
+
- features/support/hooks.rb
|
|
262
|
+
- features/support/world.rb
|
|
263
|
+
- features/test.feature
|
|
236
264
|
- spec/action/publish_spec.rb
|
|
265
|
+
- spec/data/Makefile
|
|
266
|
+
- spec/data/test.tar
|
|
237
267
|
- spec/spec_helper.rb
|
|
268
|
+
- spec/utils_spec.rb
|