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.
- 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
|