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/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,683 +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 '#load' do
|
284
|
-
include_context "local paths"
|
285
|
-
let(:file) { File.join(project_dir, 'spec', 'fixtures', 'load.tar') }
|
286
|
-
context 'test image upload' do
|
287
|
-
it 'load tianon/true image' do
|
288
|
-
result = Docker::Image.load(file)
|
289
|
-
expect(result).to eq("")
|
290
|
-
end
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
describe '#refresh!' do
|
295
|
-
let(:image) { Docker::Image.create('fromImage' => 'debian:wheezy') }
|
296
|
-
|
297
|
-
it 'updates the @info hash' do
|
298
|
-
size = image.info.size
|
299
|
-
image.refresh!
|
300
|
-
expect(image.info.size).to be > size
|
301
|
-
end
|
302
|
-
|
303
|
-
context 'with an explicit connection' do
|
304
|
-
let(:connection) { Docker::Connection.new(Docker.url, Docker.options) }
|
305
|
-
let(:image) {
|
306
|
-
Docker::Image.create({'fromImage' => 'debian:wheezy'}, nil, connection)
|
307
|
-
}
|
308
|
-
|
309
|
-
it 'updates using the provided connection' do
|
310
|
-
image.refresh!
|
311
|
-
end
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
describe '.create' do
|
316
|
-
subject { described_class }
|
317
|
-
|
318
|
-
context 'when the Image does not yet exist and the body is a Hash' do
|
319
|
-
let(:image) { subject.create('fromImage' => 'swipely/base') }
|
320
|
-
let(:creds) {
|
321
|
-
{
|
322
|
-
:username => ENV['DOCKER_API_USER'],
|
323
|
-
:password => ENV['DOCKER_API_PASS'],
|
324
|
-
:email => ENV['DOCKER_API_EMAIL']
|
325
|
-
}
|
326
|
-
}
|
327
|
-
|
328
|
-
before do
|
329
|
-
Docker::Image.create('fromImage' => 'swipely/base').remove
|
330
|
-
end
|
331
|
-
after { Docker::Image.create('fromImage' => 'swipely/base') }
|
332
|
-
|
333
|
-
it 'sets the id and sends Docker.creds' do
|
334
|
-
allow(Docker).to receive(:creds).and_return(creds)
|
335
|
-
expect(image).to be_a Docker::Image
|
336
|
-
expect(image.id).to match(/\A(sha256:)?[a-fA-F0-9]+\Z/)
|
337
|
-
expect(image.id).to_not include('base')
|
338
|
-
expect(image.id).to_not be_nil
|
339
|
-
expect(image.id).to_not be_empty
|
340
|
-
end
|
341
|
-
end
|
342
|
-
|
343
|
-
context 'with a block capturing create output' do
|
344
|
-
let(:create_output) { "" }
|
345
|
-
let(:block) { Proc.new { |chunk| create_output << chunk } }
|
346
|
-
|
347
|
-
before do
|
348
|
-
Docker.creds = nil
|
349
|
-
subject.create('fromImage' => 'tianon/true').remove
|
350
|
-
end
|
351
|
-
|
352
|
-
it 'calls the block and passes build output' do
|
353
|
-
subject.create('fromImage' => 'tianon/true', &block)
|
354
|
-
expect(create_output).to match(/Pulling.*tianon\/true/)
|
355
|
-
end
|
356
|
-
end
|
357
|
-
end
|
358
|
-
|
359
|
-
describe '.get' do
|
360
|
-
subject { described_class }
|
361
|
-
let(:image) { subject.get(image_name) }
|
362
|
-
|
363
|
-
context 'when the image does exist' do
|
364
|
-
let(:image_name) { 'debian:wheezy' }
|
365
|
-
|
366
|
-
it 'returns the new image' do
|
367
|
-
expect(image).to be_a Docker::Image
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
context 'when the image does not exist' do
|
372
|
-
let(:image_name) { 'abcdefghijkl' }
|
373
|
-
|
374
|
-
before do
|
375
|
-
Docker.options = { :mock => true }
|
376
|
-
Excon.stub({ :method => :get }, { :status => 404 })
|
377
|
-
end
|
378
|
-
|
379
|
-
after do
|
380
|
-
Docker.options = {}
|
381
|
-
Excon.stubs.shift
|
382
|
-
end
|
383
|
-
|
384
|
-
it 'raises a not found error' do
|
385
|
-
expect { image }.to raise_error(Docker::Error::NotFoundError)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
describe '.save' do
|
391
|
-
include_context "local paths"
|
392
|
-
|
393
|
-
context 'when a filename is specified' do
|
394
|
-
let(:file) { "#{project_dir}/scratch.tar" }
|
395
|
-
after { FileUtils.remove(file) }
|
396
|
-
|
397
|
-
it 'exports tarball of image to specified file' do
|
398
|
-
Docker::Image.save('swipely/base', file)
|
399
|
-
expect(File.exist?(file)).to eq true
|
400
|
-
expect(File.read(file)).to_not be_nil
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
context 'when no filename is specified' do
|
405
|
-
it 'returns raw binary data as string' do
|
406
|
-
raw = Docker::Image.save('swipely/base')
|
407
|
-
expect(raw).to_not be_nil
|
408
|
-
end
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
describe '.save_stream' do
|
413
|
-
let(:image) { 'busybox:latest' }
|
414
|
-
let(:non_streamed) do
|
415
|
-
Docker.connection.get(
|
416
|
-
'/images/get',
|
417
|
-
'names' => URI.encode(image)
|
418
|
-
)
|
419
|
-
end
|
420
|
-
let(:streamed) { '' }
|
421
|
-
let(:tar_files) do
|
422
|
-
proc do |string|
|
423
|
-
Gem::Package::TarReader
|
424
|
-
.new(StringIO.new(string, 'rb'))
|
425
|
-
.map(&:full_name)
|
426
|
-
.sort
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
it 'yields each chunk of the image' do
|
431
|
-
Docker::Image.save_stream(image) { |chunk| streamed << chunk }
|
432
|
-
expect(tar_files.call(streamed)).to eq(tar_files.call(non_streamed))
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
describe '.exist?' do
|
437
|
-
subject { described_class }
|
438
|
-
let(:exists) { subject.exist?(image_name) }
|
439
|
-
|
440
|
-
context 'when the image does exist' do
|
441
|
-
let(:image_name) { 'debian:wheezy' }
|
442
|
-
|
443
|
-
it 'returns true' do
|
444
|
-
expect(exists).to eq(true)
|
445
|
-
end
|
446
|
-
end
|
447
|
-
|
448
|
-
context 'when the image does not exist' do
|
449
|
-
let(:image_name) { 'abcdefghijkl' }
|
450
|
-
|
451
|
-
before do
|
452
|
-
Docker.options = { :mock => true }
|
453
|
-
Excon.stub({ :method => :get }, { :status => 404 })
|
454
|
-
end
|
455
|
-
|
456
|
-
after do
|
457
|
-
Docker.options = {}
|
458
|
-
Excon.stubs.shift
|
459
|
-
end
|
460
|
-
|
461
|
-
it 'return false' do
|
462
|
-
expect(exists).to eq(false)
|
463
|
-
end
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
describe '.import' do
|
468
|
-
include_context "local paths"
|
469
|
-
|
470
|
-
subject { described_class }
|
471
|
-
|
472
|
-
context 'when the file does not exist' do
|
473
|
-
let(:file) { '/lol/not/a/file' }
|
474
|
-
|
475
|
-
it 'raises an error' do
|
476
|
-
expect { subject.import(file) }
|
477
|
-
.to raise_error(Docker::Error::IOError)
|
478
|
-
end
|
479
|
-
end
|
480
|
-
|
481
|
-
context 'when the file does exist' do
|
482
|
-
let(:file) { File.join(project_dir, 'spec', 'fixtures', 'export.tar') }
|
483
|
-
let(:import) { subject.import(file) }
|
484
|
-
after { import.remove(:noprune => true) }
|
485
|
-
|
486
|
-
it 'creates the Image' do
|
487
|
-
expect(import).to be_a Docker::Image
|
488
|
-
expect(import.id).to_not be_nil
|
489
|
-
end
|
490
|
-
end
|
491
|
-
|
492
|
-
context 'when the argument is a URI' do
|
493
|
-
context 'when the URI is invalid' do
|
494
|
-
it 'raises an error' do
|
495
|
-
expect { subject.import('http://google.com') }
|
496
|
-
.to raise_error(Docker::Error::IOError)
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
context 'when the URI is valid' do
|
501
|
-
let(:uri) { 'http://swipely-pub.s3.amazonaws.com/tianon_true.tar' }
|
502
|
-
let(:import) { subject.import(uri) }
|
503
|
-
after { import.remove(:noprune => true) }
|
504
|
-
|
505
|
-
it 'returns an Image' do
|
506
|
-
expect(import).to be_a Docker::Image
|
507
|
-
expect(import.id).to_not be_nil
|
508
|
-
end
|
509
|
-
end
|
510
|
-
end
|
511
|
-
end
|
512
|
-
|
513
|
-
describe '.all' do
|
514
|
-
subject { described_class }
|
515
|
-
|
516
|
-
let(:images) { subject.all(:all => true) }
|
517
|
-
before { subject.create('fromImage' => 'debian:wheezy') }
|
518
|
-
|
519
|
-
it 'materializes each Image into a Docker::Image' do
|
520
|
-
images.each do |image|
|
521
|
-
expect(image).to_not be_nil
|
522
|
-
|
523
|
-
expect(image).to be_a(described_class)
|
524
|
-
|
525
|
-
expect(image.id).to_not be_nil
|
526
|
-
|
527
|
-
%w(Created Size VirtualSize).each do |key|
|
528
|
-
expect(image.info).to have_key(key)
|
529
|
-
end
|
530
|
-
end
|
531
|
-
|
532
|
-
expect(images.length).to_not be_zero
|
533
|
-
end
|
534
|
-
end
|
535
|
-
|
536
|
-
describe '.search' do
|
537
|
-
subject { described_class }
|
538
|
-
|
539
|
-
it 'materializes each Image into a Docker::Image' do
|
540
|
-
expect(subject.search('term' => 'sshd')).to be_all { |image|
|
541
|
-
!image.id.nil? && image.is_a?(described_class)
|
542
|
-
}
|
543
|
-
end
|
544
|
-
end
|
545
|
-
|
546
|
-
describe '.build' do
|
547
|
-
subject { described_class }
|
548
|
-
context 'with an invalid Dockerfile' do
|
549
|
-
it 'throws a UnexpectedResponseError' do
|
550
|
-
expect { subject.build('lololol') }
|
551
|
-
.to raise_error(Docker::Error::UnexpectedResponseError)
|
552
|
-
end
|
553
|
-
end
|
554
|
-
|
555
|
-
context 'with a valid Dockerfile' do
|
556
|
-
context 'without query parameters' do
|
557
|
-
let(:image) { subject.build("FROM debian:wheezy\n") }
|
558
|
-
|
559
|
-
it 'builds an image' do
|
560
|
-
expect(image).to be_a Docker::Image
|
561
|
-
expect(image.id).to_not be_nil
|
562
|
-
expect(image.connection).to be_a Docker::Connection
|
563
|
-
end
|
564
|
-
end
|
565
|
-
|
566
|
-
context 'with specifying a repo in the query parameters' do
|
567
|
-
let(:image) {
|
568
|
-
subject.build(
|
569
|
-
"FROM debian:wheezy\nRUN true\n",
|
570
|
-
"t" => "#{ENV['DOCKER_API_USER']}/debian:true"
|
571
|
-
)
|
572
|
-
}
|
573
|
-
after { image.remove(:noprune => true) }
|
574
|
-
|
575
|
-
it 'builds an image and tags it' do
|
576
|
-
expect(image).to be_a Docker::Image
|
577
|
-
expect(image.id).to_not be_nil
|
578
|
-
expect(image.connection).to be_a Docker::Connection
|
579
|
-
image.refresh!
|
580
|
-
expect(image.info["RepoTags"]).to eq(
|
581
|
-
["#{ENV['DOCKER_API_USER']}/debian:true"]
|
582
|
-
)
|
583
|
-
end
|
584
|
-
end
|
585
|
-
|
586
|
-
context 'with a block capturing build output' do
|
587
|
-
let(:build_output) { "" }
|
588
|
-
let(:block) { Proc.new { |chunk| build_output << chunk } }
|
589
|
-
let!(:image) { subject.build("FROM debian:wheezy\n", &block) }
|
590
|
-
|
591
|
-
it 'calls the block and passes build output' do
|
592
|
-
expect(build_output).to match(/Step \d : FROM debian:wheezy/)
|
593
|
-
end
|
594
|
-
end
|
595
|
-
end
|
596
|
-
end
|
597
|
-
|
598
|
-
describe '.build_from_dir' do
|
599
|
-
subject { described_class }
|
600
|
-
|
601
|
-
context 'with a valid Dockerfile' do
|
602
|
-
let(:dir) {
|
603
|
-
File.join(File.dirname(__FILE__), '..', 'fixtures', 'build_from_dir')
|
604
|
-
}
|
605
|
-
let(:docker_file) { File.new("#{dir}/Dockerfile") }
|
606
|
-
let(:image) { subject.build_from_dir(dir, opts, &block) }
|
607
|
-
let(:opts) { {} }
|
608
|
-
let(:block) { Proc.new {} }
|
609
|
-
let(:container) do
|
610
|
-
Docker::Container.create(
|
611
|
-
'Image' => image.id,
|
612
|
-
'Cmd' => %w[cat /Dockerfile]
|
613
|
-
).tap(&:start).tap(&:wait)
|
614
|
-
end
|
615
|
-
let(:output) { container.streaming_logs(stdout: true) }
|
616
|
-
|
617
|
-
after(:each) do
|
618
|
-
container.remove
|
619
|
-
image.remove(:noprune => true)
|
620
|
-
end
|
621
|
-
|
622
|
-
context 'with no query parameters' do
|
623
|
-
it 'builds the image' do
|
624
|
-
expect(output).to eq(docker_file.read)
|
625
|
-
end
|
626
|
-
end
|
627
|
-
|
628
|
-
context 'with specifying a repo in the query parameters' do
|
629
|
-
let(:opts) { { "t" => "#{ENV['DOCKER_API_USER']}/debian:from_dir" } }
|
630
|
-
it 'builds the image and tags it' do
|
631
|
-
expect(output).to eq(docker_file.read)
|
632
|
-
image.refresh!
|
633
|
-
expect(image.info["RepoTags"]).to eq(
|
634
|
-
["#{ENV['DOCKER_API_USER']}/debian:from_dir"]
|
635
|
-
)
|
636
|
-
end
|
637
|
-
end
|
638
|
-
|
639
|
-
context 'with a block capturing build output' do
|
640
|
-
let(:build_output) { "" }
|
641
|
-
let(:block) { Proc.new { |chunk| build_output << chunk } }
|
642
|
-
|
643
|
-
it 'calls the block and passes build output' do
|
644
|
-
image # Create the image variable, which is lazy-loaded by Rspec
|
645
|
-
expect(build_output).to match(/Step \d : FROM debian:wheezy/)
|
646
|
-
end
|
647
|
-
|
648
|
-
context 'uses a cached version the second time' do
|
649
|
-
let(:build_output_two) { "" }
|
650
|
-
let(:block_two) { Proc.new { |chunk| build_output_two << chunk } }
|
651
|
-
let(:image_two) { subject.build_from_dir(dir, opts, &block_two) }
|
652
|
-
|
653
|
-
it 'calls the block and passes build output' do
|
654
|
-
image # Create the image variable, which is lazy-loaded by Rspec
|
655
|
-
expect(build_output).to match(/Step \d : FROM debian:wheezy/)
|
656
|
-
expect(build_output).to_not match(/Using cache/)
|
657
|
-
|
658
|
-
image_two # Create the image_two variable, which is lazy-loaded by Rspec
|
659
|
-
expect(build_output_two).to match(/Using cache/)
|
660
|
-
end
|
661
|
-
end
|
662
|
-
end
|
663
|
-
|
664
|
-
context 'with credentials passed' do
|
665
|
-
let(:creds) {
|
666
|
-
{
|
667
|
-
:username => ENV['DOCKER_API_USER'],
|
668
|
-
:password => ENV['DOCKER_API_PASS'],
|
669
|
-
:email => ENV['DOCKER_API_EMAIL'],
|
670
|
-
:serveraddress => 'https://index.docker.io/v1'
|
671
|
-
}
|
672
|
-
}
|
673
|
-
|
674
|
-
before { Docker.creds = creds }
|
675
|
-
after { Docker.creds = nil }
|
676
|
-
|
677
|
-
it 'sends X-Registry-Config header' do
|
678
|
-
expect(image.info[:headers].keys).to include('X-Registry-Config')
|
679
|
-
end
|
680
|
-
end
|
681
|
-
end
|
682
|
-
end
|
683
|
-
end
|