docker-client 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/MIT-LICENSE +21 -0
- data/README.md +87 -0
- data/Rakefile +15 -0
- data/docker.gemspec +40 -0
- data/lib/docker.rb +10 -0
- data/lib/docker/api.rb +32 -0
- data/lib/docker/connection.rb +83 -0
- data/lib/docker/error.rb +9 -0
- data/lib/docker/error/bad_parameter_error.rb +7 -0
- data/lib/docker/error/container_not_found.rb +7 -0
- data/lib/docker/error/internal_server_error.rb +7 -0
- data/lib/docker/error/not_found_error.rb +7 -0
- data/lib/docker/resource.rb +8 -0
- data/lib/docker/resource/base.rb +23 -0
- data/lib/docker/resource/container.rb +131 -0
- data/lib/docker/resource/image.rb +10 -0
- data/lib/docker/version.rb +3 -0
- data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_a_basic_request.yml +40 -0
- data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_get_request.yml +105 -0
- data/spec/cassettes/Docker_Connection/returns_a_valid_response_for_post_request.yml +30 -0
- data/spec/cassettes/Docker_Connection/returns_status_404_for_non_existent_path.yml +31 -0
- data/spec/cassettes/Docker_Connection/sets_given_query_parameters.yml +31 -0
- data/spec/cassettes/Docker_Connection/sets_given_request_headers.yml +33 -0
- data/spec/cassettes/Docker_Resource_Container/changes/inspects_the_container_s_filesystem_changes.yml +33 -0
- data/spec/cassettes/Docker_Resource_Container/create/raises_an_exception_when_called_with_invalid_options.yml +34 -0
- data/spec/cassettes/Docker_Resource_Container/create/with_many_settings.yml +36 -0
- data/spec/cassettes/Docker_Resource_Container/create/with_minimal_settings.yml +34 -0
- data/spec/cassettes/Docker_Resource_Container/kill/raises_an_exception_for_an_unknow_container.yml +31 -0
- data/spec/cassettes/Docker_Resource_Container/kill/the_container.yml +30 -0
- data/spec/cassettes/Docker_Resource_Container/lists/all_running_containers.yml +46 -0
- data/spec/cassettes/Docker_Resource_Container/lists/limit_last_created_containers.yml +34 -0
- data/spec/cassettes/Docker_Resource_Container/lists/non-running_processes_too.yml +114 -0
- data/spec/cassettes/Docker_Resource_Container/lists/processes_before_a_certain_created_container.yml +110 -0
- data/spec/cassettes/Docker_Resource_Container/lists/processes_since_a_certain_created_container.yml +34 -0
- data/spec/cassettes/Docker_Resource_Container/logs/returns_the_stderr_of_a_container.yml +25 -0
- data/spec/cassettes/Docker_Resource_Container/logs/returns_the_stdout_of_a_container.yml +25 -0
- data/spec/cassettes/Docker_Resource_Container/remove/deletes_the_container.yml +30 -0
- data/spec/cassettes/Docker_Resource_Container/remove/raises_an_exception_with_an_invalid_container.yml +31 -0
- data/spec/cassettes/Docker_Resource_Container/restarts/raises_an_exception_for_an_unknown_container.yml +31 -0
- data/spec/cassettes/Docker_Resource_Container/restarts/the_container.yml +30 -0
- data/spec/cassettes/Docker_Resource_Container/shows/the_low_level_details.yml +48 -0
- data/spec/cassettes/Docker_Resource_Container/start/brings_a_container_into_state_running.yml +30 -0
- data/spec/cassettes/Docker_Resource_Container/start/raises_an_exception_for_an_unknown_container.yml +31 -0
- data/spec/cassettes/Docker_Resource_Container/stop/halts_a_container.yml +30 -0
- data/spec/cassettes/Docker_Resource_Container/stop/raises_an_exception_for_an_unknown_container.yml +31 -0
- data/spec/cassettes/Docker_Resource_Container/wait/blocks_until_the_container_stops.yml +31 -0
- data/spec/cassettes/Docker_Resource_Container/wait/raises_an_exception_for_an_unknown_container.yml +31 -0
- data/spec/cassettes/test_setup/create_container_connection_post.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_changes.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_kill.yml +34 -0
- data/spec/cassettes/test_setup/create_container_container_lists1.yml +34 -0
- data/spec/cassettes/test_setup/create_container_container_lists2.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_logs.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_remove.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_restarts.yml +34 -0
- data/spec/cassettes/test_setup/create_container_container_shows.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_start.yml +33 -0
- data/spec/cassettes/test_setup/create_container_container_stop.yml +34 -0
- data/spec/cassettes/test_setup/create_container_container_wait.yml +33 -0
- data/spec/cassettes/test_setup/delete_container.yml +405 -0
- data/spec/cassettes/test_setup/start_container.yml +235 -0
- data/spec/cassettes/test_setup/wait_on_container.yml +63 -0
- data/spec/docker/api_spec.rb +37 -0
- data/spec/docker/connection_spec.rb +81 -0
- data/spec/docker/resource/container_spec.rb +288 -0
- data/spec/helpers.rb +48 -0
- data/spec/spec_helper.rb +31 -0
- 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
|