Class: BundleUploader
- Defined in:
- lib/ec2/amitools/uploadbundle.rb
Overview
Upload the specified file.
Constant Summary
Constants inherited from AMITool
AMITool::BACKOFF_PERIOD, AMITool::MAX_TRIES, AMITool::PROMPT_TIMEOUT
Instance Method Summary collapse
-
#check_bucket_location(bucket, bucket_location, location) ⇒ Object
Check if the bucket exists and is in an appropriate location.
-
#check_bucket_name(bucket) ⇒ Object
If we return true, we have a v2-compliant name.
-
#check_govcloud_override(s3_url) ⇒ Object
force v1 S3 addressing when using govcloud endpoint.
-
#create_bucket(s3_conn, bucket, bucket_location, location, retry_create) ⇒ Object
Create the specified bucket if it does not exist.
-
#cross_region?(location, bucket_location) ⇒ Boolean
This is very much a best effort attempt.
-
#get_availability_zone ⇒ Object
Availability zone names are generally in the format => $REGION$ZONENUMBER.
-
#get_bucket_location(s3_conn, bucket) ⇒ Object
Get the bucket’s location.
-
#get_manual ⇒ Object
——————————————————————————# Overrides ——————————————————————————#.
-
#get_md5(file) ⇒ Object
—————————————————————————-#.
- #get_name ⇒ Object
-
#get_part_info(manifest) ⇒ Object
Return a list of bundle part filename and part number tuples from the manifest.
-
#get_region ⇒ Object
——————————————————————————#.
-
#get_s3_conn(s3_url, user, pass, method, sigv, region = nil) ⇒ Object
——————————————————————————#.
- #main(p) ⇒ Object
- #upload(s3_conn, bucket, key, file, acl, retry_upload) ⇒ Object
-
#upload_bundle(url, bucket, keyprefix, user, pass, location, manifest_file, retry_stuff, part, directory, acl, skipmanifest, sigv, region) ⇒ Object
Get parameters and display help or manual if necessary.
-
#uri2string(uri) ⇒ Object
——————————————————————————#.
-
#warn_about_migrating ⇒ Object
——————————————————————————#.
Methods inherited from AMITool
#get_parameters, #handle_early_exit_parameters, #interactive?, #interactive_prompt, #retry_s3, #run, #warn_confirm
Instance Method Details
#check_bucket_location(bucket, bucket_location, location) ⇒ Object
Check if the bucket exists and is in an appropriate location.
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 130 def check_bucket_location(bucket, bucket_location, location) if bucket_location.nil? # The bucket does not exist. Safe, but we need to create it. return false end if location.nil? # The bucket exists and we don't care where it is. return true end unless [bucket_location, AwsRegion.guess_region_from_s3_bucket(bucket_location)].include?(location) # The bucket isn't where we want it. This is a problem. raise BucketLocationError.new(bucket, location, bucket_location) end # The bucket exists and is in the right place. return true end |
#check_bucket_name(bucket) ⇒ Object
If we return true, we have a v2-compliant name. If we return false, we wish to use a bad name. Otherwise we quietly wander off to die in peace.
177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 177 def check_bucket_name(bucket) if EC2::Common::S3Support::bucket_name_s3_v2_safe?(bucket) return true end = "The specified bucket is not S3 v2 safe (see S3 documentation for details):\n#{bucket}" if warn_confirm() # Assume the customer knows what he's doing. return false else # We've been asked to stop, so quietly wander off to die in peace. raise EC2StopExecution.new() end end |
#check_govcloud_override(s3_url) ⇒ Object
force v1 S3 addressing when using govcloud endpoint
192 193 194 195 196 197 198 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 192 def check_govcloud_override(s3_url) if s3_url =~ /s3-us-gov-west-1/ false else true end end |
#create_bucket(s3_conn, bucket, bucket_location, location, retry_create) ⇒ Object
Create the specified bucket if it does not exist.
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 150 def create_bucket(s3_conn, bucket, bucket_location, location, retry_create) begin if check_bucket_location(bucket, bucket_location, location) return true end $stdout.puts "Creating bucket..." retry_s3(retry_create) do error = "Could not create or access bucket #{bucket}" begin rsp = s3_conn.create_bucket(bucket, location == :unconstrained ? nil : location) rescue EC2::Common::HTTP::Error::Retrieve => e error += ": server response #{e.} #{e.code}" raise TryFailed.new(e.) rescue RuntimeError => e error += ": error message #{e.}" raise e end end end end |
#cross_region?(location, bucket_location) ⇒ Boolean
This is very much a best effort attempt. If in doubt, we don’t warn.
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 218 def cross_region?(location, bucket_location) # If the bucket exists, its S3 location is canonical. s3_region = bucket_location s3_region ||= location s3_region ||= :unconstrained region = get_region() if region.nil? # If we can't get the region, assume we're fine since there's # nothing more we can do. return false end return s3_region != AwsRegion.get_s3_location(region) end |
#get_availability_zone ⇒ Object
Availability zone names are generally in the format => $REGION$ZONENUMBER. Examples being us-east-1b, us-east-1c, etc.
85 86 87 88 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 85 def get_availability_zone() instance_data = EC2::InstanceData.new instance_data.availability_zone end |
#get_bucket_location(s3_conn, bucket) ⇒ Object
Get the bucket’s location.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 111 def get_bucket_location(s3_conn, bucket) begin response = s3_conn.get_bucket_location(bucket) rescue EC2::Common::HTTP::Error::Retrieve => e if e.code == 404 # We have a "Not found" S3 response, which probably means the bucket doesn't exist. return nil end raise e end $stdout.puts "check_bucket_location response: #{response.body}" if @debug and response.text? docroot = REXML::Document.new(response.body).root bucket_location = REXML::XPath.first(docroot, '/LocationConstraint').text bucket_location ||= :unconstrained end |
#get_manual ⇒ Object
——————————————————————————# Overrides ——————————————————————————#
330 331 332 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 330 def get_manual() UPLOAD_BUNDLE_MANUAL end |
#get_md5(file) ⇒ Object
—————————————————————————-#
75 76 77 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 75 def get_md5(file) Base64::encode64(Digest::MD5::digest(File.open(file) { |f| f.read })).strip end |
#get_name ⇒ Object
334 335 336 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 334 def get_name() UPLOAD_BUNDLE_NAME end |
#get_part_info(manifest) ⇒ Object
Return a list of bundle part filename and part number tuples from the manifest.
93 94 95 96 97 98 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 93 def get_part_info(manifest) parts = manifest.ami_part_info_list.map do |part| [part['filename'], part['index']] end parts.sort end |
#get_region ⇒ Object
——————————————————————————#
202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 202 def get_region() zone = get_availability_zone() if zone.nil? return nil end # assume region names do not have a common naming scheme. Therefore we manually go through all known region names AwsRegion.regions.each do |region| match = zone.match(region) if not match.nil? return region end end nil end |
#get_s3_conn(s3_url, user, pass, method, sigv, region = nil) ⇒ Object
——————————————————————————#
252 253 254 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 252 def get_s3_conn(s3_url, user, pass, method, sigv, region=nil) EC2::Common::S3Support.new(s3_url, user, pass, method, @debug, sigv, region) end |
#main(p) ⇒ Object
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 338 def main(p) upload_bundle(p.url, p.bucket, p.keyprefix, p.user, p.pass, p.location, p.manifest, p.retry, p.part, p.directory, p.acl, p.skipmanifest, p.sigv, p.region) end |
#upload(s3_conn, bucket, key, file, acl, retry_upload) ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 59 def upload(s3_conn, bucket, key, file, acl, retry_upload) retry_s3(retry_upload) do begin md5 = get_md5(file) s3_conn.put(bucket, key, file, {"x-amz-acl"=>acl, "content-md5"=>md5}) return rescue EC2::Common::HTTP::Error::PathInvalid => e raise FileNotFound(file) rescue => e raise TryFailed.new("Failed to upload \"#{file}\": #{e.}") end end end |
#upload_bundle(url, bucket, keyprefix, user, pass, location, manifest_file, retry_stuff, part, directory, acl, skipmanifest, sigv, region) ⇒ Object
Get parameters and display help or manual if necessary.
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 261 def upload_bundle(url, bucket, keyprefix, user, pass, location, manifest_file, retry_stuff, part, directory, acl, skipmanifest, sigv, region) begin # Get the S3 URL. s3_uri = URI.parse(url) s3_url = uri2string(s3_uri) v2_bucket = check_bucket_name(bucket) and check_govcloud_override(s3_url) s3_conn = get_s3_conn(s3_url, user, pass, (v2_bucket ? nil : :path), sigv, region) # Get current location and bucket location. bucket_location = get_bucket_location(s3_conn, bucket) # Load manifest. xml = File.open(manifest_file) { |f| f.read } manifest = ManifestV20071010.new(xml) # If in interactive mode, warn when bundling a kernel into our AMI and we are uploading cross-region if interactive? and manifest.kernel_id and cross_region?(location, bucket_location) () end # Create storage bucket if required. create_bucket(s3_conn, bucket, bucket_location, location, retry_stuff) # Upload AMI bundle parts. $stdout.puts "Uploading bundled image parts to the S3 bucket #{bucket} ..." get_part_info(manifest).each do |part_info| if part.nil? or (part_info[1] >= part) path = File.join(directory, part_info[0]) upload(s3_conn, bucket, keyprefix + part_info[0], path, acl, retry_stuff) $stdout.puts "Uploaded #{part_info[0]}" else $stdout.puts "Skipping #{part_info[0]}" end end # Encrypt and upload manifest. unless skipmanifest $stdout.puts "Uploading manifest ..." upload(s3_conn, bucket, keyprefix + File::basename(manifest_file), manifest_file, acl, retry_stuff) $stdout.puts "Uploaded manifest." $stdout.puts 'Manifest uploaded to: %s/%s' % [bucket, keyprefix + File::basename(manifest_file)] else $stdout.puts "Skipping manifest." end $stdout.puts 'Bundle upload completed.' rescue EC2::Common::HTTP::Error => e $stderr.puts e.backtrace if @debug raise S3Error.new(e.) end end |
#uri2string(uri) ⇒ Object
——————————————————————————#
102 103 104 105 106 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 102 def uri2string(uri) s = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}" # Remove the trailing '/'. return (s[-1..-1] == "/" ? s[0..-2] : s) end |
#warn_about_migrating ⇒ Object
——————————————————————————#
238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/ec2/amitools/uploadbundle.rb', line 238 def () = ["You are bundling in one region, but uploading to another. If the kernel", "or ramdisk associated with this AMI are not in the target region, AMI", "registration will fail.", "You can use the ec2-migrate-manifest tool to update your manifest file", "with a kernel and ramdisk that exist in the target region.", ].join("\n") unless warn_confirm() raise EC2StopExecution.new() end end |