docker-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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