docker-api 1.15.0 → 1.16.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.
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
@@ -6,7 +6,7 @@ describe Docker::Event do
6
6
 
7
7
  let(:status) { "start" }
8
8
  let(:id) { "398c9f77b5d2" }
9
- let(:from) { "base:latest" }
9
+ let(:from) { "debian:wheezy" }
10
10
  let(:time) { 1381956164 }
11
11
 
12
12
  let(:expected_string) {
@@ -26,7 +26,7 @@ describe Docker::Event do
26
26
  .and_call_original
27
27
  fork do
28
28
  sleep 1
29
- Docker::Image.create('fromImage' => 'base').run('bash')
29
+ Docker::Image.create('fromImage' => 'debian:wheezy').run('bash')
30
30
  end
31
31
  Docker::Event.stream do |event|
32
32
  puts "#{event}"
@@ -46,7 +46,7 @@ describe Docker::Event do
46
46
  .and_call_original
47
47
  fork do
48
48
  sleep 1
49
- Docker::Image.create('fromImage' => 'base').run('bash')
49
+ Docker::Image.create('fromImage' => 'debian:wheezy').run('bash')
50
50
  end
51
51
  Docker::Event.since(time) do |event|
52
52
  puts "#{event}"
@@ -61,7 +61,7 @@ describe Docker::Event do
61
61
  subject { Docker::Event.new_event(response_body, nil, nil) }
62
62
  let(:status) { "start" }
63
63
  let(:id) { "398c9f77b5d2" }
64
- let(:from) { "base:latest" }
64
+ let(:from) { "debian:wheezy" }
65
65
  let(:time) { 1381956164 }
66
66
  let(:response_body) {
67
67
  "{\"status\":\"#{status}\",\"id\":\"#{id}\""\
@@ -0,0 +1,197 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docker::Exec do
4
+
5
+ describe '#to_s' do
6
+ subject {
7
+ described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
8
+ }
9
+
10
+ let(:id) { 'bf119e2' }
11
+ let(:connection) { Docker.connection }
12
+ let(:expected_string) {
13
+ "Docker::Exec { :id => #{id}, :connection => #{connection} }"
14
+ }
15
+ before do
16
+ {
17
+ :@id => id,
18
+ :@connection => connection
19
+ }.each { |k, v| subject.instance_variable_set(k, v) }
20
+ end
21
+
22
+ its(:to_s) { should == expected_string }
23
+ end
24
+
25
+ describe '.create' do
26
+ subject { described_class }
27
+
28
+ context 'when the HTTP request returns a 201' do
29
+ let(:container) {
30
+ Docker::Container.create(
31
+ 'Cmd' => %w[sleep 5],
32
+ 'Image' => 'debian:wheezy'
33
+ ).start!
34
+ }
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', :vcr 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 '#start!' do
74
+ let(:container) {
75
+ Docker::Container.create(
76
+ 'Cmd' => %w[sleep 10],
77
+ 'Image' => 'debian:wheezy'
78
+ ).start!
79
+ }
80
+
81
+ context 'when the exec instance does not exist' do
82
+ subject do
83
+ described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
84
+ end
85
+
86
+ it 'raises an error', :vcr do
87
+ skip 'The Docker API returns a 200 (docker/docker#9341)'
88
+ expect { subject.start! }.to raise_error(Docker::Error::NotFoundError)
89
+ end
90
+ end
91
+
92
+ context 'when :detach is set to false' do
93
+ subject {
94
+ described_class.create(
95
+ 'Container' => container.id,
96
+ 'AttachStdout' => true,
97
+ 'Cmd' => ['bash','-c','sleep 2; echo hello']
98
+ )
99
+ }
100
+ after { container.kill!.remove }
101
+
102
+ it 'returns the stdout and stderr messages', :vcr do
103
+ expect(subject.start!).to eq([["hello\n"],[]])
104
+ end
105
+
106
+ context 'block is passed' do
107
+ it 'attaches to the stream', :vcr do
108
+ chunk = nil
109
+ subject.start! do |stream, c|
110
+ chunk ||= c
111
+ end
112
+ expect(chunk).to eq("hello\n")
113
+ end
114
+ end
115
+ end
116
+
117
+ context 'when :detach is set to true' do
118
+ subject {
119
+ described_class.create('Container' => container.id, 'Cmd' => %w[date])
120
+ }
121
+ after { container.kill!.remove }
122
+
123
+ it 'returns empty stdout and stderr messages', :vcr do
124
+ expect(subject.start!(:detach => true)).to eq([[],[]])
125
+ end
126
+ end
127
+
128
+ context 'when the command has already run' do
129
+ subject {
130
+ described_class.create('Container' => container.id, 'Cmd' => ['date'])
131
+ }
132
+ before { subject.start! }
133
+ after { container.kill!.remove }
134
+
135
+ it 'raises an error', :vcr do
136
+ skip 'The Docker API returns a 200 (docker/docker#9341)'
137
+ expect { subject.start! }.to raise_error(Docker::Error::NotFoundError)
138
+ end
139
+ end
140
+
141
+ context 'when the HTTP request returns a 201' do
142
+ subject {
143
+ described_class.create('Container' => container.id, 'Cmd' => ['date'])
144
+ }
145
+ after { container.kill!.remove }
146
+
147
+ it 'starts the exec instance', :vcr do
148
+ expect { subject.start! }.not_to raise_error
149
+ end
150
+ end
151
+ end
152
+
153
+ describe '#resize' do
154
+ let(:container) {
155
+ Docker::Container.create(
156
+ 'Cmd' => %w[sleep 20],
157
+ 'Image' => 'debian:wheezy'
158
+ ).start!
159
+ }
160
+
161
+ context 'when exec instance has TTY enabled' do
162
+ let(:instance) do
163
+ described_class.create(
164
+ 'Container' => container.id,
165
+ 'AttachStdin' => true,
166
+ 'Tty' => true,
167
+ 'Cmd' => %w[/bin/bash]
168
+ )
169
+ end
170
+ after do
171
+ container.kill!
172
+ sleep 1
173
+ container.remove
174
+ end
175
+
176
+ it 'returns a 200', :vcr do
177
+ t = Thread.new do
178
+ instance.start!(:tty => true)
179
+ end
180
+ sleep 1
181
+ expect { instance.resize(:h => 10, :w => 30) }.not_to raise_error
182
+ t.kill
183
+ end
184
+ end
185
+
186
+ context 'when the exec instance does not exist' do
187
+ subject do
188
+ described_class.send(:new, Docker.connection, 'id' => rand(10000).to_s)
189
+ end
190
+
191
+ it 'raises an error', :vcr do
192
+ skip 'The Docker API returns a 200 (docker/docker#9341)'
193
+ expect { subject.resize }.to raise_error(Docker::Error::NotFoundError)
194
+ end
195
+ end
196
+ end
197
+ end
@@ -8,7 +8,7 @@ describe Docker::Image do
8
8
  let(:connection) { Docker.connection }
9
9
 
10
10
  let(:info) do
11
- {"id" => "bf119e2", "Repository" => "base", "Tag" => "latest",
11
+ {"id" => "bf119e2", "Repository" => "debian", "Tag" => "wheezy",
12
12
  "Created" => 1364102658, "Size" => 24653, "VirtualSize" => 180116135}
13
13
  end
14
14
 
@@ -21,17 +21,30 @@ describe Docker::Image do
21
21
  end
22
22
 
23
23
  describe '#remove' do
24
- let(:id) { subject.id }
25
- subject { described_class.create('fromImage' => 'base') }
26
24
 
27
- it 'removes the Image', :vcr do
28
- subject.remove(:force => true)
29
- expect(Docker::Image.all.map(&:id)).to_not include(id)
25
+ context 'when no name is given' do
26
+ let(:id) { subject.id }
27
+ subject { described_class.create('fromImage' => 'busybox') }
28
+
29
+ it 'removes the Image', :vcr do
30
+ subject.remove(:force => true)
31
+ expect(Docker::Image.all.map(&:id)).to_not include(id)
32
+ end
33
+ end
34
+
35
+ context 'when a valid tag is given' do
36
+ it 'untags the Image'
37
+ end
38
+
39
+ context 'when an invalid tag is given' do
40
+ it 'raises an error'
30
41
  end
31
42
  end
32
43
 
33
44
  describe '#insert_local' do
34
- subject { described_class.build('from base') }
45
+ include_context "local paths"
46
+
47
+ subject { described_class.create('fromImage' => 'debian:wheezy') }
35
48
 
36
49
  let(:rm) { false }
37
50
  let(:new_image) {
@@ -49,23 +62,33 @@ describe Docker::Image do
49
62
  end
50
63
 
51
64
  context 'when the local file does exist' do
52
- let(:file) { './Gemfile' }
65
+ let(:file) { File.join(project_dir, 'Gemfile') }
53
66
  let(:gemfile) { File.read('Gemfile') }
67
+ let(:container) { new_image.run('cat /Gemfile') }
68
+ after do
69
+ container.tap(&:wait).remove
70
+ new_image.remove
71
+ end
54
72
 
55
73
  it 'creates a new Image that has that file', :vcr do
56
- chunk = nil
57
- new_image.run('cat /Gemfile').attach { |stream, c|
58
- chunk ||= c
59
- }
60
- expect(chunk).to eq(gemfile)
74
+ output = container.streaming_logs(stdout: true)
75
+ expect(output).to eq(gemfile)
61
76
  end
62
77
  end
63
78
 
64
79
  context 'when a direcory is passed' do
65
80
  let(:new_image) {
66
- subject.insert_local('localPath' => './lib', 'outputPath' => '/lib')
81
+ subject.insert_local(
82
+ 'localPath' => File.join(project_dir, 'lib'),
83
+ 'outputPath' => '/lib'
84
+ )
67
85
  }
68
- let(:response) { new_image.run('ls -a /lib/docker').attach.flatten.first }
86
+ let(:container) { new_image.run('ls -a /lib/docker') }
87
+ let(:response) { container.streaming_logs(stdout: true) }
88
+ after do
89
+ container.tap(&:wait).remove
90
+ new_image.remove
91
+ end
69
92
 
70
93
  it 'inserts the directory', :vcr do
71
94
  expect(response.split("\n").sort).to eq(Dir.entries('lib/docker').sort)
@@ -73,21 +96,29 @@ describe Docker::Image do
73
96
  end
74
97
 
75
98
  context 'when there are multiple files passed' do
76
- let(:file) { ['./Gemfile', './Rakefile'] }
99
+ let(:file) {
100
+ [File.join(project_dir, 'Gemfile'), File.join(project_dir, 'LICENSE')]
101
+ }
77
102
  let(:gemfile) { File.read('Gemfile') }
78
- let(:rakefile) { File.read('Rakefile') }
103
+ let(:license) { File.read('LICENSE') }
104
+ let(:container) { new_image.run('cat /Gemfile /LICENSE') }
79
105
  let(:response) {
80
- new_image.run('cat /Gemfile /Rakefile').attach
106
+ container.streaming_logs(stdout: true)
81
107
  }
108
+ after do
109
+ container.tap(&:wait).remove
110
+ new_image.remove
111
+ end
82
112
 
83
113
  it 'creates a new Image that has each file', :vcr do
84
- expect(response).to eq([[gemfile, rakefile],[]])
114
+ expect(response).to eq("#{gemfile}#{license}")
85
115
  end
86
116
  end
87
117
 
88
118
  context 'when removing intermediate containers' do
89
119
  let(:rm) { true }
90
- let(:file) { './Gemfile' }
120
+ let(:file) { File.join(project_dir, 'Gemfile') }
121
+ after(:each) { new_image.remove }
91
122
 
92
123
  it 'leave no intermediate containers', :vcr do
93
124
  expect { new_image }.to change {
@@ -104,47 +135,29 @@ describe Docker::Image do
104
135
  describe '#push' do
105
136
  let(:credentials) {
106
137
  {
107
- 'username' => 'nahiluhmot',
108
- 'password' => '******',
138
+ 'username' => ENV['DOCKER_API_USER'],
139
+ 'password' => ENV['DOCKER_API_PASS'],
109
140
  'serveraddress' => 'https://index.docker.io/v1',
110
- 'email' => 'tomhulihan@swipely.com'
141
+ 'email' => ENV['DOCKER_API_EMAIL']
111
142
  }
112
143
  }
113
- let(:base_image) {
114
- described_class.create('fromImage' => 'base')
115
- }
116
- let(:container) {
117
- base_image.run('true')
118
- }
119
- let(:new_image) {
120
- container.commit('repo' => 'nahiluhmot/base2')
121
- Docker::Image.all(:all => true).select { |image|
122
- image.info['RepoTags'].include?('nahiluhmot/base2:latest')
123
- }.first
144
+ let(:repo_tag) { "#{ENV['DOCKER_API_USER']}/true" }
145
+ let(:image) {
146
+ described_class.build("FROM tianon/true\n", "t" => repo_tag).refresh!
124
147
  }
148
+ after { image.remove(:name => repo_tag, :noprune => true) }
125
149
 
126
150
  it 'pushes the Image', :vcr do
127
- new_image.push(credentials)
151
+ image.push(credentials)
152
+ end
153
+
154
+ context 'when a tag is specified' do
155
+ it 'pushes that specific tag'
128
156
  end
129
157
 
130
158
  context 'when there are no credentials' do
131
159
  let(:credentials) { nil }
132
- let(:image) {
133
- Docker::Image.create('fromImage' => 'registry', 'tag' => 'latest')
134
- }
135
-
136
- let(:container) { Docker::Container.create('Image' => image.id) }
137
-
138
- before do
139
- opts = {
140
- "PortBindings" => {
141
- "5000/tcp" => [{"HostPort" => "5000"}]
142
- }
143
- }
144
- container.start!(opts)
145
- sleep 10 # for some reason the registry isn't ready right away
146
- image.tag('repo' => 'localhost:5000/registry', 'tag' => 'test')
147
- end
160
+ let(:repo_tag) { "localhost:5000/true" }
148
161
 
149
162
  it 'still pushes', :vcr do
150
163
  expect { image.push }.to_not raise_error
@@ -153,16 +166,17 @@ describe Docker::Image do
153
166
  end
154
167
 
155
168
  describe '#tag' do
156
- subject { described_class.create('fromImage' => 'base') }
169
+ subject { described_class.create('fromImage' => 'debian:wheezy') }
170
+ after { subject.remove(:name => 'teh:latest', :noprune => true) }
157
171
 
158
172
  it 'tags the image with the repo name', :vcr do
159
- subject.tag(:repo => :base2, :force => true)
160
- expect(subject.info['RepoTags']).to include 'base2:latest'
173
+ subject.tag(:repo => :teh, :force => true)
174
+ expect(subject.info['RepoTags']).to include 'teh:latest'
161
175
  end
162
176
  end
163
177
 
164
178
  describe '#json' do
165
- subject { described_class.create('fromImage' => 'base') }
179
+ subject { described_class.create('fromImage' => 'debian:wheezy') }
166
180
  let(:json) { subject.json }
167
181
 
168
182
  it 'returns additional information about image image', :vcr do
@@ -172,7 +186,7 @@ describe Docker::Image do
172
186
  end
173
187
 
174
188
  describe '#history' do
175
- subject { described_class.create('fromImage' => 'base') }
189
+ subject { described_class.create('fromImage' => 'debian:wheezy') }
176
190
  let(:history) { subject.history }
177
191
 
178
192
  it 'returns the history of the Image', :vcr do
@@ -183,48 +197,51 @@ describe Docker::Image do
183
197
  end
184
198
 
185
199
  describe '#run' do
186
- subject { described_class.create('fromImage' => 'base') }
187
- let(:output) { subject.run(cmd).attach }
200
+ subject { described_class.create('fromImage' => 'debian:wheezy') }
201
+ let(:container) { subject.run(cmd) }
202
+ let(:output) { container.streaming_logs(stdout: true) }
188
203
 
189
204
  context 'when the argument is a String', :vcr do
190
205
  let(:cmd) { 'ls /lib64/' }
206
+ after { container.tap(&:wait).remove }
207
+
191
208
  it 'splits the String by spaces and creates a new Container' do
192
- expect(output).to eq([["ld-linux-x86-64.so.2\n"],[]])
209
+ expect(output).to eq("ld-linux-x86-64.so.2\n")
193
210
  end
194
211
  end
195
212
 
196
213
  context 'when the argument is an Array' do
197
214
  let(:cmd) { %w[which pwd] }
215
+ after { container.tap(&:wait).remove }
198
216
 
199
217
  it 'creates a new Container', :vcr do
200
- expect(output).to eq([["/bin/pwd\n"],[]])
218
+ expect(output).to eq("/bin/pwd\n")
201
219
  end
202
220
  end
203
221
 
204
222
  context 'when the argument is nil', :vcr do
205
223
  let(:cmd) { nil }
206
- context 'no command configured in image'do
224
+ context 'no command configured in image' do
225
+ subject { described_class.create('fromImage' => 'scratch') }
207
226
  it 'should raise an error if no command is specified' do
208
- expect {output}.to raise_error(Docker::Error::ServerError,
227
+ expect {container}.to raise_error(Docker::Error::ServerError,
209
228
  "No command specified.")
210
229
  end
211
230
  end
212
231
 
213
232
  context "command configured in image" do
214
- let(:container) {Docker::Container.create('Cmd' => %w[true],
215
- 'Image' => 'base')}
216
- subject { container.commit('run' => {"Cmd" => %w[pwd]}) }
233
+ let(:cmd) { 'pwd' }
234
+ after { container.tap(&:wait).remove }
217
235
 
218
236
  it 'should normally show result if image has Cmd configured' do
219
- skip 'The docs say this should work, but it clearly does not'
220
- expect(output).to eql [["/\n"],[]]
237
+ expect(output).to eql "/\n"
221
238
  end
222
239
  end
223
240
  end
224
241
  end
225
242
 
226
243
  describe '#refresh!' do
227
- let(:image) { Docker::Image.create('fromImage' => 'base') }
244
+ let(:image) { Docker::Image.create('fromImage' => 'debian:wheezy') }
228
245
 
229
246
  it 'updates the @info hash', :vcr do
230
247
  size = image.info.size
@@ -237,16 +254,17 @@ describe Docker::Image do
237
254
  subject { described_class }
238
255
 
239
256
  context 'when the Image does not yet exist and the body is a Hash' do
240
- let(:image) { subject.create('fromImage' => 'ubuntu') }
257
+ let(:image) { subject.create('fromImage' => 'swipely/scratch') }
241
258
  let(:creds) {
242
259
  {
243
- :username => 'tlunter',
244
- :password => '************',
245
- :email => 'tlunter@gmail.com'
260
+ :username => ENV['DOCKER_API_USER'],
261
+ :password => ENV['DOCKER_API_PASS'],
262
+ :email => ENV['DOCKER_API_EMAIL']
246
263
  }
247
264
  }
248
265
 
249
266
  before { Docker.creds = creds }
267
+ after { image.remove(:name => 'swipely/scratch', :noprune => true) }
250
268
 
251
269
  it 'sets the id and sends Docker.creds', :vcr do
252
270
  expect(image).to be_a Docker::Image
@@ -264,7 +282,7 @@ describe Docker::Image do
264
282
  let(:image) { subject.get(image_name) }
265
283
 
266
284
  context 'when the image does exist' do
267
- let(:image_name) { 'base' }
285
+ let(:image_name) { 'debian:wheezy' }
268
286
 
269
287
  it 'returns the new image', :vcr do
270
288
  expect(image).to be_a Docker::Image
@@ -295,7 +313,7 @@ describe Docker::Image do
295
313
  let(:exists) { subject.exist?(image_name) }
296
314
 
297
315
  context 'when the image does exist' do
298
- let(:image_name) { 'base' }
316
+ let(:image_name) { 'debian:wheezy' }
299
317
 
300
318
  it 'returns true', :vcr do
301
319
  expect(exists).to eq(true)
@@ -322,6 +340,8 @@ describe Docker::Image do
322
340
  end
323
341
 
324
342
  describe '.import' do
343
+ include_context "local paths"
344
+
325
345
  subject { described_class }
326
346
 
327
347
  context 'when the file does not exist' do
@@ -334,15 +354,11 @@ describe Docker::Image do
334
354
  end
335
355
 
336
356
  context 'when the file does exist' do
337
- let(:file) { 'spec/fixtures/export.tar' }
338
- let(:body) { StringIO.new }
339
-
340
- before do
341
- allow(Docker::Image).to receive(:open).with(file).and_yield(body)
342
- end
357
+ let(:file) { File.join(project_dir, 'spec', 'fixtures', 'export.tar') }
358
+ let(:import) { subject.import(file) }
359
+ after { import.remove(:noprune => true) }
343
360
 
344
361
  it 'creates the Image', :vcr do
345
- import = subject.import(file)
346
362
  expect(import).to be_a Docker::Image
347
363
  expect(import.id).to_not be_nil
348
364
  end
@@ -357,12 +373,13 @@ describe Docker::Image do
357
373
  end
358
374
 
359
375
  context 'when the URI is valid' do
360
- let(:uri) { 'http://swipely-pub.s3.amazonaws.com/ubuntu.tar' }
376
+ let(:uri) { 'http://swipely-pub.s3.amazonaws.com/tianon_true.tar' }
377
+ let(:import) { subject.import(uri) }
378
+ after { import.remove(:noprune => true) }
361
379
 
362
380
  it 'returns an Image', :vcr do
363
- image = subject.import(uri)
364
- expect(image).to be_a Docker::Image
365
- expect(image.id).to_not be_nil
381
+ expect(import).to be_a Docker::Image
382
+ expect(import.id).to_not be_nil
366
383
  end
367
384
  end
368
385
  end
@@ -372,7 +389,7 @@ describe Docker::Image do
372
389
  subject { described_class }
373
390
 
374
391
  let(:images) { subject.all(:all => true) }
375
- before { subject.create('fromImage' => 'base') }
392
+ before { subject.create('fromImage' => 'debian:wheezy') }
376
393
 
377
394
  it 'materializes each Image into a Docker::Image', :vcr do
378
395
  images.each do |image|
@@ -412,7 +429,7 @@ describe Docker::Image do
412
429
 
413
430
  context 'with a valid Dockerfile' do
414
431
  context 'without query parameters' do
415
- let(:image) { subject.build("from base\n") }
432
+ let(:image) { subject.build("FROM debian:wheezy\n") }
416
433
 
417
434
  it 'builds an image', :vcr do
418
435
  expect(image).to be_a Docker::Image
@@ -423,25 +440,31 @@ describe Docker::Image do
423
440
 
424
441
  context 'with specifying a repo in the query parameters' do
425
442
  let(:image) {
426
- subject.build("from base\nrun true\n", "t" => "swipely/base")
443
+ subject.build(
444
+ "FROM debian:wheezy\nRUN true\n",
445
+ "t" => "#{ENV['DOCKER_API_USER']}/debian:true"
446
+ )
427
447
  }
428
- let(:images) { subject.all }
448
+ after { image.remove(:noprune => true) }
429
449
 
430
450
  it 'builds an image and tags it', :vcr do
431
451
  expect(image).to be_a Docker::Image
432
452
  expect(image.id).to_not be_nil
433
453
  expect(image.connection).to be_a Docker::Connection
434
- expect(images.first.info["RepoTags"]).to eq(["swipely/base:latest"])
454
+ image.refresh!
455
+ expect(image.info["RepoTags"]).to eq(
456
+ ["#{ENV['DOCKER_API_USER']}/debian:true"]
457
+ )
435
458
  end
436
459
  end
437
460
 
438
461
  context 'with a block capturing build output' do
439
462
  let(:build_output) { "" }
440
463
  let(:block) { Proc.new { |chunk| build_output << chunk } }
441
- let!(:image) { subject.build("FROM base\n", &block) }
464
+ let!(:image) { subject.build("FROM debian:wheezy\n", &block) }
442
465
 
443
466
  it 'calls the block and passes build output', :vcr do
444
- expect(build_output).to match(/Step 0 : FROM base/)
467
+ expect(build_output).to match(/Step 0 : FROM debian:wheezy/)
445
468
  end
446
469
  end
447
470
  end
@@ -463,20 +486,26 @@ describe Docker::Image do
463
486
  'Cmd' => %w[cat /Dockerfile])
464
487
  end
465
488
  let(:output) { container.tap(&:start)
466
- .attach(:stderr => true) }
467
- let(:images) { subject.all }
489
+ .streaming_logs(stdout: true) }
490
+ after(:each) do
491
+ container.tap(&:wait).remove
492
+ image.remove(:noprune => true)
493
+ end
468
494
 
469
495
  context 'with no query parameters' do
470
496
  it 'builds the image', :vcr do
471
- expect(output).to eq([[docker_file.read],[]])
497
+ expect(output).to eq(docker_file.read)
472
498
  end
473
499
  end
474
500
 
475
501
  context 'with specifying a repo in the query parameters' do
476
- let(:opts) { { "t" => "swipely/base2" } }
502
+ let(:opts) { { "t" => "#{ENV['DOCKER_API_USER']}/debian:from_dir" } }
477
503
  it 'builds the image and tags it', :vcr do
478
- expect(output).to eq([[docker_file.read],[]])
479
- expect(images.first.info["RepoTags"]).to eq(["swipely/base2:latest"])
504
+ expect(output).to eq(docker_file.read)
505
+ image.refresh!
506
+ expect(image.info["RepoTags"]).to eq(
507
+ ["#{ENV['DOCKER_API_USER']}/debian:from_dir"]
508
+ )
480
509
  end
481
510
  end
482
511
 
@@ -486,16 +515,16 @@ describe Docker::Image do
486
515
 
487
516
  it 'calls the block and passes build output', :vcr do
488
517
  image # Create the image variable, which is lazy-loaded by Rspec
489
- expect(build_output).to match(/Step 0 : from base/)
518
+ expect(build_output).to match(/Step 0 : FROM debian:wheezy/)
490
519
  end
491
520
  end
492
521
 
493
522
  context 'with credentials passed' do
494
523
  let(:creds) {
495
524
  {
496
- :username => 'nahiluhmot',
497
- :password => '*********',
498
- :email => 'hulihan.tom159@gmail.com',
525
+ :username => ENV['DOCKER_API_USER'],
526
+ :password => ENV['DOCKER_API_PASS'],
527
+ :email => ENV['DOCKER_API_EMAIL'],
499
528
  :serveraddress => 'https://index.docker.io/v1'
500
529
  }
501
530
  }