boxgrinder-build 0.9.3 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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