docker-api 1.27.0 → 2.0.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 +5 -5
- data/README.md +110 -10
- data/lib/docker.rb +7 -14
- data/lib/docker/connection.rb +3 -3
- data/lib/docker/container.rb +103 -45
- data/lib/docker/event.rb +100 -14
- data/lib/docker/exec.rb +6 -6
- data/lib/docker/image.rb +32 -11
- data/lib/docker/network.rb +16 -12
- data/lib/docker/util.rb +56 -16
- data/lib/docker/version.rb +1 -4
- data/lib/docker/volume.rb +8 -4
- metadata +16 -66
- data/.cane +0 -2
- data/.gitignore +0 -6
- data/.rspec +0 -1
- data/.simplecov +0 -4
- data/.travis.yml +0 -25
- data/Dockerfile +0 -2
- data/Gemfile +0 -3
- data/Rakefile +0 -54
- data/TESTING.md +0 -49
- data/docker-api.gemspec +0 -28
- data/script/docker +0 -149
- data/script/docker.conf +0 -61
- data/script/install_docker.sh +0 -35
- data/spec/docker/connection_spec.rb +0 -123
- data/spec/docker/container_spec.rb +0 -801
- data/spec/docker/event_spec.rb +0 -89
- data/spec/docker/exec_spec.rb +0 -181
- data/spec/docker/image_spec.rb +0 -683
- data/spec/docker/messages_spec.rb +0 -97
- data/spec/docker/messages_stack.rb +0 -26
- data/spec/docker/network_spec.rb +0 -150
- data/spec/docker/util_spec.rb +0 -154
- data/spec/docker/volume_spec.rb +0 -46
- data/spec/docker_spec.rb +0 -258
- data/spec/fixtures/build_from_dir/Dockerfile +0 -2
- data/spec/fixtures/export.tar +0 -0
- data/spec/fixtures/load.tar +0 -0
- data/spec/fixtures/top/Dockerfile +0 -2
- data/spec/spec_helper.rb +0 -36
data/script/docker.conf
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
description "Docker daemon"
|
2
|
-
|
3
|
-
start on (local-filesystems and net-device-up IFACE!=lo)
|
4
|
-
stop on runlevel [!2345]
|
5
|
-
limit nofile 524288 1048576
|
6
|
-
limit nproc 524288 1048576
|
7
|
-
|
8
|
-
respawn
|
9
|
-
|
10
|
-
kill timeout 20
|
11
|
-
|
12
|
-
pre-start script
|
13
|
-
# see also https://github.com/tianon/cgroupfs-mount/blob/master/cgroupfs-mount
|
14
|
-
if grep -v '^#' /etc/fstab | grep -q cgroup \
|
15
|
-
|| [ ! -e /proc/cgroups ] \
|
16
|
-
|| [ ! -d /sys/fs/cgroup ]; then
|
17
|
-
exit 0
|
18
|
-
fi
|
19
|
-
if ! mountpoint -q /sys/fs/cgroup; then
|
20
|
-
mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup /sys/fs/cgroup
|
21
|
-
fi
|
22
|
-
(
|
23
|
-
cd /sys/fs/cgroup
|
24
|
-
for sys in $(awk '!/^#/ { if ($4 == 1) print $1 }' /proc/cgroups); do
|
25
|
-
mkdir -p $sys
|
26
|
-
if ! mountpoint -q $sys; then
|
27
|
-
if ! mount -n -t cgroup -o $sys cgroup $sys; then
|
28
|
-
rmdir $sys || true
|
29
|
-
fi
|
30
|
-
fi
|
31
|
-
done
|
32
|
-
)
|
33
|
-
end script
|
34
|
-
|
35
|
-
script
|
36
|
-
# modify these in /etc/default/$UPSTART_JOB (/etc/default/docker)
|
37
|
-
DOCKER=/usr/bin/$UPSTART_JOB
|
38
|
-
DOCKER_OPTS=
|
39
|
-
if [ -f /etc/default/$UPSTART_JOB ]; then
|
40
|
-
. /etc/default/$UPSTART_JOB
|
41
|
-
fi
|
42
|
-
exec "$DOCKER" -d $DOCKER_OPTS
|
43
|
-
end script
|
44
|
-
|
45
|
-
# Don't emit "started" event until docker.sock is ready.
|
46
|
-
# See https://github.com/docker/docker/issues/6647
|
47
|
-
post-start script
|
48
|
-
DOCKER_OPTS=
|
49
|
-
if [ -f /etc/default/$UPSTART_JOB ]; then
|
50
|
-
. /etc/default/$UPSTART_JOB
|
51
|
-
fi
|
52
|
-
if ! printf "%s" "$DOCKER_OPTS" | grep -qE -e '-H|--host'; then
|
53
|
-
while ! [ -e /var/run/docker.sock ]; do
|
54
|
-
initctl status $UPSTART_JOB | grep -qE "(stop|respawn)/" && exit 1
|
55
|
-
echo "Waiting for /var/run/docker.sock"
|
56
|
-
sleep 0.1
|
57
|
-
done
|
58
|
-
echo "/var/run/docker.sock is up"
|
59
|
-
fi
|
60
|
-
end script
|
61
|
-
|
data/script/install_docker.sh
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
set -x
|
3
|
-
set -e
|
4
|
-
|
5
|
-
# argv[0]
|
6
|
-
DOCKER_VERSION=$1
|
7
|
-
|
8
|
-
# disable travis default installation
|
9
|
-
service docker stop
|
10
|
-
apt-get -y --purge remove docker-engine
|
11
|
-
|
12
|
-
# install gpg key for docker rpo
|
13
|
-
apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv 58118E89F3A912897C070ADBF76221572C52609D
|
14
|
-
|
15
|
-
# enable docker repo
|
16
|
-
echo 'deb "https://apt.dockerproject.org/repo" ubuntu-trusty main' >> /etc/apt/sources.list.d/docker-main.list
|
17
|
-
apt-get update -o Dir::Etc::sourcelist='sources.list.d/docker-main.list' -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0'
|
18
|
-
apt-cache gencaches
|
19
|
-
|
20
|
-
# install package
|
21
|
-
apt-get -y --force-yes install docker-engine=${DOCKER_VERSION}-0~trusty
|
22
|
-
echo 'DOCKER_OPTS="-H unix:///var/run/docker.sock --pidfile=/var/run/docker.pid"' > /etc/default/docker
|
23
|
-
cat /etc/default/docker
|
24
|
-
|
25
|
-
# docker 1.6 packages did not come with init files
|
26
|
-
if [[ $DOCKER_VERSION == 1.6.* ]]
|
27
|
-
then
|
28
|
-
cp script/docker.conf /etc/init/docker.conf
|
29
|
-
cp script/docker /etc/init.d/docker
|
30
|
-
chmod +x /etc/init.d/docker
|
31
|
-
else
|
32
|
-
service docker stop
|
33
|
-
fi
|
34
|
-
|
35
|
-
service docker start
|
@@ -1,123 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Docker::Connection do
|
4
|
-
subject { described_class.new('http://localhost:4243', {}) }
|
5
|
-
|
6
|
-
describe '#initialize' do
|
7
|
-
let(:url) { 'http://localhost:4243' }
|
8
|
-
let(:options) { {} }
|
9
|
-
subject { described_class.new(url, options) }
|
10
|
-
|
11
|
-
context 'when the first argument is not a String' do
|
12
|
-
let(:url) { :lol_not_a_string }
|
13
|
-
|
14
|
-
it 'raises an error' do
|
15
|
-
expect { subject }.to raise_error(Docker::Error::ArgumentError)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'when the first argument is a String' do
|
20
|
-
context 'and the url is a unix socket' do
|
21
|
-
let(:url) { 'unix:///var/run/docker.sock' }
|
22
|
-
|
23
|
-
it 'sets the socket path in the options' do
|
24
|
-
expect(subject.url).to eq('unix:///')
|
25
|
-
expect(subject.options).to include(:socket => '/var/run/docker.sock')
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
context 'but the second argument is not a Hash' do
|
30
|
-
let(:options) { :lol_not_a_hash }
|
31
|
-
|
32
|
-
it 'raises an error' do
|
33
|
-
expect { subject }.to raise_error(Docker::Error::ArgumentError)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
context 'and the second argument is a Hash' do
|
38
|
-
it 'sets the url and options' do
|
39
|
-
expect(subject.url).to eq url
|
40
|
-
expect(subject.options).to eq options
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
context 'url conversion to uri' do
|
46
|
-
context 'when the url does not contain a scheme' do
|
47
|
-
let(:url) { 'localhost:4243' }
|
48
|
-
|
49
|
-
it 'adds the scheme to the url' do
|
50
|
-
expect(subject.url).to eq "http://#{url}"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'when the url is a complete uri' do
|
55
|
-
let(:url) { 'http://localhost:4243' }
|
56
|
-
|
57
|
-
it 'leaves the url intact' do
|
58
|
-
expect(subject.url).to eq url
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe '#resource' do
|
65
|
-
its(:resource) { should be_a Excon::Connection }
|
66
|
-
end
|
67
|
-
|
68
|
-
describe '#request' do
|
69
|
-
let(:method) { :get }
|
70
|
-
let(:path) { '/test' }
|
71
|
-
let(:query) { { :all => true } }
|
72
|
-
let(:options) { { :expects => 201, :lol => true } }
|
73
|
-
let(:body) { rand(10000000) }
|
74
|
-
let(:resource) { double(:resource) }
|
75
|
-
let(:response) { double(:response, :body => body) }
|
76
|
-
let(:expected_hash) {
|
77
|
-
{
|
78
|
-
:method => method,
|
79
|
-
:path => "/v#{Docker::API_VERSION}#{path}",
|
80
|
-
:query => query,
|
81
|
-
:headers => { 'Content-Type' => 'text/plain',
|
82
|
-
'User-Agent' => "Swipely/Docker-API #{Docker::VERSION}",
|
83
|
-
},
|
84
|
-
:expects => 201,
|
85
|
-
:idempotent => true,
|
86
|
-
:lol => true
|
87
|
-
}
|
88
|
-
}
|
89
|
-
|
90
|
-
before do
|
91
|
-
allow(subject).to receive(:resource).and_return(resource)
|
92
|
-
expect(resource).to receive(:request).
|
93
|
-
with(expected_hash).
|
94
|
-
and_return(response)
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'sends #request to #resource with the compiled params' do
|
98
|
-
expect(subject.request(method, path, query, options)).to eq body
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
[:get, :put, :post, :delete].each do |method|
|
103
|
-
describe "##{method}" do
|
104
|
-
it 'is delegated to #request' do
|
105
|
-
expect(subject).to receive(:request).with(method)
|
106
|
-
subject.public_send(method)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
describe '#to_s' do
|
112
|
-
let(:url) { 'http://google.com:4000' }
|
113
|
-
let(:options) { {} }
|
114
|
-
let(:expected_string) {
|
115
|
-
"Docker::Connection { :url => #{url}, :options => #{options} }"
|
116
|
-
}
|
117
|
-
subject { described_class.new(url, options) }
|
118
|
-
|
119
|
-
it 'returns a pretty version with the url and port' do
|
120
|
-
expect(subject.to_s).to eq expected_string
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
@@ -1,801 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Docker::Container do
|
4
|
-
describe '#to_s' do
|
5
|
-
subject {
|
6
|
-
described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
|
7
|
-
}
|
8
|
-
|
9
|
-
let(:id) { 'bf119e2' }
|
10
|
-
let(:connection) { Docker.connection }
|
11
|
-
let(:expected_string) {
|
12
|
-
"Docker::Container { :id => #{id}, :connection => #{connection} }"
|
13
|
-
}
|
14
|
-
before do
|
15
|
-
{
|
16
|
-
:@id => id,
|
17
|
-
:@connection => connection
|
18
|
-
}.each { |k, v| subject.instance_variable_set(k, v) }
|
19
|
-
end
|
20
|
-
|
21
|
-
its(:to_s) { should == expected_string }
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#json' do
|
25
|
-
subject {
|
26
|
-
described_class.create('Cmd' => %w[true], 'Image' => 'debian:wheezy')
|
27
|
-
}
|
28
|
-
let(:description) { subject.json }
|
29
|
-
after(:each) { subject.remove }
|
30
|
-
|
31
|
-
it 'returns the description as a Hash' do
|
32
|
-
expect(description).to be_a Hash
|
33
|
-
expect(description['Id']).to start_with(subject.id)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe '#streaming_logs' do
|
38
|
-
let(:options) { {} }
|
39
|
-
subject do
|
40
|
-
described_class.create(
|
41
|
-
{'Cmd' => ['/bin/bash', '-lc', 'echo hello'], 'Image' => 'debian:wheezy'}.merge(options)
|
42
|
-
)
|
43
|
-
end
|
44
|
-
|
45
|
-
before(:each) { subject.tap(&:start).wait }
|
46
|
-
after(:each) { subject.remove }
|
47
|
-
|
48
|
-
context 'when not selecting any stream' do
|
49
|
-
let(:non_destination) { subject.streaming_logs }
|
50
|
-
it 'raises a client error' do
|
51
|
-
expect { non_destination }.to raise_error(Docker::Error::ClientError)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
context 'when selecting stdout' do
|
56
|
-
let(:stdout) { subject.streaming_logs(stdout: 1) }
|
57
|
-
it 'returns blank logs' do
|
58
|
-
expect(stdout).to be_a String
|
59
|
-
expect(stdout).to match("hello")
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
context 'when using a tty' do
|
64
|
-
let(:options) { { 'Tty' => true } }
|
65
|
-
|
66
|
-
let(:output) { subject.streaming_logs(stdout: 1, tty: 1) }
|
67
|
-
it 'returns `hello`' do
|
68
|
-
expect(output).to be_a(String)
|
69
|
-
expect(output).to match("hello")
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context 'when passing a block' do
|
74
|
-
let(:lines) { [] }
|
75
|
-
let(:output) { subject.streaming_logs(stdout: 1, follow: 1) { |s,c| lines << c } }
|
76
|
-
it 'returns `hello`' do
|
77
|
-
expect(output).to be_a(String)
|
78
|
-
expect(output).to match("hello")
|
79
|
-
expect(lines.join).to match("hello")
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
describe '#logs' do
|
85
|
-
subject {
|
86
|
-
described_class.create('Cmd' => "echo hello", 'Image' => 'debian:wheezy')
|
87
|
-
}
|
88
|
-
after(:each) { subject.remove }
|
89
|
-
|
90
|
-
context "when not selecting any stream" do
|
91
|
-
let(:non_destination) { subject.logs }
|
92
|
-
it 'raises a client error' do
|
93
|
-
expect { non_destination }.to raise_error(Docker::Error::ClientError)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context "when selecting stdout" do
|
98
|
-
let(:stdout) { subject.logs(stdout: 1) }
|
99
|
-
it 'returns blank logs' do
|
100
|
-
expect(stdout).to be_a String
|
101
|
-
expect(stdout).to eq ""
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
describe '#create' do
|
107
|
-
subject {
|
108
|
-
described_class.create({
|
109
|
-
'Cmd' => %w[true],
|
110
|
-
'Image' => 'debian:wheezy'
|
111
|
-
}.merge(opts))
|
112
|
-
}
|
113
|
-
|
114
|
-
context 'when creating a container named bob' do
|
115
|
-
let(:opts) { {"name" => "bob"} }
|
116
|
-
after(:each) { subject.remove }
|
117
|
-
|
118
|
-
it 'should have name set to bob' do
|
119
|
-
expect(subject.json["Name"]).to eq("/bob")
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
describe '#rename' do
|
125
|
-
subject {
|
126
|
-
described_class.create({
|
127
|
-
'name' => 'foo',
|
128
|
-
'Cmd' => %w[true],
|
129
|
-
'Image' => 'debian:wheezy'
|
130
|
-
})
|
131
|
-
}
|
132
|
-
|
133
|
-
before { subject.start }
|
134
|
-
after(:each) { subject.tap(&:wait).remove }
|
135
|
-
|
136
|
-
it 'renames the container' do
|
137
|
-
subject.rename('bar')
|
138
|
-
expect(subject.json["Name"]).to match(%r{bar})
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
describe '#changes' do
|
143
|
-
subject {
|
144
|
-
described_class.create(
|
145
|
-
'Cmd' => %w[rm -rf /root],
|
146
|
-
'Image' => 'debian:wheezy'
|
147
|
-
)
|
148
|
-
}
|
149
|
-
let(:changes) { subject.changes }
|
150
|
-
|
151
|
-
before { subject.tap(&:start).tap(&:wait) }
|
152
|
-
after(:each) { subject.tap(&:wait).remove }
|
153
|
-
|
154
|
-
it 'returns the changes as an array' do
|
155
|
-
expect(changes).to eq [
|
156
|
-
{
|
157
|
-
"Path" => "/root",
|
158
|
-
"Kind" => 2
|
159
|
-
},
|
160
|
-
]
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
describe '#top' do
|
165
|
-
let(:dir) {
|
166
|
-
File.join(File.dirname(__FILE__), '..', 'fixtures', 'top')
|
167
|
-
}
|
168
|
-
let(:image) { Docker::Image.build_from_dir(dir) }
|
169
|
-
let(:top) { sleep 1; container.top }
|
170
|
-
let!(:container) { image.run('/while') }
|
171
|
-
after do
|
172
|
-
container.kill!.remove
|
173
|
-
image.remove
|
174
|
-
end
|
175
|
-
|
176
|
-
it 'returns the top commands as an Array' do
|
177
|
-
expect(top).to be_a Array
|
178
|
-
expect(top).to_not be_empty
|
179
|
-
expect(top.first.keys).to include('PID')
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
describe '#copy' do
|
184
|
-
let(:image) { Docker::Image.create('fromImage' => 'debian:wheezy') }
|
185
|
-
subject { image.run('touch /test').tap { |c| c.wait } }
|
186
|
-
|
187
|
-
after(:each) { subject.remove }
|
188
|
-
|
189
|
-
context 'when the file does not exist' do
|
190
|
-
it 'raises an error' do
|
191
|
-
expect { subject.copy('/lol/not/a/real/file') { |chunk| puts chunk } }
|
192
|
-
.to raise_error
|
193
|
-
end
|
194
|
-
end
|
195
|
-
|
196
|
-
context 'when the input is a file' do
|
197
|
-
it 'yields each chunk of the tarred file' do
|
198
|
-
chunks = []
|
199
|
-
subject.copy('/test') { |chunk| chunks << chunk }
|
200
|
-
chunks = chunks.join("\n")
|
201
|
-
expect(chunks).to be_include('test')
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
context 'when the input is a directory' do
|
206
|
-
it 'yields each chunk of the tarred directory' do
|
207
|
-
chunks = []
|
208
|
-
subject.copy('/etc/logrotate.d') { |chunk| chunks << chunk }
|
209
|
-
chunks = chunks.join("\n")
|
210
|
-
expect(%w[apt dpkg]).to be_all { |file| chunks.include?(file) }
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
describe '#archive_in', :docker_1_8 do
|
216
|
-
let(:license_path) { File.absolute_path(File.join(__FILE__, '..', '..', '..', 'LICENSE')) }
|
217
|
-
subject { Docker::Container.create('Image' => 'debian:wheezy', 'Cmd' => ['/bin/sh']) }
|
218
|
-
let(:committed_image) { subject.commit }
|
219
|
-
let(:ls_container) { committed_image.run('ls /').tap(&:wait) }
|
220
|
-
let(:output) { ls_container.streaming_logs(stdout: true, stderr: true) }
|
221
|
-
|
222
|
-
after do
|
223
|
-
subject.remove
|
224
|
-
end
|
225
|
-
|
226
|
-
context 'when the input is a tar' do
|
227
|
-
after do
|
228
|
-
ls_container.remove
|
229
|
-
committed_image.remove
|
230
|
-
end
|
231
|
-
|
232
|
-
it 'file exists in the container' do
|
233
|
-
subject.archive_in(license_path, '/', overwrite: false)
|
234
|
-
expect(output).to include('LICENSE')
|
235
|
-
end
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
describe '#archive_in_stream', :docker_1_8 do
|
240
|
-
let(:tar) { StringIO.new(Docker::Util.create_tar('/lol' => 'TEST')) }
|
241
|
-
subject { Docker::Container.create('Image' => 'debian:wheezy', 'Cmd' => ['/bin/sh']) }
|
242
|
-
let(:committed_image) { subject.commit }
|
243
|
-
let(:ls_container) { committed_image.run('ls /').tap(&:wait) }
|
244
|
-
let(:output) { ls_container.streaming_logs(stdout: true, stderr: true) }
|
245
|
-
|
246
|
-
after do
|
247
|
-
subject.remove
|
248
|
-
end
|
249
|
-
|
250
|
-
context 'when the input is a tar' do
|
251
|
-
after do
|
252
|
-
ls_container.remove
|
253
|
-
committed_image.remove
|
254
|
-
end
|
255
|
-
|
256
|
-
it 'file exists in the container' do
|
257
|
-
subject.archive_in_stream('/', overwrite: false) { tar.read }
|
258
|
-
expect(output).to include('lol')
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
context 'when the input would overwrite a directory with a file' do
|
263
|
-
let(:tar) { StringIO.new(Docker::Util.create_tar('/etc' => 'TEST')) }
|
264
|
-
|
265
|
-
it 'raises an error' do
|
266
|
-
# Docs say this should return a client error: clearly wrong
|
267
|
-
# https://docs.docker.com/engine/reference/api/docker_remote_api_v1.21/
|
268
|
-
# #extract-an-archive-of-files-or-folders-to-a-directory-in-a-container
|
269
|
-
expect {
|
270
|
-
subject.archive_in_stream('/', overwrite: false) { tar.read }
|
271
|
-
}.to raise_error(Docker::Error::ServerError)
|
272
|
-
end
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
|
-
describe '#archive_out', :docker_1_8 do
|
277
|
-
subject { Docker::Container.create('Image' => 'debian:wheezy', 'Cmd' => ['touch','/test']) }
|
278
|
-
|
279
|
-
after { subject.remove }
|
280
|
-
|
281
|
-
context 'when the file does not exist' do
|
282
|
-
it 'raises an error' do
|
283
|
-
subject.start
|
284
|
-
subject.wait
|
285
|
-
|
286
|
-
expect { subject.archive_out('/lol') { |chunk| puts chunk } }
|
287
|
-
.to raise_error(Docker::Error::NotFoundError)
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
context 'when the input is a file' do
|
292
|
-
it 'yields each chunk of the tarred file' do
|
293
|
-
subject.start; subject.wait
|
294
|
-
|
295
|
-
chunks = []
|
296
|
-
subject.archive_out('/test') { |chunk| chunks << chunk }
|
297
|
-
chunks = chunks.join("\n")
|
298
|
-
expect(chunks).to be_include('test')
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
context 'when the input is a directory' do
|
303
|
-
it 'yields each chunk of the tarred directory' do
|
304
|
-
subject.start; subject.wait
|
305
|
-
|
306
|
-
chunks = []
|
307
|
-
subject.archive_out('/etc/logrotate.d') { |chunk| chunks << chunk }
|
308
|
-
chunks = chunks.join("\n")
|
309
|
-
expect(%w[apt dpkg]).to be_all { |file| chunks.include?(file) }
|
310
|
-
end
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
describe '#export' do
|
315
|
-
subject { described_class.create('Cmd' => %w[/true],
|
316
|
-
'Image' => 'tianon/true') }
|
317
|
-
before { subject.start }
|
318
|
-
after { subject.tap(&:wait).remove }
|
319
|
-
|
320
|
-
it 'yields each chunk' do
|
321
|
-
first = nil
|
322
|
-
subject.export do |chunk|
|
323
|
-
first ||= chunk
|
324
|
-
end
|
325
|
-
expect(first[257..261]).to eq "ustar" # Make sure the export is a tar.
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
describe '#attach' do
|
330
|
-
subject {
|
331
|
-
described_class.create(
|
332
|
-
'Cmd' => ['bash','-c','sleep 2; echo hello'],
|
333
|
-
'Image' => 'debian:wheezy'
|
334
|
-
)
|
335
|
-
}
|
336
|
-
|
337
|
-
before { subject.start }
|
338
|
-
after(:each) { subject.stop.remove }
|
339
|
-
|
340
|
-
context 'with normal sized chunks' do
|
341
|
-
it 'yields each chunk' do
|
342
|
-
chunk = nil
|
343
|
-
subject.attach do |stream, c|
|
344
|
-
chunk ||= c
|
345
|
-
end
|
346
|
-
expect(chunk).to eq("hello\n")
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
context 'with very small chunks' do
|
351
|
-
before do
|
352
|
-
Docker.options = { :chunk_size => 1 }
|
353
|
-
end
|
354
|
-
|
355
|
-
after do
|
356
|
-
Docker.options = {}
|
357
|
-
end
|
358
|
-
|
359
|
-
it 'yields each chunk' do
|
360
|
-
chunk = nil
|
361
|
-
subject.attach do |stream, c|
|
362
|
-
chunk ||= c
|
363
|
-
end
|
364
|
-
expect(chunk).to eq("hello\n")
|
365
|
-
end
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
describe '#attach with stdin' do
|
370
|
-
it 'yields the output' do
|
371
|
-
container = described_class.create(
|
372
|
-
'Cmd' => %w[cat],
|
373
|
-
'Image' => 'debian:wheezy',
|
374
|
-
'OpenStdin' => true,
|
375
|
-
'StdinOnce' => true
|
376
|
-
)
|
377
|
-
chunk = nil
|
378
|
-
container
|
379
|
-
.tap(&:start)
|
380
|
-
.attach(stdin: StringIO.new("foo\nbar\n")) do |stream, c|
|
381
|
-
chunk ||= c
|
382
|
-
end
|
383
|
-
container.tap(&:wait).remove
|
384
|
-
|
385
|
-
expect(chunk).to eq("foo\nbar\n")
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
describe '#start' do
|
390
|
-
subject {
|
391
|
-
described_class.create(
|
392
|
-
'Cmd' => %w[test -d /foo],
|
393
|
-
'Image' => 'debian:wheezy',
|
394
|
-
'Volumes' => {'/foo' => {}}
|
395
|
-
)
|
396
|
-
}
|
397
|
-
let(:all) { Docker::Container.all(all: true) }
|
398
|
-
|
399
|
-
before { subject.start('Binds' => ["/tmp:/foo"]) }
|
400
|
-
after(:each) { subject.remove }
|
401
|
-
|
402
|
-
it 'starts the container' do
|
403
|
-
expect(all.map(&:id)).to be_any { |id| id.start_with?(subject.id) }
|
404
|
-
expect(subject.wait(10)['StatusCode']).to be_zero
|
405
|
-
end
|
406
|
-
end
|
407
|
-
|
408
|
-
describe '#stop' do
|
409
|
-
subject {
|
410
|
-
described_class.create('Cmd' => %w[true], 'Image' => 'debian:wheezy')
|
411
|
-
}
|
412
|
-
|
413
|
-
before { subject.tap(&:start).stop('timeout' => '10') }
|
414
|
-
after { subject.remove }
|
415
|
-
|
416
|
-
it 'stops the container' do
|
417
|
-
expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
|
418
|
-
id.start_with?(subject.id)
|
419
|
-
}
|
420
|
-
expect(described_class.all.map(&:id)).to be_none { |id|
|
421
|
-
id.start_with?(subject.id)
|
422
|
-
}
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
describe '#exec' do
|
427
|
-
subject {
|
428
|
-
described_class.create(
|
429
|
-
'Cmd' => %w[sleep 20],
|
430
|
-
'Image' => 'debian:wheezy'
|
431
|
-
).start
|
432
|
-
}
|
433
|
-
after { subject.kill!.remove }
|
434
|
-
|
435
|
-
context 'when passed only a command' do
|
436
|
-
let(:output) { subject.exec(['bash','-c','sleep 2; echo hello']) }
|
437
|
-
|
438
|
-
it 'returns the stdout/stderr messages and exit code' do
|
439
|
-
expect(output).to eq([["hello\n"], [], 0])
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
context 'when detach is true' do
|
444
|
-
let(:output) { subject.exec(['date'], detach: true) }
|
445
|
-
|
446
|
-
it 'returns the Docker::Exec object' do
|
447
|
-
expect(output).to be_a Docker::Exec
|
448
|
-
expect(output.id).to_not be_nil
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
|
-
context 'when passed a block' do
|
453
|
-
it 'streams the stdout/stderr messages' do
|
454
|
-
chunk = nil
|
455
|
-
subject.exec(['bash','-c','sleep 2; echo hello']) do |stream, c|
|
456
|
-
chunk ||= c
|
457
|
-
end
|
458
|
-
expect(chunk).to eq("hello\n")
|
459
|
-
end
|
460
|
-
end
|
461
|
-
|
462
|
-
context 'when stdin object is passed' do
|
463
|
-
let(:output) { subject.exec(['cat'], stdin: StringIO.new("hello")) }
|
464
|
-
|
465
|
-
it 'returns the stdout/stderr messages' do
|
466
|
-
expect(output).to eq([["hello"],[],0])
|
467
|
-
end
|
468
|
-
end
|
469
|
-
|
470
|
-
context 'when tty is true' do
|
471
|
-
let(:command) { [
|
472
|
-
"bash", "-c",
|
473
|
-
"if [ -t 1 ]; then echo -n \"I'm a TTY!\"; fi"
|
474
|
-
] }
|
475
|
-
let(:output) { subject.exec(command, tty: true) }
|
476
|
-
|
477
|
-
it 'returns the raw stdout/stderr output' do
|
478
|
-
expect(output).to eq([["I'm a TTY!"], [], 0])
|
479
|
-
end
|
480
|
-
end
|
481
|
-
end
|
482
|
-
|
483
|
-
describe '#kill' do
|
484
|
-
let(:command) { ['/bin/bash', '-c', 'while [ 1 ]; do echo hello; done'] }
|
485
|
-
subject {
|
486
|
-
described_class.create('Cmd' => command, 'Image' => 'debian:wheezy')
|
487
|
-
}
|
488
|
-
|
489
|
-
before { subject.start }
|
490
|
-
after(:each) {subject.remove }
|
491
|
-
|
492
|
-
it 'kills the container' do
|
493
|
-
subject.kill
|
494
|
-
expect(described_class.all.map(&:id)).to be_none { |id|
|
495
|
-
id.start_with?(subject.id)
|
496
|
-
}
|
497
|
-
expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
|
498
|
-
id.start_with?(subject.id)
|
499
|
-
}
|
500
|
-
end
|
501
|
-
|
502
|
-
context 'with a kill signal' do
|
503
|
-
let(:command) {
|
504
|
-
[
|
505
|
-
'/bin/bash',
|
506
|
-
'-c',
|
507
|
-
'trap echo SIGTERM; while [ 1 ]; do echo hello; done'
|
508
|
-
]
|
509
|
-
}
|
510
|
-
it 'kills the container' do
|
511
|
-
subject.kill(:signal => "SIGTERM")
|
512
|
-
expect(described_class.all.map(&:id)).to be_any { |id|
|
513
|
-
id.start_with?(subject.id)
|
514
|
-
}
|
515
|
-
expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
|
516
|
-
id.start_with?(subject.id)
|
517
|
-
}
|
518
|
-
|
519
|
-
subject.kill(:signal => "SIGKILL")
|
520
|
-
expect(described_class.all.map(&:id)).to be_none { |id|
|
521
|
-
id.start_with?(subject.id)
|
522
|
-
}
|
523
|
-
expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
|
524
|
-
id.start_with?(subject.id)
|
525
|
-
}
|
526
|
-
end
|
527
|
-
end
|
528
|
-
end
|
529
|
-
|
530
|
-
describe '#delete' do
|
531
|
-
subject {
|
532
|
-
described_class.create('Cmd' => ['ls'], 'Image' => 'debian:wheezy')
|
533
|
-
}
|
534
|
-
|
535
|
-
it 'deletes the container' do
|
536
|
-
subject.delete(:force => true)
|
537
|
-
expect(described_class.all.map(&:id)).to be_none { |id|
|
538
|
-
id.start_with?(subject.id)
|
539
|
-
}
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
describe '#restart' do
|
544
|
-
subject {
|
545
|
-
described_class.create('Cmd' => %w[sleep 10], 'Image' => 'debian:wheezy')
|
546
|
-
}
|
547
|
-
|
548
|
-
before { subject.start }
|
549
|
-
after { subject.kill!.remove }
|
550
|
-
|
551
|
-
it 'restarts the container' do
|
552
|
-
expect(described_class.all.map(&:id)).to be_any { |id|
|
553
|
-
id.start_with?(subject.id)
|
554
|
-
}
|
555
|
-
subject.stop
|
556
|
-
expect(described_class.all.map(&:id)).to be_none { |id|
|
557
|
-
id.start_with?(subject.id)
|
558
|
-
}
|
559
|
-
subject.restart('timeout' => '10')
|
560
|
-
expect(described_class.all.map(&:id)).to be_any { |id|
|
561
|
-
id.start_with?(subject.id)
|
562
|
-
}
|
563
|
-
end
|
564
|
-
end
|
565
|
-
|
566
|
-
describe '#pause' do
|
567
|
-
subject {
|
568
|
-
described_class.create(
|
569
|
-
'Cmd' => %w[sleep 50],
|
570
|
-
'Image' => 'debian:wheezy'
|
571
|
-
).start
|
572
|
-
}
|
573
|
-
after { subject.unpause.kill!.remove }
|
574
|
-
|
575
|
-
it 'pauses the container' do
|
576
|
-
subject.pause
|
577
|
-
expect(described_class.get(subject.id).info['State']['Paused']).to be true
|
578
|
-
end
|
579
|
-
end
|
580
|
-
|
581
|
-
describe '#unpause' do
|
582
|
-
subject {
|
583
|
-
described_class.create(
|
584
|
-
'Cmd' => %w[sleep 50],
|
585
|
-
'Image' => 'debian:wheezy'
|
586
|
-
).start
|
587
|
-
}
|
588
|
-
before { subject.pause }
|
589
|
-
after { subject.kill!.remove }
|
590
|
-
|
591
|
-
it 'unpauses the container' do
|
592
|
-
subject.unpause
|
593
|
-
expect(
|
594
|
-
described_class.get(subject.id).info['State']['Paused']
|
595
|
-
).to be false
|
596
|
-
end
|
597
|
-
end
|
598
|
-
|
599
|
-
describe '#wait' do
|
600
|
-
subject {
|
601
|
-
described_class.create(
|
602
|
-
'Cmd' => %w[tar nonsense],
|
603
|
-
'Image' => 'debian:wheezy'
|
604
|
-
)
|
605
|
-
}
|
606
|
-
|
607
|
-
before { subject.start }
|
608
|
-
after(:each) { subject.remove }
|
609
|
-
|
610
|
-
it 'waits for the command to finish' do
|
611
|
-
expect(subject.wait['StatusCode']).to_not be_zero
|
612
|
-
end
|
613
|
-
|
614
|
-
context 'when an argument is given' do
|
615
|
-
subject { described_class.create('Cmd' => %w[sleep 5],
|
616
|
-
'Image' => 'debian:wheezy') }
|
617
|
-
|
618
|
-
it 'sets the :read_timeout to that amount of time' do
|
619
|
-
expect(subject.wait(6)['StatusCode']).to be_zero
|
620
|
-
end
|
621
|
-
|
622
|
-
context 'and a command runs for too long' do
|
623
|
-
it 'raises a ServerError' do
|
624
|
-
expect{subject.wait(4)}.to raise_error(Docker::Error::TimeoutError)
|
625
|
-
subject.tap(&:wait)
|
626
|
-
end
|
627
|
-
end
|
628
|
-
end
|
629
|
-
end
|
630
|
-
|
631
|
-
describe '#run' do
|
632
|
-
let(:run_command) { subject.run('ls') }
|
633
|
-
|
634
|
-
context 'when the Container\'s command does not return status code of 0' do
|
635
|
-
subject { described_class.create('Cmd' => %w[false],
|
636
|
-
'Image' => 'debian:wheezy') }
|
637
|
-
|
638
|
-
after do
|
639
|
-
subject.remove
|
640
|
-
end
|
641
|
-
|
642
|
-
it 'raises an error' do
|
643
|
-
expect { run_command }
|
644
|
-
.to raise_error(Docker::Error::UnexpectedResponseError)
|
645
|
-
end
|
646
|
-
end
|
647
|
-
|
648
|
-
context 'when the Container\'s command returns a status code of 0' do
|
649
|
-
subject { described_class.create('Cmd' => %w[pwd],
|
650
|
-
'Image' => 'debian:wheezy') }
|
651
|
-
after do
|
652
|
-
subject.remove
|
653
|
-
image = run_command.json['Image']
|
654
|
-
run_command.remove
|
655
|
-
Docker::Image.get(image).history.each do |layer|
|
656
|
-
next unless layer['CreatedBy'] == 'pwd'
|
657
|
-
Docker::Image.get(layer['Id']).remove(:noprune => true)
|
658
|
-
end
|
659
|
-
end
|
660
|
-
|
661
|
-
it 'creates a new container to run the specified command' do
|
662
|
-
expect(run_command.wait['StatusCode']).to be_zero
|
663
|
-
end
|
664
|
-
end
|
665
|
-
end
|
666
|
-
|
667
|
-
describe '#commit' do
|
668
|
-
subject {
|
669
|
-
described_class.create('Cmd' => %w[true], 'Image' => 'debian:wheezy')
|
670
|
-
}
|
671
|
-
let(:image) { subject.commit }
|
672
|
-
|
673
|
-
after(:each) do
|
674
|
-
subject.remove
|
675
|
-
image.remove
|
676
|
-
end
|
677
|
-
|
678
|
-
it 'creates a new Image from the Container\'s changes' do
|
679
|
-
subject.tap(&:start).wait
|
680
|
-
|
681
|
-
expect(image).to be_a Docker::Image
|
682
|
-
expect(image.id).to_not be_nil
|
683
|
-
end
|
684
|
-
|
685
|
-
context 'if run is passed, it saves the command in the image' do
|
686
|
-
let(:image) { subject.commit }
|
687
|
-
let(:container) { image.run('pwd') }
|
688
|
-
|
689
|
-
it 'saves the command' do
|
690
|
-
container.wait
|
691
|
-
expect(container.attach(logs: true, stream: false)).to eql [["/\n"],[]]
|
692
|
-
container.remove
|
693
|
-
end
|
694
|
-
end
|
695
|
-
end
|
696
|
-
|
697
|
-
describe '.create' do
|
698
|
-
subject { described_class }
|
699
|
-
|
700
|
-
context 'when the Container does not yet exist' do
|
701
|
-
context 'when the HTTP request does not return a 200' do
|
702
|
-
before do
|
703
|
-
Docker.options = { :mock => true }
|
704
|
-
Excon.stub({ :method => :post }, { :status => 400 })
|
705
|
-
end
|
706
|
-
after do
|
707
|
-
Excon.stubs.shift
|
708
|
-
Docker.options = {}
|
709
|
-
end
|
710
|
-
|
711
|
-
it 'raises an error' do
|
712
|
-
expect { subject.create }.to raise_error(Docker::Error::ClientError)
|
713
|
-
end
|
714
|
-
end
|
715
|
-
|
716
|
-
context 'when the HTTP request returns a 200' do
|
717
|
-
let(:options) do
|
718
|
-
{
|
719
|
-
"Cmd" => ["date"],
|
720
|
-
"Image" => "debian:wheezy",
|
721
|
-
}
|
722
|
-
end
|
723
|
-
let(:container) { subject.create(options) }
|
724
|
-
after { container.remove }
|
725
|
-
|
726
|
-
it 'sets the id' do
|
727
|
-
expect(container).to be_a Docker::Container
|
728
|
-
expect(container.id).to_not be_nil
|
729
|
-
expect(container.connection).to_not be_nil
|
730
|
-
end
|
731
|
-
end
|
732
|
-
end
|
733
|
-
end
|
734
|
-
|
735
|
-
describe '.get' do
|
736
|
-
subject { described_class }
|
737
|
-
|
738
|
-
context 'when the HTTP response is not a 200' do
|
739
|
-
before do
|
740
|
-
Docker.options = { :mock => true }
|
741
|
-
Excon.stub({ :method => :get }, { :status => 500 })
|
742
|
-
end
|
743
|
-
after do
|
744
|
-
Excon.stubs.shift
|
745
|
-
Docker.options = {}
|
746
|
-
end
|
747
|
-
|
748
|
-
it 'raises an error' do
|
749
|
-
expect { subject.get('randomID') }
|
750
|
-
.to raise_error(Docker::Error::ServerError)
|
751
|
-
end
|
752
|
-
end
|
753
|
-
|
754
|
-
context 'when the HTTP response is a 200' do
|
755
|
-
let(:container) {
|
756
|
-
subject.create('Cmd' => ['ls'], 'Image' => 'debian:wheezy')
|
757
|
-
}
|
758
|
-
after { container.remove }
|
759
|
-
|
760
|
-
it 'materializes the Container into a Docker::Container' do
|
761
|
-
expect(subject.get(container.id)).to be_a Docker::Container
|
762
|
-
end
|
763
|
-
end
|
764
|
-
|
765
|
-
end
|
766
|
-
|
767
|
-
describe '.all' do
|
768
|
-
subject { described_class }
|
769
|
-
|
770
|
-
context 'when the HTTP response is not a 200' do
|
771
|
-
before do
|
772
|
-
Docker.options = { :mock => true }
|
773
|
-
Excon.stub({ :method => :get }, { :status => 500 })
|
774
|
-
end
|
775
|
-
after do
|
776
|
-
Excon.stubs.shift
|
777
|
-
Docker.options = {}
|
778
|
-
end
|
779
|
-
|
780
|
-
it 'raises an error' do
|
781
|
-
expect { subject.all }
|
782
|
-
.to raise_error(Docker::Error::ServerError)
|
783
|
-
end
|
784
|
-
end
|
785
|
-
|
786
|
-
context 'when the HTTP response is a 200' do
|
787
|
-
let(:container) {
|
788
|
-
subject.create('Cmd' => ['ls'], 'Image' => 'debian:wheezy')
|
789
|
-
}
|
790
|
-
before { container }
|
791
|
-
after { container.remove }
|
792
|
-
|
793
|
-
it 'materializes each Container into a Docker::Container' do
|
794
|
-
expect(subject.all(:all => true)).to be_all { |container|
|
795
|
-
container.is_a?(Docker::Container)
|
796
|
-
}
|
797
|
-
expect(subject.all(:all => true).length).to_not be_zero
|
798
|
-
end
|
799
|
-
end
|
800
|
-
end
|
801
|
-
end
|