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.
Files changed (41) hide show
  1. data/CHANGELOG +12 -0
  2. data/Manifest +6 -5
  3. data/README.md +1 -1
  4. data/Rakefile +13 -20
  5. data/boxgrinder-build.gemspec +7 -10
  6. data/lib/boxgrinder-build/helpers/aws-helper.rb +81 -0
  7. data/lib/boxgrinder-build/helpers/ec2-helper.rb +182 -0
  8. data/lib/boxgrinder-build/helpers/guestfs-helper.rb +5 -1
  9. data/lib/boxgrinder-build/helpers/s3-helper.rb +124 -0
  10. data/lib/boxgrinder-build/plugins/delivery/ebs/ebs-plugin.rb +95 -299
  11. data/lib/boxgrinder-build/plugins/delivery/elastichosts/elastichosts-plugin.rb +2 -1
  12. data/lib/boxgrinder-build/plugins/delivery/s3/s3-plugin.rb +67 -133
  13. data/lib/boxgrinder-build/plugins/os/rpm-based/rpm-based-os-plugin.rb +15 -14
  14. data/rubygem-boxgrinder-build.spec +23 -28
  15. data/spec/appliance-spec.rb +1 -0
  16. data/spec/helpers/augeas-helper-spec.rb +1 -0
  17. data/spec/helpers/ec2-helper-spec.rb +260 -0
  18. data/spec/helpers/guestfs-helper-spec.rb +34 -7
  19. data/spec/helpers/image-helper-spec.rb +1 -0
  20. data/spec/helpers/linux-helper-spec.rb +1 -0
  21. data/spec/helpers/package-helper-spec.rb +1 -0
  22. data/spec/helpers/plugin-helper-spec.rb +1 -0
  23. data/spec/helpers/s3-helper-spec.rb +168 -0
  24. data/spec/managers/plugin-manager-spec.rb +1 -0
  25. data/spec/plugins/base-plugin-spec.rb +1 -1
  26. data/spec/plugins/delivery/ebs/ebs-plugin-spec.rb +115 -204
  27. data/spec/plugins/delivery/elastichosts/elastichosts-plugin-spec.rb +5 -4
  28. data/spec/plugins/delivery/local/local-plugin-spec.rb +1 -0
  29. data/spec/plugins/delivery/s3/s3-plugin-spec.rb +143 -134
  30. data/spec/plugins/delivery/sftp/sftp-plugin-spec.rb +1 -0
  31. data/spec/plugins/os/centos/centos-plugin-spec.rb +1 -0
  32. data/spec/plugins/os/fedora/fedora-plugin-spec.rb +1 -0
  33. data/spec/plugins/os/rhel/rhel-plugin-spec.rb +1 -0
  34. data/spec/plugins/os/rpm-based/kickstart-spec.rb +5 -1
  35. data/spec/plugins/os/rpm-based/rpm-based-os-plugin-spec.rb +9 -7
  36. data/spec/plugins/os/rpm-based/rpm-dependency-validator-spec.rb +1 -0
  37. data/spec/plugins/os/sl/sl-plugin-spec.rb +1 -0
  38. data/spec/plugins/platform/ec2/ec2-plugin-spec.rb +1 -0
  39. data/spec/plugins/platform/virtualbox/virtualbox-plugin-spec.rb +1 -0
  40. data/spec/plugins/platform/vmware/vmware-plugin-spec.rb +1 -0
  41. 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 'AWS'
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
- raise PluginValidationError, "You are trying to run this plugin on invalid platform. You can run the EBS delivery plugin only on EC2." unless valid_platform?
36
+ @ec2_endpoints = EC2Helper::endpoints
83
37
 
84
- @current_availability_zone = get_ec2_availability_zone; @log.trace @current_availability_zone
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
- ami_info = ami_info(ebs_appliance_name)
116
-
117
- if ami_info and @plugin_config['overwrite']
118
- @log.info "Overwrite is enabled. Stomping existing assets"
119
- stomp_ebs(ami_info)
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 fractions as per https://issues.jboss.org/browse/BGBUILD-224
132
- volume_id = @ec2.create_volume(:size => size.ceil.to_s, :availability_zone => @plugin_config['availability_zone'])['volumeId']
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 #{volume_id} created."
137
- @log.debug "Waiting for EBS volume #{volume_id} to be available..."
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('available', volume_id)
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
- # read current instance id
149
- instance_id = open('http://169.254.169.254/latest/meta-data/instance-id').string
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: #{instance_id}"
109
+ @log.trace "Got: #{current_instance.id}"
152
110
  @log.info "Attaching created volume..."
153
-
154
111
  # attach the volume to current host
155
- @ec2.attach_volume(:device => "/dev/sd#{suffix}", :volume_id => volume_id, :instance_id => instance_id)
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('in-use', volume_id)
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
- snapshot_id = @ec2.create_snapshot(
186
- :volume_id => volume_id,
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
- image_id = @ec2.register_image(
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 => KERNELS[@region][:kernel][@appliance_config.hardware.base_arch][:aki],
224
- :name => ebs_appliance_name,
225
- :description => ebs_appliance_description)['imageId']
226
-
227
- rescue Timeout::Error
228
- @log.error "Timed out. Manual intervention may be necessary to complete the task."
229
- raise
230
- end
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 get_volume_info(volume_id)
236
- begin
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
- def snapshot_info(snapshot_id)
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 block_device_from_ami(ami_info, device_name)
259
- ami_info.blockDeviceMapping.item.each do |device|
260
- return device if device.deviceName == device_name
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 get_instances(ami_id)
266
- #EC2 Gem has yet to be updated with new filters, once the patches have been pulled then :image_id filter will be picked up
267
- instances_info = @ec2.describe_instances(:image_id => ami_id).reservationSet
268
- instances=[]
269
- instances_info["item"].each do
270
- |item| item["instancesSet"]["item"].each do |i|
271
- instances.push i if i.imageId == ami_id #TODO remove check after gem update
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
- if device #if there is the anticipated device on the image
283
- snapshot_info = snapshot_info(device.ebs.snapshotId)
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
- begin
318
- @log.debug "Deregistering AMI"
319
- @ec2.deregister_image(:image_id => ami_info.imageId)
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
- if !@plugin_config['preserve_snapshots'] and snapshot_info #if the snapshot exists
325
- begin
326
- @log.debug "Deleting snapshot #{snapshot_info.snapshotId}"
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(get_ec2_availability_zone)
441
- return true if KERNELS.has_key? region
442
- @log.warn "You may be using an ec2 region that BoxGrinder Build is not aware of: #{region}, BoxGrinder Build knows of: #{KERNELS.join(", ")}"
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