docker-api 1.15.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -1
  3. data/Rakefile +37 -0
  4. data/TESTING.md +61 -0
  5. data/lib/docker.rb +8 -1
  6. data/lib/docker/container.rb +44 -80
  7. data/lib/docker/exec.rb +98 -0
  8. data/lib/docker/image.rb +4 -3
  9. data/lib/docker/util.rb +79 -0
  10. data/lib/docker/version.rb +2 -2
  11. data/spec/docker/container_spec.rb +192 -56
  12. data/spec/docker/event_spec.rb +4 -4
  13. data/spec/docker/exec_spec.rb +197 -0
  14. data/spec/docker/image_spec.rb +131 -102
  15. data/spec/docker_spec.rb +37 -27
  16. data/spec/fixtures/build_from_dir/Dockerfile +2 -2
  17. data/spec/fixtures/export.tar +0 -0
  18. data/spec/fixtures/top/Dockerfile +2 -2
  19. data/spec/spec_helper.rb +10 -0
  20. data/spec/support/vcr.rb +5 -0
  21. data/spec/vcr/Docker/_authenticate_/with_valid_credentials/logs_in_and_sets_the_creds.yml +6 -8
  22. data/spec/vcr/Docker/_info/returns_the_info_as_a_Hash.yml +7 -9
  23. data/spec/vcr/Docker/_validate_version/when_nothing_is_raised/validate_version_/.yml +7 -9
  24. data/spec/vcr/Docker/_version/returns_the_version_as_a_Hash.yml +7 -9
  25. data/spec/vcr/Docker_Container/_all/when_the_HTTP_response_is_a_200/materializes_each_Container_into_a_Docker_Container.yml +46 -139
  26. data/spec/vcr/Docker_Container/_attach/with_normal_sized_chunks/yields_each_chunk.yml +62 -20
  27. data/spec/vcr/Docker_Container/_attach/with_very_small_chunks/yields_each_chunk.yml +62 -20
  28. data/spec/vcr/Docker_Container/_changes/returns_the_changes_as_an_array.yml +71 -28
  29. data/spec/vcr/Docker_Container/_commit/creates_a_new_Image_from_the_Container_s_changes.yml +69 -23
  30. data/spec/vcr/Docker_Container/_copy/when_the_file_does_not_exist/raises_an_error.yml +134 -0
  31. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_directory/yields_each_chunk_of_the_tarred_directory.yml +73 -290
  32. data/spec/vcr/Docker_Container/_copy/when_the_input_is_a_file/yields_each_chunk_of_the_tarred_file.yml +51 -211
  33. data/spec/vcr/Docker_Container/_create/when_creating_a_container_named_bob/should_have_name_set_to_bob.yml +36 -17
  34. data/spec/vcr/Docker_Container/_create/when_the_Container_does_not_yet_exist/when_the_HTTP_request_returns_a_200/sets_the_id.yml +30 -9
  35. data/spec/vcr/Docker_Container/_delete/deletes_the_container.yml +17 -23
  36. data/spec/vcr/Docker_Container/_exec/when_detach_is_true/returns_the_Docker_Exec_object.yml +151 -0
  37. data/spec/vcr/Docker_Container/_exec/when_passed_a_block/streams_the_stdout/stderr_messages.yml +153 -0
  38. data/spec/vcr/Docker_Container/_exec/when_passed_only_a_command/returns_the_stdout/stderr_messages.yml +153 -0
  39. data/spec/vcr/Docker_Container/_exec/when_stdin_object_is_passed/returns_the_stdout/stderr_messages.yml +100 -0
  40. data/spec/vcr/Docker_Container/_exec/when_tty_is_true/returns_the_raw_stdout/stderr_output.yml +152 -0
  41. data/spec/vcr/Docker_Container/_export/yields_each_chunk.yml +263 -28
  42. data/spec/vcr/Docker_Container/_get/when_the_HTTP_response_is_a_200/materializes_the_Container_into_a_Docker_Container.yml +36 -17
  43. data/spec/vcr/Docker_Container/_json/returns_the_description_as_a_Hash.yml +36 -17
  44. data/spec/vcr/Docker_Container/_kill/kills_the_container.yml +55 -53
  45. data/spec/vcr/Docker_Container/_kill/with_a_kill_signal/kills_the_container.yml +74 -96
  46. data/spec/vcr/Docker_Container/_logs/when_not_selecting_any_stream/raises_a_client_error.yml +84 -81
  47. data/spec/vcr/Docker_Container/_logs/when_selecting_stdout/returns_blank_logs.yml +34 -15
  48. data/spec/vcr/Docker_Container/_pause/pauses_the_container.yml +98 -45
  49. data/spec/vcr/Docker_Container/_restart/restarts_the_container.yml +88 -56
  50. data/spec/vcr/Docker_Container/_run/when_the_Container_s_command_does_not_return_status_code_of_0/raises_an_error.yml +39 -22
  51. 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 +212 -60
  52. data/spec/vcr/Docker_Container/_start/starts_the_container.yml +45 -30
  53. data/spec/vcr/Docker_Container/_stop/stops_the_container.yml +54 -83
  54. data/spec/vcr/Docker_Container/_streaming_logs/when_not_selecting_any_stream/raises_a_client_error.yml +84 -81
  55. data/spec/vcr/Docker_Container/_streaming_logs/when_selecting_stdout/returns_blank_logs.yml +46 -30
  56. data/spec/vcr/Docker_Container/_top/returns_the_top_commands_as_an_Array.yml +103 -40
  57. data/spec/vcr/Docker_Container/_unpause/unpauses_the_container.yml +81 -57
  58. data/spec/vcr/Docker_Container/_wait/waits_for_the_command_to_finish.yml +39 -22
  59. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/sets_the_read_timeout_to_that_amount_of_time.yml +39 -22
  60. data/spec/vcr/Docker_Exec/_create/when_the_HTTP_request_returns_a_201/sets_the_id.yml +128 -0
  61. data/spec/vcr/Docker_Exec/_resize/when_exec_instance_has_TTY_enabled/returns_a_200.yml +155 -0
  62. data/spec/vcr/Docker_Exec/_start_/when_detach_is_set_to_false/block_is_passed/attaches_to_the_stream.yml +152 -0
  63. data/spec/vcr/Docker_Exec/_start_/when_detach_is_set_to_false/returns_the_stdout_and_stderr_messages.yml +152 -0
  64. data/spec/vcr/Docker_Exec/_start_/when_detach_is_set_to_true/returns_empty_stdout_and_stderr_messages.yml +151 -0
  65. data/spec/vcr/Docker_Exec/_start_/when_the_HTTP_request_returns_a_201/starts_the_exec_instance.yml +151 -0
  66. data/spec/vcr/Docker_Exec/_start_/when_the_command_has_already_run/raises_an_error.yml +151 -0
  67. data/spec/vcr/Docker_Image/_all/materializes_each_Image_into_a_Docker_Image.yml +86 -200
  68. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/with_a_block_capturing_build_output/calls_the_block_and_passes_build_output.yml +10 -14
  69. 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 +147 -39
  70. data/spec/vcr/Docker_Image/_build/with_a_valid_Dockerfile/without_query_parameters/builds_an_image.yml +10 -14
  71. data/spec/vcr/Docker_Image/_build/with_an_invalid_Dockerfile/throws_a_UnexpectedResponseError.yml +12 -14
  72. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_a_block_capturing_build_output/calls_the_block_and_passes_build_output.yml +118 -16
  73. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_credentials_passed/sends_X-Registry-Config_header.yml +118 -14
  74. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_no_query_parameters/builds_the_image.yml +109 -37
  75. 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 +214 -60
  76. data/spec/vcr/Docker_Image/_create/when_the_Image_does_not_yet_exist_and_the_body_is_a_Hash/sets_the_id_and_sends_Docker_creds.yml +41 -2631
  77. data/spec/vcr/Docker_Image/_exist_/when_the_image_does_exist/returns_true.yml +12 -19
  78. data/spec/vcr/Docker_Image/_get/when_the_image_does_exist/returns_the_new_image.yml +7 -9
  79. data/spec/vcr/Docker_Image/_history/returns_the_history_of_the_Image.yml +17 -198
  80. data/spec/vcr/Docker_Image/_import/when_the_argument_is_a_URI/when_the_URI_is_invalid/raises_an_error.yml +26 -30
  81. data/spec/vcr/Docker_Image/_import/when_the_argument_is_a_URI/when_the_URI_is_valid/returns_an_Image.yml +246 -22
  82. data/spec/vcr/Docker_Image/_import/when_the_file_does_exist/creates_the_Image.yml +35 -10
  83. data/spec/vcr/Docker_Image/_insert_local/when_a_direcory_is_passed/inserts_the_directory.yml +1249 -74
  84. data/spec/vcr/Docker_Image/_insert_local/when_removing_intermediate_containers/creates_a_new_image.yml +84 -94
  85. data/spec/vcr/Docker_Image/_insert_local/when_removing_intermediate_containers/leave_no_intermediate_containers.yml +70 -92
  86. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_exist/creates_a_new_Image_that_has_that_file.yml +125 -98
  87. data/spec/vcr/Docker_Image/_insert_local/when_the_local_file_does_not_exist/raises_an_error.yml +13 -60
  88. data/spec/vcr/Docker_Image/_insert_local/when_there_are_multiple_files_passed/creates_a_new_Image_that_has_each_file.yml +190 -135
  89. data/spec/vcr/Docker_Image/_json/returns_additional_information_about_image_image.yml +15 -198
  90. data/spec/vcr/Docker_Image/_push/pushes_the_Image.yml +169 -264
  91. data/spec/vcr/Docker_Image/_push/when_there_are_no_credentials/still_pushes.yml +175 -4543
  92. data/spec/vcr/Docker_Image/_refresh_/updates_the_info_hash.yml +90 -206
  93. data/spec/vcr/Docker_Image/_remove/when_no_name_is_given/removes_the_Image.yml +300 -0
  94. data/spec/vcr/Docker_Image/_run/when_the_argument_is_a_String/splits_the_String_by_spaces_and_creates_a_new_Container.yml +77 -207
  95. data/spec/vcr/Docker_Image/_run/when_the_argument_is_an_Array/creates_a_new_Container.yml +77 -207
  96. 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 +160 -0
  97. 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 +16 -197
  98. data/spec/vcr/Docker_Image/_search/materializes_each_Image_into_a_Docker_Image.yml +151 -73
  99. data/spec/vcr/Docker_Image/_tag/tags_the_image_with_the_repo_name.yml +42 -196
  100. metadata +67 -41
  101. data/spec/vcr/Docker_Container/_commit/if_run_is_passed_it_saves_the_command_in_the_image/saves_the_command.yml +0 -58
  102. data/spec/vcr/Docker_Container/_streaming_logs/when_not_selecting_any_stream/returns_the_error_message.yml +0 -163
  103. data/spec/vcr/Docker_Container/_wait/when_an_argument_is_given/and_a_command_runs_for_too_long/raises_a_ServerError.yml +0 -58
  104. data/spec/vcr/Docker_Image/_build_from_dir/with_a_valid_Dockerfile/with_credentials_passed/sends_Docker_creds.yml +0 -41
  105. data/spec/vcr/Docker_Image/_remove/removes_the_Image.yml +0 -276
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 026147324341d46ecc633b106fa941316435e612
4
- data.tar.gz: c9022722fa623436a9a7f5b5ece2383c946eb21d
3
+ metadata.gz: d13e05e91b0dbfbae522e12e17e7a6785347f5b7
4
+ data.tar.gz: 83e0f7511f3e284747d4cabbbb3a69cf701ed5d4
5
5
  SHA512:
6
- metadata.gz: 0361890e0d8bd23b86c4b1cc362a4f7d5f327222ea0261c8a1159daaa331f5b7b90d5a93e8142558fd2e514b5d22ff7481fa80b60fc6fb64a55c6868e6bff1b6
7
- data.tar.gz: 4d22cb2d2fb45120b9a7485e4be4e8a35b56085287c397ae624be1d6c6036acfe4cbb57879afa096fa8089d3e03524f54542841c9ae626087611086fee07a57f
6
+ metadata.gz: a8f18cbb168d6808180a9adac203a94b37bf82d9bbb43bf9fe9c63fd9dba4b7baab4756cee3a3d9eb30e9cbf0c9a178d6d8506faca9d32656620f162bb5f6571
7
+ data.tar.gz: dd860931831eceadb0bae490710edbc6f52189c918999b0edb41b2d3c88961a53359bf597eee953869745730279e8d68ec6fa146a3eca06f1395e8e58d26ccda
data/README.md CHANGED
@@ -286,6 +286,32 @@ container.commit
286
286
  container.run('pwd', 10)
287
287
  # => Docker::Image { :id => 4427be4199ac, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
288
288
 
289
+ # Run an Exec instance inside the container and capture its output
290
+ container.exec('date')
291
+ # => [["Wed Nov 26 11:10:30 CST 2014\n"], []]
292
+
293
+ # Launch an Exec instance without capturing its output
294
+ container.exec('./my_service', detach: true)
295
+ # => Docker::Exec { :id => be4eaeb8d28a, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
296
+
297
+ # Parse the output of an Exec instance
298
+ container.exec('find / -name *') { |stream, chunk| puts "#{stream}: #{chunk}" }
299
+ stderr: 2013/10/30 17:16:24 Unable to locate find / -name *
300
+ # => [[], ["2013/10/30 17:16:24 Unable to locate find / -name *\n"]]
301
+
302
+ # Run an Exec instance by grab only the STDOUT output
303
+ container.exec('date', stderr: false)
304
+ # => [["Wed Nov 26 11:10:30 CST 2014\n"], []]
305
+
306
+ # Pass input to an Exec instance command via Stdin
307
+ container.exec('cat', stdin: StringIO.new("foo\nbar\n"))
308
+ # => [["foo\nbar\n"], []]
309
+
310
+ # Get the raw stream of data from an Exec instance
311
+ command = ["bash", "-c", "if [ -t 1 ]; then echo -n \"I'm a TTY!\"; fi"]
312
+ container.exec(command, tty: true)
313
+ # => [["I'm a TTY!"], []]
314
+
289
315
  # Delete a Container.
290
316
  container.delete(:force => true)
291
317
  # => nil
@@ -340,4 +366,3 @@ License
340
366
  -----
341
367
 
342
368
  This program is licensed under the MIT license. See LICENSE for details.
343
-
data/Rakefile CHANGED
@@ -15,6 +15,43 @@ Cane::RakeTask.new(:quality) do |cane|
15
15
  cane.canefile = '.cane'
16
16
  end
17
17
 
18
+ dir = File.expand_path(File.dirname(__FILE__))
19
+ namespace :vcr do
20
+ desc 'Run the full test suite from scratch'
21
+ task :spec => [:unpack, :record]
22
+
23
+ desc 'Download the necessary base images'
24
+ task :unpack do
25
+ %w( registry busybox tianon/true scratch ).each do |image|
26
+ system "docker pull #{image}"
27
+ end
28
+ end
29
+
30
+ desc 'Run spec tests and record VCR cassettes'
31
+ task :record do
32
+ begin
33
+ FileUtils.remove_dir("#{dir}/spec/vcr", true)
34
+ registry = Docker::Container.create(
35
+ 'name' => 'registry',
36
+ 'Image' => 'registry',
37
+ 'Env' => ["GUNICORN_OPTS=[--preload]"],
38
+ 'ExposedPorts' => {
39
+ '5000/tcp' => {}
40
+ },
41
+ 'HostConfig' => {
42
+ 'PortBindings' => { '5000/tcp' => [{ 'HostPort' => '5000' }] }
43
+ }
44
+ )
45
+ registry.start
46
+ Rake::Task["spec"].invoke
47
+ rescue
48
+ # nothing
49
+ ensure
50
+ registry.kill!.remove
51
+ end
52
+ end
53
+ end
54
+
18
55
  desc 'Pull an Ubuntu image'
19
56
  image 'ubuntu:13.10' do
20
57
  puts "Pulling ubuntu:13.10"
@@ -0,0 +1,61 @@
1
+ # Prerequisites
2
+ To develop on this gem, you must the following installed:
3
+ * a sane Ruby 1.9+ environment with `bundler`
4
+ ```shell
5
+ $ gem install bundler
6
+ ```
7
+ * Docker v1.3.1 or greater
8
+
9
+
10
+
11
+ # Getting Started
12
+ 1. Clone the git repository from Github:
13
+ ```shell
14
+ $ git clone git@github.com:swipely/docker-api.git
15
+ ```
16
+ 2. Install the dependencies using Bundler
17
+ ```shell
18
+ $ bundle install
19
+ ```
20
+ 3. Create a branch for your changes
21
+ ```shell
22
+ $ git checkout -b my_bug_fix
23
+ ```
24
+ 4. Make any changes
25
+ 5. Write tests to support those changes.
26
+ 6. Run the tests:
27
+ * `bundle exec rake vcr:test`
28
+ 7. Assuming the tests pass, open a Pull Request on Github.
29
+
30
+ # Using Rakefile Commands
31
+ This repository comes with five Rake commands to assist in your testing of the code.
32
+
33
+ ## `rake spec`
34
+ This command will run Rspec tests normally on your local system. Be careful that VCR will behave "weirdly" if you currently have the Docker daemon running.
35
+
36
+ ## `rake quality`
37
+ This command runs a code quality threshold checker to hinder bad code.
38
+
39
+ ## `rake vcr`
40
+ This gem uses [VCR](https://relishapp.com/vcr/vcr) to record and replay HTTP requests made to the Docker API. The `vcr` namespace is used to record and replay spec tests inside of a Docker container. This will allow each developer to run and rerecord VCR cassettes in a consistent environment.
41
+
42
+ ### Setting Up Environment Variables
43
+ Certain Rspec tests will require your credentials to the Docker Hub. If you do not have a Docker Hub account, you can sign up for one [here](https://hub.docker.com/account/signup/). To avoid hard-coding credentials into the code the test suite leverages three Environment Variables: `DOCKER_API_USER`, `DOCKER_API_PASS`, and `DOCKER_API_EMAIL`. You will need to configure your work environment (shell profile, IDE, etc) with these values in order to successfully re-record VCR cassettes.
44
+
45
+ ```shell
46
+ export DOCKER_API_USER='your_docker_hub_user'
47
+ export DOCKER_API_PASS='your_docker_hub_password'
48
+ export DOCKER_API_EMAIL='your_docker_hub_email_address'
49
+ ```
50
+
51
+ ### `rake vcr:spec`
52
+ This command will download the necessary Docker images and then run the Rspec tests while recording your VCR cassettes.
53
+
54
+ ### `rake vcr:unpack`
55
+ This command will download the necessary Docker image.
56
+
57
+ ### `rake vcr:record`
58
+ This is the command you will use to record a new set of VCR cassettes. This command runs the following procedures:
59
+ 1. Delete the existing `spec/vcr` directory.
60
+ 2. Launch a temporary local Docker registry
61
+ 3. Record new VCR cassettes by running the Rspec test suite against a live Docker daemon.
@@ -24,6 +24,7 @@ module Docker
24
24
  require 'docker/base'
25
25
  require 'docker/container'
26
26
  require 'docker/event'
27
+ require 'docker/exec'
27
28
  require 'docker/image'
28
29
  require 'docker/messages'
29
30
  require 'docker/util'
@@ -78,6 +79,12 @@ module Docker
78
79
  @connection ||= Connection.new(url, options)
79
80
  end
80
81
 
82
+ def reset!
83
+ @url = nil
84
+ @options = nil
85
+ reset_connection!
86
+ end
87
+
81
88
  def reset_connection!
82
89
  @connection = nil
83
90
  end
@@ -113,6 +120,6 @@ module Docker
113
120
 
114
121
  module_function :default_socket_url, :env_url, :url, :url=, :env_options,
115
122
  :options, :options=, :creds, :creds=, :logger, :logger=,
116
- :connection, :reset_connection!, :version, :info,
123
+ :connection, :reset!, :reset_connection!, :version, :info,
117
124
  :authenticate!, :validate_version!
118
125
  end
@@ -32,6 +32,44 @@ class Docker::Container
32
32
  end
33
33
  end
34
34
 
35
+ # Create an Exec instance inside the container
36
+ #
37
+ # @param command [String, Array] The command to run inside the Exec instance
38
+ # @param options [Hash] The options to pass to Docker::Exec
39
+ #
40
+ # @return [Docker::Exec] The Exec instance
41
+ def exec(command, opts = {}, &block)
42
+ # Establish values
43
+ tty = opts.delete(:tty) || false
44
+ detach = opts.delete(:detach) || false
45
+ stdin = opts.delete(:stdin)
46
+ stdout = opts.delete(:stdout) || !detach
47
+ stderr = opts.delete(:stderr) || !detach
48
+
49
+ # Create Exec Instance
50
+ instance = Docker::Exec.create(
51
+ 'Container' => self.id,
52
+ 'AttachStdin' => !!stdin,
53
+ 'AttachStdout' => stdout,
54
+ 'AttachStderr' => stderr,
55
+ 'Tty' => tty,
56
+ 'Cmd' => command
57
+ )
58
+
59
+ start_opts = {
60
+ :tty => tty,
61
+ :stdin => stdin,
62
+ :detach => detach
63
+ }
64
+
65
+ if detach
66
+ instance.start!(start_opts)
67
+ return instance
68
+ else
69
+ instance.start!(start_opts, &block)
70
+ end
71
+ end
72
+
35
73
  # Export the Container as a tar.
36
74
  def export(&block)
37
75
  connection.get(path_for(:export), {}, :response_block => block)
@@ -55,9 +93,10 @@ class Docker::Container
55
93
  # If attaching to stdin, we must hijack the underlying TCP connection
56
94
  # so we can stream stdin to the remote Docker process
57
95
  opts[:stdin] = true
58
- excon_params[:hijack_block] = hijack_for(stdin, block, msgs, tty)
96
+ excon_params[:hijack_block] = Docker::Util.hijack_for(stdin, block,
97
+ msgs, tty)
59
98
  else
60
- excon_params[:response_block] = attach_for(block, msgs, tty)
99
+ excon_params[:response_block] = Docker::Util.attach_for(block, msgs, tty)
61
100
  end
62
101
 
63
102
  connection.post(
@@ -101,10 +140,10 @@ class Docker::Container
101
140
 
102
141
  def streaming_logs(opts = {}, &block)
103
142
  msgs = Docker::Messages.new
104
- excon_params = { response_block: attach_for(block, msgs, false) }
143
+ excon_params = {response_block: Docker::Util.attach_for(block, msgs, false)}
105
144
 
106
145
  connection.get(path_for(:logs), opts, excon_params)
107
- msgs.all_messages.join("\n")
146
+ msgs.all_messages.join
108
147
  end
109
148
 
110
149
  def start!(opts = {})
@@ -201,81 +240,6 @@ class Docker::Container
201
240
  "/containers/#{self.id}/#{resource}"
202
241
  end
203
242
 
204
- def hijack_for(stdin, block, msg_stack, tty)
205
- attach_block = attach_for(block, msg_stack, tty)
206
-
207
- lambda do |socket|
208
- debug "hijack: hijacking the HTTP socket"
209
- threads = []
210
-
211
- debug "hijack: starting stdin copy thread"
212
- threads << Thread.start do
213
- debug "hijack: copying stdin => socket"
214
- IO.copy_stream stdin, socket
215
-
216
- debug "hijack: closing write end of hijacked socket"
217
- socket.close_write
218
- end
219
-
220
- debug "hijack: starting hijacked socket read thread"
221
- threads << Thread.start do
222
- debug "hijack: reading from hijacked socket"
223
-
224
- begin
225
- while chunk = socket.readpartial(512)
226
- debug "hijack: got #{chunk.bytesize} bytes from hijacked socket"
227
- attach_block.call chunk, nil, nil
228
- end
229
- rescue EOFError
230
- end
231
-
232
- debug "hijack: killing stdin copy thread"
233
- threads.first.kill
234
- end
235
-
236
- threads.each(&:join)
237
- end
238
- end
239
-
240
- # Method that takes chunks and calls the attached block for each mux'd message
241
- def attach_for(block, msg_stack, tty)
242
- # If TTY is enabled expect raw data and append to stdout
243
- if tty
244
- attach_for_tty(block, msg_stack)
245
- else
246
- attach_for_multiplex(block, msg_stack)
247
- end
248
- end
249
-
250
- def attach_for_tty(block, msg_stack)
251
- return lambda do |c,r,t|
252
- msg_stack.stdout_messages << c
253
- msg_stack.all_messages << c
254
- block.call c if block
255
- end
256
- end
257
-
258
- def attach_for_multiplex(block, msg_stack)
259
- messages = Docker::Messages.new
260
- lambda do |c,r,t|
261
- messages = messages.decipher_messages(c)
262
- msg_stack.append(messages)
263
-
264
- unless block.nil?
265
- messages.stdout_messages.each do |msg|
266
- block.call(:stdout, msg)
267
- end
268
- messages.stderr_messages.each do |msg|
269
- block.call(:stderr, msg)
270
- end
271
- end
272
- end
273
- end
274
-
275
- def debug(msg)
276
- Docker.logger.debug(msg) if Docker.logger
277
- end
278
-
279
- private :path_for, :attach_for, :attach_for_tty, :attach_for_multiplex, :debug
243
+ private :path_for
280
244
  private_class_method :new
281
245
  end
@@ -0,0 +1,98 @@
1
+ # This class represents a Docker Exec Instance.
2
+ class Docker::Exec
3
+ include Docker::Base
4
+
5
+ # Convert details about the object into a string
6
+ #
7
+ # @return [String] String representation of the Exec instance object
8
+ def to_s
9
+ "Docker::Exec { :id => #{self.id}, :connection => #{self.connection} }"
10
+ end
11
+
12
+ # Create a new Exec instance in a running container. Please note, this does
13
+ # NOT execute the instance - you must run #start. Also, each instance is
14
+ # one-time use only.
15
+ #
16
+ # @param options [Hash] Parameters to pass in to the API.
17
+ # @param conn [Docker::Connection] Connection to Docker Remote API
18
+ #
19
+ # @return [Docker::Exec] self
20
+ def self.create(options = {}, conn = Docker.connection)
21
+ container = options.delete('Container')
22
+ resp = conn.post("/containers/#{container}/exec", {},
23
+ :body => options.to_json)
24
+ hash = Docker::Util.parse_json(resp) || {}
25
+ new(conn, hash)
26
+ end
27
+
28
+ # Start the Exec instance. The Exec instance is deleted after this so this
29
+ # command can only be run once.
30
+ #
31
+ # @param options [Hash] Options to dictate behavior of the instance
32
+ # @option options [Object] :stdin (nil) The object to pass to STDIN.
33
+ # @option options [TrueClass, FalseClass] :detach (false) Whether to attach
34
+ # to STDOUT/STDERR.
35
+ # @option options [TrueClass, FalseClass] :tty (false) Whether to attach using
36
+ # a pseudo-TTY.
37
+ #
38
+ # @return [Array, Array] The STDOUT and STDERR responses
39
+ def start!(options = {}, &block)
40
+
41
+ # Parse the Options
42
+ tty = !!options.delete(:tty)
43
+ detached = !!options.delete(:detach)
44
+ stdin = options[:stdin]
45
+
46
+ # Create API Request Body
47
+ body = {
48
+ "Tty" => tty,
49
+ "Detach" => detached
50
+ }
51
+ excon_params = { :body => body.to_json }
52
+
53
+ msgs = Docker::Messages.new
54
+ unless detached
55
+ if stdin
56
+ excon_params[:hijack_block] = Docker::Util.hijack_for(stdin, block,
57
+ msgs, tty)
58
+ else
59
+ excon_params[:response_block] = Docker::Util.attach_for(block,
60
+ msgs, tty)
61
+ end
62
+ end
63
+
64
+ connection.post(path_for(:start), nil, excon_params)
65
+ [msgs.stdout_messages, msgs.stderr_messages]
66
+ end
67
+
68
+ # #start! performs the associated action and returns the output.
69
+ # #start does the same, but rescues from ServerErrors.
70
+ [:start].each do |method|
71
+ define_method(method) do |*args|
72
+ begin; public_send(:"#{method}!", *args); rescue ServerError; self end
73
+ end
74
+ end
75
+
76
+ # Resize the TTY associated with the Exec instance
77
+ #
78
+ # @param query [Hash] API query parameters
79
+ # @option query [Fixnum] h Height of the TTY
80
+ # @option query [Fixnum] w Width of the TTY
81
+ #
82
+ # @return [Docker::Exec] self
83
+ def resize(query = {})
84
+ connection.post(path_for(:resize), query)
85
+ self
86
+ end
87
+
88
+ # Get the request URI for the given endpoint
89
+ #
90
+ # @param endpoint [Symbol] The endpoint to grab
91
+ # @return [String] The full Remote API endpoint with ID
92
+ def path_for(endpoint)
93
+ "/exec/#{self.id}/#{endpoint}"
94
+ end
95
+
96
+ private :path_for
97
+ private_class_method :new
98
+ end
@@ -23,7 +23,7 @@ class Docker::Image
23
23
 
24
24
  # Push the Image to the Docker registry.
25
25
  def push(creds = nil, options = {})
26
- repo_tag = info['RepoTags'].first
26
+ repo_tag = options.delete(:repo_tag) || info['RepoTags'].first
27
27
  raise ArgumentError "Image is untagged" if repo_tag.nil?
28
28
  repo, tag = Docker::Util.parse_repo_tag(repo_tag)
29
29
  raise ArgumentError, "Image does not have a name to push." if repo.nil?
@@ -63,7 +63,8 @@ class Docker::Image
63
63
 
64
64
  # Remove the Image from the server.
65
65
  def remove(opts = {})
66
- connection.delete("/images/#{self.id}", opts)
66
+ name = opts.delete(:name) || self.id
67
+ connection.delete("/images/#{name}", opts)
67
68
  end
68
69
  alias_method :delete, :remove
69
70
 
@@ -211,7 +212,7 @@ end
211
212
 
212
213
  # A method to build the config header and merge it into the
213
214
  # headers sent by build_from_dir.
214
- def self.build_headers(creds)
215
+ def self.build_headers(creds=nil)
215
216
  credentials = creds || Docker.creds || {}
216
217
  config_header = Docker::Util.build_config_header(credentials)
217
218