docker-api 1.28.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +99 -9
- 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 +30 -9
- 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 -695
- 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/spec/docker/event_spec.rb
DELETED
@@ -1,89 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Docker::Event do
|
4
|
-
describe "#to_s" do
|
5
|
-
subject { described_class.new(status, id, from, time) }
|
6
|
-
|
7
|
-
let(:status) { "start" }
|
8
|
-
let(:id) { "398c9f77b5d2" }
|
9
|
-
let(:from) { "debian:wheezy" }
|
10
|
-
let(:time) { 1381956164 }
|
11
|
-
|
12
|
-
let(:expected_string) {
|
13
|
-
"Docker::Event { :status => #{status}, :id => #{id}, "\
|
14
|
-
":from => #{from}, :time => #{time.to_s} }"
|
15
|
-
}
|
16
|
-
|
17
|
-
it "equals the expected string" do
|
18
|
-
expect(subject.to_s).to eq(expected_string)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe ".stream" do
|
23
|
-
let(:container) { Docker::Image.create('fromImage' => 'debian:wheezy').run('bash') }
|
24
|
-
it 'receives at least 4 events' do
|
25
|
-
expect(Docker::Event)
|
26
|
-
.to receive(:new_event)
|
27
|
-
.at_least(4).times
|
28
|
-
.and_call_original
|
29
|
-
|
30
|
-
stream_thread = Thread.new do
|
31
|
-
Docker::Event.stream do |event|
|
32
|
-
puts "#{event}"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
stream_thread.join(0.1)
|
37
|
-
container.wait
|
38
|
-
stream_thread.join(10)
|
39
|
-
stream_thread.kill
|
40
|
-
|
41
|
-
container.remove
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe ".since" do
|
46
|
-
let(:time) { Time.now.to_i + 1 }
|
47
|
-
let(:container) { Docker::Image.create('fromImage' => 'debian:wheezy').run('bash') }
|
48
|
-
|
49
|
-
it 'receives at least 4 events' do
|
50
|
-
expect(Docker::Event)
|
51
|
-
.to receive(:new_event)
|
52
|
-
.at_least(4).times
|
53
|
-
.and_call_original
|
54
|
-
|
55
|
-
stream_thread = Thread.new do
|
56
|
-
Docker::Event.since(time) do |event|
|
57
|
-
puts "#{event}"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
stream_thread.join(0.1)
|
62
|
-
container.wait
|
63
|
-
stream_thread.join(10)
|
64
|
-
stream_thread.kill
|
65
|
-
|
66
|
-
container.remove
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe ".new_event" do
|
71
|
-
subject { Docker::Event.new_event(response_body, nil, nil) }
|
72
|
-
let(:status) { "start" }
|
73
|
-
let(:id) { "398c9f77b5d2" }
|
74
|
-
let(:from) { "debian:wheezy" }
|
75
|
-
let(:time) { 1381956164 }
|
76
|
-
let(:response_body) {
|
77
|
-
"{\"status\":\"#{status}\",\"id\":\"#{id}\""\
|
78
|
-
",\"from\":\"#{from}\",\"time\":#{time}}"
|
79
|
-
}
|
80
|
-
|
81
|
-
it "returns a Docker::Event" do
|
82
|
-
expect(subject).to be_kind_of(Docker::Event)
|
83
|
-
expect(subject.status).to eq(status)
|
84
|
-
expect(subject.id).to eq(id)
|
85
|
-
expect(subject.from).to eq(from)
|
86
|
-
expect(subject.time).to eq(time)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
data/spec/docker/exec_spec.rb
DELETED
@@ -1,181 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Docker::Exec do
|
4
|
-
let(:container) {
|
5
|
-
Docker::Container.create(
|
6
|
-
'Cmd' => %w(sleep 300),
|
7
|
-
'Image' => 'debian:wheezy'
|
8
|
-
).start!
|
9
|
-
}
|
10
|
-
|
11
|
-
describe '#to_s' do
|
12
|
-
subject {
|
13
|
-
described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
|
14
|
-
}
|
15
|
-
|
16
|
-
let(:id) { 'bf119e2' }
|
17
|
-
let(:connection) { Docker.connection }
|
18
|
-
let(:expected_string) {
|
19
|
-
"Docker::Exec { :id => #{id}, :connection => #{connection} }"
|
20
|
-
}
|
21
|
-
before do
|
22
|
-
{
|
23
|
-
:@id => id,
|
24
|
-
:@connection => connection
|
25
|
-
}.each { |k, v| subject.instance_variable_set(k, v) }
|
26
|
-
end
|
27
|
-
|
28
|
-
its(:to_s) { should == expected_string }
|
29
|
-
end
|
30
|
-
|
31
|
-
describe '.create' do
|
32
|
-
subject { described_class }
|
33
|
-
|
34
|
-
context 'when the HTTP request returns a 201' do
|
35
|
-
let(:options) do
|
36
|
-
{
|
37
|
-
'AttachStdin' => false,
|
38
|
-
'AttachStdout' => false,
|
39
|
-
'AttachStderr' => false,
|
40
|
-
'Tty' => false,
|
41
|
-
'Cmd' => [
|
42
|
-
'date'
|
43
|
-
],
|
44
|
-
'Container' => container.id
|
45
|
-
}
|
46
|
-
end
|
47
|
-
let(:process) { subject.create(options) }
|
48
|
-
after { container.kill!.remove }
|
49
|
-
|
50
|
-
it 'sets the id' do
|
51
|
-
expect(process).to be_a Docker::Exec
|
52
|
-
expect(process.id).to_not be_nil
|
53
|
-
expect(process.connection).to_not be_nil
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
context 'when the parent container does not exist' do
|
58
|
-
before do
|
59
|
-
Docker.options = { :mock => true }
|
60
|
-
Excon.stub({ :method => :post }, { :status => 404 })
|
61
|
-
end
|
62
|
-
after do
|
63
|
-
Excon.stubs.shift
|
64
|
-
Docker.options = {}
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'raises an error' do
|
68
|
-
expect { subject.create }.to raise_error(Docker::Error::NotFoundError)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
describe '#json' do
|
74
|
-
subject {
|
75
|
-
described_class.create(
|
76
|
-
'Container' => container.id,
|
77
|
-
'Detach' => true,
|
78
|
-
'Cmd' => %w[true]
|
79
|
-
)
|
80
|
-
}
|
81
|
-
|
82
|
-
let(:description) { subject.json }
|
83
|
-
before { subject.start! }
|
84
|
-
after { container.kill!.remove }
|
85
|
-
|
86
|
-
it 'returns the description as a Hash' do
|
87
|
-
expect(description).to be_a Hash
|
88
|
-
expect(description['ID']).to start_with(subject.id)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe '#start!' do
|
93
|
-
context 'when the exec instance does not exist' do
|
94
|
-
subject do
|
95
|
-
described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'raises an error' do
|
99
|
-
expect { subject.start! }.to raise_error(Docker::Error::NotFoundError)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
context 'when :detach is set to false' do
|
104
|
-
subject {
|
105
|
-
described_class.create(
|
106
|
-
'Container' => container.id,
|
107
|
-
'AttachStdout' => true,
|
108
|
-
'Cmd' => ['bash','-c','sleep 2; echo hello']
|
109
|
-
)
|
110
|
-
}
|
111
|
-
after { container.kill!.remove }
|
112
|
-
|
113
|
-
it 'returns the stdout and stderr messages' do
|
114
|
-
expect(subject.start!).to eq([["hello\n"],[],0])
|
115
|
-
end
|
116
|
-
|
117
|
-
context 'block is passed' do
|
118
|
-
it 'attaches to the stream' do
|
119
|
-
chunk = nil
|
120
|
-
result = subject.start! do |stream, c|
|
121
|
-
chunk ||= c
|
122
|
-
end
|
123
|
-
expect(chunk).to eq("hello\n")
|
124
|
-
expect(result).to eq([["hello\n"], [], 0])
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
context 'when :detach is set to true' do
|
130
|
-
subject {
|
131
|
-
described_class.create('Container' => container.id, 'Cmd' => %w[date])
|
132
|
-
}
|
133
|
-
after { container.kill!.remove }
|
134
|
-
|
135
|
-
it 'returns empty stdout/stderr messages with exitcode' do
|
136
|
-
expect(subject.start!(:detach => true).length).to eq(3)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
context 'when :wait set long time value' do
|
141
|
-
subject {
|
142
|
-
described_class.create(
|
143
|
-
'Container' => container.id,
|
144
|
-
'AttachStdout' => true,
|
145
|
-
'Cmd' => %w[true]
|
146
|
-
)
|
147
|
-
}
|
148
|
-
after { container.kill!.remove }
|
149
|
-
|
150
|
-
it 'returns empty stdout and stderr messages with exitcode' do
|
151
|
-
expect(subject.start!(:wait => 100)).to eq([[], [], 0])
|
152
|
-
end
|
153
|
-
end
|
154
|
-
|
155
|
-
context 'when :wait set short time value' do
|
156
|
-
subject {
|
157
|
-
described_class.create(
|
158
|
-
'Container' => container.id,
|
159
|
-
'AttachStdout' => true,
|
160
|
-
'Cmd' => ['bash', '-c', 'sleep 2; echo hello']
|
161
|
-
)
|
162
|
-
}
|
163
|
-
after { container.kill!.remove }
|
164
|
-
|
165
|
-
it 'raises an error' do
|
166
|
-
expect { subject.start!(:wait => 1) }.to raise_error(Docker::Error::TimeoutError)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
context 'when the HTTP request returns a 201' do
|
171
|
-
subject {
|
172
|
-
described_class.create('Container' => container.id, 'Cmd' => ['date'])
|
173
|
-
}
|
174
|
-
after { container.kill!.remove }
|
175
|
-
|
176
|
-
it 'starts the exec instance' do
|
177
|
-
expect { subject.start! }.not_to raise_error
|
178
|
-
end
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
data/spec/docker/image_spec.rb
DELETED
@@ -1,695 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Docker::Image do
|
4
|
-
describe '#to_s' do
|
5
|
-
subject { described_class.new(Docker.connection, info) }
|
6
|
-
|
7
|
-
let(:id) { 'bf119e2' }
|
8
|
-
let(:connection) { Docker.connection }
|
9
|
-
|
10
|
-
let(:info) do
|
11
|
-
{"id" => "bf119e2", "Repository" => "debian", "Tag" => "wheezy",
|
12
|
-
"Created" => 1364102658, "Size" => 24653, "VirtualSize" => 180116135}
|
13
|
-
end
|
14
|
-
|
15
|
-
let(:expected_string) do
|
16
|
-
"Docker::Image { :id => #{id}, :info => #{info.inspect}, "\
|
17
|
-
":connection => #{connection} }"
|
18
|
-
end
|
19
|
-
|
20
|
-
its(:to_s) { should == expected_string }
|
21
|
-
end
|
22
|
-
|
23
|
-
describe '#remove' do
|
24
|
-
|
25
|
-
context 'when no name is given' do
|
26
|
-
let(:id) { subject.id }
|
27
|
-
subject { described_class.create('fromImage' => 'busybox:latest') }
|
28
|
-
after { described_class.create('fromImage' => 'busybox:latest') }
|
29
|
-
|
30
|
-
it 'removes the Image' do
|
31
|
-
subject.remove(:force => true)
|
32
|
-
expect(Docker::Image.all.map(&:id)).to_not include(id)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
context 'when a valid tag is given' do
|
37
|
-
it 'untags the Image'
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'when an invalid tag is given' do
|
41
|
-
it 'raises an error'
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
describe '#insert_local' do
|
46
|
-
include_context "local paths"
|
47
|
-
|
48
|
-
subject { described_class.create('fromImage' => 'debian:wheezy') }
|
49
|
-
|
50
|
-
let(:rm) { false }
|
51
|
-
let(:new_image) {
|
52
|
-
opts = {'localPath' => file, 'outputPath' => '/'}
|
53
|
-
opts[:rm] = true if rm
|
54
|
-
subject.insert_local(opts)
|
55
|
-
}
|
56
|
-
|
57
|
-
context 'when the local file does not exist' do
|
58
|
-
let(:file) { '/lol/not/a/file' }
|
59
|
-
|
60
|
-
it 'raises an error' do
|
61
|
-
expect { new_image }.to raise_error(Docker::Error::ArgumentError)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context 'when the local file does exist' do
|
66
|
-
let(:file) { File.join(project_dir, 'Gemfile') }
|
67
|
-
let(:gemfile) { File.read('Gemfile') }
|
68
|
-
let(:container) { new_image.run('cat /Gemfile').tap(&:wait) }
|
69
|
-
after do
|
70
|
-
container.remove
|
71
|
-
new_image.remove
|
72
|
-
end
|
73
|
-
|
74
|
-
it 'creates a new Image that has that file' do
|
75
|
-
output = container.streaming_logs(stdout: true)
|
76
|
-
expect(output).to eq(gemfile)
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
context 'when a direcory is passed' do
|
81
|
-
let(:new_image) {
|
82
|
-
subject.insert_local(
|
83
|
-
'localPath' => File.join(project_dir, 'lib'),
|
84
|
-
'outputPath' => '/lib'
|
85
|
-
)
|
86
|
-
}
|
87
|
-
let(:container) { new_image.run('ls -a /lib/docker') }
|
88
|
-
let(:response) { container.tap(&:wait).streaming_logs(stdout: true) }
|
89
|
-
after do
|
90
|
-
container.tap(&:wait).remove
|
91
|
-
new_image.remove
|
92
|
-
end
|
93
|
-
|
94
|
-
it 'inserts the directory' do
|
95
|
-
expect(response.split("\n").sort).to eq(Dir.entries('lib/docker').sort)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
context 'when there are multiple files passed' do
|
100
|
-
let(:file) {
|
101
|
-
[File.join(project_dir, 'Gemfile'), File.join(project_dir, 'LICENSE')]
|
102
|
-
}
|
103
|
-
let(:gemfile) { File.read('Gemfile') }
|
104
|
-
let(:license) { File.read('LICENSE') }
|
105
|
-
let(:container) { new_image.run('cat /Gemfile /LICENSE') }
|
106
|
-
let(:response) {
|
107
|
-
container.tap(&:wait).streaming_logs(stdout: true)
|
108
|
-
}
|
109
|
-
after do
|
110
|
-
container.remove
|
111
|
-
new_image.remove
|
112
|
-
end
|
113
|
-
|
114
|
-
it 'creates a new Image that has each file' do
|
115
|
-
expect(response).to eq("#{gemfile}#{license}")
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context 'when removing intermediate containers' do
|
120
|
-
let(:rm) { true }
|
121
|
-
let(:file) { File.join(project_dir, 'Gemfile') }
|
122
|
-
after(:each) { new_image.remove }
|
123
|
-
|
124
|
-
it 'leave no intermediate containers' do
|
125
|
-
expect { new_image }.to change {
|
126
|
-
Docker::Container.all(:all => true).count
|
127
|
-
}.by 0
|
128
|
-
end
|
129
|
-
|
130
|
-
it 'creates a new image' do
|
131
|
-
expect{new_image}.to change{Docker::Image.all.count}.by 1
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
describe '#push' do
|
137
|
-
let(:credentials) {
|
138
|
-
{
|
139
|
-
'username' => ENV['DOCKER_API_USER'],
|
140
|
-
'password' => ENV['DOCKER_API_PASS'],
|
141
|
-
'serveraddress' => 'https://index.docker.io/v1',
|
142
|
-
'email' => ENV['DOCKER_API_EMAIL']
|
143
|
-
}
|
144
|
-
}
|
145
|
-
let(:repo_tag) { "#{ENV['DOCKER_API_USER']}/true" }
|
146
|
-
let(:image) {
|
147
|
-
described_class.build("FROM tianon/true\n", "t" => repo_tag).refresh!
|
148
|
-
}
|
149
|
-
after { image.remove(:name => repo_tag, :noprune => true) }
|
150
|
-
|
151
|
-
it 'pushes the Image' do
|
152
|
-
image.push(credentials)
|
153
|
-
end
|
154
|
-
|
155
|
-
it 'streams output from push' do
|
156
|
-
expect { |b| image.push(credentials, &b) }
|
157
|
-
.to yield_control.at_least(1)
|
158
|
-
end
|
159
|
-
|
160
|
-
context 'when a tag is specified' do
|
161
|
-
it 'pushes that specific tag'
|
162
|
-
end
|
163
|
-
|
164
|
-
context 'when the image was retrived by get' do
|
165
|
-
let(:image) {
|
166
|
-
described_class.build("FROM tianon/true\n", "t" => repo_tag).refresh!
|
167
|
-
described_class.get(repo_tag)
|
168
|
-
}
|
169
|
-
|
170
|
-
context 'when no tag is specified' do
|
171
|
-
it 'looks up the first repo tag' do
|
172
|
-
expect { image.push }.to_not raise_error
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
context 'when there are no credentials' do
|
178
|
-
let(:credentials) { nil }
|
179
|
-
let(:repo_tag) { "localhost:5000/true" }
|
180
|
-
|
181
|
-
it 'still pushes' do
|
182
|
-
expect { image.push }.to_not raise_error
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
describe '#tag' do
|
188
|
-
subject { described_class.create('fromImage' => 'debian:wheezy') }
|
189
|
-
after { subject.remove(:name => 'teh:latest', :noprune => true) }
|
190
|
-
|
191
|
-
it 'tags the image with the repo name' do
|
192
|
-
subject.tag(:repo => :teh, :force => true)
|
193
|
-
expect(subject.info['RepoTags']).to include 'teh:latest'
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
describe '#json' do
|
198
|
-
subject { described_class.create('fromImage' => 'debian:wheezy') }
|
199
|
-
let(:json) { subject.json }
|
200
|
-
|
201
|
-
it 'returns additional information about image image' do
|
202
|
-
expect(json).to be_a Hash
|
203
|
-
expect(json.length).to_not be_zero
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
describe '#history' do
|
208
|
-
subject { described_class.create('fromImage' => 'debian:wheezy') }
|
209
|
-
let(:history) { subject.history }
|
210
|
-
|
211
|
-
it 'returns the history of the Image' do
|
212
|
-
expect(history).to be_a Array
|
213
|
-
expect(history.length).to_not be_zero
|
214
|
-
expect(history).to be_all { |elem| elem.is_a? Hash }
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
describe '#run' do
|
219
|
-
subject { described_class.create('fromImage' => 'debian:wheezy') }
|
220
|
-
let(:container) { subject.run(cmd).tap(&:wait) }
|
221
|
-
let(:output) { container.streaming_logs(stdout: true) }
|
222
|
-
|
223
|
-
context 'when the argument is a String' do
|
224
|
-
let(:cmd) { 'ls /lib64/' }
|
225
|
-
after { container.remove }
|
226
|
-
|
227
|
-
it 'splits the String by spaces and creates a new Container' do
|
228
|
-
expect(output).to eq("ld-linux-x86-64.so.2\n")
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
context 'when the argument is an Array' do
|
233
|
-
let(:cmd) { %w[which pwd] }
|
234
|
-
after { container.remove }
|
235
|
-
|
236
|
-
it 'creates a new Container' do
|
237
|
-
expect(output).to eq("/bin/pwd\n")
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
context 'when the argument is nil' do
|
242
|
-
let(:cmd) { nil }
|
243
|
-
context 'no command configured in image' do
|
244
|
-
subject { described_class.create('fromImage' => 'swipely/base') }
|
245
|
-
it 'should raise an error if no command is specified' do
|
246
|
-
expect {container}.to raise_error(Docker::Error::ServerError,
|
247
|
-
"No command specified.")
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
context "command configured in image" do
|
252
|
-
let(:cmd) { 'pwd' }
|
253
|
-
after { container.remove }
|
254
|
-
|
255
|
-
it 'should normally show result if image has Cmd configured' do
|
256
|
-
expect(output).to eql "/\n"
|
257
|
-
end
|
258
|
-
end
|
259
|
-
end
|
260
|
-
end
|
261
|
-
|
262
|
-
describe '#save' do
|
263
|
-
let(:image) { Docker::Image.get('busybox') }
|
264
|
-
|
265
|
-
it 'calls the class method' do
|
266
|
-
expect(Docker::Image).to receive(:save)
|
267
|
-
.with(image.id, 'busybox.tar', anything)
|
268
|
-
image.save('busybox.tar')
|
269
|
-
end
|
270
|
-
end
|
271
|
-
|
272
|
-
describe '#save_stream' do
|
273
|
-
let(:image) { Docker::Image.get('busybox') }
|
274
|
-
let(:block) { proc { |chunk| puts chunk } }
|
275
|
-
|
276
|
-
it 'calls the class method' do
|
277
|
-
expect(Docker::Image).to receive(:save_stream)
|
278
|
-
.with(image.id, instance_of(Hash), instance_of(Docker::Connection))
|
279
|
-
image.save_stream(:chunk_size => 1024 * 1024, &block)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
describe '#refresh!' do
|
284
|
-
let(:image) { Docker::Image.create('fromImage' => 'debian:wheezy') }
|
285
|
-
|
286
|
-
it 'updates the @info hash' do
|
287
|
-
size = image.info.size
|
288
|
-
image.refresh!
|
289
|
-
expect(image.info.size).to be > size
|
290
|
-
end
|
291
|
-
|
292
|
-
context 'with an explicit connection' do
|
293
|
-
let(:connection) { Docker::Connection.new(Docker.url, Docker.options) }
|
294
|
-
let(:image) {
|
295
|
-
Docker::Image.create({'fromImage' => 'debian:wheezy'}, nil, connection)
|
296
|
-
}
|
297
|
-
|
298
|
-
it 'updates using the provided connection' do
|
299
|
-
image.refresh!
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
describe '.load' do
|
305
|
-
include_context "local paths"
|
306
|
-
let(:file) { File.join(project_dir, 'spec', 'fixtures', 'load.tar') }
|
307
|
-
|
308
|
-
context 'when the argument is a String' do
|
309
|
-
it 'loads tianon/true image from the file system' do
|
310
|
-
result = Docker::Image.load(file)
|
311
|
-
expect(result).to eq("")
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
context 'when the argument is an IO' do
|
316
|
-
let(:io) { File.open(file) }
|
317
|
-
|
318
|
-
after { io.close }
|
319
|
-
|
320
|
-
it 'loads tinan/true image from the IO' do
|
321
|
-
result = Docker::Image.load(io)
|
322
|
-
expect(result).to eq("")
|
323
|
-
end
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
describe '.create' do
|
328
|
-
subject { described_class }
|
329
|
-
|
330
|
-
context 'when the Image does not yet exist and the body is a Hash' do
|
331
|
-
let(:image) { subject.create('fromImage' => 'swipely/base') }
|
332
|
-
let(:creds) {
|
333
|
-
{
|
334
|
-
:username => ENV['DOCKER_API_USER'],
|
335
|
-
:password => ENV['DOCKER_API_PASS'],
|
336
|
-
:email => ENV['DOCKER_API_EMAIL']
|
337
|
-
}
|
338
|
-
}
|
339
|
-
|
340
|
-
before do
|
341
|
-
Docker::Image.create('fromImage' => 'swipely/base').remove
|
342
|
-
end
|
343
|
-
after { Docker::Image.create('fromImage' => 'swipely/base') }
|
344
|
-
|
345
|
-
it 'sets the id and sends Docker.creds' do
|
346
|
-
allow(Docker).to receive(:creds).and_return(creds)
|
347
|
-
expect(image).to be_a Docker::Image
|
348
|
-
expect(image.id).to match(/\A(sha256:)?[a-fA-F0-9]+\Z/)
|
349
|
-
expect(image.id).to_not include('base')
|
350
|
-
expect(image.id).to_not be_nil
|
351
|
-
expect(image.id).to_not be_empty
|
352
|
-
end
|
353
|
-
end
|
354
|
-
|
355
|
-
context 'with a block capturing create output' do
|
356
|
-
let(:create_output) { "" }
|
357
|
-
let(:block) { Proc.new { |chunk| create_output << chunk } }
|
358
|
-
|
359
|
-
before do
|
360
|
-
Docker.creds = nil
|
361
|
-
subject.create('fromImage' => 'tianon/true').remove
|
362
|
-
end
|
363
|
-
|
364
|
-
it 'calls the block and passes build output' do
|
365
|
-
subject.create('fromImage' => 'tianon/true', &block)
|
366
|
-
expect(create_output).to match(/Pulling.*tianon\/true/)
|
367
|
-
end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
describe '.get' do
|
372
|
-
subject { described_class }
|
373
|
-
let(:image) { subject.get(image_name) }
|
374
|
-
|
375
|
-
context 'when the image does exist' do
|
376
|
-
let(:image_name) { 'debian:wheezy' }
|
377
|
-
|
378
|
-
it 'returns the new image' do
|
379
|
-
expect(image).to be_a Docker::Image
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
383
|
-
context 'when the image does not exist' do
|
384
|
-
let(:image_name) { 'abcdefghijkl' }
|
385
|
-
|
386
|
-
before do
|
387
|
-
Docker.options = { :mock => true }
|
388
|
-
Excon.stub({ :method => :get }, { :status => 404 })
|
389
|
-
end
|
390
|
-
|
391
|
-
after do
|
392
|
-
Docker.options = {}
|
393
|
-
Excon.stubs.shift
|
394
|
-
end
|
395
|
-
|
396
|
-
it 'raises a not found error' do
|
397
|
-
expect { image }.to raise_error(Docker::Error::NotFoundError)
|
398
|
-
end
|
399
|
-
end
|
400
|
-
end
|
401
|
-
|
402
|
-
describe '.save' do
|
403
|
-
include_context "local paths"
|
404
|
-
|
405
|
-
context 'when a filename is specified' do
|
406
|
-
let(:file) { "#{project_dir}/scratch.tar" }
|
407
|
-
after { FileUtils.remove(file) }
|
408
|
-
|
409
|
-
it 'exports tarball of image to specified file' do
|
410
|
-
Docker::Image.save('swipely/base', file)
|
411
|
-
expect(File.exist?(file)).to eq true
|
412
|
-
expect(File.read(file)).to_not be_nil
|
413
|
-
end
|
414
|
-
end
|
415
|
-
|
416
|
-
context 'when no filename is specified' do
|
417
|
-
it 'returns raw binary data as string' do
|
418
|
-
raw = Docker::Image.save('swipely/base')
|
419
|
-
expect(raw).to_not be_nil
|
420
|
-
end
|
421
|
-
end
|
422
|
-
end
|
423
|
-
|
424
|
-
describe '.save_stream' do
|
425
|
-
let(:image) { 'busybox:latest' }
|
426
|
-
let(:non_streamed) do
|
427
|
-
Docker.connection.get(
|
428
|
-
'/images/get',
|
429
|
-
'names' => URI.encode(image)
|
430
|
-
)
|
431
|
-
end
|
432
|
-
let(:streamed) { '' }
|
433
|
-
let(:tar_files) do
|
434
|
-
proc do |string|
|
435
|
-
Gem::Package::TarReader
|
436
|
-
.new(StringIO.new(string, 'rb'))
|
437
|
-
.map(&:full_name)
|
438
|
-
.sort
|
439
|
-
end
|
440
|
-
end
|
441
|
-
|
442
|
-
it 'yields each chunk of the image' do
|
443
|
-
Docker::Image.save_stream(image) { |chunk| streamed << chunk }
|
444
|
-
expect(tar_files.call(streamed)).to eq(tar_files.call(non_streamed))
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
describe '.exist?' do
|
449
|
-
subject { described_class }
|
450
|
-
let(:exists) { subject.exist?(image_name) }
|
451
|
-
|
452
|
-
context 'when the image does exist' do
|
453
|
-
let(:image_name) { 'debian:wheezy' }
|
454
|
-
|
455
|
-
it 'returns true' do
|
456
|
-
expect(exists).to eq(true)
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
context 'when the image does not exist' do
|
461
|
-
let(:image_name) { 'abcdefghijkl' }
|
462
|
-
|
463
|
-
before do
|
464
|
-
Docker.options = { :mock => true }
|
465
|
-
Excon.stub({ :method => :get }, { :status => 404 })
|
466
|
-
end
|
467
|
-
|
468
|
-
after do
|
469
|
-
Docker.options = {}
|
470
|
-
Excon.stubs.shift
|
471
|
-
end
|
472
|
-
|
473
|
-
it 'return false' do
|
474
|
-
expect(exists).to eq(false)
|
475
|
-
end
|
476
|
-
end
|
477
|
-
end
|
478
|
-
|
479
|
-
describe '.import' do
|
480
|
-
include_context "local paths"
|
481
|
-
|
482
|
-
subject { described_class }
|
483
|
-
|
484
|
-
context 'when the file does not exist' do
|
485
|
-
let(:file) { '/lol/not/a/file' }
|
486
|
-
|
487
|
-
it 'raises an error' do
|
488
|
-
expect { subject.import(file) }
|
489
|
-
.to raise_error(Docker::Error::IOError)
|
490
|
-
end
|
491
|
-
end
|
492
|
-
|
493
|
-
context 'when the file does exist' do
|
494
|
-
let(:file) { File.join(project_dir, 'spec', 'fixtures', 'export.tar') }
|
495
|
-
let(:import) { subject.import(file) }
|
496
|
-
after { import.remove(:noprune => true) }
|
497
|
-
|
498
|
-
it 'creates the Image' do
|
499
|
-
expect(import).to be_a Docker::Image
|
500
|
-
expect(import.id).to_not be_nil
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
context 'when the argument is a URI' do
|
505
|
-
context 'when the URI is invalid' do
|
506
|
-
it 'raises an error' do
|
507
|
-
expect { subject.import('http://google.com') }
|
508
|
-
.to raise_error(Docker::Error::IOError)
|
509
|
-
end
|
510
|
-
end
|
511
|
-
|
512
|
-
context 'when the URI is valid' do
|
513
|
-
let(:uri) { 'http://swipely-pub.s3.amazonaws.com/tianon_true.tar' }
|
514
|
-
let(:import) { subject.import(uri) }
|
515
|
-
after { import.remove(:noprune => true) }
|
516
|
-
|
517
|
-
it 'returns an Image' do
|
518
|
-
expect(import).to be_a Docker::Image
|
519
|
-
expect(import.id).to_not be_nil
|
520
|
-
end
|
521
|
-
end
|
522
|
-
end
|
523
|
-
end
|
524
|
-
|
525
|
-
describe '.all' do
|
526
|
-
subject { described_class }
|
527
|
-
|
528
|
-
let(:images) { subject.all(:all => true) }
|
529
|
-
before { subject.create('fromImage' => 'debian:wheezy') }
|
530
|
-
|
531
|
-
it 'materializes each Image into a Docker::Image' do
|
532
|
-
images.each do |image|
|
533
|
-
expect(image).to_not be_nil
|
534
|
-
|
535
|
-
expect(image).to be_a(described_class)
|
536
|
-
|
537
|
-
expect(image.id).to_not be_nil
|
538
|
-
|
539
|
-
%w(Created Size VirtualSize).each do |key|
|
540
|
-
expect(image.info).to have_key(key)
|
541
|
-
end
|
542
|
-
end
|
543
|
-
|
544
|
-
expect(images.length).to_not be_zero
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
describe '.search' do
|
549
|
-
subject { described_class }
|
550
|
-
|
551
|
-
it 'materializes each Image into a Docker::Image' do
|
552
|
-
expect(subject.search('term' => 'sshd')).to be_all { |image|
|
553
|
-
!image.id.nil? && image.is_a?(described_class)
|
554
|
-
}
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
|
-
describe '.build' do
|
559
|
-
subject { described_class }
|
560
|
-
context 'with an invalid Dockerfile' do
|
561
|
-
it 'throws a UnexpectedResponseError' do
|
562
|
-
expect { subject.build('lololol') }
|
563
|
-
.to raise_error(Docker::Error::UnexpectedResponseError)
|
564
|
-
end
|
565
|
-
end
|
566
|
-
|
567
|
-
context 'with a valid Dockerfile' do
|
568
|
-
context 'without query parameters' do
|
569
|
-
let(:image) { subject.build("FROM debian:wheezy\n") }
|
570
|
-
|
571
|
-
it 'builds an image' do
|
572
|
-
expect(image).to be_a Docker::Image
|
573
|
-
expect(image.id).to_not be_nil
|
574
|
-
expect(image.connection).to be_a Docker::Connection
|
575
|
-
end
|
576
|
-
end
|
577
|
-
|
578
|
-
context 'with specifying a repo in the query parameters' do
|
579
|
-
let(:image) {
|
580
|
-
subject.build(
|
581
|
-
"FROM debian:wheezy\nRUN true\n",
|
582
|
-
"t" => "#{ENV['DOCKER_API_USER']}/debian:true"
|
583
|
-
)
|
584
|
-
}
|
585
|
-
after { image.remove(:noprune => true) }
|
586
|
-
|
587
|
-
it 'builds an image and tags it' do
|
588
|
-
expect(image).to be_a Docker::Image
|
589
|
-
expect(image.id).to_not be_nil
|
590
|
-
expect(image.connection).to be_a Docker::Connection
|
591
|
-
image.refresh!
|
592
|
-
expect(image.info["RepoTags"]).to eq(
|
593
|
-
["#{ENV['DOCKER_API_USER']}/debian:true"]
|
594
|
-
)
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
context 'with a block capturing build output' do
|
599
|
-
let(:build_output) { "" }
|
600
|
-
let(:block) { Proc.new { |chunk| build_output << chunk } }
|
601
|
-
let!(:image) { subject.build("FROM debian:wheezy\n", &block) }
|
602
|
-
|
603
|
-
it 'calls the block and passes build output' do
|
604
|
-
expect(build_output).to match(/Step \d : FROM debian:wheezy/)
|
605
|
-
end
|
606
|
-
end
|
607
|
-
end
|
608
|
-
end
|
609
|
-
|
610
|
-
describe '.build_from_dir' do
|
611
|
-
subject { described_class }
|
612
|
-
|
613
|
-
context 'with a valid Dockerfile' do
|
614
|
-
let(:dir) {
|
615
|
-
File.join(File.dirname(__FILE__), '..', 'fixtures', 'build_from_dir')
|
616
|
-
}
|
617
|
-
let(:docker_file) { File.new("#{dir}/Dockerfile") }
|
618
|
-
let(:image) { subject.build_from_dir(dir, opts, &block) }
|
619
|
-
let(:opts) { {} }
|
620
|
-
let(:block) { Proc.new {} }
|
621
|
-
let(:container) do
|
622
|
-
Docker::Container.create(
|
623
|
-
'Image' => image.id,
|
624
|
-
'Cmd' => %w[cat /Dockerfile]
|
625
|
-
).tap(&:start).tap(&:wait)
|
626
|
-
end
|
627
|
-
let(:output) { container.streaming_logs(stdout: true) }
|
628
|
-
|
629
|
-
after(:each) do
|
630
|
-
container.remove
|
631
|
-
image.remove(:noprune => true)
|
632
|
-
end
|
633
|
-
|
634
|
-
context 'with no query parameters' do
|
635
|
-
it 'builds the image' do
|
636
|
-
expect(output).to eq(docker_file.read)
|
637
|
-
end
|
638
|
-
end
|
639
|
-
|
640
|
-
context 'with specifying a repo in the query parameters' do
|
641
|
-
let(:opts) { { "t" => "#{ENV['DOCKER_API_USER']}/debian:from_dir" } }
|
642
|
-
it 'builds the image and tags it' do
|
643
|
-
expect(output).to eq(docker_file.read)
|
644
|
-
image.refresh!
|
645
|
-
expect(image.info["RepoTags"]).to eq(
|
646
|
-
["#{ENV['DOCKER_API_USER']}/debian:from_dir"]
|
647
|
-
)
|
648
|
-
end
|
649
|
-
end
|
650
|
-
|
651
|
-
context 'with a block capturing build output' do
|
652
|
-
let(:build_output) { "" }
|
653
|
-
let(:block) { Proc.new { |chunk| build_output << chunk } }
|
654
|
-
|
655
|
-
it 'calls the block and passes build output' do
|
656
|
-
image # Create the image variable, which is lazy-loaded by Rspec
|
657
|
-
expect(build_output).to match(/Step \d : FROM debian:wheezy/)
|
658
|
-
end
|
659
|
-
|
660
|
-
context 'uses a cached version the second time' do
|
661
|
-
let(:build_output_two) { "" }
|
662
|
-
let(:block_two) { Proc.new { |chunk| build_output_two << chunk } }
|
663
|
-
let(:image_two) { subject.build_from_dir(dir, opts, &block_two) }
|
664
|
-
|
665
|
-
it 'calls the block and passes build output' do
|
666
|
-
image # Create the image variable, which is lazy-loaded by Rspec
|
667
|
-
expect(build_output).to match(/Step \d : FROM debian:wheezy/)
|
668
|
-
expect(build_output).to_not match(/Using cache/)
|
669
|
-
|
670
|
-
image_two # Create the image_two variable, which is lazy-loaded by Rspec
|
671
|
-
expect(build_output_two).to match(/Using cache/)
|
672
|
-
end
|
673
|
-
end
|
674
|
-
end
|
675
|
-
|
676
|
-
context 'with credentials passed' do
|
677
|
-
let(:creds) {
|
678
|
-
{
|
679
|
-
:username => ENV['DOCKER_API_USER'],
|
680
|
-
:password => ENV['DOCKER_API_PASS'],
|
681
|
-
:email => ENV['DOCKER_API_EMAIL'],
|
682
|
-
:serveraddress => 'https://index.docker.io/v1'
|
683
|
-
}
|
684
|
-
}
|
685
|
-
|
686
|
-
before { Docker.creds = creds }
|
687
|
-
after { Docker.creds = nil }
|
688
|
-
|
689
|
-
it 'sends X-Registry-Config header' do
|
690
|
-
expect(image.info[:headers].keys).to include('X-Registry-Config')
|
691
|
-
end
|
692
|
-
end
|
693
|
-
end
|
694
|
-
end
|
695
|
-
end
|