mbailey-chef 0.9.12.1
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/LICENSE +201 -0
- data/README.rdoc +172 -0
- data/bin/chef-client +26 -0
- data/bin/chef-solo +25 -0
- data/bin/knife +26 -0
- data/bin/shef +34 -0
- data/distro/README +2 -0
- data/distro/common/man/man1/chef-indexer.1 +42 -0
- data/distro/common/man/man1/chef-server-webui.1 +106 -0
- data/distro/common/man/man1/chef-server.1 +107 -0
- data/distro/common/man/man1/chef-solr-indexer.1 +55 -0
- data/distro/common/man/man1/chef-solr.1 +55 -0
- data/distro/common/man/man8/chef-client.8 +63 -0
- data/distro/common/man/man8/chef-solo.8 +57 -0
- data/distro/common/man/man8/chef-solr-rebuild.8 +37 -0
- data/distro/common/man/man8/knife.8 +1349 -0
- data/distro/common/man/man8/shef.8 +45 -0
- data/distro/common/markdown/README +3 -0
- data/distro/common/markdown/knife.mkd +832 -0
- data/distro/debian/etc/default/chef-client +4 -0
- data/distro/debian/etc/default/chef-server +9 -0
- data/distro/debian/etc/default/chef-server-webui +9 -0
- data/distro/debian/etc/default/chef-solr +7 -0
- data/distro/debian/etc/default/chef-solr-indexer +7 -0
- data/distro/debian/etc/init/chef-client.conf +17 -0
- data/distro/debian/etc/init/chef-server-webui.conf +17 -0
- data/distro/debian/etc/init/chef-server.conf +17 -0
- data/distro/debian/etc/init/chef-solr-indexer.conf +17 -0
- data/distro/debian/etc/init/chef-solr.conf +17 -0
- data/distro/debian/etc/init.d/chef-client +175 -0
- data/distro/debian/etc/init.d/chef-server +122 -0
- data/distro/debian/etc/init.d/chef-server-webui +123 -0
- data/distro/debian/etc/init.d/chef-solr +177 -0
- data/distro/debian/etc/init.d/chef-solr-indexer +176 -0
- data/distro/redhat/etc/init.d/chef-client +106 -0
- data/distro/redhat/etc/init.d/chef-server +112 -0
- data/distro/redhat/etc/init.d/chef-server-webui +112 -0
- data/distro/redhat/etc/init.d/chef-solr +104 -0
- data/distro/redhat/etc/init.d/chef-solr-indexer +104 -0
- data/distro/redhat/etc/logrotate.d/chef-client +8 -0
- data/distro/redhat/etc/logrotate.d/chef-server +8 -0
- data/distro/redhat/etc/logrotate.d/chef-server-webui +8 -0
- data/distro/redhat/etc/logrotate.d/chef-solr +8 -0
- data/distro/redhat/etc/logrotate.d/chef-solr-indexer +8 -0
- data/distro/redhat/etc/sysconfig/chef-client +15 -0
- data/distro/redhat/etc/sysconfig/chef-server +14 -0
- data/distro/redhat/etc/sysconfig/chef-server-webui +14 -0
- data/distro/redhat/etc/sysconfig/chef-solr +8 -0
- data/distro/redhat/etc/sysconfig/chef-solr-indexer +7 -0
- data/lib/chef/api_client.rb +264 -0
- data/lib/chef/application/agent.rb +18 -0
- data/lib/chef/application/client.rb +246 -0
- data/lib/chef/application/knife.rb +171 -0
- data/lib/chef/application/solo.rb +215 -0
- data/lib/chef/application.rb +127 -0
- data/lib/chef/applications.rb +4 -0
- data/lib/chef/certificate.rb +194 -0
- data/lib/chef/checksum.rb +182 -0
- data/lib/chef/checksum_cache.rb +173 -0
- data/lib/chef/client.rb +304 -0
- data/lib/chef/config.rb +240 -0
- data/lib/chef/cookbook/cookbook_collection.rb +44 -0
- data/lib/chef/cookbook/file_system_file_vendor.rb +54 -0
- data/lib/chef/cookbook/file_vendor.rb +48 -0
- data/lib/chef/cookbook/metadata/version.rb +87 -0
- data/lib/chef/cookbook/metadata.rb +500 -0
- data/lib/chef/cookbook/remote_file_vendor.rb +87 -0
- data/lib/chef/cookbook/syntax_check.rb +136 -0
- data/lib/chef/cookbook_loader.rb +227 -0
- data/lib/chef/cookbook_site_streaming_uploader.rb +244 -0
- data/lib/chef/cookbook_uploader.rb +103 -0
- data/lib/chef/cookbook_version.rb +930 -0
- data/lib/chef/couchdb.rb +239 -0
- data/lib/chef/daemon.rb +172 -0
- data/lib/chef/data_bag.rb +215 -0
- data/lib/chef/data_bag_item.rb +228 -0
- data/lib/chef/exceptions.rb +66 -0
- data/lib/chef/file_access_control.rb +140 -0
- data/lib/chef/file_cache.rb +218 -0
- data/lib/chef/handler/json_file.rb +58 -0
- data/lib/chef/handler.rb +206 -0
- data/lib/chef/index_queue/amqp_client.rb +113 -0
- data/lib/chef/index_queue/consumer.rb +76 -0
- data/lib/chef/index_queue/indexable.rb +76 -0
- data/lib/chef/index_queue.rb +29 -0
- data/lib/chef/knife/bootstrap/archlinux-gems.erb +44 -0
- data/lib/chef/knife/bootstrap/centos5-gems.erb +41 -0
- data/lib/chef/knife/bootstrap/client-install.vbs +80 -0
- data/lib/chef/knife/bootstrap/fedora13-gems.erb +38 -0
- data/lib/chef/knife/bootstrap/ubuntu10.04-apt.erb +32 -0
- data/lib/chef/knife/bootstrap/ubuntu10.04-gems.erb +43 -0
- data/lib/chef/knife/bootstrap/windows-gems.erb +34 -0
- data/lib/chef/knife/bootstrap.rb +181 -0
- data/lib/chef/knife/client_bulk_delete.rb +40 -0
- data/lib/chef/knife/client_create.rb +70 -0
- data/lib/chef/knife/client_delete.rb +45 -0
- data/lib/chef/knife/client_edit.rb +45 -0
- data/lib/chef/knife/client_list.rb +40 -0
- data/lib/chef/knife/client_reregister.rb +56 -0
- data/lib/chef/knife/client_show.rb +50 -0
- data/lib/chef/knife/configure.rb +140 -0
- data/lib/chef/knife/configure_client.rb +52 -0
- data/lib/chef/knife/cookbook_bulk_delete.rb +58 -0
- data/lib/chef/knife/cookbook_create.rb +209 -0
- data/lib/chef/knife/cookbook_delete.rb +143 -0
- data/lib/chef/knife/cookbook_download.rb +130 -0
- data/lib/chef/knife/cookbook_list.rb +41 -0
- data/lib/chef/knife/cookbook_metadata.rb +82 -0
- data/lib/chef/knife/cookbook_metadata_from_file.rb +40 -0
- data/lib/chef/knife/cookbook_show.rb +98 -0
- data/lib/chef/knife/cookbook_site_download.rb +58 -0
- data/lib/chef/knife/cookbook_site_list.rb +56 -0
- data/lib/chef/knife/cookbook_site_search.rb +51 -0
- data/lib/chef/knife/cookbook_site_share.rb +109 -0
- data/lib/chef/knife/cookbook_site_show.rb +57 -0
- data/lib/chef/knife/cookbook_site_unshare.rb +52 -0
- data/lib/chef/knife/cookbook_site_vendor.rb +133 -0
- data/lib/chef/knife/cookbook_test.rb +82 -0
- data/lib/chef/knife/cookbook_upload.rb +95 -0
- data/lib/chef/knife/data_bag_create.rb +59 -0
- data/lib/chef/knife/data_bag_delete.rb +48 -0
- data/lib/chef/knife/data_bag_edit.rb +50 -0
- data/lib/chef/knife/data_bag_from_file.rb +44 -0
- data/lib/chef/knife/data_bag_list.rb +43 -0
- data/lib/chef/knife/data_bag_show.rb +41 -0
- data/lib/chef/knife/ec2_instance_data.rb +46 -0
- data/lib/chef/knife/ec2_server_create.rb +216 -0
- data/lib/chef/knife/ec2_server_delete.rb +90 -0
- data/lib/chef/knife/ec2_server_list.rb +88 -0
- data/lib/chef/knife/exec.rb +52 -0
- data/lib/chef/knife/index_rebuild.rb +51 -0
- data/lib/chef/knife/node_bulk_delete.rb +43 -0
- data/lib/chef/knife/node_create.rb +47 -0
- data/lib/chef/knife/node_delete.rb +44 -0
- data/lib/chef/knife/node_edit.rb +44 -0
- data/lib/chef/knife/node_from_file.rb +42 -0
- data/lib/chef/knife/node_list.rb +41 -0
- data/lib/chef/knife/node_run_list_add.rb +64 -0
- data/lib/chef/knife/node_run_list_remove.rb +45 -0
- data/lib/chef/knife/node_show.rb +54 -0
- data/lib/chef/knife/rackspace_server_create.rb +156 -0
- data/lib/chef/knife/rackspace_server_delete.rb +57 -0
- data/lib/chef/knife/rackspace_server_list.rb +59 -0
- data/lib/chef/knife/recipe_list.rb +32 -0
- data/lib/chef/knife/role_bulk_delete.rb +44 -0
- data/lib/chef/knife/role_create.rb +52 -0
- data/lib/chef/knife/role_delete.rb +44 -0
- data/lib/chef/knife/role_edit.rb +45 -0
- data/lib/chef/knife/role_from_file.rb +46 -0
- data/lib/chef/knife/role_list.rb +40 -0
- data/lib/chef/knife/role_show.rb +51 -0
- data/lib/chef/knife/search.rb +94 -0
- data/lib/chef/knife/slicehost_images_list.rb +53 -0
- data/lib/chef/knife/slicehost_server_create.rb +103 -0
- data/lib/chef/knife/slicehost_server_delete.rb +61 -0
- data/lib/chef/knife/slicehost_server_list.rb +64 -0
- data/lib/chef/knife/ssh.rb +328 -0
- data/lib/chef/knife/status.rb +87 -0
- data/lib/chef/knife/terremark_server_create.rb +152 -0
- data/lib/chef/knife/terremark_server_delete.rb +87 -0
- data/lib/chef/knife/terremark_server_list.rb +77 -0
- data/lib/chef/knife/windows_bootstrap.rb +154 -0
- data/lib/chef/knife.rb +522 -0
- data/lib/chef/log.rb +61 -0
- data/lib/chef/mixin/check_helper.rb +31 -0
- data/lib/chef/mixin/checksum.rb +32 -0
- data/lib/chef/mixin/command/unix.rb +215 -0
- data/lib/chef/mixin/command/windows.rb +72 -0
- data/lib/chef/mixin/command.rb +220 -0
- data/lib/chef/mixin/convert_to_class_name.rb +63 -0
- data/lib/chef/mixin/create_path.rb +56 -0
- data/lib/chef/mixin/deep_merge.rb +225 -0
- data/lib/chef/mixin/deprecation.rb +65 -0
- data/lib/chef/mixin/find_preferred_file.rb +92 -0
- data/lib/chef/mixin/from_file.rb +50 -0
- data/lib/chef/mixin/language.rb +156 -0
- data/lib/chef/mixin/language_include_attribute.rb +61 -0
- data/lib/chef/mixin/language_include_recipe.rb +52 -0
- data/lib/chef/mixin/params_validate.rb +225 -0
- data/lib/chef/mixin/recipe_definition_dsl_core.rb +81 -0
- data/lib/chef/mixin/shell_out.rb +38 -0
- data/lib/chef/mixin/template.rb +95 -0
- data/lib/chef/mixin/xml_escape.rb +140 -0
- data/lib/chef/mixins.rb +16 -0
- data/lib/chef/monkey_patches/dir.rb +36 -0
- data/lib/chef/monkey_patches/string.rb +28 -0
- data/lib/chef/monkey_patches/tempfile.rb +64 -0
- data/lib/chef/node/attribute.rb +465 -0
- data/lib/chef/node.rb +601 -0
- data/lib/chef/openid_registration.rb +187 -0
- data/lib/chef/platform.rb +371 -0
- data/lib/chef/provider/breakpoint.rb +36 -0
- data/lib/chef/provider/cookbook_file.rb +100 -0
- data/lib/chef/provider/cron/solaris.rb +195 -0
- data/lib/chef/provider/cron.rb +186 -0
- data/lib/chef/provider/deploy/revision.rb +73 -0
- data/lib/chef/provider/deploy/timestamped.rb +33 -0
- data/lib/chef/provider/deploy.rb +319 -0
- data/lib/chef/provider/directory.rb +72 -0
- data/lib/chef/provider/env/windows.rb +75 -0
- data/lib/chef/provider/env.rb +152 -0
- data/lib/chef/provider/erl_call.rb +72 -0
- data/lib/chef/provider/execute.rb +58 -0
- data/lib/chef/provider/file.rb +213 -0
- data/lib/chef/provider/git.rb +211 -0
- data/lib/chef/provider/group/dscl.rb +121 -0
- data/lib/chef/provider/group/gpasswd.rb +53 -0
- data/lib/chef/provider/group/groupadd.rb +78 -0
- data/lib/chef/provider/group/pw.rb +84 -0
- data/lib/chef/provider/group/usermod.rb +57 -0
- data/lib/chef/provider/group/windows.rb +79 -0
- data/lib/chef/provider/group.rb +133 -0
- data/lib/chef/provider/http_request.rb +122 -0
- data/lib/chef/provider/ifconfig.rb +132 -0
- data/lib/chef/provider/link.rb +161 -0
- data/lib/chef/provider/log.rb +54 -0
- data/lib/chef/provider/mdadm.rb +91 -0
- data/lib/chef/provider/mount/mount.rb +232 -0
- data/lib/chef/provider/mount/windows.rb +80 -0
- data/lib/chef/provider/mount.rb +117 -0
- data/lib/chef/provider/ohai.rb +41 -0
- data/lib/chef/provider/package/apt.rb +110 -0
- data/lib/chef/provider/package/dpkg.rb +112 -0
- data/lib/chef/provider/package/easy_install.rb +114 -0
- data/lib/chef/provider/package/freebsd.rb +123 -0
- data/lib/chef/provider/package/macports.rb +105 -0
- data/lib/chef/provider/package/pacman.rb +101 -0
- data/lib/chef/provider/package/portage.rb +124 -0
- data/lib/chef/provider/package/rpm.rb +101 -0
- data/lib/chef/provider/package/rubygems.rb +462 -0
- data/lib/chef/provider/package/solaris.rb +127 -0
- data/lib/chef/provider/package/yum-dump.py +128 -0
- data/lib/chef/provider/package/yum.rb +222 -0
- data/lib/chef/provider/package/zypper.rb +133 -0
- data/lib/chef/provider/package.rb +160 -0
- data/lib/chef/provider/remote_directory.rb +140 -0
- data/lib/chef/provider/remote_file.rb +120 -0
- data/lib/chef/provider/route.rb +195 -0
- data/lib/chef/provider/ruby_block.rb +33 -0
- data/lib/chef/provider/script.rb +55 -0
- data/lib/chef/provider/service/arch.rb +109 -0
- data/lib/chef/provider/service/debian.rb +105 -0
- data/lib/chef/provider/service/freebsd.rb +156 -0
- data/lib/chef/provider/service/gentoo.rb +54 -0
- data/lib/chef/provider/service/init.rb +71 -0
- data/lib/chef/provider/service/redhat.rb +60 -0
- data/lib/chef/provider/service/simple.rb +118 -0
- data/lib/chef/provider/service/solaris.rb +85 -0
- data/lib/chef/provider/service/upstart.rb +192 -0
- data/lib/chef/provider/service/windows.rb +129 -0
- data/lib/chef/provider/service.rb +128 -0
- data/lib/chef/provider/subversion.rb +159 -0
- data/lib/chef/provider/template.rb +105 -0
- data/lib/chef/provider/user/dscl.rb +280 -0
- data/lib/chef/provider/user/pw.rb +113 -0
- data/lib/chef/provider/user/useradd.rb +137 -0
- data/lib/chef/provider/user/windows.rb +124 -0
- data/lib/chef/provider/user.rb +187 -0
- data/lib/chef/provider.rb +124 -0
- data/lib/chef/providers.rb +91 -0
- data/lib/chef/recipe.rb +130 -0
- data/lib/chef/resource/apt_package.rb +34 -0
- data/lib/chef/resource/bash.rb +33 -0
- data/lib/chef/resource/breakpoint.rb +35 -0
- data/lib/chef/resource/cookbook_file.rb +45 -0
- data/lib/chef/resource/cron.rb +188 -0
- data/lib/chef/resource/csh.rb +33 -0
- data/lib/chef/resource/deploy.rb +371 -0
- data/lib/chef/resource/deploy_revision.rb +35 -0
- data/lib/chef/resource/directory.rb +76 -0
- data/lib/chef/resource/dpkg_package.rb +34 -0
- data/lib/chef/resource/easy_install_package.rb +41 -0
- data/lib/chef/resource/env.rb +58 -0
- data/lib/chef/resource/erl_call.rb +83 -0
- data/lib/chef/resource/execute.rb +127 -0
- data/lib/chef/resource/file.rb +92 -0
- data/lib/chef/resource/freebsd_package.rb +35 -0
- data/lib/chef/resource/gem_package.rb +49 -0
- data/lib/chef/resource/git.rb +36 -0
- data/lib/chef/resource/group.rb +70 -0
- data/lib/chef/resource/http_request.rb +61 -0
- data/lib/chef/resource/ifconfig.rb +134 -0
- data/lib/chef/resource/link.rb +78 -0
- data/lib/chef/resource/log.rb +62 -0
- data/lib/chef/resource/macports_package.rb +29 -0
- data/lib/chef/resource/mdadm.rb +82 -0
- data/lib/chef/resource/mount.rb +135 -0
- data/lib/chef/resource/ohai.rb +40 -0
- data/lib/chef/resource/package.rb +80 -0
- data/lib/chef/resource/pacman_package.rb +33 -0
- data/lib/chef/resource/perl.rb +33 -0
- data/lib/chef/resource/portage_package.rb +33 -0
- data/lib/chef/resource/python.rb +33 -0
- data/lib/chef/resource/remote_directory.rb +109 -0
- data/lib/chef/resource/remote_file.rb +83 -0
- data/lib/chef/resource/route.rb +135 -0
- data/lib/chef/resource/rpm_package.rb +34 -0
- data/lib/chef/resource/ruby.rb +33 -0
- data/lib/chef/resource/ruby_block.rb +40 -0
- data/lib/chef/resource/scm.rb +146 -0
- data/lib/chef/resource/script.rb +60 -0
- data/lib/chef/resource/service.rb +160 -0
- data/lib/chef/resource/solaris_package.rb +36 -0
- data/lib/chef/resource/subversion.rb +36 -0
- data/lib/chef/resource/template.rb +69 -0
- data/lib/chef/resource/timestamped_deploy.rb +31 -0
- data/lib/chef/resource/user.rb +130 -0
- data/lib/chef/resource/yum_package.rb +43 -0
- data/lib/chef/resource.rb +523 -0
- data/lib/chef/resource_collection/stepable_iterator.rb +124 -0
- data/lib/chef/resource_collection.rb +217 -0
- data/lib/chef/resource_definition.rb +67 -0
- data/lib/chef/resource_definition_list.rb +38 -0
- data/lib/chef/resources.rb +64 -0
- data/lib/chef/rest/auth_credentials.rb +78 -0
- data/lib/chef/rest/cookie_jar.rb +31 -0
- data/lib/chef/rest/rest_request.rb +188 -0
- data/lib/chef/rest.rb +394 -0
- data/lib/chef/role.rb +287 -0
- data/lib/chef/run_context.rb +110 -0
- data/lib/chef/run_list/run_list_expansion.rb +172 -0
- data/lib/chef/run_list/run_list_item.rb +78 -0
- data/lib/chef/run_list.rb +150 -0
- data/lib/chef/run_status.rb +121 -0
- data/lib/chef/runner.rb +107 -0
- data/lib/chef/sandbox.rb +153 -0
- data/lib/chef/search/query.rb +60 -0
- data/lib/chef/shef/ext.rb +568 -0
- data/lib/chef/shef/model_wrapper.rb +120 -0
- data/lib/chef/shef/shef_rest.rb +28 -0
- data/lib/chef/shef/shef_session.rb +271 -0
- data/lib/chef/shef.rb +325 -0
- data/lib/chef/shell_out.rb +413 -0
- data/lib/chef/streaming_cookbook_uploader.rb +201 -0
- data/lib/chef/tasks/chef_repo.rake +256 -0
- data/lib/chef/util/file_edit.rb +122 -0
- data/lib/chef/util/windows/net_group.rb +101 -0
- data/lib/chef/util/windows/net_use.rb +121 -0
- data/lib/chef/util/windows/net_user.rb +198 -0
- data/lib/chef/util/windows/volume.rb +59 -0
- data/lib/chef/util/windows.rb +56 -0
- data/lib/chef/version.rb +21 -0
- data/lib/chef/webui_user.rb +231 -0
- data/lib/chef.rb +39 -0
- metadata +533 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Author:: Daniel DeLeo (<dan@opscode.com>)
|
|
3
|
+
# Copyright:: Copyright (c) 2010 Opscode, Inc.
|
|
4
|
+
# License:: Apache License, Version 2.0
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
#
|
|
18
|
+
|
|
19
|
+
require 'etc'
|
|
20
|
+
require 'tmpdir'
|
|
21
|
+
require 'chef/log'
|
|
22
|
+
require 'fcntl'
|
|
23
|
+
require 'chef/exceptions'
|
|
24
|
+
|
|
25
|
+
class Chef
|
|
26
|
+
|
|
27
|
+
# == Chef::ShellOut
|
|
28
|
+
# Provides a simplified interface to shelling out yet still collecting both
|
|
29
|
+
# standard out and standard error and providing full control over environment,
|
|
30
|
+
# working directory, uid, gid, etc.
|
|
31
|
+
#
|
|
32
|
+
# No means for passing input to the subprocess is provided, nor is there any
|
|
33
|
+
# way to inspect the output of the command as it is being read. If you need
|
|
34
|
+
# to do that, you have to use popen4 (in Chef::Mixin::Command)
|
|
35
|
+
#
|
|
36
|
+
# === Platform Support
|
|
37
|
+
# Chef::ShellOut uses Kernel.fork() and is therefore unsuitable for Windows
|
|
38
|
+
# or jruby.
|
|
39
|
+
class ShellOut
|
|
40
|
+
READ_WAIT_TIME = 0.01
|
|
41
|
+
READ_SIZE = 4096
|
|
42
|
+
DEFAULT_READ_TIMEOUT = 60
|
|
43
|
+
DEFAULT_ENVIRONMENT = {'LC_ALL' => 'C'}
|
|
44
|
+
|
|
45
|
+
attr_accessor :user, :group, :cwd, :valid_exit_codes
|
|
46
|
+
attr_reader :command, :umask, :environment
|
|
47
|
+
attr_writer :timeout
|
|
48
|
+
attr_reader :execution_time
|
|
49
|
+
|
|
50
|
+
attr_reader :stdout, :stderr, :status
|
|
51
|
+
|
|
52
|
+
attr_reader :stdin_pipe, :stdout_pipe, :stderr_pipe, :process_status_pipe
|
|
53
|
+
|
|
54
|
+
# === Arguments:
|
|
55
|
+
# Takes a single command, or a list of command fragments. These are used
|
|
56
|
+
# as arguments to Kernel.exec. See the Kernel.exec documentation for more
|
|
57
|
+
# explanation of how arguments are evaluated. The last argument can be an
|
|
58
|
+
# options Hash.
|
|
59
|
+
# === Options:
|
|
60
|
+
# If the last argument is a Hash, it is removed from the list of args passed
|
|
61
|
+
# to exec and used as an options hash. The following options are available:
|
|
62
|
+
# * +user+: the user the commmand should run as. if an integer is given, it is
|
|
63
|
+
# used as a uid. A string is treated as a username and resolved to a uid
|
|
64
|
+
# with Etc.getpwnam
|
|
65
|
+
# * +group+: the group the command should run as. works similarly to +user+
|
|
66
|
+
# * +cwd+: the directory to chdir to before running the command
|
|
67
|
+
# * +umask+: a umask to set before running the command. If given as an Integer,
|
|
68
|
+
# be sure to use two leading zeros so it's parsed as Octal. A string will
|
|
69
|
+
# be treated as an octal integer
|
|
70
|
+
# * +returns+: one or more Integer values to use as valid exit codes for the
|
|
71
|
+
# subprocess. This only has an effect if you call +error!+ after
|
|
72
|
+
# +run_command+.
|
|
73
|
+
# * +environment+: a Hash of environment variables to set before the command
|
|
74
|
+
# is run. By default, the environment will *always* be set to 'LC_ALL' => 'C'
|
|
75
|
+
# to prevent issues with multibyte characters in Ruby 1.8. To avoid this,
|
|
76
|
+
# use :environment => nil for *no* extra environment settings, or
|
|
77
|
+
# :environment => {'LC_ALL'=>nil, ...} to set other environment settings
|
|
78
|
+
# without changing the locale.
|
|
79
|
+
# * +timeout+: a Numeric value for the number of seconds to wait on the
|
|
80
|
+
# child process before raising an Exception. This is calculated as the
|
|
81
|
+
# total amount of time that ShellOut waited on the child process without
|
|
82
|
+
# receiving any output (i.e., IO.select returned nil). Default is 60
|
|
83
|
+
# seconds. Note: the stdlib Timeout library is not used.
|
|
84
|
+
# === Examples:
|
|
85
|
+
# Invoke find(1) to search for .rb files:
|
|
86
|
+
# find = Chef::ShellOut.new("find . -name '*.rb'")
|
|
87
|
+
# find.run_command
|
|
88
|
+
# # If all went well, the results are on +stdout+
|
|
89
|
+
# puts find.stdout
|
|
90
|
+
# # find(1) prints diagnostic info to STDERR:
|
|
91
|
+
# puts "error messages" + find.stderr
|
|
92
|
+
# # Raise an exception if it didn't exit with 0
|
|
93
|
+
# find.error!
|
|
94
|
+
# Run a command as the +www+ user with no extra ENV settings from +/tmp+
|
|
95
|
+
# cmd = Chef::ShellOut.new("apachectl", "start", :user => 'www', :env => nil, :cwd => '/tmp')
|
|
96
|
+
# cmd.run_command # etc.
|
|
97
|
+
def initialize(*command_args)
|
|
98
|
+
@stdout, @stderr = '', ''
|
|
99
|
+
@environment = DEFAULT_ENVIRONMENT
|
|
100
|
+
@cwd = Dir.tmpdir
|
|
101
|
+
@valid_exit_codes = [0]
|
|
102
|
+
|
|
103
|
+
if command_args.last.is_a?(Hash)
|
|
104
|
+
parse_options(command_args.pop)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
@command = command_args.size == 1 ? command_args.first : command_args
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def umask=(new_umask)
|
|
111
|
+
@umask = (new_umask.respond_to?(:oct) ? new_umask.oct : new_umask.to_i) & 007777
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def uid
|
|
115
|
+
return nil unless user
|
|
116
|
+
user.kind_of?(Integer) ? user : Etc.getpwnam(user.to_s).uid
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def gid
|
|
120
|
+
return nil unless group
|
|
121
|
+
group.kind_of?(Integer) ? group : Etc.getgrnam(group.to_s).gid
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def timeout
|
|
125
|
+
@timeout || DEFAULT_READ_TIMEOUT
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Creates a String showing the output of the command, including a banner
|
|
129
|
+
# showing the exact command executed. Used by +invalid!+ to show command
|
|
130
|
+
# results when the command exited with an unexpected status.
|
|
131
|
+
def format_for_exception
|
|
132
|
+
msg = ""
|
|
133
|
+
msg << "---- Begin output of #{command} ----\n"
|
|
134
|
+
msg << "STDOUT: #{stdout.strip}\n"
|
|
135
|
+
msg << "STDERR: #{stderr.strip}\n"
|
|
136
|
+
msg << "---- End output of #{command} ----\n"
|
|
137
|
+
msg << "Ran #{command} returned #{status.exitstatus}" if status
|
|
138
|
+
msg
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def exitstatus
|
|
142
|
+
@status && @status.exitstatus
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Run the command, writing the command's standard out and standard error
|
|
146
|
+
# to +stdout+ and +stderr+, and saving its exit status object to +status+
|
|
147
|
+
# === Returns
|
|
148
|
+
# returns +self+; +stdout+, +stderr+, +status+, and +exitstatus+ will be
|
|
149
|
+
# populated with results of the command
|
|
150
|
+
# === Raises
|
|
151
|
+
# * Errno::EACCES when you are not privileged to execute the command
|
|
152
|
+
# * Errno::ENOENT when the command is not available on the system (or not
|
|
153
|
+
# in the current $PATH)
|
|
154
|
+
# * Chef::Exceptions::CommandTimeout when the command does not complete
|
|
155
|
+
# within +timeout+ seconds (default: 60s)
|
|
156
|
+
def run_command
|
|
157
|
+
Chef::Log.debug("sh(#{@command})")
|
|
158
|
+
@child_pid = fork_subprocess
|
|
159
|
+
|
|
160
|
+
configure_parent_process_file_descriptors
|
|
161
|
+
propagate_pre_exec_failure
|
|
162
|
+
|
|
163
|
+
@result = nil
|
|
164
|
+
@execution_time = 0
|
|
165
|
+
|
|
166
|
+
# Ruby 1.8.7 and 1.8.6 from mid 2009 try to allocate objects during GC
|
|
167
|
+
# when calling IO.select and IO#read. Some OS Vendors are not interested
|
|
168
|
+
# in updating their ruby packages (Apple, *cough*) and we *have to*
|
|
169
|
+
# make it work. So I give you this epic hack:
|
|
170
|
+
GC.disable
|
|
171
|
+
until @status
|
|
172
|
+
ready = IO.select(open_pipes, nil, nil, READ_WAIT_TIME)
|
|
173
|
+
unless ready
|
|
174
|
+
@execution_time += READ_WAIT_TIME
|
|
175
|
+
if @execution_time >= timeout && !@result
|
|
176
|
+
raise Chef::Exceptions::CommandTimeout, "command timed out:\n#{format_for_exception}"
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
if ready && ready.first.include?(child_stdout)
|
|
181
|
+
read_stdout_to_buffer
|
|
182
|
+
end
|
|
183
|
+
if ready && ready.first.include?(child_stderr)
|
|
184
|
+
read_stderr_to_buffer
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
unless @status
|
|
188
|
+
# make one more pass to get the last of the output after the
|
|
189
|
+
# child process dies
|
|
190
|
+
if results = Process.waitpid2(@child_pid, Process::WNOHANG)
|
|
191
|
+
@status = results.last
|
|
192
|
+
redo
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
self
|
|
197
|
+
rescue Exception
|
|
198
|
+
# do our best to kill zombies
|
|
199
|
+
Process.waitpid2(@child_pid, Process::WNOHANG) rescue nil
|
|
200
|
+
raise
|
|
201
|
+
ensure
|
|
202
|
+
# no matter what happens, turn the GC back on, and hope whatever busted
|
|
203
|
+
# version of ruby we're on doesn't allocate some objects during the next
|
|
204
|
+
# GC run.
|
|
205
|
+
GC.enable
|
|
206
|
+
close_all_pipes
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Checks the +exitstatus+ against the set of +valid_exit_codes+. If
|
|
210
|
+
# +exitstatus+ is not in the list of +valid_exit_codes+, calls +invalid!+,
|
|
211
|
+
# which raises an Exception.
|
|
212
|
+
# === Returns
|
|
213
|
+
# nil::: always returns nil when it does not raise
|
|
214
|
+
# === Raises
|
|
215
|
+
# Chef::Exceptions::ShellCommandFailed::: via +invalid!+
|
|
216
|
+
def error!
|
|
217
|
+
unless Array(valid_exit_codes).include?(exitstatus)
|
|
218
|
+
invalid!("Expected process to exit 0, but it exited with #{exitstatus}")
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# Raises a Chef::Exceptions::ShellCommandFailed exception, appending the
|
|
223
|
+
# command's stdout, stderr, and exitstatus to the exception message.
|
|
224
|
+
# === Arguments
|
|
225
|
+
# +msg+: A String to use as the basis of the exception message. The
|
|
226
|
+
# default explanation is very generic, providing a more informative message
|
|
227
|
+
# is highly encouraged.
|
|
228
|
+
# === Raises
|
|
229
|
+
# Chef::Exceptions::ShellCommandFailed always
|
|
230
|
+
def invalid!(msg=nil)
|
|
231
|
+
msg ||= "Command produced unexpected results"
|
|
232
|
+
raise Chef::Exceptions::ShellCommandFailed, msg + "\n" + format_for_exception
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def inspect
|
|
236
|
+
"<#{self.class.name}##{object_id}: command: '#@command' process_status: #{@status.inspect} " +
|
|
237
|
+
"stdout: '#{stdout.strip}' stderr: '#{stderr.strip}' child_pid: #{@child_pid.inspect} " +
|
|
238
|
+
"environment: #{@environment.inspect} timeout: #{timeout} user: #@user group: #@group working_dir: #@cwd >"
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
private
|
|
242
|
+
|
|
243
|
+
def parse_options(opts)
|
|
244
|
+
opts.each do |option, setting|
|
|
245
|
+
case option.to_s
|
|
246
|
+
when 'cwd'
|
|
247
|
+
self.cwd = setting
|
|
248
|
+
when 'user'
|
|
249
|
+
self.user = setting
|
|
250
|
+
when 'group'
|
|
251
|
+
self.group = setting
|
|
252
|
+
when 'umask'
|
|
253
|
+
self.umask = setting
|
|
254
|
+
when 'timeout'
|
|
255
|
+
self.timeout = setting
|
|
256
|
+
when 'returns'
|
|
257
|
+
self.valid_exit_codes = Array(setting)
|
|
258
|
+
when 'environment', 'env'
|
|
259
|
+
# passing :environment => nil means don't set any new ENV vars
|
|
260
|
+
setting.nil? ? @environment = {} : @environment.merge!(setting)
|
|
261
|
+
else
|
|
262
|
+
raise Chef::Exceptions::InvalidCommandOption, "option '#{option.inspect}' is not a valid option for #{self.class.name}"
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def set_user
|
|
268
|
+
if user
|
|
269
|
+
Process.euid = uid
|
|
270
|
+
Process.uid = uid
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def set_group
|
|
275
|
+
if group
|
|
276
|
+
Process.egid = gid
|
|
277
|
+
Process.gid = gid
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
def set_environment
|
|
282
|
+
environment.each do |env_var,value|
|
|
283
|
+
ENV[env_var] = value
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def set_umask
|
|
288
|
+
File.umask(umask) if umask
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def initialize_ipc
|
|
292
|
+
@stdout_pipe, @stderr_pipe, @process_status_pipe = IO.pipe, IO.pipe, IO.pipe
|
|
293
|
+
@process_status_pipe.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def child_stdout
|
|
297
|
+
@stdout_pipe[0]
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def child_stderr
|
|
301
|
+
@stderr_pipe[0]
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def child_process_status
|
|
305
|
+
@process_status_pipe[0]
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def close_all_pipes
|
|
309
|
+
child_stdout.close unless child_stdout.closed?
|
|
310
|
+
child_stderr.close unless child_stderr.closed?
|
|
311
|
+
child_process_status.close unless child_process_status.closed?
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# replace stdout, and stderr with pipes to the parent, and close the
|
|
315
|
+
# reader side of the error marshaling side channel. Close STDIN so when we
|
|
316
|
+
# exec, the new program will know it's never getting input ever.
|
|
317
|
+
def configure_subprocess_file_descriptors
|
|
318
|
+
process_status_pipe.first.close
|
|
319
|
+
|
|
320
|
+
# HACK: for some reason, just STDIN.close isn't good enough when running
|
|
321
|
+
# under ruby 1.9.2, so make it good enough:
|
|
322
|
+
stdin_reader, stdin_writer = IO.pipe
|
|
323
|
+
stdin_writer.close
|
|
324
|
+
STDIN.reopen stdin_reader
|
|
325
|
+
stdin_reader.close
|
|
326
|
+
|
|
327
|
+
stdout_pipe.first.close
|
|
328
|
+
STDOUT.reopen stdout_pipe.last
|
|
329
|
+
stdout_pipe.last.close
|
|
330
|
+
|
|
331
|
+
stderr_pipe.first.close
|
|
332
|
+
STDERR.reopen stderr_pipe.last
|
|
333
|
+
stderr_pipe.last.close
|
|
334
|
+
|
|
335
|
+
STDOUT.sync = STDERR.sync = true
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
def configure_parent_process_file_descriptors
|
|
339
|
+
# Close the sides of the pipes we don't care about
|
|
340
|
+
stdout_pipe.last.close
|
|
341
|
+
stderr_pipe.last.close
|
|
342
|
+
process_status_pipe.last.close
|
|
343
|
+
# Get output as it happens rather than buffered
|
|
344
|
+
child_stdout.sync = true
|
|
345
|
+
child_stderr.sync = true
|
|
346
|
+
|
|
347
|
+
true
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Some patch levels of ruby in wide use (in particular the ruby 1.8.6 on OSX)
|
|
351
|
+
# segfault when you IO.select a pipe that's reached eof. Weak sauce.
|
|
352
|
+
def open_pipes
|
|
353
|
+
@open_pipes ||= [child_stdout, child_stderr]
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def read_stdout_to_buffer
|
|
357
|
+
while chunk = child_stdout.read_nonblock(READ_SIZE)
|
|
358
|
+
@stdout << chunk
|
|
359
|
+
end
|
|
360
|
+
rescue Errno::EAGAIN
|
|
361
|
+
rescue EOFError
|
|
362
|
+
open_pipes.delete_at(0)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def read_stderr_to_buffer
|
|
366
|
+
while chunk = child_stderr.read_nonblock(READ_SIZE)
|
|
367
|
+
@stderr << chunk
|
|
368
|
+
end
|
|
369
|
+
rescue Errno::EAGAIN
|
|
370
|
+
rescue EOFError
|
|
371
|
+
open_pipes.delete_at(1)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def fork_subprocess
|
|
375
|
+
initialize_ipc
|
|
376
|
+
|
|
377
|
+
fork do
|
|
378
|
+
configure_subprocess_file_descriptors
|
|
379
|
+
|
|
380
|
+
set_user
|
|
381
|
+
set_group
|
|
382
|
+
set_environment
|
|
383
|
+
set_umask
|
|
384
|
+
|
|
385
|
+
begin
|
|
386
|
+
command.kind_of?(Array) ? exec(*command) : exec(command)
|
|
387
|
+
|
|
388
|
+
raise 'forty-two' # Should never get here
|
|
389
|
+
rescue Exception => e
|
|
390
|
+
Marshal.dump(e, process_status_pipe.last)
|
|
391
|
+
process_status_pipe.last.flush
|
|
392
|
+
end
|
|
393
|
+
process_status_pipe.last.close unless (process_status_pipe.last.closed?)
|
|
394
|
+
exit!
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# Attempt to get a Marshaled error from the side-channel.
|
|
399
|
+
# If it's there, un-marshal it and raise. If it's not there,
|
|
400
|
+
# assume everything went well.
|
|
401
|
+
def propagate_pre_exec_failure
|
|
402
|
+
begin
|
|
403
|
+
e = Marshal.load child_process_status
|
|
404
|
+
raise(Exception === e ? e : "unknown failure: #{e.inspect}")
|
|
405
|
+
rescue EOFError # If we get an EOF error, then the exec was successful
|
|
406
|
+
true
|
|
407
|
+
ensure
|
|
408
|
+
child_process_status.close
|
|
409
|
+
end
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
end
|
|
413
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# inspired by/cargo-culted from http://stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
|
|
2
|
+
# On Apr 6, 2010, at 3:00 PM, Stanislav Vitvitskiy wrote:
|
|
3
|
+
#
|
|
4
|
+
# It's free to use / modify / distribute. No need to mention anything. Just copy/paste and use.
|
|
5
|
+
#
|
|
6
|
+
# Regards,
|
|
7
|
+
# Stan
|
|
8
|
+
|
|
9
|
+
require 'net/http'
|
|
10
|
+
require 'mixlib/authentication/signedheaderauth'
|
|
11
|
+
require 'openssl'
|
|
12
|
+
require 'chef/version'
|
|
13
|
+
|
|
14
|
+
class Chef
|
|
15
|
+
class StreamingCookbookUploader
|
|
16
|
+
|
|
17
|
+
DefaultHeaders = { 'accept' => 'application/json', 'x-chef-version' => ::Chef::VERSION }
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
|
|
21
|
+
def post(to_url, user_id, secret_key_filename, params = {}, headers = {})
|
|
22
|
+
make_request(:post, to_url, user_id, secret_key_filename, params, headers)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def put(to_url, user_id, secret_key_filename, params = {}, headers = {})
|
|
26
|
+
make_request(:put, to_url, user_id, secret_key_filename, params, headers)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {})
|
|
30
|
+
boundary = '----RubyMultipartClient' + rand(1000000).to_s + 'ZZZZZ'
|
|
31
|
+
parts = []
|
|
32
|
+
content_file = nil
|
|
33
|
+
|
|
34
|
+
timestamp = Time.now.utc.iso8601
|
|
35
|
+
secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename))
|
|
36
|
+
|
|
37
|
+
unless params.nil? || params.empty?
|
|
38
|
+
params.each do |key, value|
|
|
39
|
+
if value.kind_of?(File)
|
|
40
|
+
content_file = value
|
|
41
|
+
filepath = value.path
|
|
42
|
+
filename = File.basename(filepath)
|
|
43
|
+
parts << StringPart.new( "--" + boundary + "\r\n" +
|
|
44
|
+
"Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" +
|
|
45
|
+
"Content-Type: application/octet-stream\r\n\r\n")
|
|
46
|
+
parts << StreamPart.new(value, File.size(filepath))
|
|
47
|
+
parts << StringPart.new("\r\n")
|
|
48
|
+
else
|
|
49
|
+
parts << StringPart.new( "--" + boundary + "\r\n" +
|
|
50
|
+
"Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n")
|
|
51
|
+
parts << StringPart.new(value.to_s + "\r\n")
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
parts << StringPart.new("--" + boundary + "--\r\n")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
body_stream = MultipartStream.new(parts)
|
|
58
|
+
|
|
59
|
+
timestamp = Time.now.utc.iso8601
|
|
60
|
+
|
|
61
|
+
url = URI.parse(to_url)
|
|
62
|
+
|
|
63
|
+
Chef::Log.logger.debug("Signing: method: #{http_verb}, path: #{url.path}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{timestamp}")
|
|
64
|
+
|
|
65
|
+
# We use the body for signing the request if the file parameter
|
|
66
|
+
# wasn't a valid file or wasn't included. Extract the body (with
|
|
67
|
+
# multi-part delimiters intact) to sign the request.
|
|
68
|
+
# TODO: tim: 2009-12-28: It'd be nice to remove this special case, and
|
|
69
|
+
# always hash the entire request body. In the file case it would just be
|
|
70
|
+
# expanded multipart text - the entire body of the POST.
|
|
71
|
+
content_body = parts.inject("") { |result,part| result + part.read(0, part.size) }
|
|
72
|
+
content_file.rewind if content_file # we consumed the file for the above operation, so rewind it.
|
|
73
|
+
|
|
74
|
+
signing_options = {
|
|
75
|
+
:http_method=>http_verb,
|
|
76
|
+
:path=>url.path,
|
|
77
|
+
:user_id=>user_id,
|
|
78
|
+
:timestamp=>timestamp}
|
|
79
|
+
(content_file && signing_options[:file] = content_file) || (signing_options[:body] = (content_body || ""))
|
|
80
|
+
|
|
81
|
+
headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object(signing_options).sign(secret_key))
|
|
82
|
+
|
|
83
|
+
content_file.rewind if content_file
|
|
84
|
+
|
|
85
|
+
# net/http doesn't like symbols for header keys, so we'll to_s each one just in case
|
|
86
|
+
headers = DefaultHeaders.merge(Hash[*headers.map{ |k,v| [k.to_s, v] }.flatten])
|
|
87
|
+
|
|
88
|
+
req = case http_verb
|
|
89
|
+
when :put
|
|
90
|
+
Net::HTTP::Put.new(url.path, headers)
|
|
91
|
+
when :post
|
|
92
|
+
Net::HTTP::Post.new(url.path, headers)
|
|
93
|
+
end
|
|
94
|
+
req.content_length = body_stream.size
|
|
95
|
+
req.content_type = 'multipart/form-data; boundary=' + boundary unless parts.empty?
|
|
96
|
+
req.body_stream = body_stream
|
|
97
|
+
|
|
98
|
+
http = Net::HTTP.new(url.host, url.port)
|
|
99
|
+
if url.scheme == "https"
|
|
100
|
+
http.use_ssl = true
|
|
101
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
102
|
+
end
|
|
103
|
+
res = http.request(req)
|
|
104
|
+
#res = http.start {|http_proc| http_proc.request(req) }
|
|
105
|
+
|
|
106
|
+
# alias status to code and to_s to body for test purposes
|
|
107
|
+
# TODO: stop the following madness!
|
|
108
|
+
class << res
|
|
109
|
+
alias :to_s :body
|
|
110
|
+
|
|
111
|
+
# BUGBUG this makes the response compatible with what respsonse_steps expects to test headers (response.headers[] -> response[])
|
|
112
|
+
def headers
|
|
113
|
+
self
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def status
|
|
117
|
+
code.to_i
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
res
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
class StreamPart
|
|
126
|
+
def initialize(stream, size)
|
|
127
|
+
@stream, @size = stream, size
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def size
|
|
131
|
+
@size
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# read the specified amount from the stream
|
|
135
|
+
def read(offset, how_much)
|
|
136
|
+
@stream.read(how_much)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
class StringPart
|
|
141
|
+
def initialize(str)
|
|
142
|
+
@str = str
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def size
|
|
146
|
+
@str.length
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# read the specified amount from the string startiung at the offset
|
|
150
|
+
def read(offset, how_much)
|
|
151
|
+
@str[offset, how_much]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
class MultipartStream
|
|
156
|
+
def initialize(parts)
|
|
157
|
+
@parts = parts
|
|
158
|
+
@part_no = 0
|
|
159
|
+
@part_offset = 0
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def size
|
|
163
|
+
@parts.inject(0) {|size, part| size + part.size}
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def read(how_much)
|
|
167
|
+
return nil if @part_no >= @parts.size
|
|
168
|
+
|
|
169
|
+
how_much_current_part = @parts[@part_no].size - @part_offset
|
|
170
|
+
|
|
171
|
+
how_much_current_part = if how_much_current_part > how_much
|
|
172
|
+
how_much
|
|
173
|
+
else
|
|
174
|
+
how_much_current_part
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
how_much_next_part = how_much - how_much_current_part
|
|
178
|
+
|
|
179
|
+
current_part = @parts[@part_no].read(@part_offset, how_much_current_part)
|
|
180
|
+
|
|
181
|
+
# recurse into the next part if the current one was not large enough
|
|
182
|
+
if how_much_next_part > 0
|
|
183
|
+
@part_no += 1
|
|
184
|
+
@part_offset = 0
|
|
185
|
+
next_part = read(how_much_next_part)
|
|
186
|
+
current_part + if next_part
|
|
187
|
+
next_part
|
|
188
|
+
else
|
|
189
|
+
''
|
|
190
|
+
end
|
|
191
|
+
else
|
|
192
|
+
@part_offset += how_much_current_part
|
|
193
|
+
current_part
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
end
|