docker-api 1.6.0 → 1.7.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +0 -1
  3. data/README.md +6 -7
  4. data/docker-api.gemspec +3 -4
  5. data/lib/docker.rb +10 -5
  6. data/lib/docker/connection.rb +2 -0
  7. data/lib/docker/container.rb +39 -4
  8. data/lib/docker/error.rb +6 -0
  9. data/lib/docker/event.rb +40 -0
  10. data/lib/docker/image.rb +37 -13
  11. data/lib/docker/util.rb +25 -1
  12. data/lib/docker/version.rb +2 -2
  13. data/spec/docker/container_spec.rb +37 -19
  14. data/spec/docker/event_spec.rb +79 -0
  15. data/spec/docker/image_spec.rb +89 -30
  16. data/spec/docker/util_spec.rb +83 -0
  17. data/spec/docker_spec.rb +47 -2
  18. data/spec/fixtures/{Dockerfile → build_from_dir/Dockerfile} +0 -0
  19. data/spec/fixtures/top/Dockerfile +2 -0
  20. data/spec/support/vcr.rb +1 -4
  21. data/spec/vcr/Docker/_authenticate_/with_valid_credentials/logs_in_and_sets_the_creds.yml +33 -0
  22. data/spec/vcr/Docker/_info/returns_the_info_as_a_Hash.yml +7 -7
  23. data/spec/vcr/Docker/_validate_version/when_nothing_is_raised/validate_version_/.yml +6 -6
  24. data/spec/vcr/Docker/_version/returns_the_version_as_a_Hash.yml +6 -6
  25. data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +27 -97
  26. data/spec/vcr/Docker_Container/_attach/yields_each_chunk.yml +18 -19
  27. data/spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml +22 -22
  28. data/spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml +18 -18
  29. data/spec/vcr/Docker_Container/_commit/if_run_is_passed_it_saves_the_command_in_the_image/saves_the_command.yml +178 -0
  30. data/spec/vcr/Docker_Container/_copy/when_the_file_does_not_exist/raises_an_error.yml +54 -24
  31. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_directory/yields_each_chunk_of_the_tarred_directory.yml +27 -60
  32. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_file/yields_each_chunk_of_the_tarred_file.yml +27 -60
  33. data/spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml +7 -7
  34. data/spec/vcr/Docker_Container/_delete/deletes_the_container.yml +16 -46
  35. data/spec/vcr/Docker_Container/_export/yields_each_chunk.yml +24 -17
  36. data/spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml +14 -14
  37. data/spec/vcr/Docker_Container/_kill/kills_the_container.yml +36 -49
  38. data/spec/vcr/Docker_Container/_restart/restarts_the_container.yml +43 -43
  39. data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_does_not_return_status_code_of_0/raises_an_error.yml +17 -17
  40. data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_returns_a_status_code_of_0/creates_a_new_container_to_run_the_specified_command.yml +47 -47
  41. data/spec/vcr/Docker_Container/_start/starts_the_container.yml +25 -25
  42. data/spec/vcr/Docker_Container/_stop/stops_the_container.yml +40 -53
  43. data/spec/vcr/Docker_Container/_top/returns_the_top_commands_as_an_Array.yml +89 -25
  44. data/spec/vcr/Docker_Container/_wait/waits_for_the_command_to_finish.yml +17 -17
  45. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/and_a_command_runs_for_too_long/raises_a_ServerError.yml +12 -12
  46. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/sets_the_read_timeout_to_that_amount_of_time.yml +17 -17
  47. data/spec/vcr/Docker_Image/_all/materializes_each_Image_into_a_Docker_Image.yml +12 -12
  48. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/with_specifying_a_repo_in_the_query_parameters/builds_an_image_and_tags_it.yml +110 -0
  49. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/{builds_an_image.yml → without_query_parameters/builds_an_image.yml} +5 -5
  50. data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +7 -9
  51. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_no_query_parameters/builds_the_image.yml +121 -0
  52. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_specifying_a_repo_in_the_query_parameters/builds_the_image_and_tags_it.yml +151 -0
  53. data/spec/vcr/Docker_Image/_create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/sets_the_id.yml +5 -5
  54. data/spec/vcr/Docker_Image/_history/returns_the_history_of_the_Image.yml +10 -10
  55. data/spec/vcr/Docker_Image/_insert/inserts_the_url_s_file_into_a_new_Image.yml +53 -109
  56. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_exist/creates_a_new_Image_that_has_that_file.yml +32 -68
  57. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_not_exist/raises_an_error.yml +5 -5
  58. data/spec/vcr/Docker_Image/_insert_local/when_there_are_multiple_files_passed/creates_a_new_Image_that_has_each_file.yml +40 -68
  59. data/spec/vcr/Docker_Image/_json/returns_additional_information_about_image_image.yml +10 -10
  60. data/spec/vcr/Docker_Image/_push/pushes_the_Image.yml +161 -0
  61. data/spec/vcr/Docker_Image/_remove/removes_the_Image.yml +17 -17
  62. data/spec/vcr/Docker_Image/_run/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +23 -24
  63. data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +23 -24
  64. data/spec/vcr/Docker_Image/_run/when_the_argument_is_nil/command_configured_in_image/should_normally_show_result_if_image_has_Cmd_configured.yml +148 -0
  65. data/spec/vcr/Docker_Image/_run/when_the_argument_is_nil/no_command_configured_in_image/should_raise_an_error_if_no_command_is_specified.yml +68 -0
  66. data/spec/vcr/Docker_Image/_search/materializes_each_Image_into_a_Docker_Image.yml +13 -11
  67. data/spec/vcr/Docker_Image/_tag/tags_the_image_with_the_repo_name.yml +10 -10
  68. metadata +32 -24
@@ -0,0 +1,79 @@
1
+ require 'spec_helper'
2
+
3
+ describe Docker::Event do
4
+ describe "#to_s" do
5
+ subject { described_class.new(status, id, from, time) }
6
+
7
+ let(:status) { "start" }
8
+ let(:id) { "398c9f77b5d2" }
9
+ let(:from) { "base:latest" }
10
+ let(:time) { 1381956164 }
11
+
12
+ let(:expected_string) {
13
+ "Docker::Event { :status => #{status}, :id => #{id}, "\
14
+ ":from => #{from}, :time => #{time.to_s} }"
15
+ }
16
+
17
+ it "equals the expected string" do
18
+ expect(subject.to_s).to eq(expected_string)
19
+ end
20
+ end
21
+
22
+ describe ".stream" do
23
+ it 'receives three events', :vcr do
24
+ pending "get VCR to record events that break"
25
+ Docker::Event.should_receive(:new_event).exactly(3).times
26
+ .and_call_original
27
+ fork do
28
+ sleep 1
29
+ Docker::Image.create('fromImage' => 'base').run('bash')
30
+ end
31
+ Docker::Event.stream do |event|
32
+ puts "#{event}"
33
+ if event.status == "die"
34
+ break
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ describe ".since" do
41
+ let!(:time) { Time.now.to_i }
42
+
43
+ it 'receives three events', :vcr do
44
+ pending "get VCR to record events that break"
45
+ Docker::Event.should_receive(:new_event).exactly(3).times
46
+ .and_call_original
47
+ fork do
48
+ sleep 1
49
+ Docker::Image.create('fromImage' => 'base').run('bash')
50
+ end
51
+ Docker::Event.since(time) do |event|
52
+ puts "#{event}"
53
+ if event.status == "die"
54
+ break
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ describe ".new_event" do
61
+ subject { Docker::Event.new_event(response_body, nil, nil) }
62
+ let(:status) { "start" }
63
+ let(:id) { "398c9f77b5d2" }
64
+ let(:from) { "base:latest" }
65
+ let(:time) { 1381956164 }
66
+ let(:response_body) {
67
+ "{\"status\":\"#{status}\",\"id\":\"#{id}\""\
68
+ ",\"from\":\"#{from}\",\"time\":#{time}}"
69
+ }
70
+
71
+ it "returns a Docker::Event" do
72
+ expect(subject).to be_kind_of(Docker::Event)
73
+ expect(subject.status).to eq(status)
74
+ expect(subject.id).to eq(id)
75
+ expect(subject.from).to eq(from)
76
+ expect(subject.time).to eq(time)
77
+ end
78
+ end
79
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Docker::Image do
4
4
  describe '#to_s' do
5
- subject { described_class.send(:new, Docker.connection, id) }
5
+ subject { described_class.new(Docker.connection, id, info) }
6
6
 
7
7
  let(:id) { 'bf119e2' }
8
8
  let(:connection) { Docker.connection }
@@ -17,14 +17,6 @@ describe Docker::Image do
17
17
  ":connection => #{connection} }"
18
18
  end
19
19
 
20
- before do
21
- {
22
- :@id => id,
23
- :@connection => connection,
24
- :@info => info
25
- }.each { |k, v| subject.instance_variable_set(k, v) }
26
- end
27
-
28
20
  its(:to_s) { should == expected_string }
29
21
  end
30
22
 
@@ -42,10 +34,10 @@ describe Docker::Image do
42
34
  subject { described_class.build('from base') }
43
35
  let(:new_image) { subject.insert(:path => '/stallman',
44
36
  :url => 'http://stallman.org') }
45
- let(:ls_output) { new_image.run('ls /').attach.split("\n") }
37
+ let(:ls_output) { new_image.run('ls /').attach }
46
38
 
47
39
  it 'inserts the url\'s file into a new Image', :vcr do
48
- ls_output.should include('stallman')
40
+ expect(ls_output.first.first).to include('stallman')
49
41
  end
50
42
  end
51
43
 
@@ -69,9 +61,11 @@ describe Docker::Image do
69
61
  let(:gemfile) { File.read('Gemfile') }
70
62
 
71
63
  it 'creates a new Image that has that file', :vcr do
72
- new_image.run('cat /Gemfile').start.attach { |chunk|
73
- chunk.should == gemfile
64
+ chunk = nil
65
+ new_image.run('cat /Gemfile').attach { |stream, c|
66
+ chunk ||= c
74
67
  }
68
+ expect(chunk).to eq(gemfile)
75
69
  end
76
70
  end
77
71
 
@@ -79,21 +73,38 @@ describe Docker::Image do
79
73
  let(:file) { ['./Gemfile', './Rakefile'] }
80
74
  let(:gemfile) { File.read('Gemfile') }
81
75
  let(:rakefile) { File.read('Rakefile') }
76
+ let(:response) {
77
+ new_image.run('cat /Gemfile /Rakefile').attach
78
+ }
82
79
 
83
80
  it 'creates a new Image that has each file', :vcr do
84
- new_image.run('cat /Gemfile /Rakefile').start.attach do |chunk|
85
- chunk.should == gemfile + rakefile
86
- end
81
+ expect(response).to eq([[gemfile, rakefile],[]])
87
82
  end
88
83
  end
89
84
  end
90
85
 
91
86
  describe '#push' do
92
87
  subject { described_class.create('fromImage' => 'base') }
88
+ let(:credentials) {
89
+ {
90
+ :username => 'test',
91
+ :password => 'test',
92
+ :auth => '',
93
+ :email => 'test@test.com'
94
+ }
95
+ }
96
+ let(:base_image) {
97
+ described_class.create('fromImage' => 'base')
98
+ }
99
+ let(:container) {
100
+ base_image.run('true')
101
+ }
102
+ let(:new_image) {
103
+ container.commit('repo' => 'test/base')
104
+ }
93
105
 
94
106
  it 'pushes the Image', :vcr do
95
- pending 'I don\'t want to push the Image to the Docker Registry'
96
- subject.push
107
+ new_image.push(credentials)
97
108
  end
98
109
  end
99
110
 
@@ -134,7 +145,7 @@ describe Docker::Image do
134
145
  context 'when the argument is a String', :vcr do
135
146
  let(:cmd) { 'ls /lib64/' }
136
147
  it 'splits the String by spaces and creates a new Container' do
137
- output.should == "ld-linux-x86-64.so.2\n"
148
+ expect(output).to eq([["ld-linux-x86-64.so.2\n"],[]])
138
149
  end
139
150
  end
140
151
 
@@ -142,7 +153,26 @@ describe Docker::Image do
142
153
  let(:cmd) { %[which pwd] }
143
154
 
144
155
  it 'creates a new Container', :vcr do
145
- output.should == "/bin/pwd\n"
156
+ expect(output).to eq([["/bin/pwd\n"],[]])
157
+ end
158
+ end
159
+
160
+ context 'when the argument is nil', :vcr do
161
+ let(:cmd) { nil }
162
+ context 'no command configured in image'do
163
+ it 'should raise an error if no command is specified' do
164
+ expect {output}.to raise_error(Docker::Error::ServerError,
165
+ "No command specified.")
166
+ end
167
+ end
168
+
169
+ context "command configured in image" do
170
+ let(:container) {Docker::Container.create('Cmd' => %w[true],
171
+ 'Image' => 'base')}
172
+ subject { container.commit('run' => {"Cmd" => %w[pwd]}) }
173
+ it 'should normally show result if image has Cmd configured' do
174
+ expect(output).to eql [["/\n"],[]]
175
+ end
146
176
  end
147
177
  end
148
178
  end
@@ -229,12 +259,28 @@ describe Docker::Image do
229
259
  end
230
260
 
231
261
  context 'with a valid Dockerfile' do
232
- let(:image) { subject.build("from base\n") }
262
+ context 'without query parameters' do
263
+ let(:image) { subject.build("from base\n") }
233
264
 
234
- it 'builds an image', :vcr do
235
- image.should be_a Docker::Image
236
- image.id.should_not be_nil
237
- image.connection.should be_a Docker::Connection
265
+ it 'builds an image', :vcr do
266
+ expect(image).to be_a Docker::Image
267
+ expect(image.id).to_not be_nil
268
+ expect(image.connection).to be_a Docker::Connection
269
+ end
270
+ end
271
+
272
+ context 'with specifying a repo in the query parameters' do
273
+ let(:image) {
274
+ subject.build("from base\nrun true\n", "t" => "swipely/base")
275
+ }
276
+ let(:images) { subject.all }
277
+
278
+ it 'builds an image and tags it', :vcr do
279
+ expect(image).to be_a Docker::Image
280
+ expect(image.id).to_not be_nil
281
+ expect(image.connection).to be_a Docker::Connection
282
+ expect(images.first.info["Repository"]).to eq("swipely/base")
283
+ end
238
284
  end
239
285
  end
240
286
  end
@@ -243,19 +289,32 @@ describe Docker::Image do
243
289
  subject { described_class }
244
290
 
245
291
  context 'with a valid Dockerfile' do
246
- let(:dir) { File.join(File.dirname(__FILE__), '..', 'fixtures') }
292
+ let(:dir) {
293
+ File.join(File.dirname(__FILE__), '..', 'fixtures', 'build_from_dir')
294
+ }
247
295
  let(:docker_file) { File.new("#{dir}/Dockerfile") }
248
- let(:image) { subject.build_from_dir(dir) }
296
+ let(:image) { subject.build_from_dir(dir, opts) }
297
+ let(:opts) { {} }
249
298
  let(:container) do
250
299
  Docker::Container.create('Image' => image.id,
251
300
  'Cmd' => %w[cat /Dockerfile])
252
301
  end
253
302
  let(:output) { container.tap(&:start)
254
303
  .attach(:stderr => true) }
304
+ let(:images) { subject.all }
255
305
 
256
- it 'builds the image', :vcr do
257
- pending 'webmock / vcr issue'
258
- output.should == docker_file.tap(&:rewind).read
306
+ context 'with no query parameters' do
307
+ it 'builds the image', :vcr do
308
+ expect(output).to eq([[docker_file.read],[]])
309
+ end
310
+ end
311
+
312
+ context 'with specifying a repo in the query parameters' do
313
+ let(:opts) { { "t" => "swipely/base2" } }
314
+ it 'builds the image and tags it', :vcr do
315
+ expect(output).to eq([[docker_file.read],[]])
316
+ expect(images.first.info["Repository"]).to eq("swipely/base2")
317
+ end
259
318
  end
260
319
  end
261
320
  end
@@ -40,4 +40,87 @@ describe Docker::Util do
40
40
  end
41
41
  end
42
42
  end
43
+
44
+ describe '.build_auth_header' do
45
+ subject { described_class }
46
+
47
+ let(:credentials) {
48
+ {
49
+ :username => 'test',
50
+ :password => 'password',
51
+ :email => 'test@example.com',
52
+ :serveraddress => 'https://registry.com/'
53
+ }
54
+ }
55
+ let(:credential_string) { credentials.to_json }
56
+ let(:x_registry_auth) { Base64.encode64(credential_string).gsub(/\n/, '') }
57
+ let(:expected_headers) { { 'X-Registry-Auth' => x_registry_auth } }
58
+
59
+
60
+ context 'given credentials as a Hash' do
61
+ it 'returns an X-Registry-Auth header encoded' do
62
+ expect(subject.build_auth_header(credentials)).to eq(expected_headers)
63
+ end
64
+ end
65
+
66
+ context 'given credentials as a String' do
67
+ it 'returns an X-Registry-Auth header encoded' do
68
+ expect(
69
+ subject.build_auth_header(credential_string)
70
+ ).to eq(expected_headers)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#decipher_messages' do
76
+ context 'given both standard out and standard error' do
77
+ let(:raw_text) {
78
+ "\x01\x00\x00\x00\x00\x00\x00\x01a\x02\x00\x00\x00\x00\x00\x00\x01b"
79
+ }
80
+ let(:expected_messages) { [["a"], ["b"]] }
81
+
82
+ it "returns a single message" do
83
+ expect(
84
+ Docker::Util.decipher_messages(raw_text)
85
+ ).to eq(expected_messages)
86
+ end
87
+ end
88
+
89
+ context 'given a single header' do
90
+ let(:raw_text) { "\x01\x00\x00\x00\x00\x00\x00\x01a" }
91
+ let(:expected_messages) { [["a"], []] }
92
+
93
+ it "returns a single message" do
94
+ expect(
95
+ Docker::Util.decipher_messages(raw_text)
96
+ ).to eq(expected_messages)
97
+ end
98
+ end
99
+
100
+ context 'given two headers' do
101
+ let(:raw_text) {
102
+ "\x01\x00\x00\x00\x00\x00\x00\x01a\x01\x00\x00\x00\x00\x00\x00\x01b"
103
+ }
104
+ let(:expected_messages) { [["a", "b"], []] }
105
+
106
+ it "returns both messages" do
107
+ expect(
108
+ Docker::Util.decipher_messages(raw_text)
109
+ ).to eq(expected_messages)
110
+ end
111
+ end
112
+
113
+ context 'given a header for text longer then 255 characters' do
114
+ let(:raw_text) {
115
+ "\x01\x00\x00\x00\x00\x00\x01\x01" + ("a" * 257)
116
+ }
117
+ let(:expected_messages) { [[("a" * 257)], []] }
118
+
119
+ it "returns both messages" do
120
+ expect(
121
+ Docker::Util.decipher_messages(raw_text)
122
+ ).to eq(expected_messages)
123
+ end
124
+ end
125
+ end
43
126
  end
data/spec/docker_spec.rb CHANGED
@@ -87,8 +87,53 @@ describe Docker do
87
87
  end
88
88
 
89
89
  describe '#authenticate!' do
90
- it 'logs in' do
91
- pending
90
+ subject { described_class }
91
+
92
+ let(:authentication) {
93
+ subject.authenticate!(credentials)
94
+ }
95
+
96
+ after do
97
+ Docker.creds = nil
98
+ end
99
+
100
+ context 'with valid credentials' do
101
+ # Used valid credentials to record VCR and then changed
102
+ # cassette to match these credentials
103
+ let(:credentials) {
104
+ {
105
+ :username => 'test',
106
+ :password => 'account',
107
+ :email => 'test@test.com',
108
+ :serveraddress => 'https://index.docker.io/v1/'
109
+ }
110
+ }
111
+
112
+ it 'logs in and sets the creds', :vcr do
113
+ expect(authentication).to be_true
114
+ expect(Docker.creds).to eq(credentials.to_json)
115
+ end
116
+ end
117
+
118
+ context 'with invalid credentials' do
119
+ # Recorded the VCR with these credentials
120
+ # to purposely fail
121
+ let(:credentials) {
122
+ {
123
+ :username => 'test',
124
+ :password => 'password',
125
+ :email => 'test@example.com',
126
+ :serveraddress => 'https://index.docker.io/v1/'
127
+ }
128
+ }
129
+
130
+ it "raises an error and doesn't set the creds", :vcr do
131
+ pending "VCR won't record when Excon::Expects fail"
132
+ expect {
133
+ authentication
134
+ }.to raise_error(Docker::Error::AuthenticationError)
135
+ expect(Docker.creds).to be_nil
136
+ end
92
137
  end
93
138
  end
94
139
 
@@ -0,0 +1,2 @@
1
+ from base
2
+ run printf '#! /bin/sh\nwhile true\ndo\ntrue\ndone\n' > /while && chmod +x /while
data/spec/support/vcr.rb CHANGED
@@ -1,11 +1,8 @@
1
- require 'webmock'
2
1
  require 'vcr'
3
2
 
4
- WebMock.disable_net_connect!
5
-
6
3
  VCR.configure do |c|
7
4
  c.allow_http_connections_when_no_cassette = false
8
- c.hook_into :webmock
5
+ c.hook_into :excon
9
6
  c.cassette_library_dir = File.join(File.dirname(__FILE__), '..', 'vcr')
10
7
  c.configure_rspec_metadata!
11
8
  end
@@ -0,0 +1,33 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: unix:///var/run/docker.sock/v1.6/auth
6
+ body:
7
+ encoding: UTF-8
8
+ string: ! '{"username":"test","password":"account","email":"test@test.com","serveraddress":"https://index.docker.io/v1/"}'
9
+ headers:
10
+ User-Agent:
11
+ - Swipely/Docker-API 1.6.0
12
+ Content-Type:
13
+ - application/json
14
+ response:
15
+ status:
16
+ code: 200
17
+ message:
18
+ headers:
19
+ !binary "Q29udGVudC1UeXBl":
20
+ - !binary |-
21
+ YXBwbGljYXRpb24vanNvbg==
22
+ !binary "Q29udGVudC1MZW5ndGg=":
23
+ - !binary |-
24
+ Mjg=
25
+ !binary "RGF0ZQ==":
26
+ - !binary |-
27
+ RnJpLCAyNSBPY3QgMjAxMyAxNjoyMzo1NSBHTVQ=
28
+ body:
29
+ encoding: US-ASCII
30
+ string: ! '{"Status":"Login Succeeded"}'
31
+ http_version:
32
+ recorded_at: Fri, 25 Oct 2013 16:23:55 GMT
33
+ recorded_with: VCR 2.6.0