1
# Copyright 2008 Amazon.com, Inc. or its affiliates. All Rights
2
# Reserved. Licensed under the Amazon Software License (the
3
# "License"). You may not use this file except in compliance with the
4
# License. A copy of the License is located at
5
# http://aws.amazon.com/asl or in the "license" file accompanying this
6
# file. This file is distributed on an "AS IS" BASIS, WITHOUT
7
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
8
# the License for the specific language governing permissions and
9
# limitations under the License.
11
require 'ec2/common/s3support'
12
require 'ec2/amitools/migratebundleparameters'
13
require 'ec2/amitools/migratemanifest'
14
require 'ec2/amitools/downloadbundle'
15
require 'ec2/amitools/uploadbundle'
17
require 'ec2/amitools/tool_base'
19
MIGRATE_BUNDLE_NAME = 'ec2-migrate-bundle'
21
#------------------------------------------------------------------------------#
23
MIGRATE_BUNDLE_MANUAL =<<TEXT
24
#{MIGRATE_BUNDLE_NAME} is a command line tool to assist with migrating AMIs to new regions.
26
#{MIGRATE_BUNDLE_NAME} will:
27
- download the manifest of the specified AMI
28
- attempt to automatically find replacement kernels and ramdisks
29
- optionally replace kernels and ramdisks with user-specified replacements
30
- copy AMI into new bucket
31
- upload new migrated manifest into new bucket
33
This tool will not register the AMI in the new region.
37
class BundleMigrator < AMITool
39
# If we return true, we have a v2-compliant name.
40
# If we return false, we wish to use a bad name.
41
# Otherwise we quietly wander off to die in peace.
42
def check_bucket_name(bucket, interactive)
43
if EC2::Common::S3Support::bucket_name_s3_v2_safe?(bucket)
48
$stdout.puts "The specified bucket is not S3 v2 safe (see S3 documentation for details):"
50
$stdout.print "Are you sure you want to continue? [y/N]: "
52
Timeout::timeout(PROMPT_TIMEOUT) do
54
if instr[0..0] =~ /[Yy]/
57
raise EC2FatalError.new(2, nil)
60
raise PromptTimeout.new("bucket name confirmation")
63
# If we're in batch mode, assume the customer knows what he wants.
68
def with_temp_dir(manifest_name)
69
# Set up temporary dir
70
tempdir = File::join(Dir::tmpdir, "ami-migration-#{manifest_name}".gsub("/","-"))
71
if File::exists?(tempdir)
72
raise EC2FatalError.new(2, "Temporary directory '#{tempdir}' already exists. Please delete or rename it and try again.")
76
# Let the caller use it
78
result = yield tempdir
81
FileUtils::rm_rf(tempdir)
85
FileUtils::rm_rf(tempdir)
90
s = "#{uri.scheme}://#{uri.host}:#{uri.port}#{uri.path}"
91
# Remove the trailing '/'.
92
return (s[-1..-1] == "/" ? s[0..-2] : s)
95
def make_s3_connection(s3_url, user, pass, bucket)
96
s3_uri = URI.parse(s3_url)
97
s3_url = uri2string(s3_uri)
98
v2_bucket = EC2::Common::S3Support::bucket_name_s3_v2_safe?(bucket)
99
EC2::Common::S3Support.new(s3_url, user, pass, (v2_bucket ? nil : :path))
102
def download_manifest(s3_conn, bucket, manifest_name, manifest_path, user_pk_path, retry_stuff)
103
BundleDownloader.new().download_manifest(s3_conn,
111
def get_part_filenames(manifest_path, user_cert_path)
112
manifest = ManifestMigrator.new().get_manifest(manifest_path, user_cert_path)
113
manifest.parts.collect { |part| part.filename }.sort
116
def copy_part(s3_conn, bucket, dest_bucket, part, acl, retry_copy)
117
source = "/#{bucket}/#{part}"
118
retry_s3(retry_copy) do
120
s3_conn.copy(dest_bucket, part, source, {"x-amz-acl"=>acl})
123
raise TryFailed.new("Failed to copy \"#{part}\": #{e.message}")
128
def create_bucket(dest_s3_conn, dest_bucket, location, retry_stuff)
129
uploader = BundleUploader.new()
130
dest_bucket_location = uploader.get_bucket_location(dest_s3_conn, dest_bucket)
131
uploader.create_bucket(dest_s3_conn, dest_bucket, dest_bucket_location, location, retry_stuff)
134
def upload_manifest(s3_conn, manifest_name, manifest_path, bucket, tempdir, acl, retry_stuff)
135
BundleUploader.new().upload(s3_conn, bucket, manifest_name, manifest_path, acl, retry_stuff)
138
def migrate_bundle(s3_url,
156
src_s3_conn = make_s3_connection(s3_url, user, pass, bucket)
157
dest_s3_conn = make_s3_connection(s3_url, user, pass, dest_bucket)
159
# Locate destination bucket and create it if necessary.
160
bu = BundleUploader.new()
161
bucket_location = bu.get_bucket_location(dest_s3_conn, dest_bucket)
162
bu.create_bucket(dest_s3_conn, dest_bucket, bucket_location, location, retry_stuff)
164
# Region/location hack:
166
location ||= bucket_location
168
region = "eu-west-1" if location == "EU"
169
puts "Region not provided, guessing from S3 location: #{region}"
172
with_temp_dir(manifest_name) do |tempdir|
173
manifest_path = File::join(tempdir, "temp-migration.manifest.xml")
174
download_manifest(src_s3_conn, bucket, manifest_name, manifest_path, user_pk_path, retry_stuff)
176
ManifestMigrator.new().migrate_manifest(manifest_path,
187
create_bucket(dest_s3_conn, dest_bucket, location, retry_stuff)
188
get_part_filenames(manifest_path, user_cert_path).each do |part|
189
$stdout.puts("Copying '#{part}'...")
190
copy_part(dest_s3_conn, bucket, dest_bucket, part, acl, retry_stuff)
192
upload_manifest(dest_s3_conn, manifest_name, manifest_path, dest_bucket, tempdir, acl, retry_stuff)
196
#------------------------------------------------------------------------------#
198
#------------------------------------------------------------------------------#
201
MIGRATE_BUNDLE_MANUAL
209
check_bucket_name(p.dest_bucket, p.interactive?)
211
migrate_bundle(p.s3_url,
229
$stdout.puts("\nYour new bundle is in S3 at the following location:")
230
$stdout.puts("#{p.dest_bucket}/#{p.manifest_name}")
231
$stdout.puts("Please register it using your favorite EC2 client.")
236
#------------------------------------------------------------------------------#
237
# Script entry point. Execute only if this file is being executed.
239
BundleMigrator.new().run(MigrateBundleParameters)