docker-api 1.15.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -1
  3. data/Rakefile +37 -0
  4. data/TESTING.md +61 -0
  5. data/lib/docker.rb +8 -1
  6. data/lib/docker/container.rb +44 -80
  7. data/lib/docker/exec.rb +98 -0
  8. data/lib/docker/image.rb +4 -3
  9. data/lib/docker/util.rb +79 -0
  10. data/lib/docker/version.rb +2 -2
  11. data/spec/docker/container_spec.rb +192 -56
  12. data/spec/docker/event_spec.rb +4 -4
  13. data/spec/docker/exec_spec.rb +197 -0
  14. data/spec/docker/image_spec.rb +131 -102
  15. data/spec/docker_spec.rb +37 -27
  16. data/spec/fixtures/build_from_dir/Dockerfile +2 -2
  17. data/spec/fixtures/export.tar +0 -0
  18. data/spec/fixtures/top/Dockerfile +2 -2
  19. data/spec/spec_helper.rb +10 -0
  20. data/spec/support/vcr.rb +5 -0
  21. data/spec/vcr/Docker/_authenticate_/with_valid_credentials/logs_in_and_sets_the_creds.yml +6 -8
  22. data/spec/vcr/Docker/_info/returns_the_info_as_a_Hash.yml +7 -9
  23. data/spec/vcr/Docker/_validate_version/when_nothing_is_raised/validate_version_/.yml +7 -9
  24. data/spec/vcr/Docker/_version/returns_the_version_as_a_Hash.yml +7 -9
  25. data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +46 -139
  26. data/spec/vcr/Docker_Container/_attach/with_normal_sized_chunks/yields_each_chunk.yml +62 -20
  27. data/spec/vcr/Docker_Container/_attach/with_very_small_chunks/yields_each_chunk.yml +62 -20
  28. data/spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml +71 -28
  29. data/spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml +69 -23
  30. data/spec/vcr/Docker_Container/_copy/when_the_file_does_not_exist/raises_an_error.yml +134 -0
  31. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_directory/yields_each_chunk_of_the_tarred_directory.yml +73 -290
  32. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_file/yields_each_chunk_of_the_tarred_file.yml +51 -211
  33. data/spec/vcr/Docker_Container/_create/when_creating_a_container_named_bob/should_have_name_set_to_bob.yml +36 -17
  34. data/spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml +30 -9
  35. data/spec/vcr/Docker_Container/_delete/deletes_the_container.yml +17 -23
  36. data/spec/vcr/Docker_Container/_exec/when_detach_is_true/returns_the_Docker_Exec_object.yml +151 -0
  37. data/spec/vcr/Docker_Container/_exec/when_passed_a_block/streams_the_stdout/stderr_messages.yml +153 -0
  38. data/spec/vcr/Docker_Container/_exec/when_passed_only_a_command/returns_the_stdout/stderr_messages.yml +153 -0
  39. data/spec/vcr/Docker_Container/_exec/when_stdin_object_is_passed/returns_the_stdout/stderr_messages.yml +100 -0
  40. data/spec/vcr/Docker_Container/_exec/when_tty_is_true/returns_the_raw_stdout/stderr_output.yml +152 -0
  41. data/spec/vcr/Docker_Container/_export/yields_each_chunk.yml +263 -28
  42. data/spec/vcr/Docker_Container/_get/when_the_HTTP_response_is_a_200/materializes_the_Container_into_a_Docker_Container.yml +36 -17
  43. data/spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml +36 -17
  44. data/spec/vcr/Docker_Container/_kill/kills_the_container.yml +55 -53
  45. data/spec/vcr/Docker_Container/_kill/with_a_kill_signal/kills_the_container.yml +74 -96
  46. data/spec/vcr/Docker_Container/_logs/when_not_selecting_any_stream/raises_a_client_error.yml +84 -81
  47. data/spec/vcr/Docker_Container/_logs/when_selecting_stdout/returns_blank_logs.yml +34 -15
  48. data/spec/vcr/Docker_Container/_pause/pauses_the_container.yml +98 -45
  49. data/spec/vcr/Docker_Container/_restart/restarts_the_container.yml +88 -56
  50. data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_does_not_return_status_code_of_0/raises_an_error.yml +39 -22
  51. data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_returns_a_status_code_of_0/creates_a_new_container_to_run_the_specified_command.yml +212 -60
  52. data/spec/vcr/Docker_Container/_start/starts_the_container.yml +45 -30
  53. data/spec/vcr/Docker_Container/_stop/stops_the_container.yml +54 -83
  54. data/spec/vcr/Docker_Container/_streaming_logs/when_not_selecting_any_stream/raises_a_client_error.yml +84 -81
  55. data/spec/vcr/Docker_Container/_streaming_logs/when_selecting_stdout/returns_blank_logs.yml +46 -30
  56. data/spec/vcr/Docker_Container/_top/returns_the_top_commands_as_an_Array.yml +103 -40
  57. data/spec/vcr/Docker_Container/_unpause/unpauses_the_container.yml +81 -57
  58. data/spec/vcr/Docker_Container/_wait/waits_for_the_command_to_finish.yml +39 -22
  59. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/sets_the_read_timeout_to_that_amount_of_time.yml +39 -22
  60. data/spec/vcr/Docker_Exec/_create/when_the_HTTP_request_returns_a_201/sets_the_id.yml +128 -0
  61. data/spec/vcr/Docker_Exec/_resize/when_exec_instance_has_TTY_enabled/returns_a_200.yml +155 -0
  62. data/spec/vcr/Docker_Exec/_start_/when_detach_is_set_to_false/block_is_passed/attaches_to_the_stream.yml +152 -0
  63. data/spec/vcr/Docker_Exec/_start_/when_detach_is_set_to_false/returns_the_stdout_and_stderr_messages.yml +152 -0
  64. data/spec/vcr/Docker_Exec/_start_/when_detach_is_set_to_true/returns_empty_stdout_and_stderr_messages.yml +151 -0
  65. data/spec/vcr/Docker_Exec/_start_/when_the_HTTP_request_returns_a_201/starts_the_exec_instance.yml +151 -0
  66. data/spec/vcr/Docker_Exec/_start_/when_the_command_has_already_run/raises_an_error.yml +151 -0
  67. data/spec/vcr/Docker_Image/_all/materializes_each_Image_into_a_Docker_Image.yml +86 -200
  68. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/with_a_block_capturing_build_output/calls_the_block_and_passes_build_output.yml +10 -14
  69. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/with_specifying_a_repo_in_the_query_parameters/builds_an_image_and_tags_it.yml +147 -39
  70. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/without_query_parameters/builds_an_image.yml +10 -14
  71. data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +12 -14
  72. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_a_block_capturing_build_output/calls_the_block_and_passes_build_output.yml +118 -16
  73. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_credentials_passed/sends_X-Registry-Config_header.yml +118 -14
  74. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_no_query_parameters/builds_the_image.yml +109 -37
  75. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_specifying_a_repo_in_the_query_parameters/builds_the_image_and_tags_it.yml +214 -60
  76. data/spec/vcr/Docker_Image/_create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/sets_the_id_and_sends_Docker_creds.yml +41 -2631
  77. data/spec/vcr/Docker_Image/_exist_/when_the_image_does_exist/returns_true.yml +12 -19
  78. data/spec/vcr/Docker_Image/_get/when_the_image_does_exist/returns_the_new_image.yml +7 -9
  79. data/spec/vcr/Docker_Image/_history/returns_the_history_of_the_Image.yml +17 -198
  80. data/spec/vcr/Docker_Image/_import/when_the_argument_is_a_URI/when_the_URI_is_invalid/raises_an_error.yml +26 -30
  81. data/spec/vcr/Docker_Image/_import/when_the_argument_is_a_URI/when_the_URI_is_valid/returns_an_Image.yml +246 -22
  82. data/spec/vcr/Docker_Image/_import/when_the_file_does_exist/creates_the_Image.yml +35 -10
  83. data/spec/vcr/Docker_Image/_insert_local/when_a_direcory_is_passed/inserts_the_directory.yml +1249 -74
  84. data/spec/vcr/Docker_Image/_insert_local/when_removing_intermediate_containers/creates_a_new_image.yml +84 -94
  85. data/spec/vcr/Docker_Image/_insert_local/when_removing_intermediate_containers/leave_no_intermediate_containers.yml +70 -92
  86. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_exist/creates_a_new_Image_that_has_that_file.yml +125 -98
  87. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_not_exist/raises_an_error.yml +13 -60
  88. data/spec/vcr/Docker_Image/_insert_local/when_there_are_multiple_files_passed/creates_a_new_Image_that_has_each_file.yml +190 -135
  89. data/spec/vcr/Docker_Image/_json/returns_additional_information_about_image_image.yml +15 -198
  90. data/spec/vcr/Docker_Image/_push/pushes_the_Image.yml +169 -264
  91. data/spec/vcr/Docker_Image/_push/when_there_are_no_credentials/still_pushes.yml +175 -4543
  92. data/spec/vcr/Docker_Image/_refresh_/updates_the_info_hash.yml +90 -206
  93. data/spec/vcr/Docker_Image/_remove/when_no_name_is_given/removes_the_Image.yml +300 -0
  94. data/spec/vcr/Docker_Image/_run/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +77 -207
  95. data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +77 -207
  96. data/spec/vcr/Docker_Image/_run/when_the_argument_is_nil/command_configured_in_image/should_normally_show_result_if_image_has_Cmd_configured.yml +160 -0
  97. data/spec/vcr/Docker_Image/_run/when_the_argument_is_nil/no_command_configured_in_image/should_raise_an_error_if_no_command_is_specified.yml +16 -197
  98. data/spec/vcr/Docker_Image/_search/materializes_each_Image_into_a_Docker_Image.yml +151 -73
  99. data/spec/vcr/Docker_Image/_tag/tags_the_image_with_the_repo_name.yml +42 -196
  100. metadata +67 -41
  101. data/spec/vcr/Docker_Container/_commit/if_run_is_passed_it_saves_the_command_in_the_image/saves_the_command.yml +0 -58
  102. data/spec/vcr/Docker_Container/_streaming_logs/when_not_selecting_any_stream/returns_the_error_message.yml +0 -163
  103. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/and_a_command_runs_for_too_long/raises_a_ServerError.yml +0 -58
  104. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_credentials_passed/sends_Docker_creds.yml +0 -41
  105. data/spec/vcr/Docker_Image/_remove/removes_the_Image.yml +0 -276
@@ -5,6 +5,85 @@ module Docker::Util
5
5
 
6
6
  module_function
7
7
 
8
+ # Attaches to a HTTP stream
9
+ #
10
+ # @param block
11
+ # @param msg_stack [Docker::Messages]
12
+ # @param tty [boolean]
13
+ def attach_for(block, msg_stack, tty = false)
14
+ # If TTY is enabled expect raw data and append to stdout
15
+ if tty
16
+ attach_for_tty(block, msg_stack)
17
+ else
18
+ attach_for_multiplex(block, msg_stack)
19
+ end
20
+ end
21
+
22
+ def attach_for_tty(block, msg_stack)
23
+ return lambda do |c,r,t|
24
+ msg_stack.stdout_messages << c
25
+ msg_stack.all_messages << c
26
+ block.call c if block
27
+ end
28
+ end
29
+
30
+ def attach_for_multiplex(block, msg_stack)
31
+ messages = Docker::Messages.new
32
+ lambda do |c,r,t|
33
+ messages = messages.decipher_messages(c)
34
+ msg_stack.append(messages)
35
+
36
+ unless block.nil?
37
+ messages.stdout_messages.each do |msg|
38
+ block.call(:stdout, msg)
39
+ end
40
+ messages.stderr_messages.each do |msg|
41
+ block.call(:stderr, msg)
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def debug(msg)
48
+ Docker.logger.debug(msg) if Docker.logger
49
+ end
50
+
51
+ def hijack_for(stdin, block, msg_stack, tty)
52
+ attach_block = attach_for(block, msg_stack, tty)
53
+
54
+ lambda do |socket|
55
+ debug "hijack: hijacking the HTTP socket"
56
+ threads = []
57
+
58
+ debug "hijack: starting stdin copy thread"
59
+ threads << Thread.start do
60
+ debug "hijack: copying stdin => socket"
61
+ IO.copy_stream stdin, socket
62
+
63
+ debug "hijack: closing write end of hijacked socket"
64
+ socket.close_write
65
+ end
66
+
67
+ debug "hijack: starting hijacked socket read thread"
68
+ threads << Thread.start do
69
+ debug "hijack: reading from hijacked socket"
70
+
71
+ begin
72
+ while chunk = socket.readpartial(512)
73
+ debug "hijack: got #{chunk.bytesize} bytes from hijacked socket"
74
+ attach_block.call chunk, nil, nil
75
+ end
76
+ rescue EOFError
77
+ end
78
+
79
+ debug "hijack: killing stdin copy thread"
80
+ threads.first.kill
81
+ end
82
+
83
+ threads.each(&:join)
84
+ end
85
+ end
86
+
8
87
  def parse_json(body)
9
88
  JSON.parse(body) unless body.nil? || body.empty? || (body == 'null')
10
89
  rescue JSON::ParserError => ex
@@ -1,7 +1,7 @@
1
1
  module Docker
2
2
  # The version of the docker-api gem.
3
- VERSION = '1.15.0'
3
+ VERSION = '1.16.0'
4
4
 
5
5
  # The version of the compatible Docker remote API.
6
- API_VERSION = '1.12'
6
+ API_VERSION = '1.15'
7
7
  end
@@ -24,8 +24,11 @@ describe Docker::Container do
24
24
  end
25
25
 
26
26
  describe '#json' do
27
- subject { described_class.create('Cmd' => %w[true], 'Image' => 'base') }
27
+ subject {
28
+ described_class.create('Cmd' => %w[true], 'Image' => 'debian:wheezy')
29
+ }
28
30
  let(:description) { subject.json }
31
+ after(:each) { subject.remove }
29
32
 
30
33
  it 'returns the description as a Hash', :vcr do
31
34
  expect(description).to be_a Hash
@@ -34,7 +37,10 @@ describe Docker::Container do
34
37
  end
35
38
 
36
39
  describe '#streaming_logs' do
37
- subject { described_class.create('Cmd' => "echo hello", 'Image' => 'base') }
40
+ subject {
41
+ described_class.create('Cmd' => "echo hello", 'Image' => 'debian:wheezy')
42
+ }
43
+ after(:each) { subject.remove }
38
44
 
39
45
  context "when not selecting any stream" do
40
46
  let(:non_destination) { subject.logs }
@@ -53,7 +59,10 @@ describe Docker::Container do
53
59
  end
54
60
 
55
61
  describe '#logs' do
56
- subject { described_class.create('Cmd' => "echo hello", 'Image' => 'base') }
62
+ subject {
63
+ described_class.create('Cmd' => "echo hello", 'Image' => 'debian:wheezy')
64
+ }
65
+ after(:each) { subject.remove }
57
66
 
58
67
  context "when not selecting any stream" do
59
68
  let(:non_destination) { subject.logs }
@@ -73,11 +82,15 @@ describe Docker::Container do
73
82
 
74
83
  describe '#create' do
75
84
  subject {
76
- described_class.create({'Cmd' => %w[true], 'Image' => 'base'}.merge(opts))
85
+ described_class.create({
86
+ 'Cmd' => %w[true],
87
+ 'Image' => 'debian:wheezy'
88
+ }.merge(opts))
77
89
  }
78
90
 
79
91
  context 'when creating a container named bob' do
80
92
  let(:opts) { {"name" => "bob"} }
93
+ after(:each) { subject.remove }
81
94
 
82
95
  it 'should have name set to bob', :vcr do
83
96
  expect(subject.json["Name"]).to eq "/bob"
@@ -87,11 +100,15 @@ describe Docker::Container do
87
100
 
88
101
  describe '#changes' do
89
102
  subject {
90
- described_class.create('Cmd' => %w[rm -rf /root], 'Image' => 'base')
103
+ described_class.create(
104
+ 'Cmd' => %w[rm -rf /root],
105
+ 'Image' => 'debian:wheezy'
106
+ )
91
107
  }
92
108
  let(:changes) { subject.changes }
93
109
 
94
110
  before { subject.tap(&:start).tap(&:wait) }
111
+ after(:each) { subject.tap(&:wait).remove }
95
112
 
96
113
  it 'returns the changes as an array', :vcr do
97
114
  expect(changes).to eq [
@@ -110,26 +127,29 @@ describe Docker::Container do
110
127
  let(:image) { Docker::Image.build_from_dir(dir) }
111
128
  let(:top) { sleep 1; container.top }
112
129
  let!(:container) { image.run('/while') }
130
+ after do
131
+ container.kill!.remove
132
+ image.remove
133
+ end
113
134
 
114
135
  it 'returns the top commands as an Array', :vcr do
115
136
  expect(top).to be_a Array
116
137
  expect(top).to_not be_empty
117
- expect(top.first.keys).to eq %w(UID PID PPID C STIME TTY TIME CMD)
138
+ expect(top.first.keys).to include('PID')
118
139
  end
119
140
  end
120
141
 
121
142
  describe '#copy' do
122
- subject {
123
- Docker::Image.create(
124
- 'fromImage' => 'base'
125
- ).run('touch /test').tap { |c| c.wait }
126
- }
143
+ let(:image) { Docker::Image.create('fromImage' => 'debian:wheezy') }
144
+ subject { image.run('touch /test').tap { |c| c.wait } }
145
+
146
+ after(:each) { subject.remove }
127
147
 
128
148
  context 'when the file does not exist' do
129
149
  it 'raises an error', :vcr do
130
150
  skip 'Docker no longer returns a 500 when the file does not exist'
131
- expect { subject.copy('/lol/not/a/real/file') { |chunk| puts chunk } }
132
- .to raise_error
151
+ # expect { subject.copy('/lol/not/a/real/file') { |chunk| puts chunk } }
152
+ # .to raise_error
133
153
  end
134
154
  end
135
155
 
@@ -145,21 +165,19 @@ describe Docker::Container do
145
165
  context 'when the input is a directory' do
146
166
  it 'yields each chunk of the tarred directory', :vcr do
147
167
  chunks = []
148
- subject.copy('/etc/vim') { |chunk| chunks << chunk }
168
+ subject.copy('/etc/logrotate.d') { |chunk| chunks << chunk }
149
169
  chunks = chunks.join("\n")
150
- expect(%w[vimrc vimrc.tiny]).to be_all { |file| chunks.include?(file) }
170
+ expect(%w[apt dpkg]).to be_all { |file| chunks.include?(file) }
151
171
  end
152
172
  end
153
173
  end
154
174
 
155
175
  describe '#export' do
156
176
  subject { described_class.create('Cmd' => %w[rm -rf / --no-preserve-root],
157
- 'Image' => 'base') }
177
+ 'Image' => 'tianon/true') }
158
178
  before { subject.start }
179
+ after { subject.tap(&:wait).remove }
159
180
 
160
- # If you have to re-record this VCR, PLEASE edit it so that it's only ~200
161
- # lines. This is only because we don't want our gem to be a few hundred
162
- # megabytes.
163
181
  it 'yields each chunk', :vcr do
164
182
  first = nil
165
183
  subject.export do |chunk|
@@ -170,9 +188,15 @@ describe Docker::Container do
170
188
  end
171
189
 
172
190
  describe '#attach' do
173
- subject { described_class.create('Cmd' => %w[pwd], 'Image' => 'base') }
191
+ subject {
192
+ described_class.create(
193
+ 'Cmd' => ['bash','-c','sleep 2; echo hello'],
194
+ 'Image' => 'debian:wheezy'
195
+ )
196
+ }
174
197
 
175
198
  before { subject.start }
199
+ after(:each) { subject.stop.remove }
176
200
 
177
201
  context 'with normal sized chunks' do
178
202
  it 'yields each chunk', :vcr do
@@ -180,7 +204,7 @@ describe Docker::Container do
180
204
  subject.attach do |stream, c|
181
205
  chunk ||= c
182
206
  end
183
- expect(chunk).to eq("/\n")
207
+ expect(chunk).to eq("hello\n")
184
208
  end
185
209
  end
186
210
 
@@ -198,7 +222,7 @@ describe Docker::Container do
198
222
  subject.attach do |stream, c|
199
223
  chunk ||= c
200
224
  end
201
- expect(chunk).to eq("/\n")
225
+ expect(chunk).to eq("hello\n")
202
226
  end
203
227
  end
204
228
  end
@@ -211,7 +235,7 @@ describe Docker::Container do
211
235
  skip 'HTTP socket hijacking not compatible with VCR'
212
236
  container = described_class.create(
213
237
  'Cmd' => %w[cat],
214
- 'Image' => 'base',
238
+ 'Image' => 'debian:wheezy',
215
239
  'OpenStdin' => true,
216
240
  'StdinOnce' => true
217
241
  )
@@ -227,13 +251,14 @@ describe Docker::Container do
227
251
  subject {
228
252
  described_class.create(
229
253
  'Cmd' => %w[test -d /foo],
230
- 'Image' => 'base',
254
+ 'Image' => 'debian:wheezy',
231
255
  'Volumes' => {'/foo' => {}}
232
256
  )
233
257
  }
234
258
  let(:all) { Docker::Container.all }
235
259
 
236
260
  before { subject.start('Binds' => ["/tmp:/foo"]) }
261
+ after(:each) { subject.remove }
237
262
 
238
263
  it 'starts the container', :vcr do
239
264
  expect(all.map(&:id)).to be_any { |id| id.start_with?(subject.id) }
@@ -242,9 +267,12 @@ describe Docker::Container do
242
267
  end
243
268
 
244
269
  describe '#stop' do
245
- subject { described_class.create('Cmd' => %w[true], 'Image' => 'base') }
270
+ subject {
271
+ described_class.create('Cmd' => %w[true], 'Image' => 'debian:wheezy')
272
+ }
246
273
 
247
274
  before { subject.tap(&:start).stop('timeout' => '10') }
275
+ after { subject.remove }
248
276
 
249
277
  it 'stops the container', :vcr do
250
278
  expect(described_class.all(:all => true).map(&:id)).to be_any { |id|
@@ -256,13 +284,72 @@ describe Docker::Container do
256
284
  end
257
285
  end
258
286
 
287
+ describe '#exec' do
288
+ subject {
289
+ described_class.create(
290
+ 'Cmd' => %w[sleep 20],
291
+ 'Image' => 'debian:wheezy'
292
+ ).start
293
+ }
294
+ after { subject.kill!.remove }
295
+
296
+ context 'when passed only a command' do
297
+ let(:output) { subject.exec(['bash','-c','sleep 2; echo hello']) }
298
+
299
+ it 'returns the stdout/stderr messages', :vcr do
300
+ expect(output).to eq([["hello\n"], []])
301
+ end
302
+ end
303
+
304
+ context 'when detach is true' do
305
+ let(:output) { subject.exec('date', detach: true) }
306
+
307
+ it 'returns the Docker::Exec object', :vcr do
308
+ expect(output).to be_a Docker::Exec
309
+ expect(output.id).to_not be_nil
310
+ end
311
+ end
312
+
313
+ context 'when passed a block' do
314
+ it 'streams the stdout/stderr messages', :vcr do
315
+ chunk = nil
316
+ subject.exec(['bash','-c','sleep 2; echo hello']) do |stream, c|
317
+ chunk ||= c
318
+ end
319
+ expect(chunk).to eq("hello\n")
320
+ end
321
+ end
322
+
323
+ context 'when stdin object is passed' do
324
+ let(:output) { subject.exec('cat', stdin: StringIO.new("hello")) }
325
+
326
+ it 'returns the stdout/stderr messages', :vcr do
327
+ skip 'HTTP socket hijacking not compatible with VCR'
328
+ expect(output).to eq([["hello"],[]])
329
+ end
330
+ end
331
+
332
+ context 'when tty is true' do
333
+ let(:command) { [
334
+ "bash", "-c",
335
+ "if [ -t 1 ]; then echo -n \"I'm a TTY!\"; fi"
336
+ ] }
337
+ let(:output) { subject.exec(command, tty: true) }
338
+
339
+ it 'returns the raw stdout/stderr output', :vcr do
340
+ expect(output).to eq([["I'm a TTY!"], []])
341
+ end
342
+ end
343
+ end
344
+
259
345
  describe '#kill' do
260
346
  let(:command) { ['/bin/bash', '-c', 'while [ 1 ]; do echo hello; done'] }
261
- subject { described_class.create('Cmd' => command, 'Image' => 'base') }
347
+ subject {
348
+ described_class.create('Cmd' => command, 'Image' => 'debian:wheezy')
349
+ }
262
350
 
263
- before do
264
- subject.start
265
- end
351
+ before { subject.start }
352
+ after(:each) {subject.remove }
266
353
 
267
354
  it 'kills the container', :vcr do
268
355
  subject.kill
@@ -303,7 +390,9 @@ describe Docker::Container do
303
390
  end
304
391
 
305
392
  describe '#delete' do
306
- subject { described_class.create('Cmd' => ['ls'], 'Image' => 'base') }
393
+ subject {
394
+ described_class.create('Cmd' => ['ls'], 'Image' => 'debian:wheezy')
395
+ }
307
396
 
308
397
  it 'deletes the container', :vcr do
309
398
  subject.delete(:force => true)
@@ -314,9 +403,12 @@ describe Docker::Container do
314
403
  end
315
404
 
316
405
  describe '#restart' do
317
- subject { described_class.create('Cmd' => %w[sleep 50], 'Image' => 'base') }
406
+ subject {
407
+ described_class.create('Cmd' => %w[sleep 10], 'Image' => 'debian:wheezy')
408
+ }
318
409
 
319
410
  before { subject.start }
411
+ after { subject.kill!.remove }
320
412
 
321
413
  it 'restarts the container', :vcr do
322
414
  expect(described_class.all.map(&:id)).to be_any { |id|
@@ -335,8 +427,12 @@ describe Docker::Container do
335
427
 
336
428
  describe '#pause' do
337
429
  subject {
338
- described_class.create('Cmd' => %w[sleep 50], 'Image' => 'base').start
430
+ described_class.create(
431
+ 'Cmd' => %w[sleep 50],
432
+ 'Image' => 'debian:wheezy'
433
+ ).start
339
434
  }
435
+ after { subject.unpause.kill!.remove }
340
436
 
341
437
  it 'pauses the container', :vcr do
342
438
  subject.pause
@@ -346,9 +442,13 @@ describe Docker::Container do
346
442
 
347
443
  describe '#unpause' do
348
444
  subject {
349
- described_class.create('Cmd' => %w[sleep 50], 'Image' => 'base').start
445
+ described_class.create(
446
+ 'Cmd' => %w[sleep 50],
447
+ 'Image' => 'debian:wheezy'
448
+ ).start
350
449
  }
351
450
  before { subject.pause }
451
+ after { subject.kill!.remove }
352
452
 
353
453
  it 'unpauses the container', :vcr do
354
454
  subject.unpause
@@ -359,10 +459,15 @@ describe Docker::Container do
359
459
  end
360
460
 
361
461
  describe '#wait' do
362
- subject { described_class.create('Cmd' => %w[tar nonsense],
363
- 'Image' => 'base') }
462
+ subject {
463
+ described_class.create(
464
+ 'Cmd' => %w[tar nonsense],
465
+ 'Image' => 'debian:wheezy'
466
+ )
467
+ }
364
468
 
365
469
  before { subject.start }
470
+ after(:each) { subject.remove }
366
471
 
367
472
  it 'waits for the command to finish', :vcr do
368
473
  expect(subject.wait['StatusCode']).to_not be_zero
@@ -370,26 +475,33 @@ describe Docker::Container do
370
475
 
371
476
  context 'when an argument is given' do
372
477
  subject { described_class.create('Cmd' => %w[sleep 5],
373
- 'Image' => 'base') }
478
+ 'Image' => 'debian:wheezy') }
374
479
 
375
480
  it 'sets the :read_timeout to that amount of time', :vcr do
376
481
  expect(subject.wait(6)['StatusCode']).to be_zero
377
482
  end
378
483
 
379
- context 'and a command runs for too long' do
380
- it 'raises a ServerError', :vcr do
381
- skip "VCR doesn't like to record errors"
382
- expect { subject.wait(4) }.to raise_error(Docker::Error::TimeoutError)
383
- end
384
- end
484
+ # context 'and a command runs for too long' do
485
+ # after(:each) { subject.remove }
486
+ #
487
+ # it 'raises a ServerError', :vcr do
488
+ # skip "VCR doesn't like to record errors"
489
+ # expect{subject.wait(4)}.to raise_error(Docker::Error::TimeoutError)
490
+ # end
491
+ # end
385
492
  end
386
493
  end
387
494
 
388
495
  describe '#run' do
389
496
  let(:run_command) { subject.run('ls') }
497
+
390
498
  context 'when the Container\'s command does not return status code of 0' do
391
- subject { described_class.create('Cmd' => %w[lol not a real command],
392
- 'Image' => 'base') }
499
+ subject { described_class.create('Cmd' => %w[false],
500
+ 'Image' => 'debian:wheezy') }
501
+
502
+ after do
503
+ subject.remove
504
+ end
393
505
 
394
506
  it 'raises an error', :vcr do
395
507
  expect { run_command }
@@ -399,7 +511,16 @@ describe Docker::Container do
399
511
 
400
512
  context 'when the Container\'s command returns a status code of 0' do
401
513
  subject { described_class.create('Cmd' => %w[pwd],
402
- 'Image' => 'base') }
514
+ 'Image' => 'debian:wheezy') }
515
+ after do
516
+ subject.remove
517
+ image = run_command.json['Image']
518
+ run_command.remove
519
+ Docker::Image.get(image).history.each do |layer|
520
+ next unless layer['CreatedBy'] == 'pwd'
521
+ Docker::Image.get(layer['Id']).remove(:noprune => true)
522
+ end
523
+ end
403
524
 
404
525
  it 'creates a new container to run the specified command', :vcr do
405
526
  expect(run_command.wait['StatusCode']).to be_zero
@@ -408,23 +529,30 @@ describe Docker::Container do
408
529
  end
409
530
 
410
531
  describe '#commit' do
411
- subject { described_class.create('Cmd' => %w[true], 'Image' => 'base') }
532
+ subject {
533
+ described_class.create('Cmd' => %w[true], 'Image' => 'debian:wheezy')
534
+ }
412
535
  let(:image) { subject.commit }
413
536
 
414
537
  before { subject.start }
538
+ after(:each) do
539
+ subject.remove
540
+ image.remove
541
+ end
415
542
 
416
543
  it 'creates a new Image from the Container\'s changes', :vcr do
417
544
  expect(image).to be_a Docker::Image
418
545
  expect(image.id).to_not be_nil
419
546
  end
420
547
 
421
- context 'if run is passed, it saves the command in the image', :vcr do
422
- let(:image) { subject.commit }
423
- it 'saves the command' do
424
- skip 'This is no longer working in v0.8'
425
- expect(image.run('pwd').attach).to eql [["/\n"],[]]
426
- end
427
- end
548
+ # context 'if run is passed, it saves the command in the image', :vcr do
549
+ # let(:image) { subject.commit }
550
+ #
551
+ # it 'saves the command' do
552
+ # skip 'This is no longer working in v0.8'
553
+ # expect(image.run('pwd').attach).to eql [["/\n"],[]]
554
+ # end
555
+ # end
428
556
 
429
557
  end
430
558
 
@@ -464,12 +592,13 @@ describe Docker::Container do
464
592
  "Env" => nil,
465
593
  "Cmd" => ["date"],
466
594
  "Dns" => nil,
467
- "Image" => "base",
595
+ "Image" => "debian:wheezy",
468
596
  "Volumes" => {},
469
597
  "VolumesFrom" => ""
470
598
  }
471
599
  end
472
600
  let(:container) { subject.create(options) }
601
+ after { container.remove }
473
602
 
474
603
  it 'sets the id', :vcr do
475
604
  expect(container).to be_a Docker::Container
@@ -500,7 +629,10 @@ describe Docker::Container do
500
629
  end
501
630
 
502
631
  context 'when the HTTP response is a 200' do
503
- let(:container) { subject.create('Cmd' => ['ls'], 'Image' => 'base') }
632
+ let(:container) {
633
+ subject.create('Cmd' => ['ls'], 'Image' => 'debian:wheezy')
634
+ }
635
+ after { container.remove }
504
636
 
505
637
  it 'materializes the Container into a Docker::Container', :vcr do
506
638
  expect(subject.get(container.id)).to be_a Docker::Container
@@ -529,7 +661,11 @@ describe Docker::Container do
529
661
  end
530
662
 
531
663
  context 'when the HTTP response is a 200' do
532
- before { described_class.create('Cmd' => ['ls'], 'Image' => 'base') }
664
+ let(:container) {
665
+ subject.create('Cmd' => ['ls'], 'Image' => 'debian:wheezy')
666
+ }
667
+ before { container }
668
+ after { container.remove }
533
669
 
534
670
  it 'materializes each Container into a Docker::Container', :vcr do
535
671
  expect(subject.all(:all => true)).to be_all { |container|