Class: Chef::Knife::Core::CookbookSiteStreamingUploader
- Inherits:
-
Object
- Object
- Chef::Knife::Core::CookbookSiteStreamingUploader
- Defined in:
- lib/chef/knife/core/cookbook_site_streaming_uploader.rb
Overview
Chef::Knife::Core::CookbookSiteStreamingUploader
A streaming multipart HTTP upload implementation. Used to upload cookbooks (in tarball form) to supermarket.chef.io
inspired by stanislavvitvitskiy.blogspot.com/2008/12/multipart-post-in-ruby.html
Defined Under Namespace
Classes: MultipartStream, StreamPart, StringPart
Constant Summary collapse
- DefaultHeaders =
rubocop:disable Naming/ConstantName
{ "accept" => "application/json", "x-chef-version" => ::Chef::VERSION }.freeze
Class Method Summary collapse
- .create_build_dir(cookbook) ⇒ Object
- .make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {}) ⇒ Object
- .post(to_url, user_id, secret_key_filename, params = {}, headers = {}) ⇒ Object
- .put(to_url, user_id, secret_key_filename, params = {}, headers = {}) ⇒ Object
Class Method Details
.create_build_dir(cookbook) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/chef/knife/core/cookbook_site_streaming_uploader.rb', line 47 def create_build_dir(cookbook) tmp_cookbook_path = Tempfile.new("#{ChefUtils::Dist::Infra::SHORT}-#{cookbook.name}-build") tmp_cookbook_path.close tmp_cookbook_dir = tmp_cookbook_path.path File.unlink(tmp_cookbook_dir) FileUtils.mkdir_p(tmp_cookbook_dir) Chef::Log.trace("Staging at #{tmp_cookbook_dir}") checksums_to_on_disk_paths = cookbook.checksums cookbook.each_file do |manifest_record| path_in_cookbook = manifest_record[:path] on_disk_path = checksums_to_on_disk_paths[manifest_record[:checksum]] dest = File.join(tmp_cookbook_dir, cookbook.name.to_s, path_in_cookbook) FileUtils.mkdir_p(File.dirname(dest)) Chef::Log.trace("Staging #{on_disk_path} to #{dest}") FileUtils.cp(on_disk_path, dest) end # First, generate metadata Chef::Log.trace("Generating metadata") kcm = Chef::Knife::CookbookMetadata.new kcm.config[:cookbook_path] = [ tmp_cookbook_dir ] kcm.name_args = [ cookbook.name.to_s ] kcm.run tmp_cookbook_dir end |
.make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {}) ⇒ Object
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/chef/knife/core/cookbook_site_streaming_uploader.rb', line 82 def make_request(http_verb, to_url, user_id, secret_key_filename, params = {}, headers = {}) boundary = "----RubyMultipartClient" + rand(1000000).to_s + "ZZZZZ" parts = [] content_file = nil secret_key = OpenSSL::PKey::RSA.new(File.read(secret_key_filename)) unless params.nil? || params.empty? params.each do |key, value| if value.is_a?(File) content_file = value filepath = value.path filename = File.basename(filepath) parts << StringPart.new( "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + key.to_s + "\"; filename=\"" + filename + "\"\r\n" + "Content-Type: application/octet-stream\r\n\r\n") parts << StreamPart.new(value, File.size(filepath)) parts << StringPart.new("\r\n") else parts << StringPart.new( "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"" + key.to_s + "\"\r\n\r\n") parts << StringPart.new(value.to_s + "\r\n") end end parts << StringPart.new("--" + boundary + "--\r\n") end body_stream = MultipartStream.new(parts) = Time.now.utc.iso8601 url = URI.parse(to_url) Chef::Log.logger.debug("Signing: method: #{http_verb}, url: #{url}, file: #{content_file}, User-id: #{user_id}, Timestamp: #{}") # We use the body for signing the request if the file parameter # wasn't a valid file or wasn't included. Extract the body (with # multi-part delimiters intact) to sign the request. # TODO: tim: 2009-12-28: It'd be nice to remove this special case, and # always hash the entire request body. In the file case it would just be # expanded multipart text - the entire body of the POST. content_body = parts.inject("") { |result, part| result + part.read(0, part.size) } content_file.rewind if content_file # we consumed the file for the above operation, so rewind it. = { http_method: http_verb, path: url.path, user_id: user_id, timestamp: } (content_file && [:file] = content_file) || ([:body] = (content_body || "")) headers.merge!(Mixlib::Authentication::SignedHeaderAuth.signing_object().sign(secret_key)) content_file.rewind if content_file # net/http doesn't like symbols for header keys, so we'll to_s each one just in case headers = DefaultHeaders.merge(Hash[*headers.map { |k, v| [k.to_s, v] }.flatten]) req = case http_verb when :put Net::HTTP::Put.new(url.path, headers) when :post Net::HTTP::Post.new(url.path, headers) end req.content_length = body_stream.size req.content_type = "multipart/form-data; boundary=" + boundary unless parts.empty? req.body_stream = body_stream http = Chef::HTTP::BasicClient.new(url).http_client res = http.request(req) # alias status to code and to_s to body for test purposes # TODO: stop the following madness! class << res alias :to_s :body # BUG this makes the response compatible with what response_steps expects to test headers (response.headers[] -> response[]) def headers # rubocop:disable Lint/NestedMethodDefinition self end def status # rubocop:disable Lint/NestedMethodDefinition code.to_i end end res end |
.post(to_url, user_id, secret_key_filename, params = {}, headers = {}) ⇒ Object
74 75 76 |
# File 'lib/chef/knife/core/cookbook_site_streaming_uploader.rb', line 74 def post(to_url, user_id, secret_key_filename, params = {}, headers = {}) make_request(:post, to_url, user_id, secret_key_filename, params, headers) end |
.put(to_url, user_id, secret_key_filename, params = {}, headers = {}) ⇒ Object
78 79 80 |
# File 'lib/chef/knife/core/cookbook_site_streaming_uploader.rb', line 78 def put(to_url, user_id, secret_key_filename, params = {}, headers = {}) make_request(:put, to_url, user_id, secret_key_filename, params, headers) end |