Class: Chef::CookbookUploader
Instance Attribute Summary collapse
-
#concurrency ⇒ Object
readonly
Returns the value of attribute concurrency.
-
#cookbooks ⇒ Object
readonly
Returns the value of attribute cookbooks.
-
#opts ⇒ Object
readonly
Returns the value of attribute opts.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#rest ⇒ Object
readonly
Returns the value of attribute rest.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(cookbooks, path, opts = {}) ⇒ CookbookUploader
constructor
Creates a new CookbookUploader.
- #upload_cookbooks ⇒ Object
- #uploader_function_for(file, checksum, url, checksums_to_upload) ⇒ Object
- #validate_cookbooks ⇒ Object
- #worker_thread(work_queue) ⇒ Object
Constructor Details
#initialize(cookbooks, path, opts = {}) ⇒ CookbookUploader
Creates a new CookbookUploader.
Arguments:
- cookbooks:
-
A Chef::CookbookVersion or array of them describing the cookbook(s) to be uploaded
- path:
-
A String or Array of Strings representing the base paths to the cookbook repositories.
- opts:
-
(optional) An options Hash
Options:
-
:force indicates that the uploader should set the force option when
uploading the cookbook. This allows frozen CookbookVersion documents on the server to be overwritten (otherwise a 409 is returned by the server) -
:rest A Chef::REST object that you have configured the way you like it.
If you don't provide this, one will be created using the values in Chef::Config. -
:concurrency An integer that decided how many threads will be used to
perform concurrent uploads
56 57 58 59 60 61 |
# File 'lib/chef/cookbook_uploader.rb', line 56 def initialize(cookbooks, path, opts={}) @path, @opts = path, opts @cookbooks = Array(cookbooks) @rest = opts[:rest] || Chef::REST.new(Chef::Config[:chef_server_url]) @concurrency = opts[:concurrency] || 10 end |
Instance Attribute Details
#concurrency ⇒ Object (readonly)
Returns the value of attribute concurrency.
37 38 39 |
# File 'lib/chef/cookbook_uploader.rb', line 37 def concurrency @concurrency end |
#cookbooks ⇒ Object (readonly)
Returns the value of attribute cookbooks.
33 34 35 |
# File 'lib/chef/cookbook_uploader.rb', line 33 def cookbooks @cookbooks end |
#opts ⇒ Object (readonly)
Returns the value of attribute opts.
35 36 37 |
# File 'lib/chef/cookbook_uploader.rb', line 35 def opts @opts end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
34 35 36 |
# File 'lib/chef/cookbook_uploader.rb', line 34 def path @path end |
#rest ⇒ Object (readonly)
Returns the value of attribute rest.
36 37 38 |
# File 'lib/chef/cookbook_uploader.rb', line 36 def rest @rest end |
Class Method Details
.setup_worker_threads(concurrency = 10) ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/chef/cookbook_uploader.rb', line 20 def self.setup_worker_threads(concurrency=10) @worker_threads ||= begin work_queue (1..concurrency).map do Thread.new do loop do work_queue.pop.call end end end end end |
.work_queue ⇒ Object
16 17 18 |
# File 'lib/chef/cookbook_uploader.rb', line 16 def self.work_queue @work_queue ||= Queue.new end |
Instance Method Details
#upload_cookbooks ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 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 |
# File 'lib/chef/cookbook_uploader.rb', line 63 def upload_cookbooks Thread.abort_on_exception = true # Syntax Check validate_cookbooks # generate checksums of cookbook files and create a sandbox checksum_files = {} cookbooks.each do |cb| Chef::Log.info("Saving #{cb.name}") checksum_files.merge!(cb.checksums) end checksums = checksum_files.inject({}){|memo,elt| memo[elt.first]=nil ; memo} new_sandbox = rest.post_rest("sandboxes", { :checksums => checksums }) Chef::Log.info("Uploading files") self.class.setup_worker_threads(concurrency) checksums_to_upload = Set.new # upload the new checksums and commit the sandbox new_sandbox['checksums'].each do |checksum, info| if info['needs_upload'] == true checksums_to_upload << checksum Chef::Log.info("Uploading #{checksum_files[checksum]} (checksum hex = #{checksum}) to #{info['url']}") self.class.work_queue << uploader_function_for(checksum_files[checksum], checksum, info['url'], checksums_to_upload) else Chef::Log.debug("#{checksum_files[checksum]} has not changed") end end until checksums_to_upload.empty? sleep 0.1 end sandbox_url = new_sandbox['uri'] Chef::Log.debug("Committing sandbox") # Retry if S3 is claims a checksum doesn't exist (the eventual # in eventual consistency) retries = 0 begin rest.put_rest(sandbox_url, {:is_completed => true}) rescue Net::HTTPServerException => e if e. =~ /^400/ && (retries += 1) <= 5 sleep 2 retry else raise end end # files are uploaded, so save the manifest cookbooks.each do |cb| save_url = opts[:force] ? cb.force_save_url : cb.save_url begin rest.put_rest(save_url, cb) rescue Net::HTTPServerException => e case e.response.code when "409" raise Chef::Exceptions::CookbookFrozen, "Version #{cb.version} of cookbook #{cb.name} is frozen. Use --force to override." else raise end end end Chef::Log.info("Upload complete!") end |
#uploader_function_for(file, checksum, url, checksums_to_upload) ⇒ Object
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 |
# File 'lib/chef/cookbook_uploader.rb', line 136 def uploader_function_for(file, checksum, url, checksums_to_upload) lambda do # Checksum is the hexadecimal representation of the md5, # but we need the base64 encoding for the content-md5 # header checksum64 = Base64.encode64([checksum].pack("H*")).strip = Time.now.utc.iso8601 file_contents = File.open(file, "rb") {|f| f.read} # TODO - 5/28/2010, cw: make signing and sending the request streaming headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, "accept" => 'application/json' } if rest.signing_key sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object( :http_method => :put, :path => URI.parse(url).path, :body => file_contents, :timestamp => , :user_id => rest.client_name ) headers.merge!(sign_obj.sign(OpenSSL::PKey::RSA.new(rest.signing_key))) end begin Chef::HTTP::Simple.new(url, :headers=>headers).put(url, file_contents) checksums_to_upload.delete(checksum) rescue Net::HTTPServerException, Net::HTTPFatalError, Errno::ECONNREFUSED, Timeout::Error, Errno::ETIMEDOUT, SocketError => e = "Failed to upload #{file} (#{checksum}) to #{url} : #{e.message}" << "\n#{e.response.body}" if e.respond_to?(:response) Chef::Knife.ui.error() raise end end end |
#validate_cookbooks ⇒ Object
169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/chef/cookbook_uploader.rb', line 169 def validate_cookbooks cookbooks.each do |cb| syntax_checker = Chef::Cookbook::SyntaxCheck.for_cookbook(cb.name, @user_cookbook_path) Chef::Log.info("Validating ruby files") exit(1) unless syntax_checker.validate_ruby_files Chef::Log.info("Validating templates") exit(1) unless syntax_checker.validate_templates Chef::Log.info("Syntax OK") true end end |
#worker_thread(work_queue) ⇒ Object
133 134 |
# File 'lib/chef/cookbook_uploader.rb', line 133 def worker_thread(work_queue) end |