docker-client 0.1.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 (74) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +9 -0
  6. data/LICENSE.txt +22 -0
  7. data/MIT-LICENSE +21 -0
  8. data/README.md +87 -0
  9. data/Rakefile +15 -0
  10. data/docker.gemspec +40 -0
  11. data/lib/docker.rb +10 -0
  12. data/lib/docker/api.rb +32 -0
  13. data/lib/docker/connection.rb +83 -0
  14. data/lib/docker/error.rb +9 -0
  15. data/lib/docker/error/bad_parameter_error.rb +7 -0
  16. data/lib/docker/error/container_not_found.rb +7 -0
  17. data/lib/docker/error/internal_server_error.rb +7 -0
  18. data/lib/docker/error/not_found_error.rb +7 -0
  19. data/lib/docker/resource.rb +8 -0
  20. data/lib/docker/resource/base.rb +23 -0
  21. data/lib/docker/resource/container.rb +131 -0
  22. data/lib/docker/resource/image.rb +10 -0
  23. data/lib/docker/version.rb +3 -0
  24. data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_a_basic_request.yml +40 -0
  25. data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_get_request.yml +105 -0
  26. data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_post_request.yml +30 -0
  27. data/spec/cassettes/Docker_Connection/returns_status_404_for_non_existent_path.yml +31 -0
  28. data/spec/cassettes/Docker_Connection/sets_given_query_parameters.yml +31 -0
  29. data/spec/cassettes/Docker_Connection/sets_given_request_headers.yml +33 -0
  30. data/spec/cassettes/Docker_Resource_Container/changes/inspects_the_container_s_filesystem_changes.yml +33 -0
  31. data/spec/cassettes/Docker_Resource_Container/create/raises_an_exception_when_called_with_invalid_options.yml +34 -0
  32. data/spec/cassettes/Docker_Resource_Container/create/with_many_settings.yml +36 -0
  33. data/spec/cassettes/Docker_Resource_Container/create/with_minimal_settings.yml +34 -0
  34. data/spec/cassettes/Docker_Resource_Container/kill/raises_an_exception_for_an_unknow_container.yml +31 -0
  35. data/spec/cassettes/Docker_Resource_Container/kill/the_container.yml +30 -0
  36. data/spec/cassettes/Docker_Resource_Container/lists/all_running_containers.yml +46 -0
  37. data/spec/cassettes/Docker_Resource_Container/lists/limit_last_created_containers.yml +34 -0
  38. data/spec/cassettes/Docker_Resource_Container/lists/non-running_processes_too.yml +114 -0
  39. data/spec/cassettes/Docker_Resource_Container/lists/processes_before_a_certain_created_container.yml +110 -0
  40. data/spec/cassettes/Docker_Resource_Container/lists/processes_since_a_certain_created_container.yml +34 -0
  41. data/spec/cassettes/Docker_Resource_Container/logs/returns_the_stderr_of_a_container.yml +25 -0
  42. data/spec/cassettes/Docker_Resource_Container/logs/returns_the_stdout_of_a_container.yml +25 -0
  43. data/spec/cassettes/Docker_Resource_Container/remove/deletes_the_container.yml +30 -0
  44. data/spec/cassettes/Docker_Resource_Container/remove/raises_an_exception_with_an_invalid_container.yml +31 -0
  45. data/spec/cassettes/Docker_Resource_Container/restarts/raises_an_exception_for_an_unknown_container.yml +31 -0
  46. data/spec/cassettes/Docker_Resource_Container/restarts/the_container.yml +30 -0
  47. data/spec/cassettes/Docker_Resource_Container/shows/the_low_level_details.yml +48 -0
  48. data/spec/cassettes/Docker_Resource_Container/start/brings_a_container_into_state_running.yml +30 -0
  49. data/spec/cassettes/Docker_Resource_Container/start/raises_an_exception_for_an_unknown_container.yml +31 -0
  50. data/spec/cassettes/Docker_Resource_Container/stop/halts_a_container.yml +30 -0
  51. data/spec/cassettes/Docker_Resource_Container/stop/raises_an_exception_for_an_unknown_container.yml +31 -0
  52. data/spec/cassettes/Docker_Resource_Container/wait/blocks_until_the_container_stops.yml +31 -0
  53. data/spec/cassettes/Docker_Resource_Container/wait/raises_an_exception_for_an_unknown_container.yml +31 -0
  54. data/spec/cassettes/test_setup/create_container_connection_post.yml +33 -0
  55. data/spec/cassettes/test_setup/create_container_container_changes.yml +33 -0
  56. data/spec/cassettes/test_setup/create_container_container_kill.yml +34 -0
  57. data/spec/cassettes/test_setup/create_container_container_lists1.yml +34 -0
  58. data/spec/cassettes/test_setup/create_container_container_lists2.yml +33 -0
  59. data/spec/cassettes/test_setup/create_container_container_logs.yml +33 -0
  60. data/spec/cassettes/test_setup/create_container_container_remove.yml +33 -0
  61. data/spec/cassettes/test_setup/create_container_container_restarts.yml +34 -0
  62. data/spec/cassettes/test_setup/create_container_container_shows.yml +33 -0
  63. data/spec/cassettes/test_setup/create_container_container_start.yml +33 -0
  64. data/spec/cassettes/test_setup/create_container_container_stop.yml +34 -0
  65. data/spec/cassettes/test_setup/create_container_container_wait.yml +33 -0
  66. data/spec/cassettes/test_setup/delete_container.yml +405 -0
  67. data/spec/cassettes/test_setup/start_container.yml +235 -0
  68. data/spec/cassettes/test_setup/wait_on_container.yml +63 -0
  69. data/spec/docker/api_spec.rb +37 -0
  70. data/spec/docker/connection_spec.rb +81 -0
  71. data/spec/docker/resource/container_spec.rb +288 -0
  72. data/spec/helpers.rb +48 -0
  73. data/spec/spec_helper.rb +31 -0
  74. metadata +361 -0
@@ -0,0 +1,235 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://10.0.5.5:4243/containers/12433dcd5b7a/start
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ response:
13
+ status:
14
+ code: 204
15
+ message: !binary |-
16
+ Tm8gQ29udGVudA==
17
+ headers:
18
+ !binary "RGF0ZQ==":
19
+ - !binary |-
20
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNToxMyBHTVQ=
21
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
22
+ - !binary |-
23
+ Y2h1bmtlZA==
24
+ !binary "Q29udGVudC1UeXBl":
25
+ - !binary |-
26
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
27
+ body:
28
+ encoding: ASCII-8BIT
29
+ string: !binary ""
30
+ http_version:
31
+ recorded_at: Sat, 25 May 2013 16:05:15 GMT
32
+ - request:
33
+ method: post
34
+ uri: http://10.0.5.5:4243/containers/e6fa2f7eb0b6/start
35
+ body:
36
+ encoding: US-ASCII
37
+ string: ''
38
+ headers:
39
+ Content-Type:
40
+ - application/json
41
+ response:
42
+ status:
43
+ code: 204
44
+ message: !binary |-
45
+ Tm8gQ29udGVudA==
46
+ headers:
47
+ !binary "RGF0ZQ==":
48
+ - !binary |-
49
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNToxNyBHTVQ=
50
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
51
+ - !binary |-
52
+ Y2h1bmtlZA==
53
+ !binary "Q29udGVudC1UeXBl":
54
+ - !binary |-
55
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
56
+ body:
57
+ encoding: ASCII-8BIT
58
+ string: !binary ""
59
+ http_version:
60
+ recorded_at: Sat, 25 May 2013 16:05:19 GMT
61
+ - request:
62
+ method: post
63
+ uri: http://10.0.5.5:4243/containers/499c5c8ac48a/start
64
+ body:
65
+ encoding: US-ASCII
66
+ string: ''
67
+ headers:
68
+ Content-Type:
69
+ - application/json
70
+ response:
71
+ status:
72
+ code: 204
73
+ message: !binary |-
74
+ Tm8gQ29udGVudA==
75
+ headers:
76
+ !binary "RGF0ZQ==":
77
+ - !binary |-
78
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNToyMCBHTVQ=
79
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
80
+ - !binary |-
81
+ Y2h1bmtlZA==
82
+ !binary "Q29udGVudC1UeXBl":
83
+ - !binary |-
84
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
85
+ body:
86
+ encoding: ASCII-8BIT
87
+ string: !binary ""
88
+ http_version:
89
+ recorded_at: Sat, 25 May 2013 16:05:22 GMT
90
+ - request:
91
+ method: post
92
+ uri: http://10.0.5.5:4243/containers/167951f02581/start
93
+ body:
94
+ encoding: US-ASCII
95
+ string: ''
96
+ headers:
97
+ Content-Type:
98
+ - application/json
99
+ response:
100
+ status:
101
+ code: 204
102
+ message: !binary |-
103
+ Tm8gQ29udGVudA==
104
+ headers:
105
+ !binary "RGF0ZQ==":
106
+ - !binary |-
107
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNToyNCBHTVQ=
108
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
109
+ - !binary |-
110
+ Y2h1bmtlZA==
111
+ !binary "Q29udGVudC1UeXBl":
112
+ - !binary |-
113
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
114
+ body:
115
+ encoding: ASCII-8BIT
116
+ string: !binary ""
117
+ http_version:
118
+ recorded_at: Sat, 25 May 2013 16:05:26 GMT
119
+ - request:
120
+ method: post
121
+ uri: http://10.0.5.5:4243/containers/95fbeced7a97/start
122
+ body:
123
+ encoding: US-ASCII
124
+ string: ''
125
+ headers:
126
+ Content-Type:
127
+ - application/json
128
+ response:
129
+ status:
130
+ code: 204
131
+ message: !binary |-
132
+ Tm8gQ29udGVudA==
133
+ headers:
134
+ !binary "RGF0ZQ==":
135
+ - !binary |-
136
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNToyNSBHTVQ=
137
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
138
+ - !binary |-
139
+ Y2h1bmtlZA==
140
+ !binary "Q29udGVudC1UeXBl":
141
+ - !binary |-
142
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
143
+ body:
144
+ encoding: ASCII-8BIT
145
+ string: !binary ""
146
+ http_version:
147
+ recorded_at: Sat, 25 May 2013 16:05:28 GMT
148
+ - request:
149
+ method: post
150
+ uri: http://10.0.5.5:4243/containers/9f79b401cf57/start
151
+ body:
152
+ encoding: US-ASCII
153
+ string: ''
154
+ headers:
155
+ Content-Type:
156
+ - application/json
157
+ response:
158
+ status:
159
+ code: 204
160
+ message: !binary |-
161
+ Tm8gQ29udGVudA==
162
+ headers:
163
+ !binary "RGF0ZQ==":
164
+ - !binary |-
165
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNTozMSBHTVQ=
166
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
167
+ - !binary |-
168
+ Y2h1bmtlZA==
169
+ !binary "Q29udGVudC1UeXBl":
170
+ - !binary |-
171
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
172
+ body:
173
+ encoding: ASCII-8BIT
174
+ string: !binary ""
175
+ http_version:
176
+ recorded_at: Sat, 25 May 2013 16:05:34 GMT
177
+ - request:
178
+ method: post
179
+ uri: http://10.0.5.5:4243/containers/8fab97b08092/start
180
+ body:
181
+ encoding: US-ASCII
182
+ string: ''
183
+ headers:
184
+ Content-Type:
185
+ - application/json
186
+ response:
187
+ status:
188
+ code: 204
189
+ message: !binary |-
190
+ Tm8gQ29udGVudA==
191
+ headers:
192
+ !binary "RGF0ZQ==":
193
+ - !binary |-
194
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNTozMiBHTVQ=
195
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
196
+ - !binary |-
197
+ Y2h1bmtlZA==
198
+ !binary "Q29udGVudC1UeXBl":
199
+ - !binary |-
200
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
201
+ body:
202
+ encoding: ASCII-8BIT
203
+ string: !binary ""
204
+ http_version:
205
+ recorded_at: Sat, 25 May 2013 16:05:34 GMT
206
+ - request:
207
+ method: post
208
+ uri: http://10.0.5.5:4243/containers/3700641d93de/start
209
+ body:
210
+ encoding: US-ASCII
211
+ string: ''
212
+ headers:
213
+ Content-Type:
214
+ - application/json
215
+ response:
216
+ status:
217
+ code: 204
218
+ message: !binary |-
219
+ Tm8gQ29udGVudA==
220
+ headers:
221
+ !binary "RGF0ZQ==":
222
+ - !binary |-
223
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNTozMyBHTVQ=
224
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
225
+ - !binary |-
226
+ Y2h1bmtlZA==
227
+ !binary "Q29udGVudC1UeXBl":
228
+ - !binary |-
229
+ dGV4dC9wbGFpbjsgY2hhcnNldD11dGYtOA==
230
+ body:
231
+ encoding: ASCII-8BIT
232
+ string: !binary ""
233
+ http_version:
234
+ recorded_at: Sat, 25 May 2013 16:05:35 GMT
235
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,63 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: http://10.0.5.5:4243/containers/167951f02581/wait
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Content-Type:
11
+ - application/json
12
+ response:
13
+ status:
14
+ code: 200
15
+ message: !binary |-
16
+ T0s=
17
+ headers:
18
+ !binary "Q29udGVudC1UeXBl":
19
+ - !binary |-
20
+ YXBwbGljYXRpb24vanNvbg==
21
+ !binary "RGF0ZQ==":
22
+ - !binary |-
23
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNToyNCBHTVQ=
24
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
25
+ - !binary |-
26
+ Y2h1bmtlZA==
27
+ body:
28
+ encoding: ASCII-8BIT
29
+ string: !binary |-
30
+ eyJTdGF0dXNDb2RlIjowfQ==
31
+ http_version:
32
+ recorded_at: Sat, 25 May 2013 16:05:26 GMT
33
+ - request:
34
+ method: post
35
+ uri: http://10.0.5.5:4243/containers/8fab97b08092/wait
36
+ body:
37
+ encoding: US-ASCII
38
+ string: ''
39
+ headers:
40
+ Content-Type:
41
+ - application/json
42
+ response:
43
+ status:
44
+ code: 200
45
+ message: !binary |-
46
+ T0s=
47
+ headers:
48
+ !binary "Q29udGVudC1UeXBl":
49
+ - !binary |-
50
+ YXBwbGljYXRpb24vanNvbg==
51
+ !binary "RGF0ZQ==":
52
+ - !binary |-
53
+ U2F0LCAyNSBNYXkgMjAxMyAxNjowNTozMyBHTVQ=
54
+ !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
55
+ - !binary |-
56
+ Y2h1bmtlZA==
57
+ body:
58
+ encoding: ASCII-8BIT
59
+ string: !binary |-
60
+ eyJTdGF0dXNDb2RlIjowfQ==
61
+ http_version:
62
+ recorded_at: Sat, 25 May 2013 16:05:35 GMT
63
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docker::API do
4
+ subject { Docker::API.new(base_url: 'http://10.0.5.5:4243') }
5
+
6
+ it "provides a container resource" do
7
+ subject.containers.should be_kind_of(Docker::Resource::Container)
8
+ end
9
+
10
+ it "provides an image resource" do
11
+ subject.images.should be_kind_of(Docker::Resource::Image)
12
+ end
13
+
14
+ xit "provides a system resource" do
15
+
16
+ end
17
+
18
+ xit "provides a misc resource" do
19
+
20
+ end
21
+
22
+ it "sets up a connection object" do
23
+ subject.connection.should_not be_nil
24
+ subject.connection.should be_kind_of(Docker::Connection)
25
+ end
26
+
27
+ it "throws an error without a base_url configured" do
28
+ expect {
29
+ Docker::API.new({})
30
+ }.to raise_error(ArgumentError, ':base_url missing')
31
+ end
32
+
33
+
34
+ # Alternative syntax
35
+ # "Hello".should == 'Hello'
36
+ # expect("Hello").to eq("Hello")
37
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docker::Connection, :vcr do
4
+ subject { Docker::Connection.new(base_url: 'http://10.0.5.5:4243') }
5
+
6
+ it "throws an error without a base_url configured" do
7
+ expect {
8
+ Docker::Connection.new({})
9
+ }.to raise_error(ArgumentError, ':base_url missing')
10
+ end
11
+
12
+ it "sets given request headers" do
13
+ subject.get('/pseudo_request', {}, {'Content-Type' => 'application/json'})
14
+ WebMock.should have_requested(:get, "10.0.5.5:4243/pseudo_request").with(:headers => {'Content-Type' => 'application/json'})
15
+ end
16
+
17
+ it "sets given query parameters" do
18
+ subject.get('/pseudo_params', {first: 'argument', second: 'param'})
19
+ WebMock.should have_requested(:get, "10.0.5.5:4243/pseudo_params").with(:query => hash_including({'first' => 'argument', 'second' => 'param'}))
20
+ end
21
+
22
+ it "returns a valid response for a basic request" do
23
+ response = subject.send(:perform_request, :GET, '/containers/ps', {}, nil, {})
24
+ response.should be_kind_of(Docker::Connection::Response)
25
+ response.status.should == 200
26
+ response.content_type.should == "application/json"
27
+ response.body.should_not be_empty
28
+ end
29
+
30
+ it "returns status 404 for non existent path" do
31
+ response = subject.send(:perform_request, :GET, '/invalid_path', {}, nil, {})
32
+ response.status.should == 404
33
+ end
34
+
35
+ it "returns a valid response for get request" do
36
+ response = subject.get('/containers/ps?all=true', {})
37
+ response.status.should == 200
38
+ response.body.should_not be_empty
39
+ end
40
+
41
+ it "returns a valid response for post request" do
42
+ id = create_container('connection_post')
43
+ response = subject.post("/containers/#{id}/start", {}, '', {})
44
+ response.status.should == 204
45
+ response.body.should be_empty
46
+ # clean up
47
+ delete_containers(id)
48
+ end
49
+
50
+ it "raises an error for stream without block" do
51
+ expect {
52
+ subject.stream('/nothing')
53
+ }.to raise_error(ArgumentError, 'Block required to handle streaming response')
54
+ end
55
+
56
+ it "returns a stream", :live do
57
+ id = create_and_start_container(command: hello_world_command)
58
+ received_data = []
59
+ params = {stream: 1, stdout: 1}
60
+ timeout = 2
61
+
62
+ response = subject.stream("/containers/#{id}/attach", params, timeout, {}) do |data|
63
+ received_data << data
64
+ end
65
+
66
+ response.timeout.should be_true
67
+ response.status.should == 200
68
+ response.content_type.should == 'application/vnd.docker.raw-stream'
69
+ response.body.should be_nil
70
+
71
+ received_data.first.should == "hello world\n"
72
+ received_data.size.should >= 1
73
+
74
+ delete_containers(id)
75
+ end
76
+
77
+
78
+ # Alternative syntax
79
+ # "Hello".should == 'Hello'
80
+ # expect("Hello").to eq("Hello")
81
+ end
@@ -0,0 +1,288 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docker::Resource::Container do
4
+ subject { Docker::API.new(base_url: 'http://10.0.5.5:4243').containers }
5
+
6
+ describe "lists", :vcr do
7
+ before(:all) {
8
+ @c1 = create_and_start_container('container_lists1', command: hello_world_command)
9
+ @c2 = create_container('container_lists2')
10
+ }
11
+ after(:all) { delete_containers(@c1, @c2) }
12
+
13
+ it "all running containers" do
14
+ containers = subject.list
15
+ containers.should be_kind_of(Array)
16
+ containers.size.should >= 1
17
+ containers.first.should have_key("Id")
18
+ containers.first['Status'].should include('Up')
19
+ end
20
+
21
+ it "non-running processes too" do
22
+ containers = subject.list(all: true)
23
+ containers.size.should >= 2
24
+ containers.first['Status'].should include('Exit')
25
+ end
26
+
27
+ it "limit last created containers" do
28
+ containers = subject.list(limit: 1)
29
+ containers.size.should == 1
30
+ end
31
+
32
+ it "processes before a certain created container" do
33
+ containers = subject.list(before: @c2)
34
+ containers.first["Id"].should include(@c1)
35
+ end
36
+
37
+ it "processes since a certain created container" do
38
+ containers = subject.list(since: @c1)
39
+ containers.first["Id"].should include(@c2)
40
+ end
41
+ end
42
+
43
+ describe "create", :vcr do
44
+ after {
45
+ delete_containers(@container_id)
46
+ }
47
+
48
+ it "with minimal settings" do
49
+ status = subject.create(hello_world_command, 'base')
50
+ status.should be_kind_of(Hash)
51
+ status.should have_key('Id')
52
+ status.should have_key('Warnings')
53
+ status['Warnings'].should be_nil
54
+ @container_id = status['Id']
55
+ end
56
+
57
+ it "with many settings" do
58
+ options = {
59
+ 'Hostname' => 'test-container',
60
+ 'User' => 'test',
61
+ 'Memory' => 32.megabytes,
62
+ 'MemorySwap' => 64.megabytes,
63
+ 'CpuShares' => 1,
64
+ 'AttachStdin' => false,
65
+ 'AttachStdout' => true,
66
+ 'AttachStderr' => true,
67
+ 'PortSpecs' => ['80', '443'],
68
+ 'Tty' => false,
69
+ 'OpenStdin' => false,
70
+ 'StdinOnce' => false,
71
+ 'Env' => ['PORT=5000', 'ENVIRONTMENT=production'],
72
+ 'Dns' => nil,
73
+ 'Volumes' => {'/var/lib/app' => {}},
74
+ 'VolumesFrom' => ''
75
+ }
76
+ status = subject.create(['echo', 'hello world'], 'base', options)
77
+ status.should be_kind_of(Hash)
78
+ status.should have_key('Id')
79
+
80
+ # TODO Compare with show
81
+ @container_id = status['Id']
82
+ end
83
+
84
+ it "raises an exception when called with invalid options" do
85
+ options = {
86
+ 'PortSpecs' => '443',
87
+ }
88
+ expect {
89
+ subject.create(['echo', 'hello world'], 'base', options)
90
+ }.to raise_error(Docker::Error::InternalServerError)
91
+ end
92
+
93
+ end
94
+
95
+ describe "shows", :vcr do
96
+ before(:all) { @c = create_container('container_shows') }
97
+ after(:all) { delete_containers(@c)}
98
+
99
+ it "the low level details" do
100
+ details = subject.show(@c)
101
+ details.should be_kind_of(Hash)
102
+ details['Id'].should include(@c)
103
+ end
104
+ end
105
+
106
+ describe "changes", :vcr do
107
+ before(:all) {
108
+ @c = create_and_start_container('container_changes', command: ['touch', '/tmp/changes'], wait: true)
109
+ }
110
+ after(:all) { delete_containers(@c) }
111
+
112
+ it "inspects the container's filesystem changes" do
113
+ changes = subject.changes(@c)
114
+ changes.should be_kind_of(Array)
115
+ changes.any? {|c| c['path'] == '/tmp/changes'}
116
+ end
117
+ end
118
+
119
+ describe "export" do
120
+ # not yet implemented
121
+ end
122
+
123
+ describe "start", :vcr do
124
+ before(:all) { @c = create_container('container_start') }
125
+ after(:all) { delete_containers(@c)}
126
+
127
+ it "brings a container into state running" do
128
+ started = subject.start(@c)
129
+ started.should be_true
130
+ end
131
+
132
+ it "raises an exception for an unknown container" do
133
+ expect {
134
+ subject.start('invalid_id')
135
+ }.to raise_error(Docker::Error::ContainerNotFound)
136
+ end
137
+ end
138
+
139
+ describe "stop", :vcr do
140
+ before(:all) {
141
+ @c = create_and_start_container('container_stop', command: hello_world_command)
142
+ }
143
+ after(:all) { delete_containers(@c) }
144
+
145
+ it "halts a container" do
146
+ stopped = subject.stop(@c, 5)
147
+ stopped.should be_true
148
+ end
149
+
150
+ it "raises an exception for an unknown container" do
151
+ expect {
152
+ subject.stop('invalid_id')
153
+ }.to raise_error(Docker::Error::ContainerNotFound)
154
+ end
155
+ end
156
+
157
+ describe "restarts", :vcr do
158
+ before(:all) {
159
+ @c = create_and_start_container('container_restarts', command: hello_world_command)
160
+ }
161
+ after(:all) { delete_containers(@c) }
162
+
163
+ it "the container" do
164
+ status = subject.restart(@c, 3)
165
+ status.should be_true
166
+ end
167
+
168
+ it "raises an exception for an unknown container" do
169
+ expect {
170
+ subject.restart('invalid_id')
171
+ }.to raise_error(Docker::Error::ContainerNotFound)
172
+ end
173
+
174
+ end
175
+
176
+ describe "kill", :vcr do
177
+ before(:all) {
178
+ @c = create_and_start_container('container_kill', command: hello_world_command)
179
+ }
180
+ after(:all) { delete_containers(@c) }
181
+
182
+ it "the container" do
183
+ status = subject.kill(@c)
184
+ status.should be_true
185
+ end
186
+
187
+ it "raises an exception for an unknow container" do
188
+ expect {
189
+ subject.kill('invalid_id')
190
+ }.to raise_error(Docker::Error::ContainerNotFound)
191
+ end
192
+ end
193
+
194
+ describe "attach", :vcr, :live do
195
+ before(:all) {
196
+ @c = create_and_start_container('container_attach', command: hello_world_command)
197
+ }
198
+ after(:all) { delete_containers(@c) }
199
+
200
+ it "returns stdout/stderr by default as a stream" do
201
+ received_data = []
202
+ timeout = 3
203
+
204
+ response = subject.attach(@c, {}, timeout) do |data|
205
+ received_data << data
206
+ end
207
+
208
+ response.timeout.should be_true
209
+ response.status.should == 200
210
+ response.content_type.should == 'application/vnd.docker.raw-stream'
211
+ response.body.should be_nil
212
+
213
+ received_data.first.should == "hello world\n"
214
+ received_data.size.should >= 2
215
+ end
216
+
217
+ # TODO fails because docker always returns status 200
218
+ # See https://github.com/dotcloud/docker/issues/718
219
+ xit "raises an exception for an unknown container" do
220
+ expect {
221
+ subject.attach('invalid_id') { }
222
+ }.to raise_error(Docker::Error::ContainerNotFound)
223
+ end
224
+ end
225
+
226
+ describe "logs", :vcr do
227
+ before(:all) {
228
+ @c = create_and_start_container('container_logs', command: ['/bin/sh', '-c', 'echo stdout; echo stderr 1>&2'], wait: true)
229
+ }
230
+ after(:all) { delete_containers(@c) }
231
+
232
+ it "returns the stdout of a container" do
233
+ output = subject.logs(@c, {stdout: true})
234
+ output.should include("stdout")
235
+ end
236
+
237
+ it "returns the stderr of a container" do
238
+ output = subject.logs(@c, {stderr: true})
239
+ output.should include("stderr")
240
+ end
241
+
242
+ # TODO fails because docker always returns status 200
243
+ # See https://github.com/dotcloud/docker/issues/718
244
+ xit "raises an exception for an unknown container" do
245
+ expect {
246
+ subject.logs('invalid_id')
247
+ }.to raise_error(Docker::Error::ContainerNotFound)
248
+ end
249
+ end
250
+
251
+ describe "wait", :vcr do
252
+ before {
253
+ @c = create_and_start_container('container_wait', command: ['sleep', '3'])
254
+ }
255
+ after { delete_containers(@c) }
256
+
257
+ it "blocks until the container stops" do
258
+ status = subject.wait(@c)
259
+ status.should be_kind_of(Hash)
260
+ status.should have_key('StatusCode')
261
+ status['StatusCode'].should == 0
262
+ end
263
+
264
+ it "raises an exception for an unknown container" do
265
+ expect {
266
+ subject.wait('invalid_id')
267
+ }.to raise_error(Docker::Error::ContainerNotFound)
268
+ end
269
+ end
270
+
271
+ describe "remove", :vcr do
272
+ let(:container_id) {
273
+ create_container('container_remove', 'Volumes' => {'/var/lib/app' => {}})
274
+ }
275
+
276
+ it "deletes the container" do
277
+ status = subject.remove(container_id, true)
278
+ status.should be_true
279
+ end
280
+
281
+ it "raises an exception with an invalid container" do
282
+ expect {
283
+ subject.remove('invalid_ID')
284
+ }.to raise_error(Docker::Error::ContainerNotFound)
285
+ end
286
+ end
287
+
288
+ end