boxgrinder-build 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/Manifest +6 -5
- data/README.md +1 -1
- data/Rakefile +13 -20
- data/boxgrinder-build.gemspec +7 -10
- data/lib/boxgrinder-build/helpers/aws-helper.rb +81 -0
- data/lib/boxgrinder-build/helpers/ec2-helper.rb +182 -0
- data/lib/boxgrinder-build/helpers/guestfs-helper.rb +5 -1
- data/lib/boxgrinder-build/helpers/s3-helper.rb +124 -0
- data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +95 -299
- data/lib/boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin.rb +2 -1
- data/lib/boxgrinder-build/plugins/delivery/s3/s3-plugin.rb +67 -133
- data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +15 -14
- data/rubygem-boxgrinder-build.spec +23 -28
- data/spec/appliance-spec.rb +1 -0
- data/spec/helpers/augeas-helper-spec.rb +1 -0
- data/spec/helpers/ec2-helper-spec.rb +260 -0
- data/spec/helpers/guestfs-helper-spec.rb +34 -7
- data/spec/helpers/image-helper-spec.rb +1 -0
- data/spec/helpers/linux-helper-spec.rb +1 -0
- data/spec/helpers/package-helper-spec.rb +1 -0
- data/spec/helpers/plugin-helper-spec.rb +1 -0
- data/spec/helpers/s3-helper-spec.rb +168 -0
- data/spec/managers/plugin-manager-spec.rb +1 -0
- data/spec/plugins/base-plugin-spec.rb +1 -1
- data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +115 -204
- data/spec/plugins/delivery/elastichosts/elastichosts-plugin-spec.rb +5 -4
- data/spec/plugins/delivery/local/local-plugin-spec.rb +1 -0
- data/spec/plugins/delivery/s3/s3-plugin-spec.rb +143 -134
- data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +1 -0
- data/spec/plugins/os/centos/centos-plugin-spec.rb +1 -0
- data/spec/plugins/os/fedora/fedora-plugin-spec.rb +1 -0
- data/spec/plugins/os/rhel/rhel-plugin-spec.rb +1 -0
- data/spec/plugins/os/rpm-based/kickstart-spec.rb +5 -1
- data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +9 -7
- data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +1 -0
- data/spec/plugins/os/sl/sl-plugin-spec.rb +1 -0
- data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +1 -0
- data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +1 -0
- data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +1 -0
- metadata +17 -23
@@ -0,0 +1,124 @@
|
|
1
|
+
#
|
2
|
+
# Copyright 2010 Red Hat, Inc.
|
3
|
+
#
|
4
|
+
# This is free software; you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Lesser General Public License as
|
6
|
+
# published by the Free Software Foundation; either version 3 of
|
7
|
+
# the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This software is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this software; if not, write to the Free
|
16
|
+
# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
17
|
+
# 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
18
|
+
|
19
|
+
require 'boxgrinder-build/helpers/aws-helper'
|
20
|
+
|
21
|
+
module BoxGrinder
|
22
|
+
class S3Helper < AWSHelper
|
23
|
+
|
24
|
+
#AWS::S3 object should be instantiated already, as config can be inserted
|
25
|
+
#via global AWS.config or via AWS::S3.initialize
|
26
|
+
def initialize(ec2, s3, options={})
|
27
|
+
raise ArgumentError, "ec2 argument must not be nil" if ec2.nil?
|
28
|
+
raise ArgumentError, "s3 argument must not be nil" if s3.nil?
|
29
|
+
@ec2 = ec2
|
30
|
+
@s3 = s3
|
31
|
+
@log = options[:log] || LogHelper.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def bucket(options={})
|
35
|
+
defaults = {:bucket => nil, :acl => :private, :location_constraint => 'us-east-1', :create_if_missing => false}.merge!(options)
|
36
|
+
options = parse_opts(options, defaults)
|
37
|
+
|
38
|
+
s3b = @s3.buckets[options[:bucket]]
|
39
|
+
return s3b if s3b.exists?
|
40
|
+
return @s3.buckets.create(options[:bucket],
|
41
|
+
:acl => options[:acl],
|
42
|
+
:location_constraint => options[:location_constraint]) if options[:create_if_missing]
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
#aws-sdk 1.0.3 added .exists?
|
47
|
+
def object_exists?(s3_object)
|
48
|
+
s3_object.exists?
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete_folder(bucket, path)
|
52
|
+
bucket.objects.with_prefix(deslash(path)).map(&:delete)
|
53
|
+
end
|
54
|
+
|
55
|
+
def stub_s3obj(bucket, path)
|
56
|
+
bucket.objects[path]
|
57
|
+
end
|
58
|
+
|
59
|
+
def parse_path(path)
|
60
|
+
return '' if path == '/'
|
61
|
+
#Remove preceding and trailing slashes
|
62
|
+
deslash(path) << '/'
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.endpoints
|
66
|
+
ENDPOINTS
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
#Remove extraneous slashes on paths to ensure they are valid for S3
|
72
|
+
def deslash(path)
|
73
|
+
"#{path.gsub(/^(\/)*/, '').gsub(/(\/)*$/, '')}"
|
74
|
+
end
|
75
|
+
|
76
|
+
ENDPOINTS = {
|
77
|
+
'eu-west-1' => {
|
78
|
+
:endpoint => 's3-eu-west-1.amazonaws.com',
|
79
|
+
:location => 'EU',
|
80
|
+
:kernel => {
|
81
|
+
:i386 => {:aki => 'aki-4deec439'},
|
82
|
+
:x86_64 => {:aki => 'aki-4feec43b'}
|
83
|
+
}
|
84
|
+
},
|
85
|
+
|
86
|
+
'ap-southeast-1' => {
|
87
|
+
:endpoint => 's3-ap-southeast-1.amazonaws.com',
|
88
|
+
:location => 'ap-southeast-1',
|
89
|
+
:kernel => {
|
90
|
+
:i386 => {:aki => 'aki-13d5aa41'},
|
91
|
+
:x86_64 => {:aki => 'aki-11d5aa43'}
|
92
|
+
}
|
93
|
+
},
|
94
|
+
|
95
|
+
'ap-northeast-1' => {
|
96
|
+
:endpoint => 's3-ap-northeast-1.amazonaws.com',
|
97
|
+
:location => 'ap-northeast-1',
|
98
|
+
:kernel => {
|
99
|
+
:i386 => {:aki => 'aki-d209a2d3'},
|
100
|
+
:x86_64 => {:aki => 'aki-d409a2d5'}
|
101
|
+
}
|
102
|
+
},
|
103
|
+
|
104
|
+
'us-west-1' => {
|
105
|
+
:endpoint => 's3-us-west-1.amazonaws.com',
|
106
|
+
:location => 'us-west-1',
|
107
|
+
:kernel => {
|
108
|
+
:i386 => {:aki => 'aki-99a0f1dc'},
|
109
|
+
:x86_64 => {:aki => 'aki-9ba0f1de'}
|
110
|
+
}
|
111
|
+
},
|
112
|
+
|
113
|
+
'us-east-1' => {
|
114
|
+
:endpoint => 's3.amazonaws.com',
|
115
|
+
:location => '',
|
116
|
+
:kernel => {
|
117
|
+
:i386 => {:aki => 'aki-407d9529'},
|
118
|
+
:x86_64 => {:aki => 'aki-427d952b'}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -18,60 +18,14 @@
|
|
18
18
|
|
19
19
|
require 'rubygems'
|
20
20
|
require 'boxgrinder-build/plugins/base-plugin'
|
21
|
-
require '
|
21
|
+
require 'boxgrinder-build/helpers/ec2-helper'
|
22
|
+
require 'aws-sdk'
|
22
23
|
require 'open-uri'
|
23
24
|
require 'timeout'
|
24
25
|
require 'pp'
|
25
26
|
|
26
27
|
module BoxGrinder
|
27
28
|
class EBSPlugin < BasePlugin
|
28
|
-
KERNELS = {
|
29
|
-
'eu-west-1' => {
|
30
|
-
:endpoint => 'ec2.eu-west-1.amazonaws.com',
|
31
|
-
:location => 'EU',
|
32
|
-
:kernel => {
|
33
|
-
'i386' => {:aki => 'aki-4deec439'},
|
34
|
-
'x86_64' => {:aki => 'aki-4feec43b'}
|
35
|
-
}
|
36
|
-
},
|
37
|
-
|
38
|
-
'ap-southeast-1' => {
|
39
|
-
:endpoint => 'ec2.ap-southeast-1.amazonaws.com',
|
40
|
-
:location => 'ap-southeast-1',
|
41
|
-
:kernel => {
|
42
|
-
'i386' => {:aki => 'aki-13d5aa41'},
|
43
|
-
'x86_64' => {:aki => 'aki-11d5aa43'}
|
44
|
-
}
|
45
|
-
},
|
46
|
-
|
47
|
-
'ap-northeast-1' => {
|
48
|
-
:endpoint => 'ec2.ap-northeast-1.amazonaws.com',
|
49
|
-
:location => 'ap-northeast-1',
|
50
|
-
:kernel => {
|
51
|
-
'i386' => {:aki => 'aki-d209a2d3'},
|
52
|
-
'x86_64' => {:aki => 'aki-d409a2d5'}
|
53
|
-
}
|
54
|
-
|
55
|
-
},
|
56
|
-
|
57
|
-
'us-west-1' => {
|
58
|
-
:endpoint => 'ec2.us-west-1.amazonaws.com',
|
59
|
-
:location => 'us-west-1',
|
60
|
-
:kernel => {
|
61
|
-
'i386' => {:aki => 'aki-99a0f1dc'},
|
62
|
-
'x86_64' => {:aki => 'aki-9ba0f1de'}
|
63
|
-
}
|
64
|
-
},
|
65
|
-
|
66
|
-
'us-east-1' => {
|
67
|
-
:endpoint => 'ec2.amazonaws.com',
|
68
|
-
:location => '',
|
69
|
-
:kernel => {
|
70
|
-
'i386' => {:aki => 'aki-407d9529'},
|
71
|
-
'x86_64' => {:aki => 'aki-427d952b'}
|
72
|
-
}
|
73
|
-
}
|
74
|
-
}
|
75
29
|
|
76
30
|
ROOT_DEVICE_NAME = '/dev/sda1'
|
77
31
|
POLL_FREQ = 1 #second
|
@@ -79,24 +33,38 @@ module BoxGrinder
|
|
79
33
|
EC2_HOSTNAME_LOOKUP_TIMEOUT = 10
|
80
34
|
|
81
35
|
def validate
|
82
|
-
|
36
|
+
@ec2_endpoints = EC2Helper::endpoints
|
83
37
|
|
84
|
-
|
38
|
+
raise PluginValidationError, "You are trying to run this plugin on an invalid platform. You can run the EBS delivery plugin only on EC2." unless valid_platform?
|
39
|
+
|
40
|
+
@current_availability_zone = EC2Helper::current_availability_zone
|
41
|
+
@current_instance_id = EC2Helper::current_instance_id
|
42
|
+
@current_region = EC2Helper::availability_zone_to_region(@current_availability_zone)
|
85
43
|
|
86
44
|
set_default_config_value('availability_zone', @current_availability_zone)
|
87
45
|
set_default_config_value('delete_on_termination', true)
|
88
46
|
set_default_config_value('overwrite', false)
|
89
47
|
set_default_config_value('snapshot', false)
|
90
48
|
set_default_config_value('preserve_snapshots', false)
|
49
|
+
set_default_config_value('terminate_instances', false)
|
91
50
|
validate_plugin_config(['access_key', 'secret_access_key', 'account_number'], 'http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin')
|
92
51
|
|
93
52
|
raise PluginValidationError, "You can only convert to EBS type AMI appliances converted to EC2 format. Use '-p ec2' switch. For more info about EC2 plugin see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EC2_Platform_Plugin." unless @previous_plugin_info[:name] == :ec2
|
94
53
|
raise PluginValidationError, "You selected #{@plugin_config['availability_zone']} availability zone, but your instance is running in #{@current_availability_zone} zone. Please change availability zone in plugin configuration file to #{@current_availability_zone} (see http://boxgrinder.org/tutorials/boxgrinder-build-plugins/#EBS_Delivery_Plugin) or use another instance in #{@plugin_config['availability_zone']} zone to create your EBS AMI." if @plugin_config['availability_zone'] != @current_availability_zone
|
54
|
+
|
55
|
+
@plugin_config['account_number'].to_s.gsub!(/-/, '')
|
56
|
+
|
57
|
+
AWS.config(:access_key_id => @plugin_config['access_key'],
|
58
|
+
:secret_access_key => @plugin_config['secret_access_key'],
|
59
|
+
:ec2_endpoint => @ec2_endpoints[@current_region][:endpoint],
|
60
|
+
:max_retries => 5,
|
61
|
+
:use_ssl => @plugin_config['use_ssl'])
|
62
|
+
|
63
|
+
@ec2 = AWS::EC2.new
|
64
|
+
@ec2helper = EC2Helper.new(@ec2, :log => @log)
|
95
65
|
end
|
96
66
|
|
97
67
|
def after_init
|
98
|
-
@region = availability_zone_to_region(@current_availability_zone)
|
99
|
-
|
100
68
|
register_supported_os('fedora', ['13', '14', '15'])
|
101
69
|
register_supported_os('rhel', ['6'])
|
102
70
|
register_supported_os('centos', ['5'])
|
@@ -105,63 +73,50 @@ module BoxGrinder
|
|
105
73
|
def execute
|
106
74
|
ebs_appliance_description = "#{@appliance_config.summary} | Appliance version #{@appliance_config.version}.#{@appliance_config.release} | #{@appliance_config.hardware.arch} architecture"
|
107
75
|
|
108
|
-
@ec2 = AWS::EC2::Base.new(:access_key_id => @plugin_config['access_key'],
|
109
|
-
:secret_access_key => @plugin_config['secret_access_key'],
|
110
|
-
:server => KERNELS[@region][:endpoint]
|
111
|
-
)
|
112
|
-
|
113
76
|
@log.debug "Checking if appliance is already registered..."
|
77
|
+
ami = @ec2helper.ami_by_name(ebs_appliance_name)
|
114
78
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
elsif ami_info
|
121
|
-
@log.warn "EBS AMI '#{ebs_appliance_name}' is already registered as '#{ami_info.imageId}' (region: #{@region})."
|
79
|
+
if ami and @plugin_config['overwrite']
|
80
|
+
@log.info "Overwrite is enabled. Stomping existing assets."
|
81
|
+
stomp_ebs(ami)
|
82
|
+
elsif ami
|
83
|
+
@log.warn "EBS AMI '#{ami.name}' is already registered as '#{ami.id}' (region: #{@current_region})."
|
122
84
|
return
|
123
85
|
end
|
124
86
|
|
125
87
|
@log.info "Creating new EBS volume..."
|
126
|
-
|
127
88
|
size = 0
|
128
|
-
|
129
89
|
@appliance_config.hardware.partitions.each_value { |partition| size += partition['size'] }
|
130
90
|
|
131
|
-
# create_volume, ceiling to avoid
|
132
|
-
|
133
|
-
|
134
|
-
begin
|
91
|
+
# create_volume, ceiling to avoid non-Integer values as per https://issues.jboss.org/browse/BGBUILD-224
|
92
|
+
volume = @ec2.volumes.create(:size => size.ceil.to_i, :availability_zone => @plugin_config['availability_zone'])
|
135
93
|
|
136
|
-
@log.debug "Volume #{
|
137
|
-
@log.debug "Waiting for EBS volume #{
|
94
|
+
@log.debug "Volume #{volume.id} created."
|
95
|
+
@log.debug "Waiting for EBS volume #{volume.id} to be available..."
|
138
96
|
|
139
97
|
# wait for volume to be created
|
140
|
-
wait_for_volume_status(
|
98
|
+
@ec2helper.wait_for_volume_status(:available, volume)
|
141
99
|
|
142
100
|
# get first free device to mount the volume
|
143
101
|
suffix = free_device_suffix
|
144
|
-
|
102
|
+
device_name = "/dev/sd#{suffix}"
|
145
103
|
@log.trace "Got free device suffix: '#{suffix}'"
|
146
|
-
@log.trace "Reading current instance id..."
|
147
104
|
|
148
|
-
|
149
|
-
|
105
|
+
@log.trace "Reading current instance id..."
|
106
|
+
# get_current_instance
|
107
|
+
current_instance = @ec2.instances[@current_instance_id]
|
150
108
|
|
151
|
-
@log.trace "Got: #{
|
109
|
+
@log.trace "Got: #{current_instance.id}"
|
152
110
|
@log.info "Attaching created volume..."
|
153
|
-
|
154
111
|
# attach the volume to current host
|
155
|
-
|
112
|
+
volume.attach_to(current_instance, device_name)
|
156
113
|
|
157
114
|
@log.debug "Waiting for EBS volume to be attached..."
|
158
|
-
|
159
115
|
# wait for volume to be attached
|
160
|
-
wait_for_volume_status(
|
116
|
+
@ec2helper.wait_for_volume_status(:in_use, volume)
|
161
117
|
|
162
118
|
@log.debug "Waiting for the attached EBS volume to be discovered by the OS"
|
163
|
-
|
164
|
-
wait_for_volume_attachment(suffix) # add rescue block for timeout when no suffix can be found then re-raise
|
119
|
+
wait_for_volume_attachment(suffix)
|
165
120
|
|
166
121
|
@log.info "Copying data to EBS volume..."
|
167
122
|
|
@@ -173,167 +128,82 @@ module BoxGrinder
|
|
173
128
|
end
|
174
129
|
|
175
130
|
@log.debug "Detaching EBS volume..."
|
176
|
-
|
177
|
-
@ec2.detach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id)
|
131
|
+
volume.attachments.map(&:delete)
|
178
132
|
|
179
133
|
@log.debug "Waiting for EBS volume to become available..."
|
180
|
-
|
181
|
-
wait_for_volume_status('available', volume_id)
|
134
|
+
@ec2helper.wait_for_volume_status(:available, volume)
|
182
135
|
|
183
136
|
@log.info "Creating snapshot from EBS volume..."
|
137
|
+
snapshot = @ec2.snapshots.create(
|
138
|
+
:volume => volume,
|
139
|
+
:description => ebs_appliance_description)
|
184
140
|
|
185
|
-
|
186
|
-
|
187
|
-
:description => ebs_appliance_description)['snapshotId']
|
188
|
-
|
189
|
-
@log.debug "Waiting for snapshot #{snapshot_id} to be completed..."
|
190
|
-
|
191
|
-
wait_for_snapshot_status('completed', snapshot_id)
|
141
|
+
@log.debug "Waiting for snapshot #{snapshot.id} to be completed..."
|
142
|
+
@ec2helper.wait_for_snapshot_status(:completed, snapshot)
|
192
143
|
|
193
144
|
@log.debug "Deleting temporary EBS volume..."
|
194
|
-
|
195
|
-
@ec2.delete_volume(:volume_id => volume_id)
|
145
|
+
volume.delete
|
196
146
|
|
197
147
|
@log.info "Registering image..."
|
198
|
-
|
199
|
-
|
200
|
-
:block_device_mapping => [{
|
201
|
-
:device_name => '/dev/sda1',
|
202
|
-
:ebs_snapshot_id => snapshot_id,
|
203
|
-
:ebs_delete_on_termination => @plugin_config['delete_on_termination']
|
204
|
-
},
|
205
|
-
{
|
206
|
-
:device_name => '/dev/sdb',
|
207
|
-
:virtual_name => 'ephemeral0'
|
208
|
-
},
|
209
|
-
{
|
210
|
-
:device_name => '/dev/sdc',
|
211
|
-
:virtual_name => 'ephemeral1'
|
212
|
-
},
|
213
|
-
{
|
214
|
-
:device_name => '/dev/sdd',
|
215
|
-
:virtual_name => 'ephemeral2'
|
216
|
-
},
|
217
|
-
{
|
218
|
-
:device_name => '/dev/sde',
|
219
|
-
:virtual_name => 'ephemeral3'
|
220
|
-
}],
|
148
|
+
image = @ec2.images.create(
|
149
|
+
:name => ebs_appliance_name,
|
221
150
|
:root_device_name => ROOT_DEVICE_NAME,
|
151
|
+
:block_device_mappings => { ROOT_DEVICE_NAME => {
|
152
|
+
:snapshot => snapshot,
|
153
|
+
:delete_on_termination => @plugin_config['delete_on_termination']
|
154
|
+
},
|
155
|
+
'/dev/sdb' => 'ephemeral0',
|
156
|
+
'/dev/sdc' => 'ephemeral1',
|
157
|
+
'/dev/sdd' => 'ephemeral2',
|
158
|
+
'/dev/sde' => 'ephemeral3'},
|
222
159
|
:architecture => @appliance_config.hardware.base_arch,
|
223
|
-
:kernel_id =>
|
224
|
-
:
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
@log.info "EBS AMI '#{ebs_appliance_name}' registered: #{image_id} (region: #{@region})"
|
160
|
+
:kernel_id => @ec2_endpoints[@current_region][:kernel][@appliance_config.hardware.base_arch.intern][:aki],
|
161
|
+
:description => ebs_appliance_description)
|
162
|
+
|
163
|
+
@log.info "Waiting for the new EBS AMI to become available"
|
164
|
+
@ec2helper.wait_for_image_state(:available, image)
|
165
|
+
@log.info "EBS AMI '#{image.name}' registered: #{image.id} (region: #{@current_region})"
|
166
|
+
rescue Timeout::Error
|
167
|
+
@log.error "An operation timed out. Manual intervention may be necessary to complete the task."
|
168
|
+
raise
|
233
169
|
end
|
234
170
|
|
235
|
-
def
|
236
|
-
|
237
|
-
@ec2.describe_volumes(:volume_id => volume_id).volumeSet.item.each do |volume|
|
238
|
-
return volume if volume.volumeId == volume_id
|
239
|
-
end
|
240
|
-
rescue AWS::Error, AWS::InvalidVolumeIDNotFound => e# only InvalidVolumeIDNotFound should be returned when no volume found, but is not always doing so at present.
|
241
|
-
@log.trace "Error getting volume info: #{e}"
|
242
|
-
return nil
|
243
|
-
end
|
244
|
-
nil
|
171
|
+
def ami_by_name(name)
|
172
|
+
@ec2helper.ami_by_name(name, @plugin_config['account_number'])
|
245
173
|
end
|
246
174
|
|
247
|
-
|
248
|
-
begin
|
249
|
-
@ec2.describe_snapshots(:snapshot_id => snapshot_id).snapshotSet.item.each do |snapshot|
|
250
|
-
return snapshot if snapshot.snapshotId == snapshot_id
|
251
|
-
end
|
252
|
-
rescue AWS::InvalidSnapshotIDNotFound
|
253
|
-
return nil
|
254
|
-
end
|
255
|
-
nil
|
256
|
-
end
|
175
|
+
alias :already_registered? :ami_by_name
|
257
176
|
|
258
|
-
def
|
259
|
-
|
260
|
-
|
177
|
+
def terminate_instances(instances)
|
178
|
+
instances.map(&:terminate)
|
179
|
+
instances.each do |i|
|
180
|
+
@ec2helper.wait_for_instance_death(i)
|
261
181
|
end
|
262
|
-
nil
|
263
182
|
end
|
264
183
|
|
265
|
-
def
|
266
|
-
#
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
184
|
+
def stomp_ebs(ami)
|
185
|
+
#Find any instances that are running, if they are not stopped then abort.
|
186
|
+
if live = @ec2helper.live_instances(ami)
|
187
|
+
if @plugin_config['terminate_instances']
|
188
|
+
@log.info "Terminating the following instances: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
|
189
|
+
terminate_instances(live)
|
190
|
+
else
|
191
|
+
raise "There are still instances of #{ami.id} running, you should terminate them after " <<
|
192
|
+
"preserving any important data: #{live.collect{|i| "#{i.id} (#{i.status})"}.join(", ")}."
|
272
193
|
end
|
273
194
|
end
|
274
|
-
return instances.uniq unless instances.empty?
|
275
|
-
nil
|
276
|
-
end
|
277
|
-
|
278
|
-
def stomp_ebs(ami_info)
|
279
|
-
|
280
|
-
device = block_device_from_ami(ami_info, ROOT_DEVICE_NAME)
|
281
195
|
|
282
|
-
|
283
|
-
|
284
|
-
volume_id = snapshot_info.volumeId
|
285
|
-
volume_info = get_volume_info(volume_id)
|
286
|
-
|
287
|
-
@log.trace "Volume info for #{volume_id} : #{PP::pp(volume_info,"")}"
|
288
|
-
@log.info "Finding any existing image with the block store attached"
|
289
|
-
|
290
|
-
if instances = get_instances(ami_info.imageId)
|
291
|
-
raise "There are still instances of #{ami_info.imageId} running, you must stop them: #{instances.collect {|i| i.instanceId}.join(",")}"
|
292
|
-
end
|
293
|
-
|
294
|
-
if volume_info #if the physical volume exists
|
295
|
-
unless volume_info.status == 'available'
|
296
|
-
begin
|
297
|
-
@log.info "Forcibly detaching block store #{volume_info.volumeId}"
|
298
|
-
@ec2.detach_volume(:volume_id => volume_info.volumeId, :force => true)
|
299
|
-
rescue AWS::IncorrectState
|
300
|
-
@log.debug "State of the volume has changed, our data must have been stale. This should not be fatal."
|
301
|
-
end
|
302
|
-
end
|
303
|
-
|
304
|
-
@log.debug "Waiting for volume to become detached"
|
305
|
-
wait_for_volume_status('available', volume_info.volumeId)
|
306
|
-
|
307
|
-
begin
|
308
|
-
@log.info "Deleting block store"
|
309
|
-
@ec2.delete_volume(:volume_id => volume_info.volumeId)
|
310
|
-
@log.debug "Waiting for volume deletion to be confirmed"
|
311
|
-
wait_for_volume_status('deleted', volume_info.volumeId)
|
312
|
-
rescue AWS::InvalidVolumeIDNotFound
|
313
|
-
@log.debug "An external entity has probably deleted the volume just before we tried to. This should not be fatal."
|
314
|
-
end
|
315
|
-
end
|
196
|
+
@log.info("Finding the primary snapshot associated with #{ami.id}.")
|
197
|
+
primary_snapshot = @ec2helper.snapshot_by_id(ami.block_device_mappings[ami.root_device_name].snapshot_id)
|
316
198
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
rescue AWS::InvalidAMIIDUnavailable, AWS::InvalidAMIIDNotFound
|
321
|
-
@log.debug "An external entity has already deregistered the AMI just before we tried to. This should not be fatal."
|
322
|
-
end
|
199
|
+
@log.info("De-registering the EBS AMI.")
|
200
|
+
ami.deregister
|
201
|
+
@ec2helper.wait_for_image_death(ami)
|
323
202
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
@ec2.delete_snapshot(:snapshot_id => snapshot_info.snapshotId)
|
328
|
-
rescue AWS::InvalidSnapshotIDNotFound
|
329
|
-
@log.debug "An external entity has probably deleted the snapshot just before we tried to. This should not be fatal."
|
330
|
-
end
|
331
|
-
end
|
332
|
-
else
|
333
|
-
@log.error "Expected device #{ROOT_DEVICE_NAME} was not found on the image."
|
334
|
-
return false
|
203
|
+
if !@plugin_config['preserve_snapshots'] and primary_snapshot
|
204
|
+
@log.info("Deleting the primary snapshot.")
|
205
|
+
primary_snapshot.delete
|
335
206
|
end
|
336
|
-
true
|
337
207
|
end
|
338
208
|
|
339
209
|
def ebs_appliance_name
|
@@ -343,7 +213,7 @@ module BoxGrinder
|
|
343
213
|
|
344
214
|
snapshot = 1
|
345
215
|
|
346
|
-
while already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
|
216
|
+
while @ec2helper.already_registered?("#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}")
|
347
217
|
snapshot += 1
|
348
218
|
end
|
349
219
|
# Reuse the last key (if there was one)
|
@@ -352,80 +222,19 @@ module BoxGrinder
|
|
352
222
|
"#{base_path}-SNAPSHOT-#{snapshot}/#{@appliance_config.hardware.arch}"
|
353
223
|
end
|
354
224
|
|
355
|
-
def ami_info(name)
|
356
|
-
images = @ec2.describe_images(:owner_id => @plugin_config['account_number'].to_s.gsub(/-/,''))
|
357
|
-
return false if images.nil?
|
358
|
-
images = images.imagesSet
|
359
|
-
|
360
|
-
for image in images.item do
|
361
|
-
return image if image.name == name
|
362
|
-
end
|
363
|
-
false
|
364
|
-
end
|
365
|
-
|
366
|
-
def already_registered?(name)
|
367
|
-
info = ami_info(name)
|
368
|
-
return info.imageId if info
|
369
|
-
false
|
370
|
-
end
|
371
|
-
|
372
225
|
def adjust_fstab(guestfs)
|
373
226
|
guestfs.sh("cat /etc/fstab | grep -v '/mnt' | grep -v '/data' | grep -v 'swap' > /etc/fstab.new")
|
374
227
|
guestfs.mv("/etc/fstab.new", "/etc/fstab")
|
375
228
|
end
|
376
229
|
|
377
|
-
def wait_with_timeout(cycle_seconds, timeout_seconds)
|
378
|
-
Timeout::timeout(timeout_seconds) do
|
379
|
-
while not yield
|
380
|
-
sleep cycle_seconds
|
381
|
-
end
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
230
|
def wait_for_volume_attachment(suffix)
|
386
|
-
wait_with_timeout(POLL_FREQ, TIMEOUT){ device_for_suffix(suffix) != nil }
|
387
|
-
end
|
388
|
-
|
389
|
-
def wait_for_snapshot_status(status, snapshot_id)
|
390
|
-
begin
|
391
|
-
progress = -1
|
392
|
-
snapshot = nil
|
393
|
-
wait_with_timeout(POLL_FREQ, TIMEOUT) do
|
394
|
-
snapshot = @ec2.describe_snapshots(:snapshot_id => snapshot_id)['snapshotSet']['item'].first
|
395
|
-
current_progress = snapshot.progress.to_i
|
396
|
-
unless progress == current_progress
|
397
|
-
@log.info "Progress: #{current_progress}%"
|
398
|
-
progress = current_progress
|
399
|
-
end
|
400
|
-
snapshot['status'] == status
|
401
|
-
end
|
402
|
-
rescue Exception
|
403
|
-
snapshot.ownerId='<REDACTED>' #potentially sensitive?
|
404
|
-
@log.debug "Polling of snapshot #{snapshot_id} for status '#{status}' failed: " <<
|
405
|
-
"#{PP::pp(snapshot)}" unless snapshot.nil?
|
406
|
-
raise
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
def wait_for_volume_status(status, volume_id)
|
411
|
-
begin
|
412
|
-
volume=nil
|
413
|
-
wait_with_timeout(POLL_FREQ, TIMEOUT) do
|
414
|
-
volume = @ec2.describe_volumes(:volume_id => volume_id)['volumeSet']['item'].first
|
415
|
-
volume['status'] == status
|
416
|
-
end
|
417
|
-
rescue Exception
|
418
|
-
@log.debug "Polling of volume #{volume_id} for status '#{status}' failed: " <<
|
419
|
-
"#{PP::pp(volume)}" unless volume.nil?
|
420
|
-
raise
|
421
|
-
end
|
231
|
+
@ec2helper.wait_with_timeout(POLL_FREQ, TIMEOUT){ device_for_suffix(suffix) != nil }
|
422
232
|
end
|
423
233
|
|
424
234
|
def device_for_suffix(suffix)
|
425
235
|
return "/dev/sd#{suffix}" if File.exists?("/dev/sd#{suffix}")
|
426
236
|
return "/dev/xvd#{suffix}" if File.exists?("/dev/xvd#{suffix}")
|
427
237
|
nil
|
428
|
-
#raise "Device for suffix '#{suffix}' not found!"
|
429
238
|
end
|
430
239
|
|
431
240
|
def free_device_suffix
|
@@ -437,9 +246,9 @@ module BoxGrinder
|
|
437
246
|
|
438
247
|
def valid_platform?
|
439
248
|
begin
|
440
|
-
region = availability_zone_to_region(
|
441
|
-
return true if
|
442
|
-
@log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{
|
249
|
+
region = EC2Helper::availability_zone_to_region(EC2Helper::current_availability_zone)
|
250
|
+
return true if @ec2_endpoints.has_key? region
|
251
|
+
@log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{@ec2_endpoints.join(", ")}"
|
443
252
|
rescue Net::HTTPServerException => e
|
444
253
|
@log.warn "An error was returned when attempting to retrieve the ec2 hostname: #{e.to_s}"
|
445
254
|
rescue Timeout::Error => t
|
@@ -448,19 +257,6 @@ module BoxGrinder
|
|
448
257
|
false
|
449
258
|
end
|
450
259
|
|
451
|
-
def get_ec2_availability_zone
|
452
|
-
timeout(EC2_HOSTNAME_LOOKUP_TIMEOUT) do
|
453
|
-
req = Net::HTTP::Get.new('/latest/meta-data/placement/availability-zone/')
|
454
|
-
res = Net::HTTP.start('169.254.169.254', 80) {|http| http.request(req)}
|
455
|
-
return res.body if Net::HTTPSuccess
|
456
|
-
res.error!
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
def availability_zone_to_region(availability_zone)
|
461
|
-
availability_zone.scan(/((\w+)-(\w+)-(\d+))/).flatten.first
|
462
|
-
end
|
463
|
-
|
464
260
|
end
|
465
261
|
end
|
466
262
|
|