Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for archive_in and archive_out #341

Merged
merged 3 commits into from
Jan 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ rvm:
- 2.1
- 2.2
env:
- DOCKER_VERSION=1.5.0
- DOCKER_VERSION=1.6.2
- DOCKER_VERSION=1.7.1
- DOCKER_VERSION=1.8.3
Expand Down
31 changes: 31 additions & 0 deletions lib/docker/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,37 @@ def copy(path, &block)
self
end

def archive_out(path, &block)
connection.get(
path_for(:archive),
{ 'path' => path },
:response_block => block
)
self
end

def archive_in(inputs, output_path, opts = {})
file_hash = Docker::Util.file_hash_from_paths([*inputs])
tar = StringIO.new(Docker::Util.create_tar(file_hash))
archive_in_stream(output_path, opts) do
tar.read(Excon.defaults[:chunk_size]).to_s
end
end

def archive_in_stream(output_path, opts = {}, &block)
overwrite = opts[:overwrite] || opts['overwrite'] || false

connection.put(
path_for(:archive),
{ 'path' => output_path, 'noOverwriteDirNonDir' => !overwrite },
:headers => {
'Content-Type' => 'application/x-tar'
},
&block
)
self
end

# Create a new Container.
def self.create(opts = {}, conn = Docker.connection)
name = opts.delete('name')
Expand Down
99 changes: 99 additions & 0 deletions spec/docker/container_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,105 @@
end
end

describe '#archive_in', :docker_1_8 do
let(:license_path) { File.absolute_path(File.join(__FILE__, '..', '..', '..', 'LICENSE')) }
subject { Docker::Container.create('Image' => 'debian:wheezy', 'Cmd' => ['/bin/sh']) }
let(:committed_image) { subject.commit }
let(:ls_container) { committed_image.run('ls /').tap(&:wait) }
let(:output) { ls_container.streaming_logs(stdout: true, stderr: true) }

after do
subject.remove
end

context 'when the input is a tar' do
after do
ls_container.remove
committed_image.remove
end

it 'file exists in the container' do
subject.archive_in(license_path, '/', overwrite: false)
expect(output).to include('LICENSE')
end
end
end

describe '#archive_in_stream', :docker_1_8 do
let(:tar) { StringIO.new(Docker::Util.create_tar('/lol' => 'TEST')) }
subject { Docker::Container.create('Image' => 'debian:wheezy', 'Cmd' => ['/bin/sh']) }
let(:committed_image) { subject.commit }
let(:ls_container) { committed_image.run('ls /').tap(&:wait) }
let(:output) { ls_container.streaming_logs(stdout: true, stderr: true) }

after do
subject.remove
end

context 'when the input is a tar' do
after do
ls_container.remove
committed_image.remove
end

it 'file exists in the container' do
subject.archive_in_stream('/', overwrite: false) { tar.read }
expect(output).to include('lol')
end
end

context 'when the input would overwrite a directory with a file' do
let(:tar) { StringIO.new(Docker::Util.create_tar('/etc' => 'TEST')) }

it 'raises an error' do
# Docs say this should return a client error: clearly wrong
# https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/
# #extract-an-archive-of-files-or-folders-to-a-directory-in-a-container
expect {
subject.archive_in_stream('/', overwrite: false) { tar.read }
}.to raise_error(Docker::Error::ServerError)
end
end
end

describe '#archive_out', :docker_1_8 do
subject { Docker::Container.create('Image' => 'debian:wheezy', 'Cmd' => ['touch','/test']) }

after { subject.remove }

context 'when the file does not exist' do
it 'raises an error' do
subject.start
subject.wait

expect { subject.archive_out('/lol') { |chunk| puts chunk } }
.to raise_error(Docker::Error::NotFoundError)
end
end

context 'when the input is a file' do
it 'yields each chunk of the tarred file' do
subject.start; subject.wait

chunks = []
subject.archive_out('/test') { |chunk| chunks << chunk }
chunks = chunks.join("\n")
expect(chunks).to be_include('test')
end
end

context 'when the input is a directory' do
it 'yields each chunk of the tarred directory' do
subject.start; subject.wait

chunks = []
subject.archive_out('/etc/logrotate.d') { |chunk| chunks << chunk }
chunks = chunks.join("\n")
expect(%w[apt dpkg]).to be_all { |file| chunks.include?(file) }
end
end
end

describe '#export' do
subject { described_class.create('Cmd' => %w[/true],
'Image' => 'tianon/true') }
Expand Down
18 changes: 18 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,22 @@ def project_dir
config.color = true
config.formatter = :documentation
config.tty = true

case ENV['DOCKER_VERSION']
when /1\.6/
config.filter_run_including :docker_1_8 => false
config.filter_run_including :docker_1_9 => false
when /1\.7/
config.filter_run_including :docker_1_8 => false
config.filter_run_including :docker_1_9 => false
when /1\.8/
config.filter_run_including :docker_1_8 => true
config.filter_run_including :docker_1_9 => false
when /1\.9/
config.filter_run_including :docker_1_8 => true
config.filter_run_including :docker_1_9 => true
else
config.filter_run_including :docker_1_8 => true
config.filter_run_including :docker_1_9 => true
end
end